Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This is needed for the mshtml builtin object behavior tests later to pass correctly (the `f(document, ["style"]);` test). It makes no sense to ignore it anyway.
dlls/jscript/dispex.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index e5d711a..c7e4ba9 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2143,6 +2143,8 @@ HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, W if(args != buf) heap_free(args);
+ if(FAILED(hres)) + return hres; if(!r) return S_OK;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Tests for this are added later in the series. The reason we implement it now is because it is required for the *existing* tests to pass once proxies are implemented. Some of the existing mshtml tests have checks of the form:
ok(e === something, "e = " + e);
Where `e` is an element. Currently this retrieves its value prop, but once proxies are implemented for modes >= IE9, it will try to obtain its toString method when concatenating (this is the behavior for JS objects, so it's correct), making the tests throw an exception, even when otherwise passing, because of them returning E_NOTIMPL.
Implementing it now resolves this issue. Implementation of the other missing toString methods will follow after this series.
dlls/mshtml/htmlelem.c | 45 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index d9554f4..4d11d67 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -2111,9 +2111,50 @@ static HRESULT WINAPI HTMLElement_get_ondragstart(IHTMLElement *iface, VARIANT *
static HRESULT WINAPI HTMLElement_toString(IHTMLElement *iface, BSTR *String) { + static const WCHAR prefix[12] = L"[object HTML"; + static const WCHAR suffix[] = L"Element]"; + HTMLElement *This = impl_from_IHTMLElement(iface); - FIXME("(%p)->(%p)\n", This, String); - return E_NOTIMPL; + const PRUnichar *str; + nsAString tag_str; + nsresult nsres; + WCHAR *tmp, *p; + HRESULT hres; + + TRACE("(%p)->(%p)\n", This, String); + + if(!This->dom_element) { + *String = SysAllocString(L"[object HTMLCommentElement]"); + return *String ? S_OK : E_OUTOFMEMORY; + } + + nsAString_Init(&tag_str, NULL); + nsres = nsIDOMElement_GetTagName(This->dom_element, &tag_str); + if(NS_FAILED(nsres)) { + nsAString_Finish(&tag_str); + return map_nsresult(nsres); + } + nsAString_GetData(&tag_str, &str); + + tmp = heap_alloc(sizeof(prefix) + sizeof(suffix) + wcslen(str) * sizeof(WCHAR)); + if(tmp) { + memcpy(tmp, prefix, sizeof(prefix)); + p = tmp + ARRAY_SIZE(prefix); + if(*str) { + *p++ = *str++; /* copy first uppercase char */ + while(*str) + *p++ = tolower(*str++); + } + memcpy(p, suffix, sizeof(suffix)); + + *String = SysAllocString(tmp); + hres = *String ? S_OK : E_OUTOFMEMORY; + heap_free(tmp); + }else + hres = E_OUTOFMEMORY; + + nsAString_Finish(&tag_str); + return hres; }
static HRESULT WINAPI HTMLElement_put_onbeforeupdate(IHTMLElement *iface, VARIANT v)
Hi Gabriel,
On 9/20/21 4:46 PM, Gabriel Ivăncescu wrote:
memcpy(tmp, prefix, sizeof(prefix));
p = tmp + ARRAY_SIZE(prefix);
if(*str) {
*p++ = *str++; /* copy first uppercase char */
while(*str)
*p++ = tolower(*str++);
}
memcpy(p, suffix, sizeof(suffix));
This is very fragile, it would use "HTMLIframeElement" for example. We could probably store a name of the object in dispex_static_data_t.
Thanks,
Jacek
On 20/09/2021 23:36, Jacek Caban wrote:
Hi Gabriel,
On 9/20/21 4:46 PM, Gabriel Ivăncescu wrote:
+ memcpy(tmp, prefix, sizeof(prefix)); + p = tmp + ARRAY_SIZE(prefix); + if(*str) { + *p++ = *str++; /* copy first uppercase char */ + while(*str) + *p++ = tolower(*str++); + } + memcpy(p, suffix, sizeof(suffix));
This is very fragile, it would use "HTMLIframeElement" for example. We could probably store a name of the object in dispex_static_data_t.
Thanks,
Jacek
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there. For example the "div" tag is not.
What do you think about using get_tag_desc instead (in toString), and extending that table to handle all supported tags? (of course, for "generic" tags, I'll use a NULL constructor).
Unless there's a better way to retrieve its name I'm not aware of? I don't think it's needed to store it in dispex_static_data_t if we can just look it up on demand though.
Thanks, Gabriel
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote:
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
On 21/09/2021 16:31, Jacek Caban wrote:
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote:
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
Ok, I tested all the tags from:
https://www.w3schools.com/TAGS/default.ASP
It looks like there's way too many tags to properly implement each as separate object just for this. Rather, since Gecko handles them, how about something like the attached diff?
It's also easily extended if needed.
Ok I messed up the case where if(!str), but the idea stands (I tested it now).
On 9/21/21 5:50 PM, Gabriel Ivăncescu wrote:
On 21/09/2021 16:31, Jacek Caban wrote:
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote:
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
Ok, I tested all the tags from:
https://www.w3schools.com/TAGS/default.ASP
It looks like there's way too many tags to properly implement each as separate object just for this.
It's not just for this, objects that return something different class names should eventually expose different sets of interfaces (like IHTMLDivElement in case of div element). It should be easy enough to distinguish between HTMLElement and HTMLUnknownElement using the existing logic. While div would also be nice to have, as long as the logic is right, it's not too bad to return slightly wrong strings objects that we don't support yet.
Jacek
On 22/09/2021 15:55, Jacek Caban wrote:
On 9/21/21 5:50 PM, Gabriel Ivăncescu wrote:
On 21/09/2021 16:31, Jacek Caban wrote:
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote:
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
Ok, I tested all the tags from:
https://www.w3schools.com/TAGS/default.ASP
It looks like there's way too many tags to properly implement each as separate object just for this.
It's not just for this, objects that return something different class names should eventually expose different sets of interfaces (like IHTMLDivElement in case of div element). It should be easy enough to distinguish between HTMLElement and HTMLUnknownElement using the existing logic. While div would also be nice to have, as long as the logic is right, it's not too bad to return slightly wrong strings objects that we don't support yet.
Jacek
Wait I'm confused a bit here. Isn't the patch I attached going in the right direction? If not, why? It basically just adds a string name for toString for each tag.
Whether the tag itself is implemented as a separate object (like IHTMLDivElement you mentioned) or not doesn't really matter here, because even when it will be at some later point, it should still obtain the name from this table, just like we do for the constructor.
I guess I'm a bit confused as to what exactly you're referring I should do now to fix this.
Currently, all tags use the same toString implementation, even if they are already separate objects, so this would implement it for all of them in one spot without duplicating code, whether they have a constructor or not.
On 9/22/21 4:15 PM, Gabriel Ivăncescu wrote:
On 22/09/2021 15:55, Jacek Caban wrote:
On 9/21/21 5:50 PM, Gabriel Ivăncescu wrote:
On 21/09/2021 16:31, Jacek Caban wrote:
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote:
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
Ok, I tested all the tags from:
https://www.w3schools.com/TAGS/default.ASP
It looks like there's way too many tags to properly implement each as separate object just for this.
It's not just for this, objects that return something different class names should eventually expose different sets of interfaces (like IHTMLDivElement in case of div element). It should be easy enough to distinguish between HTMLElement and HTMLUnknownElement using the existing logic. While div would also be nice to have, as long as the logic is right, it's not too bad to return slightly wrong strings objects that we don't support yet.
Jacek
Wait I'm confused a bit here. Isn't the patch I attached going in the right direction? If not, why? It basically just adds a string name for toString for each tag.
Whether the tag itself is implemented as a separate object (like IHTMLDivElement you mentioned) or not doesn't really matter here, because even when it will be at some later point, it should still obtain the name from this table, just like we do for the constructor.
I guess I'm a bit confused as to what exactly you're referring I should do now to fix this.
Currently, all tags use the same toString implementation, even if they are already separate objects, so this would implement it for all of them in one spot without duplicating code, whether they have a constructor or not.
I'm not suggesting separate implementations. In fact, I think we could share the implementation with more objects than just HTML elements. toString is supposed to use a type name and we have a mechanism to store type-specific things, why don't you want to use that?
Jacek
On 22/09/2021 17:36, Jacek Caban wrote:
On 9/22/21 4:15 PM, Gabriel Ivăncescu wrote:
On 22/09/2021 15:55, Jacek Caban wrote:
On 9/21/21 5:50 PM, Gabriel Ivăncescu wrote:
On 21/09/2021 16:31, Jacek Caban wrote:
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote:
But where would we even get the name from? In HTMLElement_Create, get_tag_desc is used for specific tags, which is similar, but the "fallback" goes to HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
Ok, I tested all the tags from:
https://www.w3schools.com/TAGS/default.ASP
It looks like there's way too many tags to properly implement each as separate object just for this.
It's not just for this, objects that return something different class names should eventually expose different sets of interfaces (like IHTMLDivElement in case of div element). It should be easy enough to distinguish between HTMLElement and HTMLUnknownElement using the existing logic. While div would also be nice to have, as long as the logic is right, it's not too bad to return slightly wrong strings objects that we don't support yet.
Jacek
Wait I'm confused a bit here. Isn't the patch I attached going in the right direction? If not, why? It basically just adds a string name for toString for each tag.
Whether the tag itself is implemented as a separate object (like IHTMLDivElement you mentioned) or not doesn't really matter here, because even when it will be at some later point, it should still obtain the name from this table, just like we do for the constructor.
I guess I'm a bit confused as to what exactly you're referring I should do now to fix this.
Currently, all tags use the same toString implementation, even if they are already separate objects, so this would implement it for all of them in one spot without duplicating code, whether they have a constructor or not.
I'm not suggesting separate implementations. In fact, I think we could share the implementation with more objects than just HTML elements. toString is supposed to use a type name and we have a mechanism to store type-specific things, why don't you want to use that?
Jacek
Do you mean store them in static dispex data? But in that case, only separately implemented objects would receive their proper names—so for example "div" which is not implemented would receive something like HTMLElement. Is that what you meant?
I'm a bit worried, though, because this is accessible from javascript code, so it might break something that depends on it (like Final Fantasy XIV Online launcher, which I know calls toString, but I don't know what it does with it or whether it uses it for identification). The exact COM interfaces exposed are not that important in comparison.
On 9/22/21 5:00 PM, Gabriel Ivăncescu wrote:
On 22/09/2021 17:36, Jacek Caban wrote:
On 9/22/21 4:15 PM, Gabriel Ivăncescu wrote:
On 22/09/2021 15:55, Jacek Caban wrote:
On 9/21/21 5:50 PM, Gabriel Ivăncescu wrote:
On 21/09/2021 16:31, Jacek Caban wrote:
On 9/21/21 2:32 PM, Gabriel Ivăncescu wrote: > But where would we even get the name from? In > HTMLElement_Create, get_tag_desc is used for specific tags, > which is similar, but the "fallback" goes to > HTMLGenericElement_Create, since not all tags are included there.
Sure, but I think that's what you need, see this test for an example:
var s = document.createElement("winetest").toString(); ok(s === "[object HTMLUnknownElement]", "s = " + s);
> For example the "div" tag is not.
It should probably be made a separated object.
Thanks,
Jacek
Ok, I tested all the tags from:
https://www.w3schools.com/TAGS/default.ASP
It looks like there's way too many tags to properly implement each as separate object just for this.
It's not just for this, objects that return something different class names should eventually expose different sets of interfaces (like IHTMLDivElement in case of div element). It should be easy enough to distinguish between HTMLElement and HTMLUnknownElement using the existing logic. While div would also be nice to have, as long as the logic is right, it's not too bad to return slightly wrong strings objects that we don't support yet.
Jacek
Wait I'm confused a bit here. Isn't the patch I attached going in the right direction? If not, why? It basically just adds a string name for toString for each tag.
Whether the tag itself is implemented as a separate object (like IHTMLDivElement you mentioned) or not doesn't really matter here, because even when it will be at some later point, it should still obtain the name from this table, just like we do for the constructor.
I guess I'm a bit confused as to what exactly you're referring I should do now to fix this.
Currently, all tags use the same toString implementation, even if they are already separate objects, so this would implement it for all of them in one spot without duplicating code, whether they have a constructor or not.
I'm not suggesting separate implementations. In fact, I think we could share the implementation with more objects than just HTML elements. toString is supposed to use a type name and we have a mechanism to store type-specific things, why don't you want to use that?
Jacek
Do you mean store them in static dispex data?
Yes.
But in that case, only separately implemented objects would receive their proper names—so for example "div" which is not implemented would receive something like HTMLElement. Is that what you meant?
Yes, it's nothing new that unsupported elements don't behave exactly right.
I'm a bit worried, though, because this is accessible from javascript code, so it might break something that depends on it (like Final Fantasy XIV Online launcher, which I know calls toString, but I don't know what it does with it or whether it uses it for identification).
Sure, that's why I suggested to support interesting cases. We already support most of them. Adding a new type is as simple as a constructor and new dispex description. I think we should at least distinguish between HTMLElement and HTMLUnknownElement, maybe also div.
The exact COM interfaces exposed are not that important in comparison.
It's not COM interfaces that matter in this context, but the fact that they are supposed to be separated objects.
Jacek
Native mshtml objects act like JS objects in IE9 modes and up (the JS engine is implemented in mshtml). This implements proxy JS objects for certain dispatch objects that export a new private (internal to Wine) interface in versions ES5 and up, to achieve the same behavior. It delegates as much as possible to mshtml to implement all quirks there. Props are queried by the underlying dispatch and accesses to them forwarded to the dispatch object.
Since it's purely internal, the private interface is not part of any typelib, and for simplicity, it extends (inherits from) IDispatchEx, so we can just cast it as needed.
Given certain conditions, we query IDispatch objects on demand for the interface. If it's available, we either create a JS proxy object that is associated with the dispatch object exposing the interface, or re-acquire another one (possibly dangling). It is also associated on the other end, so that we keep a 1:1 mapping between the JS proxy object and the actual dispatch object, to avoid having multiple JS proxy objects for the same mshtml object (which would result in different dispids on them, and subtle bugs).
This keeps them both in sync at all times, on demand, without having to pre-create any objects, and deals with dynamic props and such automatically.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
There's only one caveat here: since the JS proxy object references the dispatch object, while the dispatch object is also associated to the JS proxy object, the JS proxy object must be kept alive even when its refcount is zero. We can't hold a reference from the dispatch object, because that would create a circular reference.
In effect, we *need* to know *when* the JS proxy object has no references from jscript part, to release the dispatch object. But the dispatch object itself might still remain alive (if kept alive by mshtml refs), and the JS proxy object must also remain associated in this case, hence we keep it alive even when refcount == 0, but remove the proxy association on its side.
This is slightly ugly, but it is the best approach I could think of, to avoid bugs or having to rely on more complicated hash table lookups.
Keep in mind that we have to keep the JS proxy object alive as long as it is associated with the dispatch object, because it could have proxy props retrieved, and they must always match among multiple references of this object, if they refer to the same underlying mshtml object (we can't have different dispids or "reset" them).
dlls/jscript/dispex.c | 207 +++++++++++++++++++++++++++++++++++--- dlls/jscript/engine.c | 17 +++- dlls/jscript/enumerator.c | 10 +- dlls/jscript/function.c | 98 +++++++++++++++++- dlls/jscript/jscript.h | 26 +++++ dlls/jscript/jsutils.c | 21 ++-- dlls/jscript/jsval.h | 2 +- dlls/jscript/object.c | 7 ++ dlls/jscript/vbarray.c | 4 +- 9 files changed, 356 insertions(+), 36 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index c7e4ba9..1ea0f19 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -33,6 +33,7 @@ static const GUID GUID_JScriptTypeInfo = {0xc59c6b12,0xf6c1,0x11cf,{0x88,0x35,0x typedef enum { PROP_JSVAL, PROP_BUILTIN, + PROP_PROXY, PROP_PROTREF, PROP_ACCESSOR, PROP_DELETED, @@ -50,6 +51,10 @@ struct _dispex_prop_t { const builtin_prop_t *p; DWORD ref; unsigned idx; + struct { + DISPID id; + DWORD flags; + } proxy; struct { jsdisp_t *getter; jsdisp_t *setter; @@ -199,6 +204,30 @@ static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_ return prop; }
+static inline dispex_prop_t* alloc_proxy_prop(jsdisp_t *This, dispex_prop_t *prop, const WCHAR *name, DISPID id) +{ + unsigned flags, prop_flags; + + flags = prop_flags = This->proxy->lpVtbl->GetPropFlags(This->proxy, id); + + if(flags & PROPF_METHOD) + flags |= PROPF_WRITABLE | PROPF_CONFIGURABLE; + flags &= PROPF_ALL; + + if(prop) { + prop->type = PROP_PROXY; + prop->flags = flags; + }else { + prop = alloc_prop(This, name, PROP_PROXY, flags); + if(!prop) + return NULL; + } + + prop->u.proxy.id = id; + prop->u.proxy.flags = prop_flags; + return prop; +} + static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref) { dispex_prop_t *ret; @@ -235,6 +264,23 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, pos = This->props[pos].bucket_next; }
+ if(This->proxy) { + DISPID id; + + if(FAILED(IDispatchEx_GetDispID((IDispatchEx*)This->proxy, (WCHAR*)name, make_grfdex(This->ctx, fdexNameCaseSensitive), &id))) + id = DISPID_UNKNOWN; + if(id == DISPID_UNKNOWN) { + *ret = NULL; + return S_OK; + } + prop = alloc_proxy_prop(This, NULL, name, id); + if(!prop) + return E_OUTOFMEMORY; + + *ret = prop; + return S_OK; + } + builtin = find_builtin_prop(This, name); if(builtin) { unsigned flags = builtin->flags; @@ -314,11 +360,28 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n
static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, dispex_prop_t **ret) { + unsigned hash = string_hash(name); dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(This, string_hash(name), name, &prop); + hres = find_prop_name_prot(This, hash, name, &prop); if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) { + if(This->proxy) { + DWORD fdex = make_grfdex(This->ctx, fdexNameEnsure | fdexNameCaseSensitive); + DISPID id; + + TRACE("creating proxy prop %s\n", debugstr_w(name)); + + hres = IDispatchEx_GetDispID((IDispatchEx*)This->proxy, (WCHAR*)name, fdex, &id); + if(FAILED(hres)) + return hres; + prop = alloc_proxy_prop(This, prop, name, id); + if(!prop) + return E_OUTOFMEMORY; + *ret = prop; + return S_OK; + } + TRACE("creating prop %s flags %x\n", debugstr_w(name), create_flags);
if(prop) { @@ -356,7 +419,7 @@ static IDispatch *get_this(DISPPARAMS *dp) return NULL; }
-static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret) +static HRESULT convert_params(script_ctx_t *ctx, const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret) { jsval_t *argv; unsigned cnt; @@ -374,7 +437,7 @@ static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc }
for(i = 0; i < cnt; i++) { - hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i); + hres = variant_to_jsval(ctx, dp->rgvarg+dp->cArgs-i-1, argv+i); if(FAILED(hres)) { while(i--) jsval_release(argv[i]); @@ -419,6 +482,23 @@ static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r) *r = jsval_obj(obj); } break; + case PROP_PROXY: + if(!(prop->u.proxy.flags & PROPF_METHOD)) { + hres = disp_propget(This->ctx, (IDispatch*)prop_obj->proxy, prop->u.proxy.id, r); + }else { + jsdisp_t *obj; + + hres = create_proxy_function(This->ctx, (IDispatchEx*)prop_obj->proxy, prop->u.proxy.id, prop->u.proxy.flags, &obj); + if(FAILED(hres)) + break; + + prop->type = PROP_JSVAL; + prop->u.val = jsval_obj(obj); + + jsdisp_addref(obj); + *r = jsval_obj(obj); + } + break; case PROP_JSVAL: hres = jsval_copy(prop->u.val, r); break; @@ -438,6 +518,8 @@ static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r) ERR("type %d\n", prop->type); return E_FAIL; } + if(SUCCEEDED(hres)) + hres = convert_to_proxy(This->ctx, r);
if(FAILED(hres)) { TRACE("fail %08x\n", hres); @@ -472,6 +554,10 @@ static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val) return S_OK; } return prop->u.p->setter(This->ctx, This, val); + case PROP_PROXY: + if(!(prop->flags & PROPF_WRITABLE)) + return S_OK; + return disp_propput(This->ctx, (IDispatch*)This->proxy, prop->u.proxy.id, val); case PROP_PROTREF: case PROP_DELETED: if(!This->extensible) @@ -521,7 +607,8 @@ static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t HRESULT hres;
switch(prop->type) { - case PROP_BUILTIN: { + case PROP_BUILTIN: + case PROP_PROXY: if(flags == DISPATCH_CONSTRUCT && (prop->flags & PROPF_METHOD)) { WARN("%s is not a constructor\n", debugstr_w(prop->name)); return E_INVALIDARG; @@ -530,6 +617,17 @@ static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t if(prop->name || This->builtin_info->class != JSCLASS_FUNCTION) { vdisp_t vthis;
+ if(prop->type == PROP_PROXY) { + jsdisp_t *jsdisp = to_jsdisp(jsthis); + if(jsdisp) + jsthis = (IDispatch*)jsdisp->proxy; + if(jsthis != (IDispatch*)This->proxy) { + WARN("Incompatible this %p for proxy %p, id %d\n", jsthis, This->proxy, prop->u.proxy.id); + return E_UNEXPECTED; + } + return disp_call(This->ctx, jsthis, prop->u.proxy.id, flags & ~DISPATCH_JSCRIPT_INTERNAL_MASK, argc, argv, r); + } + if(This->builtin_info->class != JSCLASS_FUNCTION && prop->u.p->invoke != JSGlobal_eval) flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; if(jsthis) @@ -542,8 +640,9 @@ static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t /* Function object calls are special case */ hres = Function_invoke(This, jsthis, flags, argc, argv, r); } + if(SUCCEEDED(hres)) + hres = convert_to_proxy(This->ctx, r); return hres; - } case PROP_PROTREF: return invoke_prop_func(This->prototype, jsthis ? jsthis : (IDispatch *)&This->IDispatchEx_iface, This->prototype->props+prop->u.ref, flags, argc, argv, r, caller); @@ -1563,7 +1662,7 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc jsval_t *argv, buf[6], r; unsigned argc;
- hres = convert_params(pdp, buf, &argc, &argv); + hres = convert_params(This->ctx, pdp, buf, &argc, &argv); if(FAILED(hres)) break;
@@ -1601,7 +1700,7 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc break; }
- hres = variant_to_jsval(pdp->rgvarg+i, &val); + hres = variant_to_jsval(This->ctx, pdp->rgvarg+i, &val); if(FAILED(hres)) break;
@@ -1618,8 +1717,16 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc return leave_script(This->ctx, hres); }
-static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret) +static HRESULT delete_prop(jsdisp_t *prop_obj, dispex_prop_t *prop, BOOL *ret) { + if(prop->type == PROP_PROXY) { + BOOL deleted; + HRESULT hres = prop_obj->proxy->lpVtbl->DeleteProp(prop_obj->proxy, prop->u.proxy.id, &deleted); + if(SUCCEEDED(hres) && deleted) + prop->type = PROP_DELETED; + return hres; + } + if(!(prop->flags & PROPF_CONFIGURABLE)) { *ret = FALSE; return S_OK; @@ -1656,7 +1763,7 @@ static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bst return S_OK; }
- return delete_prop(prop, &b); + return delete_prop(This, prop, &b); }
static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) @@ -1673,7 +1780,7 @@ static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID return DISP_E_MEMBERNOTFOUND; }
- return delete_prop(prop, &b); + return delete_prop(This, prop, &b); }
static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) @@ -1808,10 +1915,73 @@ HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsd return S_OK; }
+static const builtin_info_t proxy_dispex_info = { + JSCLASS_OBJECT, + {NULL, NULL, 0}, + 0, NULL, + NULL, + NULL +}; + +HRESULT convert_to_proxy(script_ctx_t *ctx, jsval_t *val) +{ + IWineDispatchProxyPrivate *proxy; + jsdisp_t **jsdisp_ref; + IDispatch *obj; + HRESULT hres; + + if(ctx->version < SCRIPTLANGUAGEVERSION_ES5 || !val || !is_object_instance(*val)) + return S_OK; + obj = get_object(*val); + if(!obj || to_jsdisp(obj)) + return S_OK; + + if(FAILED(IDispatch_QueryInterface(obj, &IID_IWineDispatchProxyPrivate, (void**)&proxy)) || !proxy) + return S_OK; + IDispatch_Release(obj); + + jsdisp_ref = proxy->lpVtbl->GetProxyFieldRef(proxy); + + if(*jsdisp_ref) { + /* Re-acquire the proxy if it's an old dangling proxy */ + if((*jsdisp_ref)->ref == 0) + (*jsdisp_ref)->proxy = proxy; + else + IDispatchEx_Release((IDispatchEx*)proxy); + + *val = jsval_obj(jsdisp_addref(*jsdisp_ref)); + return S_OK; + } + + hres = create_dispex(ctx, &proxy_dispex_info, ctx->object_prototype, jsdisp_ref); + if(FAILED(hres)) { + IDispatchEx_Release((IDispatchEx*)proxy); + return hres; + } + + (*jsdisp_ref)->props[0].type = PROP_PROXY; + (*jsdisp_ref)->props[0].u.proxy.id = DISPID_VALUE; + (*jsdisp_ref)->props[0].u.proxy.flags = proxy->lpVtbl->GetPropFlags(proxy, DISPID_VALUE); + (*jsdisp_ref)->proxy = proxy; + + *val = jsval_obj(*jsdisp_ref); + return S_OK; +} + void jsdisp_free(jsdisp_t *obj) { dispex_prop_t *prop;
+ /* If we are a proxy, stay alive and keep it associated with the disp, since + we can be re-acquired at some later point. When the underlying disp is + actually destroyed, it should let go and destroy us for real. */ + if(obj->proxy) { + IDispatchEx *disp = (IDispatchEx*)obj->proxy; + obj->proxy = NULL; + IDispatchEx_Release(disp); + return; + } + TRACE("(%p)\n", obj);
for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) { @@ -1931,6 +2101,8 @@ HRESULT jsdisp_call_value(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsig
if(is_class(jsfunc, JSCLASS_FUNCTION)) { hres = Function_invoke(jsfunc, jsthis, flags, argc, argv, r); + }else if(jsfunc->proxy) { + hres = disp_call_value(jsfunc->ctx, (IDispatch*)jsfunc->proxy, jsthis, flags, argc, argv, r); }else { vdisp_t vdisp;
@@ -1944,6 +2116,8 @@ HRESULT jsdisp_call_value(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsig hres = jsfunc->builtin_info->value_prop.invoke(jsfunc->ctx, &vdisp, flags, argc, argv, r); vdisp_release(&vdisp); } + if(SUCCEEDED(hres)) + hres = convert_to_proxy(jsfunc->ctx, r); return hres; }
@@ -2082,7 +2256,7 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, uns heap_free(dp.rgvarg);
if(SUCCEEDED(hres) && ret) - hres = variant_to_jsval(&retv, ret); + hres = variant_to_jsval(ctx, &retv, ret); VariantClear(&retv); return hres; } @@ -2148,7 +2322,7 @@ HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, W if(!r) return S_OK;
- hres = variant_to_jsval(&retv, r); + hres = variant_to_jsval(ctx, &retv, r); VariantClear(&retv); return hres; } @@ -2316,7 +2490,7 @@ HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val V_VT(&var) = VT_EMPTY; hres = disp_invoke(ctx, disp, id, INVOKE_PROPERTYGET, &dp, &var); if(SUCCEEDED(hres)) { - hres = variant_to_jsval(&var, val); + hres = variant_to_jsval(ctx, &var, val); VariantClear(&var); } return hres; @@ -2335,7 +2509,7 @@ HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx) if(FAILED(hres) || !prop) return hres;
- hres = delete_prop(prop, &b); + hres = delete_prop(obj, prop, &b); if(FAILED(hres)) return hres; return b ? S_OK : JS_E_INVALID_ACTION; @@ -2353,7 +2527,7 @@ HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
prop = get_prop(jsdisp, id); if(prop) - hres = delete_prop(prop, ret); + hres = delete_prop(jsdisp, prop, ret); else hres = DISP_E_MEMBERNOTFOUND;
@@ -2424,7 +2598,7 @@ HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL
hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop); if(prop) { - hres = delete_prop(prop, ret); + hres = delete_prop(jsdisp, prop, ret); }else { *ret = TRUE; hres = S_OK; @@ -2480,6 +2654,7 @@ HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_onl
switch(prop->type) { case PROP_BUILTIN: + case PROP_PROXY: case PROP_JSVAL: desc->mask |= PROPF_WRITABLE; desc->explicit_value = TRUE; diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 47b4e56..4ff4297 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -491,15 +491,24 @@ static HRESULT disp_cmp(IDispatch *disp1, IDispatch *disp2, BOOL *ret) { IObjectIdentity *identity; IUnknown *unk1, *unk2; + jsdisp_t *jsdisp; HRESULT hres;
- if(disp1 == disp2) { - *ret = TRUE; + if(!disp1 || !disp2) { + *ret = (disp1 == disp2); return S_OK; }
- if(!disp1 || !disp2) { - *ret = FALSE; + jsdisp = to_jsdisp(disp1); + if(jsdisp && jsdisp->proxy) + disp1 = (IDispatch*)jsdisp->proxy; + + jsdisp = to_jsdisp(disp2); + if(jsdisp && jsdisp->proxy) + disp2 = (IDispatch*)jsdisp->proxy; + + if(disp1 == disp2) { + *ret = TRUE; return S_OK; }
diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c index dea1940..eb6baf6 100644 --- a/dlls/jscript/enumerator.c +++ b/dlls/jscript/enumerator.c @@ -48,7 +48,7 @@ static inline EnumeratorInstance *enumerator_this(vdisp_t *jsthis) return is_vclass(jsthis, JSCLASS_ENUMERATOR) ? enumerator_from_vdisp(jsthis) : NULL; }
-static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This) +static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This, script_ctx_t *ctx) { HRESULT hres; VARIANT nextitem; @@ -64,7 +64,7 @@ static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This) hres = IEnumVARIANT_Next(This->enumvar, 1, &nextitem, NULL); if (hres == S_OK) { - hres = variant_to_jsval(&nextitem, &This->item); + hres = variant_to_jsval(ctx, &nextitem, &This->item); VariantClear(&nextitem); if (FAILED(hres)) { @@ -138,7 +138,7 @@ static HRESULT Enumerator_moveFirst(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla return hres;
This->atend = FALSE; - hres = enumvar_get_next_item(This); + hres = enumvar_get_next_item(This, ctx); if(FAILED(hres)) return hres; } @@ -161,7 +161,7 @@ static HRESULT Enumerator_moveNext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flag
if (This->enumvar) { - hres = enumvar_get_next_item(This); + hres = enumvar_get_next_item(This, ctx); if (FAILED(hres)) return hres; } @@ -276,7 +276,7 @@ static HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **re
enumerator->enumvar = enumvar; enumerator->atend = !enumvar; - hres = enumvar_get_next_item(enumerator); + hres = enumvar_get_next_item(enumerator, ctx); if (FAILED(hres)) { jsdisp_release(&enumerator->dispex); diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 318d6be..7cd83a3 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -54,6 +54,12 @@ typedef struct { const WCHAR *name; } NativeFunction;
+typedef struct { + FunctionInstance function; + IDispatchEx *proxy; + DISPID id; +} ProxyFunction; + typedef struct { FunctionInstance function; FunctionInstance *target; @@ -340,7 +346,7 @@ static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, un
TRACE("\n");
- if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP)) + if(!(function = function_this(jsthis)) && is_jsdisp(jsthis) && !get_jsdisp(jsthis)->proxy) return JS_E_FUNCTION_EXPECTED;
if(argc) { @@ -707,6 +713,96 @@ HRESULT create_builtin_constructor(script_ctx_t *ctx, builtin_invoke_t value_pro return S_OK; }
+static HRESULT ProxyFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_disp, unsigned flags, + unsigned argc, jsval_t *argv, jsval_t *r) +{ + ProxyFunction *function = (ProxyFunction*)func; + jsdisp_t *jsdisp; + + if(!this_disp) + this_disp = lookup_global_host(ctx); + + jsdisp = to_jsdisp(this_disp); + if(jsdisp) + this_disp = (IDispatch*)jsdisp->proxy; + if(this_disp != (IDispatch*)function->proxy) { + WARN("Incompatible this %p for proxy %p, id %d\n", this_disp, function->proxy, function->id); + return E_UNEXPECTED; + } + + return disp_call(ctx, this_disp, function->id, flags & ~DISPATCH_JSCRIPT_INTERNAL_MASK, argc, argv, r); +} + +static HRESULT ProxyFunction_toString(FunctionInstance *func, jsstr_t **ret) +{ + ProxyFunction *function = (ProxyFunction*)func; + BSTR name = NULL; + DWORD name_len; + jsstr_t *str; + WCHAR *ptr; + + static const WCHAR proxy_prefixW[] = L"\nfunction "; + static const WCHAR proxy_suffixW[] = L"() {\n [native code]\n}\n"; + + if(FAILED(IDispatchEx_GetMemberName(function->proxy, function->id, &name))) + name = NULL; + + name_len = name ? SysStringLen(name) : 0; + str = jsstr_alloc_buf(ARRAY_SIZE(proxy_prefixW) + ARRAY_SIZE(proxy_suffixW) + name_len - 2, &ptr); + if(!str) + return E_OUTOFMEMORY; + + memcpy(ptr, proxy_prefixW, sizeof(proxy_prefixW)); + ptr += ARRAY_SIZE(proxy_prefixW) - 1; + memcpy(ptr, name, name_len*sizeof(WCHAR)); + ptr += name_len; + memcpy(ptr, proxy_suffixW, sizeof(proxy_suffixW)); + + SysFreeString(name); + *ret = str; + return S_OK; +} + +static function_code_t *ProxyFunction_get_code(FunctionInstance *func) +{ + return NULL; +} + +static void ProxyFunction_destructor(FunctionInstance *func) +{ + ProxyFunction *function = (ProxyFunction*)func; + IDispatchEx_Release(function->proxy); +} + +static const function_vtbl_t ProxyFunctionVtbl = { + ProxyFunction_call, + ProxyFunction_toString, + ProxyFunction_get_code, + ProxyFunction_destructor +}; + +HRESULT create_proxy_function(script_ctx_t *ctx, IDispatchEx *proxy, DISPID id, DWORD flags, jsdisp_t **ret) +{ + ProxyFunction *function; + HRESULT hres; + + hres = create_function(ctx, NULL, &ProxyFunctionVtbl, sizeof(ProxyFunction), flags, FALSE, NULL, (void**)&function); + if(FAILED(hres)) + return hres; + + if(FAILED(hres)) { + jsdisp_release(&function->function.dispex); + return hres; + } + + function->proxy = proxy; + function->id = id; + IDispatchEx_AddRef(function->proxy); + + *ret = &function->function.dispex; + return S_OK; +} + static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags, unsigned argc, jsval_t *argv, jsval_t *r) { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 90a5e15..1d14271 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -47,6 +47,28 @@ #define SCRIPTLANGUAGEVERSION_ES5 0x102 #define SCRIPTLANGUAGEVERSION_ES6 0x103
+/* + * This is Wine jscript extension, used for mshtml objects so they act like JS objects. + * Extends IDispatchEx. + * + * NOTE: Keep in sync with mshtml_private.h in mshtml.dll + */ +DEFINE_GUID(IID_IWineDispatchProxyPrivate, 0xd359f2fe,0x5531,0x741b,0xa4,0x1a,0x5c,0xf9,0x2e,0xdc,0x97,0x1b); +typedef struct _IWineDispatchProxyPrivate IWineDispatchProxyPrivate; + +typedef struct { + IDispatchExVtbl dispex; + void* (STDMETHODCALLTYPE *GetProxyFieldRef)(IWineDispatchProxyPrivate *This); + DWORD (STDMETHODCALLTYPE *GetPropFlags)(IWineDispatchProxyPrivate *This, DISPID id); + HRESULT (STDMETHODCALLTYPE *DeleteProp)(IWineDispatchProxyPrivate *This, DISPID id, BOOL *deleted); +} IWineDispatchProxyPrivateVtbl; + +struct _IWineDispatchProxyPrivate { + const IWineDispatchProxyPrivateVtbl *lpVtbl; +}; + + + typedef struct _jsval_t jsval_t; typedef struct _jsstr_t jsstr_t; typedef struct _jsexcept_t jsexcept_t; @@ -91,6 +113,7 @@ typedef struct jsdisp_t jsdisp_t; extern HINSTANCE jscript_hinstance DECLSPEC_HIDDEN; HRESULT get_dispatch_typeinfo(ITypeInfo**) DECLSPEC_HIDDEN;
+/* NOTE: Keep in sync with mshtml_private.h in mshtml.dll */ #define PROPF_ARGMASK 0x00ff #define PROPF_METHOD 0x0100 #define PROPF_CONSTR 0x0200 @@ -253,6 +276,7 @@ struct jsdisp_t { BOOL extensible;
jsdisp_t *prototype; + IWineDispatchProxyPrivate *proxy;
const builtin_info_t *builtin_info; }; @@ -302,6 +326,7 @@ enum jsdisp_enum_type { HRESULT create_dispex(script_ctx_t*,const builtin_info_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT init_dispex(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DECLSPEC_HIDDEN; HRESULT init_dispex_from_constr(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DECLSPEC_HIDDEN; +HRESULT convert_to_proxy(script_ctx_t*,jsval_t*) DECLSPEC_HIDDEN;
HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT disp_call_value(script_ctx_t*,IDispatch*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; @@ -333,6 +358,7 @@ HRESULT create_builtin_function(script_ctx_t*,builtin_invoke_t,const WCHAR*,cons jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT create_builtin_constructor(script_ctx_t*,builtin_invoke_t,const WCHAR*,const builtin_info_t*,DWORD, jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN; +HRESULT create_proxy_function(script_ctx_t*,IDispatchEx*,DISPID,DWORD,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT Function_invoke(jsdisp_t*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
HRESULT Function_value(script_ctx_t*,vdisp_t*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/jsutils.c b/dlls/jscript/jsutils.c index 3c3cd01..1b1b904 100644 --- a/dlls/jscript/jsutils.c +++ b/dlls/jscript/jsutils.c @@ -246,7 +246,7 @@ HRESULT jsval_copy(jsval_t v, jsval_t *r) return E_FAIL; }
-HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) +HRESULT variant_to_jsval(script_ctx_t *ctx, VARIANT *var, jsval_t *r) { if(V_VT(var) == (VT_VARIANT|VT_BYREF)) var = V_VARIANTREF(var); @@ -285,7 +285,7 @@ HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) if(V_DISPATCH(var)) IDispatch_AddRef(V_DISPATCH(var)); *r = jsval_disp(V_DISPATCH(var)); - return S_OK; + return convert_to_proxy(ctx, r); } case VT_I1: *r = jsval_number(V_I1(var)); @@ -329,7 +329,7 @@ HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) hres = IUnknown_QueryInterface(V_UNKNOWN(var), &IID_IDispatch, (void**)&disp); if(SUCCEEDED(hres)) { *r = jsval_disp(disp); - return S_OK; + return convert_to_proxy(ctx, r); } }else { *r = jsval_disp(NULL); @@ -343,6 +343,8 @@ HRESULT variant_to_jsval(VARIANT *var, jsval_t *r)
HRESULT jsval_to_variant(jsval_t val, VARIANT *retv) { + IDispatch *disp; + switch(jsval_type(val)) { case JSV_UNDEFINED: V_VT(retv) = VT_EMPTY; @@ -352,9 +354,14 @@ HRESULT jsval_to_variant(jsval_t val, VARIANT *retv) return S_OK; case JSV_OBJECT: V_VT(retv) = VT_DISPATCH; - if(get_object(val)) - IDispatch_AddRef(get_object(val)); - V_DISPATCH(retv) = get_object(val); + disp = get_object(val); + if(disp) { + jsdisp_t *jsdisp = to_jsdisp(disp); + if(jsdisp && jsdisp->proxy) + disp = (IDispatch*)jsdisp->proxy; + IDispatch_AddRef(disp); + } + V_DISPATCH(retv) = disp; return S_OK; case JSV_STRING: V_VT(retv) = VT_BSTR; @@ -884,7 +891,7 @@ HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTY jsval_t val; HRESULT hres;
- hres = variant_to_jsval(src, &val); + hres = variant_to_jsval(ctx, src, &val); if(FAILED(hres)) return hres;
diff --git a/dlls/jscript/jsval.h b/dlls/jscript/jsval.h index 9f451b1..c79bef4 100644 --- a/dlls/jscript/jsval.h +++ b/dlls/jscript/jsval.h @@ -241,7 +241,7 @@ static inline BOOL get_bool(jsval_t v) return __JSVAL_BOOL(v); }
-HRESULT variant_to_jsval(VARIANT*,jsval_t*) DECLSPEC_HIDDEN; +HRESULT variant_to_jsval(script_ctx_t*,VARIANT*,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsval_to_variant(jsval_t,VARIANT*) DECLSPEC_HIDDEN; void jsval_release(jsval_t) DECLSPEC_HIDDEN; HRESULT jsval_copy(jsval_t,jsval_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 169b47c..cf14a89 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -57,6 +57,13 @@ static HRESULT Object_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, u jsdisp = get_jsdisp(jsthis); if(!jsdisp) { str = L"[object Object]"; + }else if(jsdisp->proxy) { + DISPID id; + if(FAILED(IDispatchEx_GetDispID((IDispatchEx*)jsdisp->proxy, (WCHAR*)L"toString", make_grfdex(ctx, fdexNameCaseSensitive), &id))) + id = DISPID_UNKNOWN; + if(id != DISPID_UNKNOWN) + return disp_call(ctx, (IDispatch*)jsdisp->proxy, id, DISPATCH_METHOD, 0, NULL, r); + str = L"[object Object]"; }else if(names[jsdisp->builtin_info->class]) { str = names[jsdisp->builtin_info->class]; }else { diff --git a/dlls/jscript/vbarray.c b/dlls/jscript/vbarray.c index 69a77f1..3294ee6 100644 --- a/dlls/jscript/vbarray.c +++ b/dlls/jscript/vbarray.c @@ -96,7 +96,7 @@ static HRESULT VBArray_getItem(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, un return hres;
if(r) { - hres = variant_to_jsval(&out, r); + hres = variant_to_jsval(ctx, &out, r); VariantClear(&out); } return hres; @@ -166,7 +166,7 @@ static HRESULT VBArray_toArray(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, un }
for(i=0; i<size; i++) { - hres = variant_to_jsval(v, &val); + hres = variant_to_jsval(ctx, v, &val); if(SUCCEEDED(hres)) { hres = jsdisp_propput_idx(array, i, val); jsval_release(val);
This implements the internal private interface for mshtml objects, so they can be used as JS proxy objects from jscript.
Note that we need to keep an association also from these objects back to the JS proxy objects. We keep a weak ref, though, to avoid circular references, and because the JS proxy object needs to know when all the jscript refs to it are gone (so we can't hold an actual ref to it).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
As explained in the previous patch, the JS proxy object and dispatch objects are both associated with one another in both directions. This allows lookups to "old dangling" JS proxy objects to return the same object with same props (and dispids) and avoid subtle bugs, which makes them as though they were directly implemented in mshtml, just like native.
Note that we *do not* keep a ref to the JS proxy object, but a weak ref. However, this is not a problem, because the JS proxy object remains alive even with refcount == 0 (as explained in previous patch, because it needs to be re-acquired with same dispids as long as the builtin dispatch object didn't die yet).
This does mean we have to somehow retrigger its "release" if the builtin object is actually destroyed when the JS proxy object has refcount == 0. Since the refcount is 0, we achieve this by AddRef and then Release to it. It is a semi hack, but it avoids further complications. The JS proxy object knows this situation because it lets go of the dispatch when refcount == 0, and re-acquires it if it has to, on demand.
dlls/mshtml/dispex.c | 84 +++++++++++++++++++++++++++++++++++- dlls/mshtml/htmlwindow.c | 46 +++++++++++++++++++- dlls/mshtml/mshtml_private.h | 27 ++++++++++++ 3 files changed, 153 insertions(+), 4 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 64ead8f..f9814b7 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -1855,7 +1855,69 @@ static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown return E_NOTIMPL; }
-static IDispatchExVtbl DispatchExVtbl = { +static inline DispatchEx *impl_from_IWineDispatchProxyPrivate(IWineDispatchProxyPrivate *iface) +{ + return impl_from_IDispatchEx((IDispatchEx*)iface); +} + +static void* WINAPI WineDispatchProxyPrivate_GetProxyFieldRef(IWineDispatchProxyPrivate *iface) +{ + DispatchEx *This = impl_from_IWineDispatchProxyPrivate(iface); + return &This->proxy; +} + +static DWORD WINAPI WineDispatchProxyPrivate_GetPropFlags(IWineDispatchProxyPrivate *iface, DISPID id) +{ + DispatchEx *This = impl_from_IWineDispatchProxyPrivate(iface); + func_info_t *func; + + TRACE("(%p)->(%x)\n", This, id); + + if(is_dynamic_dispid(id)) + return PROPF_WRITABLE | PROPF_CONFIGURABLE | PROPF_ENUMERABLE; + + if(is_custom_dispid(id)) + return PROPF_WRITABLE; + + if(FAILED(get_builtin_func(This->info, id, &func))) + return 0; + + if(func->func_disp_idx != -1) { + if(This->dynamic_data && This->dynamic_data->func_disps + && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) { + func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx; + + if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != V_DISPATCH(&entry->val)) + return PROPF_WRITABLE | PROPF_CONFIGURABLE; + } + return PROPF_METHOD | func->argc; + } + + /* FIXME: Don't add PROPF_ENUMERABLE to hidden properties */ + return PROPF_ENUMERABLE | ((func->put_vtbl_off) ? PROPF_WRITABLE : 0); +} + +static HRESULT WINAPI WineDispatchProxyPrivate_DeleteProp(IWineDispatchProxyPrivate *iface, DISPID id, BOOL *deleted) +{ + DispatchEx *This = impl_from_IWineDispatchProxyPrivate(iface); + HRESULT hres; + + hres = DispatchEx_DeleteMemberByDispID(&This->IDispatchEx_iface, id); + if(FAILED(hres)) + return hres; + + *deleted = FALSE; + if(is_dynamic_dispid(id)) { + DWORD idx = id - DISPID_DYNPROP_0; + + if(get_dynamic_data(This) && idx < This->dynamic_data->prop_cnt) + *deleted = TRUE; + } + return hres; +} + +static IWineDispatchProxyPrivateVtbl WineDispatchProxyPrivateVtbl = { + { DispatchEx_QueryInterface, DispatchEx_AddRef, DispatchEx_Release, @@ -1871,6 +1933,12 @@ static IDispatchExVtbl DispatchExVtbl = { DispatchEx_GetMemberName, DispatchEx_GetNextDispID, DispatchEx_GetNameSpaceParent + }, + + /* IWineDispatchProxyPrivate extension */ + WineDispatchProxyPrivate_GetProxyFieldRef, + WineDispatchProxyPrivate_GetPropFlags, + WineDispatchProxyPrivate_DeleteProp };
BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv) @@ -1879,6 +1947,8 @@ BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv) *ppv = &This->IDispatchEx_iface; else if(IsEqualGUID(&IID_IDispatchEx, riid)) *ppv = &This->IDispatchEx_iface; + else if(IsEqualGUID(&IID_IWineDispatchProxyPrivate, riid)) + *ppv = &This->IDispatchEx_iface; else if(IsEqualGUID(&IID_IDispatchJS, riid)) *ppv = NULL; else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) @@ -1936,6 +2006,15 @@ void release_dispex(DispatchEx *This) { dynamic_prop_t *prop;
+ /* Even though we hold a weak ref to the proxy, it is possible that it is a + dangling proxy associated with us (refcount == 0), so we need to trigger + its release again so that it is destroyed under such conditions. */ + if(This->proxy) { + IDispatch *proxy = This->proxy; + IDispatch_AddRef(proxy); + IDispatch_Release(proxy); + } + if(!This->dynamic_data) return;
@@ -1967,8 +2046,9 @@ void init_dispatch(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *da { assert(compat_mode < COMPAT_MODE_CNT);
- dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl; + dispex->IDispatchEx_iface.lpVtbl = (const IDispatchExVtbl*)&WineDispatchProxyPrivateVtbl; dispex->outer = outer; + dispex->proxy = NULL; dispex->dynamic_data = NULL;
if(data->vtbl && data->vtbl->get_compat_mode) { diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 870d7e8..5c8097f 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -161,6 +161,8 @@ static HRESULT WINAPI HTMLWindow2_QueryInterface(IHTMLWindow2 *iface, REFIID rii *ppv = &This->IHTMLWindow2_iface; }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { *ppv = &This->IDispatchEx_iface; + }else if(IsEqualGUID(&IID_IWineDispatchProxyPrivate, riid)) { + *ppv = &This->IDispatchEx_iface; }else if(IsEqualGUID(&IID_IHTMLFramesCollection2, riid)) { *ppv = &This->IHTMLWindow2_iface; }else if(IsEqualGUID(&IID_IHTMLWindow2, riid)) { @@ -3451,7 +3453,41 @@ static HRESULT WINAPI WindowDispEx_GetNameSpaceParent(IDispatchEx *iface, IUnkno return S_OK; }
-static const IDispatchExVtbl WindowDispExVtbl = { +static inline HTMLWindow *impl_from_IWineDispatchProxyPrivate(IWineDispatchProxyPrivate *iface) +{ + return impl_from_IDispatchEx((IDispatchEx*)iface); +} + +static void* WINAPI WindowWineDispProxyPrivate_GetProxyFieldRef(IWineDispatchProxyPrivate *iface) +{ + HTMLWindow *This = impl_from_IWineDispatchProxyPrivate(iface); + IWineDispatchProxyPrivate *itf = (IWineDispatchProxyPrivate*)&This->inner_window->event_target.dispex.IDispatchEx_iface; + + return itf->lpVtbl->GetProxyFieldRef(itf); +} + +static DWORD WINAPI WindowWineDispProxyPrivate_GetPropFlags(IWineDispatchProxyPrivate *iface, DISPID id) +{ + HTMLWindow *This = impl_from_IWineDispatchProxyPrivate(iface); + IWineDispatchProxyPrivate *itf = (IWineDispatchProxyPrivate*)&This->inner_window->event_target.dispex.IDispatchEx_iface; + + switch(id) { + case DISPID_IHTMLWINDOW2_LOCATION: return PROPF_WRITABLE | PROPF_ENUMERABLE; + } + + return itf->lpVtbl->GetPropFlags(itf, id); +} + +static HRESULT WINAPI WindowWineDispProxyPrivate_DeleteProp(IWineDispatchProxyPrivate *iface, DISPID id, BOOL *deleted) +{ + HTMLWindow *This = impl_from_IWineDispatchProxyPrivate(iface); + IWineDispatchProxyPrivate *itf = (IWineDispatchProxyPrivate*)&This->inner_window->event_target.dispex.IDispatchEx_iface; + + return itf->lpVtbl->DeleteProp(itf, id, deleted); +} + +static const IWineDispatchProxyPrivateVtbl WindowDispExVtbl = { + { WindowDispEx_QueryInterface, WindowDispEx_AddRef, WindowDispEx_Release, @@ -3467,6 +3503,12 @@ static const IDispatchExVtbl WindowDispExVtbl = { WindowDispEx_GetMemberName, WindowDispEx_GetNextDispID, WindowDispEx_GetNameSpaceParent + }, + + /* IWineDispatchProxyPrivate extension */ + WindowWineDispProxyPrivate_GetProxyFieldRef, + WindowWineDispProxyPrivate_GetPropFlags, + WindowWineDispProxyPrivate_DeleteProp };
static inline HTMLWindow *impl_from_IServiceProvider(IServiceProvider *iface) @@ -3708,7 +3750,7 @@ static void *alloc_window(size_t size) window->IHTMLWindow6_iface.lpVtbl = &HTMLWindow6Vtbl; window->IHTMLWindow7_iface.lpVtbl = &HTMLWindow7Vtbl; window->IHTMLPrivateWindow_iface.lpVtbl = &HTMLPrivateWindowVtbl; - window->IDispatchEx_iface.lpVtbl = &WindowDispExVtbl; + window->IDispatchEx_iface.lpVtbl = (const IDispatchExVtbl*)&WindowDispExVtbl; window->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl; window->ITravelLogClient_iface.lpVtbl = &TravelLogClientVtbl; window->IObjectIdentity_iface.lpVtbl = &ObjectIdentityVtbl; diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index c1e7e78..7bdccfc 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -44,6 +44,32 @@
#include <assert.h>
+/* NOTE: Keep in sync with jscript.h in jscript.dll */ +DEFINE_GUID(IID_IWineDispatchProxyPrivate, 0xd359f2fe,0x5531,0x741b,0xa4,0x1a,0x5c,0xf9,0x2e,0xdc,0x97,0x1b); +typedef struct _IWineDispatchProxyPrivate IWineDispatchProxyPrivate; + +typedef struct { + IDispatchExVtbl dispex; + void* (STDMETHODCALLTYPE *GetProxyFieldRef)(IWineDispatchProxyPrivate *This); + DWORD (STDMETHODCALLTYPE *GetPropFlags)(IWineDispatchProxyPrivate *This, DISPID id); + HRESULT (STDMETHODCALLTYPE *DeleteProp)(IWineDispatchProxyPrivate *This, DISPID id, BOOL *deleted); +} IWineDispatchProxyPrivateVtbl; + +struct _IWineDispatchProxyPrivate { + const IWineDispatchProxyPrivateVtbl *lpVtbl; +}; + +#define PROPF_ARGMASK 0x00ff +#define PROPF_METHOD 0x0100 +#define PROPF_CONSTR 0x0200 + +#define PROPF_ENUMERABLE 0x0400 +#define PROPF_WRITABLE 0x0800 +#define PROPF_CONFIGURABLE 0x1000 +#define PROPF_ALL (PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE) + + + #define NS_ERROR_GENERATE_FAILURE(module,code) \ ((nsresult) (((UINT32)(1u<<31)) | ((UINT32)(module+0x45)<<16) | ((UINT32)(code)))) #define NS_ERROR_GENERATE_SUCCESS(module,code) \ @@ -347,6 +373,7 @@ struct DispatchEx { IDispatchEx IDispatchEx_iface;
IUnknown *outer; + void *proxy;
dispex_data_t *info; dispex_dynamic_data_t *dynamic_data;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98336
Your paranoid android.
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
On 20/09/2021 18:08, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98336
Your paranoid android.
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
Seems like a genuine issue. Interesting that this only happens on 64-bit WoW.
Duh, problem is I forgot to set the *ret when deleting it, so it had uninitialized value. Easy fix but I'll wait to resend it when more things get changed/fixed.
On 20/09/2021 18:08, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98336
Your paranoid android.
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/engine.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 4ff4297..bac855b 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -662,12 +662,27 @@ static BOOL lookup_global_members(script_ctx_t *ctx, BSTR identifier, exprval_t
LIST_FOR_EACH_ENTRY(item, &ctx->named_items, named_item_t, entry) { if(item->flags & SCRIPTITEM_GLOBALMEMBERS) { - hres = disp_get_id(ctx, item->disp, identifier, identifier, 0, &id); + IDispatch *disp = item->disp; + + IDispatch_AddRef(disp); + if(ret) { + jsval_t val = jsval_disp(disp); + + hres = convert_to_proxy(ctx, &val); + if(SUCCEEDED(hres)) + disp = get_object(val); + else + IDispatch_AddRef(disp); + } + + hres = disp_get_id(ctx, disp, identifier, identifier, 0, &id); if(SUCCEEDED(hres)) { if(ret) - exprval_set_disp_ref(ret, item->disp, id); + exprval_set_disp_ref(ret, disp, id); + IDispatch_Release(disp); return TRUE; } + IDispatch_Release(disp); } }
@@ -779,7 +794,7 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re IDispatch_AddRef(item->disp); ret->type = EXPRVAL_JSVAL; ret->u.val = jsval_disp(item->disp); - return S_OK; + return convert_to_proxy(ctx, &ret->u.val); }
if(lookup_global_members(ctx, identifier, ret))
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/documentmode.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index c20f6ee..d4704a4 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -29,7 +29,7 @@ sync_test("elem_props", function() { ok(!(prop in elem), prop + " found in element."); }
- var v = document.documentMode; + var v = document.documentMode, r;
test_exposed("doScroll", v < 11); test_exposed("readyState", v < 11); @@ -49,6 +49,9 @@ sync_test("elem_props", function() { test_exposed("msSetPointerCapture", v >= 10); if (v >= 9) test_exposed("spellcheck", v >= 10);
+ r = Object.prototype.toString.call(elem); + ok(r === (v < 9 ? "[object Object]" : "[object HTMLHtmlElement]"), "documentElement toString returned " + r); + elem = document.createElement("style"); test_exposed("media", true); test_exposed("type", true); @@ -58,6 +61,8 @@ sync_test("elem_props", function() { test_exposed("readyState", v < 11); test_exposed("styleSheet", v < 11); test_exposed("classList", v >= 10); + r = Object.prototype.toString.call(elem); + ok(r === (v < 9 ? "[object Object]" : "[object HTMLStyleElement]"), "createElement('style') toString returned " + r); });
sync_test("doc_props", function() { @@ -785,4 +790,7 @@ sync_test("elem_attr", function() { ok(r === "cls2", "class attr = " + r); r = elem.getAttribute("className"); ok(r === "cls3", "className attr = " + r); + + r = Object.prototype.toString.call(elem); + ok(r === (v < 9 ? "[object Object]" : "[object HTMLDivElement]"), "toString returned " + r); });
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98338
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w1064_tsign (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
For document modes <= IE8, which don't use the JS object proxies, native exposes some of the function prototype properties and methods, and semi-implements the methods for typical use. This implements the same limitations on them.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Tests follow.
dlls/mshtml/dispex.c | 172 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 4 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index f9814b7..01c5656 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -34,6 +34,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
#define MAX_ARGS 16 +#define JS_E_INVALID_PROPERTY 0x800a01b6
static CRITICAL_SECTION cs_dispex_static_data; static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg = @@ -760,6 +761,19 @@ static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, return hres; }
+static inline BOOL same_disp(IDispatch *disp, DispatchEx *obj) +{ + IDispatch *tmp; + + if(!disp || FAILED(IDispatch_QueryInterface(disp, &IID_IDispatch, (void**)&disp)) || !disp) + return FALSE; + IDispatch_Release(disp); + if(FAILED(IDispatchEx_QueryInterface(&obj->IDispatchEx_iface, &IID_IDispatch, (void**)&tmp))) + return FALSE; + IDispatch_Release(tmp); + return disp == tmp; +} + static inline func_disp_t *impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, func_disp_t, IUnknown_iface); @@ -816,6 +830,113 @@ static const IUnknownVtbl FunctionUnkVtbl = { Function_Release };
+static HRESULT function_apply(func_disp_t *func, DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei) +{ + DISPPARAMS params = { 0 }; + IDispatch *disp; + DISPID dispid; + VARIANT *arg; + HRESULT hres; + + if(!func->obj) + return E_UNEXPECTED; + + if(dp->cNamedArgs) { + FIXME("named args not supported\n"); + return E_NOTIMPL; + } + + arg = dp->rgvarg + dp->cArgs - 1; + if(dp->cArgs < 1 || V_VT(arg) != VT_DISPATCH || !same_disp(V_DISPATCH(arg), func->obj)) + return CTL_E_ILLEGALFUNCTIONCALL; + + if(dp->cArgs >= 2) { + WCHAR buf[12], *name; + ULONG i, err = 0; + + arg--; + if((V_VT(arg) & ~VT_BYREF) != VT_DISPATCH) + return CTL_E_ILLEGALFUNCTIONCALL; + disp = (V_VT(arg) & VT_BYREF) ? *(IDispatch**)(V_BYREF(arg)) : V_DISPATCH(arg); + + /* get the array length */ + name = buf; + memcpy(buf, L"length", sizeof(L"length")); + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, &dispid); + if(FAILED(hres) || dispid == DISPID_UNKNOWN) + return CTL_E_ILLEGALFUNCTIONCALL; + + hres = IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, res, ei, &err); + if(FAILED(hres)) + return hres; + if(FAILED(VariantChangeType(res, res, 0, VT_UI4))) { + VariantClear(res); + return CTL_E_ILLEGALFUNCTIONCALL; + } + params.cArgs = V_UI4(res); + V_VT(res) = VT_EMPTY; + + /* alloc new params */ + if(params.cArgs) { + if(!(params.rgvarg = heap_alloc(params.cArgs * sizeof(VARIANTARG)))) + return E_OUTOFMEMORY; + for(i = 0; i < params.cArgs; i++) { + arg = params.rgvarg + params.cArgs - i - 1; + + name = buf; + swprintf(buf, ARRAY_SIZE(buf), L"%u", i); + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, &dispid); + if(FAILED(hres) || dispid == DISPID_UNKNOWN) { + hres = CTL_E_ILLEGALFUNCTIONCALL; + break; + } + hres = IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, arg, ei, &err); + if(FAILED(hres)) + break; + } + if(FAILED(hres)) { + while(i--) VariantClear(params.rgvarg + params.cArgs - i - 1); + return hres; + } + } + } + + hres = typeinfo_invoke(func->obj, func->info, DISPATCH_METHOD, ¶ms, res, ei); + + heap_free(params.rgvarg); + return hres; +} + +static HRESULT function_call(func_disp_t *func, DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei) +{ + DISPPARAMS params = { dp->rgvarg, NULL, dp->cArgs - 1, 0 }; + VARIANT *arg; + + if(!func->obj) + return E_UNEXPECTED; + + if(dp->cNamedArgs) { + FIXME("named args not supported\n"); + return E_NOTIMPL; + } + + arg = dp->rgvarg + dp->cArgs - 1; + if(dp->cArgs < 1 || V_VT(arg) != VT_DISPATCH || !same_disp(V_DISPATCH(arg), func->obj)) + return CTL_E_ILLEGALFUNCTIONCALL; + + return typeinfo_invoke(func->obj, func->info, DISPATCH_METHOD, ¶ms, res, ei); +} + +static const struct { + const WCHAR *name; + HRESULT (*method)(func_disp_t*,DISPPARAMS*,VARIANT*,EXCEPINFO*); +} function_props[] = { + { L"apply", function_apply }, + { L"arguments", NULL }, + { L"call", function_call }, + { L"length", NULL } +}; + static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface) { return CONTAINING_RECORD(iface, func_disp_t, dispex); @@ -876,10 +997,53 @@ static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPAR return hres; }
+static HRESULT function_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, DISPID *dispid) +{ + DWORD i; + + for(i = 0; i < ARRAY_SIZE(function_props); i++) { + if(!wcsicmp(name, function_props[i].name)) { + if((flags & fdexNameCaseSensitive) && wcscmp(name, function_props[i].name)) + return DISP_E_UNKNOWNNAME; + *dispid = MSHTML_DISPID_CUSTOM_MIN + i; + return S_OK; + } + } + return DISP_E_UNKNOWNNAME; +} + +static HRESULT function_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params, + VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +{ + func_disp_t *This = impl_from_DispatchEx(dispex); + DWORD idx = id - MSHTML_DISPID_CUSTOM_MIN; + + if(idx >= ARRAY_SIZE(function_props)) + return DISP_E_UNKNOWNNAME; + + switch(flags) { + case DISPATCH_METHOD|DISPATCH_PROPERTYGET: + if(!res) + return E_INVALIDARG; + /* fall through */ + case DISPATCH_METHOD: + if(function_props[idx].method) + return function_props[idx].method(This, params, res, ei); + return JS_E_INVALID_PROPERTY; + case DISPATCH_PROPERTYGET: + V_VT(res) = VT_EMPTY; /* IE returns undefined */ + break; + default: + return JS_E_INVALID_PROPERTY; + } + + return S_OK; +} + static const dispex_static_data_vtbl_t function_dispex_vtbl = { function_value, - NULL, - NULL, + function_get_dispid, + function_invoke, NULL };
@@ -1262,7 +1426,7 @@ static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISP return V_ERROR(&vhres); }
-static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res, +static HRESULT func_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) { HRESULT hres; @@ -1368,7 +1532,7 @@ static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD }
if(func->func_disp_idx != -1) - return function_invoke(This, func, flags, dp, res, ei, caller); + return func_invoke(This, func, flags, dp, res, ei, caller);
switch(flags) { case DISPATCH_PROPERTYPUT:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98339
Your paranoid android.
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
For document modes <= IE8, native allows to retrieve the semi-implemented builtin methods, and then use them to retrieve their value props (and string) or the other props (which always return undefined), but it doesn't allow calling them or otherwise doing anything else with them anymore (e.g. chaining them like f.call.call). This implements it the same way with the same limitations.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Tests follow.
dlls/mshtml/dispex.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 01c5656..2f3ca0b 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -91,7 +91,10 @@ typedef struct { DispatchEx dispex; IUnknown IUnknown_iface; LONG ref; - DispatchEx *obj; + union { + DispatchEx *obj; + DWORD idx; /* index into function_props, used when info == NULL */ + }; func_info_t *info; } func_disp_t;
@@ -126,6 +129,8 @@ PRIVATE_TID_LIST #undef XDIID };
+static func_disp_t *create_func_disp(DispatchEx*,func_info_t*); + static HRESULT load_typelib(void) { WCHAR module_path[MAX_PATH + 3]; @@ -816,7 +821,7 @@ static ULONG WINAPI Function_Release(IUnknown *iface) TRACE("(%p) ref=%d\n", This, ref);
if(!ref) { - assert(!This->obj); + assert(!This->info || !This->obj); release_dispex(&This->dispex); heap_free(This); } @@ -954,6 +959,8 @@ static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPAR return E_INVALIDARG; /* fall through */ case DISPATCH_METHOD: + if(!This->info) + return JS_E_INVALID_PROPERTY; if(!This->obj) return E_UNEXPECTED; hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei); @@ -972,7 +979,7 @@ static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPAR if(!caller) return E_ACCESSDENIED;
- name_len = SysStringLen(This->info->name); + name_len = This->info ? SysStringLen(This->info->name) : wcslen(function_props[This->idx].name); ptr = str = SysAllocStringLen(NULL, name_len + ARRAY_SIZE(func_prefixW) + ARRAY_SIZE(func_suffixW)); if(!str) return E_OUTOFMEMORY; @@ -980,7 +987,7 @@ static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPAR memcpy(ptr, func_prefixW, sizeof(func_prefixW)); ptr += ARRAY_SIZE(func_prefixW);
- memcpy(ptr, This->info->name, name_len*sizeof(WCHAR)); + memcpy(ptr, This->info ? This->info->name : function_props[This->idx].name, name_len*sizeof(WCHAR)); ptr += name_len;
memcpy(ptr, func_suffixW, sizeof(func_suffixW)); @@ -999,12 +1006,18 @@ static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPAR
static HRESULT function_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, DISPID *dispid) { + func_disp_t *This = impl_from_DispatchEx(dispex); DWORD i;
for(i = 0; i < ARRAY_SIZE(function_props); i++) { if(!wcsicmp(name, function_props[i].name)) { if((flags & fdexNameCaseSensitive) && wcscmp(name, function_props[i].name)) return DISP_E_UNKNOWNNAME; + + /* methods (apply, call) can't be chained */ + if(!This->info && function_props[i].method) + return DISP_E_UNKNOWNNAME; + *dispid = MSHTML_DISPID_CUSTOM_MIN + i; return S_OK; } @@ -1021,6 +1034,10 @@ static HRESULT function_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD fl if(idx >= ARRAY_SIZE(function_props)) return DISP_E_UNKNOWNNAME;
+ /* methods (apply, call) can't be chained */ + if(!This->info && function_props[idx].method) + return DISP_E_UNKNOWNNAME; + switch(flags) { case DISPATCH_METHOD|DISPATCH_PROPERTYGET: if(!res) @@ -1031,6 +1048,16 @@ static HRESULT function_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD fl return function_props[idx].method(This, params, res, ei); return JS_E_INVALID_PROPERTY; case DISPATCH_PROPERTYGET: + if(function_props[idx].method) { + func_disp_t *disp = create_func_disp(dispex, NULL); + if(!disp) + return E_OUTOFMEMORY; + disp->idx = idx; + + V_VT(res) = VT_DISPATCH; + V_DISPATCH(res) = (IDispatch*)&disp->dispex.IDispatchEx_iface; + break; + } V_VT(res) = VT_EMPTY; /* IE returns undefined */ break; default:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98340
Your paranoid android.
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
It is a prop, not a method.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This is needed for the tests in the next patch to pass properly, otherwise an exception is generated, because mshtml handles DISPATCH_METHOD differently (as it should).
dlls/jscript/enumerator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c index eb6baf6..bdbebf7 100644 --- a/dlls/jscript/enumerator.c +++ b/dlls/jscript/enumerator.c @@ -244,7 +244,7 @@ static HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **re /* Try to get a IEnumVARIANT by _NewEnum */ VariantInit(&varresult); hres = IDispatch_Invoke(obj, DISPID_NEWENUM, &IID_NULL, LOCALE_NEUTRAL, - DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL); + DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL); if (FAILED(hres)) { WARN("Enumerator: no DISPID_NEWENUM.\n");
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/documentmode.js | 137 ++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+)
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index d4704a4..1fb0721 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -794,3 +794,140 @@ sync_test("elem_attr", function() { r = Object.prototype.toString.call(elem); ok(r === (v < 9 ? "[object Object]" : "[object HTMLDivElement]"), "toString returned " + r); }); + +sync_test("builtin_obj", function() { + var v = document.documentMode; + var f = document.createElement; + var e; + + if(v < 9) { + ok(!(window instanceof Object), "window instance of Object"); + ok(!(document instanceof Object), "document instance of Object"); + ok(!(f.apply instanceof Function), "f.apply instance of Function"); + ok(!(f.call instanceof Function), "f.call instance of Function"); + ok(f.arguments === undefined, "f.arguments = " + f.arguments); + ok(f.length === undefined, "f.length = " + f.length); + e = 0; + try { + f.toString(); + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.toString] e = " + e); + } + else { + ok(window instanceof Object, "window not instance of Object"); + ok(document instanceof Object, "document not instance of Object"); + ok(Object.isExtensible(window), "window is not extensible"); + ok(Object.isExtensible(document), "document is not extensible"); + + ok(f.toString() === "\nfunction createElement() {\n [native code]\n}\n", "f.toString() = " + f.toString()); + ok(Object.getPrototypeOf(f) === Function.prototype, "unexpected document.createElement prototype"); + ok(Object.getPrototypeOf(f.apply) === Function.prototype, "unexpected f.apply prototype"); + ok(Object.getPrototypeOf(f.call) === Function.prototype, "unexpected f.call prototype"); + } + + e = 0; + try { + f.call(Object, "div"); + }catch(ex) { + e = ex.number; + } + ok(e === (v < 9 ? 0xa0005 : 0x0ffff) - 0x80000000, "[f.call(Object, 'div')] e = " + e); + + var elem = f.call(document, "div"); + elem.setAttribute("class", "cls"); + elem.setAttribute("className", "cls"); + ok(elem.className === "cls", "elem.className = " + elem.className); + + if(v < 9) { + e = 0; + try { + elem = f.call.call(f, document, "div"); + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[elem = f.call.call(f, document, 'div')] e = " + e); + e = 0; + try { + f = f.bind(document); + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.bind(document)] e = " + e); + elem = f.apply(document, ["style"]); + document.body.appendChild(elem); + + var enumerator = new Enumerator(document.getElementsByTagName("style")); + var enum_elem = enumerator.item(); + enumerator.moveNext(); + ok(enum_elem === elem, "enum_elem = " + enum_elem); + ok(enumerator.atEnd(), "enumerator not at end"); + + e = 0; + try { + f.apply = 0; + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.apply = 0] e = " + e); + e = 0; + try { + f.call = function() { }; + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.call = function() { }] e = " + e); + e = 0; + try { + f.arguments = 0; + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.arguments = 0] e = " + e); + e = 0; + try { + f.length = 0; + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.length = 0] e = " + e); + + f = f.apply; + ok(f.arguments === undefined, "f.apply.arguments = " + f.arguments); + ok(f.length === undefined, "f.apply.length = " + f.length); + e = 0; + try { + f.toString(); + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.apply.toString] e = " + e); + e = 0; + try { + f(document, ["style"]); + }catch(ex) { + e = ex.number; + } + ok(e === 0xa01b6 - 0x80000000, "[f.apply() indirect] e = " + e); + } + else { + elem = f.call.call(f, document, "div"); + f = f.bind(document); + elem = f.apply(null, ["style"]); + document.body.appendChild(elem); + + try { + var enumerator = new Enumerator(document.getElementsByTagName("style")); + }catch(ex) { + e = ex.number; + } + todo_wine. + ok(e === 0xa01c3 - 0x80000000, "[style Enumerator] e = " + e); + + f.apply = 0; + f.call = function() { }; + ok(f.apply === 0, "changed f.apply = ", f.apply); + ok(f.call instanceof Function, "changed f.call not instance of Function"); + } +});
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98342
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: htmldoc.c:3084: Test failed: Incorrect error code: -2146697211 htmldoc.c:3089: Test failed: Page address: L"http://test.winehq.org/tests/winehq_snapshot/" htmldoc.c:5861: Test failed: expected OnChanged_1012 htmldoc.c:5862: Test failed: expected Exec_HTTPEQUIV htmldoc.c:5864: Test failed: expected Exec_SETTITLE htmldoc.c:5905: Test failed: expected FireNavigateComplete2
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/enumerator.c | 4 ++-- dlls/jscript/error.c | 1 + dlls/jscript/jscript.h | 1 + dlls/jscript/jscript.rc | 1 + dlls/jscript/resource.h | 1 + dlls/mshtml/tests/documentmode.js | 1 - 6 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c index bdbebf7..5255448 100644 --- a/dlls/jscript/enumerator.c +++ b/dlls/jscript/enumerator.c @@ -248,7 +248,7 @@ static HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **re if (FAILED(hres)) { WARN("Enumerator: no DISPID_NEWENUM.\n"); - return E_INVALIDARG; + return JS_E_OBJECT_NOT_COLLECTION; }
if ((V_VT(&varresult) == VT_DISPATCH) || (V_VT(&varresult) == VT_UNKNOWN)) @@ -259,7 +259,7 @@ static HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **re else { FIXME("Enumerator: NewEnum unexpected type of varresult (%d).\n", V_VT(&varresult)); - hres = E_INVALIDARG; + hres = JS_E_OBJECT_NOT_COLLECTION; } VariantClear(&varresult); if (FAILED(hres)) diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index bb9e5a2..63c957f 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -461,6 +461,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_INVALID_PROPERTY: case JS_E_INVALID_ACTION: case JS_E_MISSING_ARG: + case JS_E_OBJECT_NOT_COLLECTION: case JS_E_FUNCTION_EXPECTED: case JS_E_DATE_EXPECTED: case JS_E_NUMBER_EXPECTED: diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 1d14271..58433d0 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -573,6 +573,7 @@ static inline DWORD make_grfdex(script_ctx_t *ctx, DWORD flags) #define JS_E_INVALID_PROPERTY MAKE_JSERROR(IDS_NO_PROPERTY) #define JS_E_INVALID_ACTION MAKE_JSERROR(IDS_UNSUPPORTED_ACTION) #define JS_E_MISSING_ARG MAKE_JSERROR(IDS_ARG_NOT_OPT) +#define JS_E_OBJECT_NOT_COLLECTION MAKE_JSERROR(IDS_OBJECT_NOT_COLLECTION) #define JS_E_SYNTAX MAKE_JSERROR(IDS_SYNTAX_ERROR) #define JS_E_MISSING_SEMICOLON MAKE_JSERROR(IDS_SEMICOLON) #define JS_E_MISSING_LBRACKET MAKE_JSERROR(IDS_LBRACKET) diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index 50e2c30..45e1e27 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -33,6 +33,7 @@ STRINGTABLE IDS_NO_PROPERTY "Object doesn't support this property or method" IDS_UNSUPPORTED_ACTION "Object doesn't support this action" IDS_ARG_NOT_OPT "Argument not optional" + IDS_OBJECT_NOT_COLLECTION "Object not a collection" IDS_SYNTAX_ERROR "Syntax error" IDS_SEMICOLON "Expected ';'" IDS_LBRACKET "Expected '('" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index b17f9fb..68e88db 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -31,6 +31,7 @@ #define IDS_NO_PROPERTY 0x01B6 #define IDS_UNSUPPORTED_ACTION 0x01BD #define IDS_ARG_NOT_OPT 0x01c1 +#define IDS_OBJECT_NOT_COLLECTION 0x01c3 #define IDS_SYNTAX_ERROR 0x03EA #define IDS_SEMICOLON 0x03EC #define IDS_LBRACKET 0x03ED diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 1fb0721..5e4292c 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -922,7 +922,6 @@ sync_test("builtin_obj", function() { }catch(ex) { e = ex.number; } - todo_wine. ok(e === 0xa01c3 - 0x80000000, "[style Enumerator] e = " + e);
f.apply = 0;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98343
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: htmldoc.c:3084: Test failed: Incorrect error code: -2146697211 htmldoc.c:3089: Test failed: Page address: L"http://test.winehq.org/tests/winehq_snapshot/" htmldoc.c:5861: Test failed: expected OnChanged_1012 htmldoc.c:5862: Test failed: expected Exec_HTTPEQUIV htmldoc.c:5864: Test failed: expected Exec_SETTITLE htmldoc.c:5905: Test failed: expected FireNavigateComplete2
=== debiant2 (64 bit WoW report) ===
mshtml: script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?9:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?10:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false" script.c:624: Test failed: L"/index.html?11:delete_prop: delete returned false"