diff --git a/js/xpconnect/loader/XPCOMUtils.sys.mjs b/js/xpconnect/loader/XPCOMUtils.sys.mjs index 403b17e2bef8..e860c2490f9f 100644 --- a/js/xpconnect/loader/XPCOMUtils.sys.mjs +++ b/js/xpconnect/loader/XPCOMUtils.sys.mjs @@ -381,199 +381,12 @@ export var XPCOMUtils = { writable: false, }); }, - - /** - * Defines a proxy which acts as a lazy object getter that can be passed - * around as a reference, and will only be evaluated when something in - * that object gets accessed. - * - * The evaluation can be triggered by a function call, by getting or - * setting a property, calling this as a constructor, or enumerating - * the properties of this object (e.g. during an iteration). - * - * Please note that, even after evaluated, the object given to you - * remains being the proxy object (which forwards everything to the - * real object). This is important to correctly use these objects - * in pairs of add+remove listeners, for example. - * If your use case requires access to the direct object, you can - * get it through the untrap callback. - * - * @param aObject - * The object to define the lazy getter on. - * - * You can pass null to aObject if you just want to get this - * proxy through the return value. - * - * @param aName - * The name of the getter to define on aObject. - * - * @param aInitFuncOrResource - * A function or a module that defines what this object actually - * should be when it gets evaluated. This will only ever be called once. - * - * Short-hand: If you pass a string to this parameter, it will be treated - * as the URI of a module to be imported, and aName will be used as - * the symbol to retrieve from the module. - * - * @param aStubProperties - * In this parameter, you can provide an object which contains - * properties from the original object that, when accessed, will still - * prevent the entire object from being evaluated. - * - * These can be copies or simplified versions of the original properties. - * - * One example is to provide an alternative QueryInterface implementation - * to avoid the entire object from being evaluated when it's added as an - * observer (as addObserver calls object.QueryInterface(Ci.nsIObserver)). - * - * Once the object has been evaluated, the properties from the real - * object will be used instead of the ones provided here. - * - * @param aUntrapCallback - * A function that gets called once when the object has just been evaluated. - * You can use this to do some work (e.g. setting properties) that you need - * to do on this object but that can wait until it gets evaluated. - * - * Another use case for this is to use during code development to log when - * this object gets evaluated, to make sure you're not accidentally triggering - * it earlier than expected. - */ - defineLazyProxy( - aObject, - aName, - aInitFuncOrResource, - aStubProperties, - aUntrapCallback - ) { - let initFunc = aInitFuncOrResource; - - if (typeof aInitFuncOrResource == "string") { - initFunc = () => ChromeUtils.import(aInitFuncOrResource)[aName]; - } - - let handler = new LazyProxyHandler( - aName, - initFunc, - aStubProperties, - aUntrapCallback - ); - - /* - * We cannot simply create a lazy getter for the underlying - * object and pass it as the target of the proxy, because - * just passing it in `new Proxy` means it would get - * evaluated. Becase of this, a full handler needs to be - * implemented (the LazyProxyHandler). - * - * So, an empty object is used as the target, and the handler - * replaces it on every call with the real object. - */ - let proxy = new Proxy({}, handler); - - if (aObject) { - Object.defineProperty(aObject, aName, { - value: proxy, - enumerable: true, - writable: true, - }); - } - - return proxy; - }, }; XPCOMUtils.defineLazyGetter(XPCOMUtils, "_scriptloader", () => { return Services.scriptloader; }); -/** - * LazyProxyHandler - * This class implements the handler used - * in the proxy from defineLazyProxy. - * - * This handler forwards all calls to an underlying object, - * stored as `this.realObject`, which is obtained as the returned - * value from aInitFunc, which will be called on the first time - * time that it needs to be used (with an exception in the get() trap - * for the properties provided in the `aStubProperties` parameter). - */ - -class LazyProxyHandler { - constructor(aName, aInitFunc, aStubProperties, aUntrapCallback) { - this.pending = true; - this.name = aName; - this.initFuncOrResource = aInitFunc; - this.stubProperties = aStubProperties; - this.untrapCallback = aUntrapCallback; - } - - getObject() { - if (this.pending) { - this.realObject = this.initFuncOrResource.call(null); - - if (this.untrapCallback) { - this.untrapCallback.call(null, this.realObject); - this.untrapCallback = null; - } - - this.pending = false; - this.stubProperties = null; - } - return this.realObject; - } - - getPrototypeOf(target) { - return Reflect.getPrototypeOf(this.getObject()); - } - - setPrototypeOf(target, prototype) { - return Reflect.setPrototypeOf(this.getObject(), prototype); - } - - isExtensible(target) { - return Reflect.isExtensible(this.getObject()); - } - - preventExtensions(target) { - return Reflect.preventExtensions(this.getObject()); - } - - getOwnPropertyDescriptor(target, prop) { - return Reflect.getOwnPropertyDescriptor(this.getObject(), prop); - } - - defineProperty(target, prop, descriptor) { - return Reflect.defineProperty(this.getObject(), prop, descriptor); - } - - has(target, prop) { - return Reflect.has(this.getObject(), prop); - } - - get(target, prop, receiver) { - if ( - this.pending && - this.stubProperties && - Object.prototype.hasOwnProperty.call(this.stubProperties, prop) - ) { - return this.stubProperties[prop]; - } - return Reflect.get(this.getObject(), prop, receiver); - } - - set(target, prop, value, receiver) { - return Reflect.set(this.getObject(), prop, value, receiver); - } - - deleteProperty(target, prop) { - return Reflect.deleteProperty(this.getObject(), prop); - } - - ownKeys(target) { - return Reflect.ownKeys(this.getObject()); - } -} - var XPCU_lazyPreferenceObserverQI = ChromeUtils.generateQI([ "nsIObserver", "nsISupportsWeakReference", diff --git a/js/xpconnect/tests/unit/test_lazyproxy.js b/js/xpconnect/tests/unit/test_lazyproxy.js deleted file mode 100644 index 2cf90b339d61..000000000000 --- a/js/xpconnect/tests/unit/test_lazyproxy.js +++ /dev/null @@ -1,113 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * This file tests the method defineLazyProxy from XPCOMUtils.sys.mjs. - */ - -const {XPCOMUtils} = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs"); - -add_task(function test_lazy_proxy() { - let tmp = {}; - let realObject = { - "prop1": "value1", - "prop2": "value2", - }; - - let evaluated = false; - let untrapCalled = false; - - let lazyProxy = XPCOMUtils.defineLazyProxy( - tmp, - "myLazyProxy", - - // Initiliazer function - function init() { - evaluated = true; - return realObject; - }, - - // Stub properties - { - "prop1": "stub" - }, - - // Untrap callback - function untrapCallback(obj) { - Assert.equal(obj, realObject, "The underlying object can be obtained in the untrap callback"); - untrapCalled = true; - } - ); - - // Check that the proxy returned and the one - // defined in tmp are the same. - // - // Note: Assert.strictEqual can't be used here - // because it wants to stringify the two objects - // compared, which defeats the lazy proxy. - Assert.ok(lazyProxy === tmp.myLazyProxy, "Return value and object defined are the same"); - - Assert.ok(Cu.isProxy(lazyProxy), "Returned value is in fact a proxy"); - - // Check that just using the proxy above didn't - // trigger the lazy getter evaluation. - Assert.ok(!evaluated, "The lazy proxy hasn't been evaluated yet"); - Assert.ok(!untrapCalled, "The untrap callback hasn't been called yet"); - - // Accessing a stubbed property returns the stub - // value and doesn't trigger evaluation. - Assert.equal(lazyProxy.prop1, "stub", "Accessing a stubbed property returns the stubbed value"); - - Assert.ok(!evaluated, "The access to the stubbed property above didn't evaluate the lazy proxy"); - Assert.ok(!untrapCalled, "The untrap callback hasn't been called yet"); - - // Now the access to another property will trigger - // the evaluation, as expected. - Assert.equal(lazyProxy.prop2, "value2", "Property access is correctly forwarded to the underlying object"); - - Assert.ok(evaluated, "Accessing a non-stubbed property triggered the proxy evaluation"); - Assert.ok(untrapCalled, "The untrap callback was called"); - - // The value of prop1 is now the real value and not the stub value. - Assert.equal(lazyProxy.prop1, "value1", "The value of prop1 is now the real value and not the stub one"); -}); - -add_task(function test_module_version() { - // Test that passing a string instead of an initialization function - // makes this behave like a lazy module getter. - const TEST_FILE_URI = "resource://test/TestFile.jsm"; - let underlyingObject; - - Cu.unload(TEST_FILE_URI); - - let lazyProxy = XPCOMUtils.defineLazyProxy( - null, - "TestFile", - TEST_FILE_URI, - null, /* no stubs */ - function untrapCallback(object) { - underlyingObject = object; - } - ); - - Assert.ok(!Cu.isModuleLoaded(TEST_FILE_URI), "The NetUtil module was not loaded by the lazy proxy definition"); - - // Access the object, which will evaluate the proxy. - lazyProxy.foo = "bar"; - - // Module was loaded. - Assert.ok(Cu.isModuleLoaded(TEST_FILE_URI), "The NetUtil module was loaded"); - - let { TestFile } = ChromeUtils.import(TEST_FILE_URI, {}); - - // Avoids a gigantic stringification in the logs. - Assert.ok(TestFile === underlyingObject, "The module loaded is the same as the one directly obtained by ChromeUtils.import"); - - // Proxy correctly passed the setter to the underlying object. - Assert.equal(TestFile.foo, "bar", "Proxy correctly passed the setter to the underlying object"); - - delete lazyProxy.foo; - - // Proxy correctly passed the delete operation to the underlying object. - Assert.ok(!TestFile.hasOwnProperty("foo"), "Proxy correctly passed the delete operation to the underlying object"); -}); diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 23c1270028ec..616fe6330b83 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -141,7 +141,6 @@ head = head_ongc.js [test_recursive_import.js] [test_xpcomutils.js] [test_unload.js] -[test_lazyproxy.js] [test_attributes.js] [test_params.js] [test_tearoffs.js] diff --git a/toolkit/actors/FindBarChild.sys.mjs b/toolkit/actors/FindBarChild.sys.mjs index a82615a2531e..645456ad1f69 100644 --- a/toolkit/actors/FindBarChild.sys.mjs +++ b/toolkit/actors/FindBarChild.sys.mjs @@ -17,23 +17,36 @@ export class FindBarChild extends JSWindowActorChild { this._findKey = null; - XPCOMUtils.defineLazyProxy( - this, - "FindBarContent", - () => { - const { FindBarContent } = ChromeUtils.importESModule( - "resource://gre/modules/FindBarContent.sys.mjs" - ); - return new FindBarContent(this); - }, - { inQuickFind: false, inPassThrough: false } - ); + this.inQuickFind = false; + this.inPassThrough = false; + + ChromeUtils.defineLazyGetter(this, "FindBarContent", () => { + const { FindBarContent } = ChromeUtils.importESModule( + "resource://gre/modules/FindBarContent.sys.mjs" + ); + + let findBarContent = new FindBarContent(this); + + Object.defineProperties(this, { + inQuickFind: { + get() { + return findBarContent.inQuickFind; + }, + }, + inPassThrough: { + get() { + return findBarContent.inPassThrough; + }, + }, + }); + + return findBarContent; + }); } receiveMessage(msg) { if (msg.name == "Findbar:UpdateState") { - let { FindBarContent } = this; - FindBarContent.updateState(msg.data); + this.FindBarContent.updateState(msg.data); } } @@ -65,10 +78,8 @@ export class FindBarChild extends JSWindowActorChild { } onKeypress(event) { - let { FindBarContent } = this; - - if (!FindBarContent.inPassThrough && this.eventMatchesFindShortcut(event)) { - return FindBarContent.start(event); + if (!this.inPassThrough && this.eventMatchesFindShortcut(event)) { + return this.FindBarContent.start(event); } // disable FAYT in about:blank to prevent FAYT opening unexpectedly. @@ -88,17 +99,17 @@ export class FindBarChild extends JSWindowActorChild { return null; } - if (FindBarContent.inPassThrough || FindBarContent.inQuickFind) { - return FindBarContent.onKeypress(event); + if (this.inPassThrough || this.inQuickFind) { + return this.FindBarContent.onKeypress(event); } if (event.charCode && this.shouldFastFind(event.target)) { let key = String.fromCharCode(event.charCode); if ((key == "/" || key == "'") && FindBarChild.manualFAYT) { - return FindBarContent.startQuickFind(event); + return this.FindBarContent.startQuickFind(event); } if (key != " " && FindBarChild.findAsYouType) { - return FindBarContent.startQuickFind(event, true); + return this.FindBarContent.startQuickFind(event, true); } } return null; diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js index aa3d4258ae8e..97bfaaace227 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js @@ -22,7 +22,6 @@ const callExpressionDefinitions = [ /^ChromeUtils\.defineLazyGetter\((?:globalThis|this), "(\w+)"/, /^ChromeUtils\.defineModuleGetter\((?:globalThis|this), "(\w+)"/, /^XPCOMUtils\.defineLazyPreferenceGetter\((?:globalThis|this), "(\w+)"/, - /^XPCOMUtils\.defineLazyProxy\((?:globalThis|this), "(\w+)"/, /^XPCOMUtils\.defineLazyScriptGetter\((?:globalThis|this), "(\w+)"/, /^XPCOMUtils\.defineLazyServiceGetter\((?:globalThis|this), "(\w+)"/, /^XPCOMUtils\.defineConstant\((?:globalThis|this), "(\w+)"/, diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-lazy-imports-into-globals.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-lazy-imports-into-globals.js index 433db12e0854..278ce662d1ac 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-lazy-imports-into-globals.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-lazy-imports-into-globals.js @@ -17,7 +17,6 @@ const callExpressionDefinitions = [ /^ChromeUtils\.defineLazyGetter\((?:globalThis|window), "(\w+)"/, /^ChromeUtils\.defineModuleGetter\((?:globalThis|window), "(\w+)"/, /^XPCOMUtils\.defineLazyPreferenceGetter\((?:globalThis|window), "(\w+)"/, - /^XPCOMUtils\.defineLazyProxy\((?:globalThis|window), "(\w+)"/, /^XPCOMUtils\.defineLazyScriptGetter\((?:globalThis|window), "(\w+)"/, /^XPCOMUtils\.defineLazyServiceGetter\((?:globalThis|window), "(\w+)"/, /^XPCOMUtils\.defineConstant\((?:globalThis|window), "(\w+)"/, diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js index 52ad4ce7d598..a0da8e33ab82 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js @@ -30,7 +30,6 @@ const callExpressionDefinitions = [ /^ChromeUtils\.defineLazyGetter\(lazy, "(\w+)"/, /^ChromeUtils\.defineModuleGetter\(lazy, "(\w+)"/, /^XPCOMUtils\.defineLazyPreferenceGetter\(lazy, "(\w+)"/, - /^XPCOMUtils\.defineLazyProxy\(lazy, "(\w+)"/, /^XPCOMUtils\.defineLazyScriptGetter\(lazy, "(\w+)"/, /^XPCOMUtils\.defineLazyServiceGetter\(lazy, "(\w+)"/, /^XPCOMUtils\.defineConstant\(lazy, "(\w+)"/,