diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 594b186d529c..d469d4c48ac7 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -586,6 +586,29 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self, return NS_OK; } + // If we're asked to QI to nsISimpleEnumerator and the wrapped object does not have a + // QueryInterface method, assume it is a JS iterator, and wrap it into an equivalent + // nsISimpleEnumerator. + if (aIID.Equals(NS_GET_IID(nsISimpleEnumerator))) { + bool found; + XPCJSContext* xpccx = ccx.GetContext(); + if (JS_HasPropertyById(aes.cx(), obj, + xpccx->GetStringID(xpccx->IDX_QUERY_INTERFACE), + &found) && !found) { + nsresult rv; + nsCOMPtr jsEnum; + if (!XPCConvert::JSObject2NativeInterface(aes.cx(), + getter_AddRefs(jsEnum), obj, + &NS_GET_IID(nsIJSEnumerator), + nullptr, &rv)) { + return rv; + } + nsCOMPtr res = new XPCWrappedJSIterator(jsEnum); + res.forget(aInstancePtr); + return NS_OK; + } + } + // Checks for any existing wrapper explicitly constructed for this iid. // This includes the current 'self' wrapper. This also deals with the // nsISupports case (for which it returns mRoot). @@ -611,29 +634,6 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self, return rv; } - // If we're asked to QI to nsISimpleEnumerator and the wrapped object does not have a - // QueryInterface method, assume it is a JS iterator, and wrap it into an equivalent - // nsISimpleEnumerator. - if (aIID.Equals(NS_GET_IID(nsISimpleEnumerator))) { - bool found; - XPCJSContext* xpccx = ccx.GetContext(); - if (JS_HasPropertyById(aes.cx(), obj, - xpccx->GetStringID(xpccx->IDX_QUERY_INTERFACE), - &found) && !found) { - nsresult rv; - nsCOMPtr jsEnum; - if (!XPCConvert::JSObject2NativeInterface(aes.cx(), - getter_AddRefs(jsEnum), obj, - &NS_GET_IID(nsIJSEnumerator), - nullptr, &rv)) { - return rv; - } - nsCOMPtr res = new XPCWrappedJSIterator(jsEnum); - res.forget(aInstancePtr); - return NS_OK; - } - } - // else we do the more expensive stuff... // check if the JSObject claims to implement this interface diff --git a/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js b/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js index 55adfffdf1fb..643acf504545 100644 --- a/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js +++ b/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js @@ -9,12 +9,24 @@ const Variant = Components.Constructor("@mozilla.org/variant;1", const SupportsInterfacePointer = Components.Constructor( "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer"); -function wrapEnumerator(iter) { +function wrapEnumerator1(iter) { var ip = SupportsInterfacePointer(); ip.data = iter; return ip.data.QueryInterface(Ci.nsISimpleEnumerator); } +function wrapEnumerator2(iter) { + var ip = SupportsInterfacePointer(); + ip.data = { + QueryInterface: ChromeUtils.generateQI([Ci.nsIFilePicker]), + get files() { + return iter; + }, + }; + return ip.data.QueryInterface(Ci.nsIFilePicker).files; +} + + function enumToArray(iter) { let result = []; while (iter.hasMoreElements()) { @@ -26,32 +38,34 @@ function enumToArray(iter) { add_task(async function test_wrapped_js_enumerator() { let array = [1, 2, 3, 4]; - // Test a plain JS iterator. This should automatically be wrapped into - // an equivalent nsISimpleEnumerator. - { - let iter = wrapEnumerator(array.values()); - let result = enumToArray(iter); + for (let wrapEnumerator of [wrapEnumerator1, wrapEnumerator2]) { + // Test a plain JS iterator. This should automatically be wrapped into + // an equivalent nsISimpleEnumerator. + { + let iter = wrapEnumerator(array.values()); + let result = enumToArray(iter); - deepEqual(result, array, "Got correct result"); - } + deepEqual(result, array, "Got correct result"); + } - // Test an object with a QueryInterface method, which implements - // nsISimpleEnumerator. This should be wrapped and used directly. - { - let obj = { - QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]), - _idx: 0, - hasMoreElements() { - return this._idx < array.length; - }, - getNext() { - return Variant(array[this._idx++]); - }, - }; + // Test an object with a QueryInterface method, which implements + // nsISimpleEnumerator. This should be wrapped and used directly. + { + let obj = { + QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]), + _idx: 0, + hasMoreElements() { + return this._idx < array.length; + }, + getNext() { + return Variant(array[this._idx++]); + }, + }; - let iter = wrapEnumerator(obj); - let result = enumToArray(iter); + let iter = wrapEnumerator(obj); + let result = enumToArray(iter); - deepEqual(result, array, "Got correct result"); + deepEqual(result, array, "Got correct result"); + } } });