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;
}
// 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.
// 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<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...
// 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(
"@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");
}
}
});