Bug 1489532: Check for nsISimpleEnumerator wrapper before searching existing tear-offs. r=mccr8

When an XPC wrapped JS object returns an object from a property or function
which is expected to return a given interface, we create a wrapper for that
interface regardless of whether the object has a QueryInterface method which
claims to support it. For the nsISimpleEnumerator enumerator case, the check
for that existing wrapper comes before the check for the nsISimpleEnumerator
special casing, which breaks automatic conversion in those cases.

This patch moves that check just before the check for the existing wrapper,
and just after the nsIPropertyBag special casing, which has similar semantics.

Differential Revision: https://phabricator.services.mozilla.com/D5335

--HG--
extra : rebase_source : 2201063455a2434cfa0c3c470f668fefa8528f4b
This commit is contained in:
Kris Maglione 2018-09-07 15:17:56 -07:00
Родитель 525e73f352
Коммит a4a6e3ecd9
2 изменённых файлов: 61 добавлений и 47 удалений

Просмотреть файл

@ -586,6 +586,29 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
return NS_OK; 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<nsIJSEnumerator> jsEnum;
if (!XPCConvert::JSObject2NativeInterface(aes.cx(),
getter_AddRefs(jsEnum), obj,
&NS_GET_IID(nsIJSEnumerator),
nullptr, &rv)) {
return rv;
}
nsCOMPtr<nsISimpleEnumerator> res = new XPCWrappedJSIterator(jsEnum);
res.forget(aInstancePtr);
return NS_OK;
}
}
// Checks for any existing wrapper explicitly constructed for this iid. // Checks for any existing wrapper explicitly constructed for this iid.
// This includes the current 'self' wrapper. This also deals with the // This includes the current 'self' wrapper. This also deals with the
// nsISupports case (for which it returns mRoot). // nsISupports case (for which it returns mRoot).
@ -611,29 +634,6 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
return rv; 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<nsIJSEnumerator> jsEnum;
if (!XPCConvert::JSObject2NativeInterface(aes.cx(),
getter_AddRefs(jsEnum), obj,
&NS_GET_IID(nsIJSEnumerator),
nullptr, &rv)) {
return rv;
}
nsCOMPtr<nsISimpleEnumerator> res = new XPCWrappedJSIterator(jsEnum);
res.forget(aInstancePtr);
return NS_OK;
}
}
// else we do the more expensive stuff... // else we do the more expensive stuff...
// check if the JSObject claims to implement this interface // check if the JSObject claims to implement this interface

Просмотреть файл

@ -9,12 +9,24 @@ const Variant = Components.Constructor("@mozilla.org/variant;1",
const SupportsInterfacePointer = Components.Constructor( const SupportsInterfacePointer = Components.Constructor(
"@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer"); "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer");
function wrapEnumerator(iter) { function wrapEnumerator1(iter) {
var ip = SupportsInterfacePointer(); var ip = SupportsInterfacePointer();
ip.data = iter; ip.data = iter;
return ip.data.QueryInterface(Ci.nsISimpleEnumerator); 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) { function enumToArray(iter) {
let result = []; let result = [];
while (iter.hasMoreElements()) { while (iter.hasMoreElements()) {
@ -26,32 +38,34 @@ function enumToArray(iter) {
add_task(async function test_wrapped_js_enumerator() { add_task(async function test_wrapped_js_enumerator() {
let array = [1, 2, 3, 4]; let array = [1, 2, 3, 4];
// Test a plain JS iterator. This should automatically be wrapped into for (let wrapEnumerator of [wrapEnumerator1, wrapEnumerator2]) {
// an equivalent nsISimpleEnumerator. // Test a plain JS iterator. This should automatically be wrapped into
{ // an equivalent nsISimpleEnumerator.
let iter = wrapEnumerator(array.values()); {
let result = enumToArray(iter); 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 // Test an object with a QueryInterface method, which implements
// nsISimpleEnumerator. This should be wrapped and used directly. // nsISimpleEnumerator. This should be wrapped and used directly.
{ {
let obj = { let obj = {
QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]), QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]),
_idx: 0, _idx: 0,
hasMoreElements() { hasMoreElements() {
return this._idx < array.length; return this._idx < array.length;
}, },
getNext() { getNext() {
return Variant(array[this._idx++]); return Variant(array[this._idx++]);
}, },
}; };
let iter = wrapEnumerator(obj); let iter = wrapEnumerator(obj);
let result = enumToArray(iter); let result = enumToArray(iter);
deepEqual(result, array, "Got correct result"); deepEqual(result, array, "Got correct result");
}
} }
}); });