From: Jacek Caban jacek@codeweavers.com
--- dlls/jscript/dispex.c | 10 ++- dlls/jscript/jscript.c | 4 +- dlls/jscript/jscript.h | 2 +- dlls/jscript/jsdisp.idl | 2 +- dlls/mshtml/dispex.c | 128 ++++++++++++++++++++++++++++++++--- dlls/mshtml/htmlwindow.c | 6 ++ dlls/mshtml/mshtml_private.h | 9 +++ dlls/mshtml/omnavigator.c | 17 ++--- dlls/mshtml/script.c | 1 + dlls/mshtml/tests/es5.js | 17 +++++ 10 files changed, 172 insertions(+), 24 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 95954866345..eec8bf7c1e9 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -3495,15 +3495,21 @@ static const builtin_info_t HostObject_info = { .to_string = HostObject_to_string, };
-HRESULT init_host_object(script_ctx_t *ctx, IWineJSDispatchHost *host_iface, IWineJSDispatch **ret) +HRESULT init_host_object(script_ctx_t *ctx, IWineJSDispatchHost *host_iface, IWineJSDispatch *prototype_iface, + IWineJSDispatch **ret) { HostObject *host_obj; + jsdisp_t *prototype; HRESULT hres;
if(!(host_obj = calloc(1, sizeof(*host_obj)))) return E_OUTOFMEMORY;
- hres = init_dispex(&host_obj->jsdisp, ctx, &HostObject_info, ctx->object_prototype); + if(prototype_iface) + prototype = impl_from_IWineJSDispatch(prototype_iface); + else + prototype = ctx->object_prototype; + hres = init_dispex(&host_obj->jsdisp, ctx, &HostObject_info, prototype); if(FAILED(hres)) { free(host_obj); return hres; diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 4d53e8a6832..fac025366e9 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -1452,10 +1452,10 @@ static ULONG WINAPI WineJScript_Release(IWineJScript *iface) }
static HRESULT WINAPI WineJScript_InitHostObject(IWineJScript *iface, IWineJSDispatchHost *host_obj, - IWineJSDispatch **ret) + IWineJSDispatch *prototype, IWineJSDispatch **ret) { JScript *This = impl_from_IWineJScript(iface); - return init_host_object(This->ctx, host_obj, ret); + return init_host_object(This->ctx, host_obj, prototype, ret); }
static const IWineJScriptVtbl WineJScriptVtbl = { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 48738ca82f4..3691ca562f8 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -242,7 +242,7 @@ enum jsdisp_enum_type { HRESULT create_dispex(script_ctx_t*,const builtin_info_t*,jsdisp_t*,jsdisp_t**); HRESULT init_dispex(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*); HRESULT init_dispex_from_constr(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*); -HRESULT init_host_object(script_ctx_t*,IWineJSDispatchHost*,IWineJSDispatch**); +HRESULT init_host_object(script_ctx_t*,IWineJSDispatchHost*,IWineJSDispatch*,IWineJSDispatch**);
HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,WORD,unsigned,jsval_t*,jsval_t*); HRESULT disp_call_name(script_ctx_t*,IDispatch*,const WCHAR*,WORD,unsigned,jsval_t*,jsval_t*); diff --git a/dlls/jscript/jsdisp.idl b/dlls/jscript/jsdisp.idl index 518edabba9d..35419178e97 100644 --- a/dlls/jscript/jsdisp.idl +++ b/dlls/jscript/jsdisp.idl @@ -75,5 +75,5 @@ interface IWineJSDispatchHost : IDispatchEx ] interface IWineJScript : IUnknown { - HRESULT InitHostObject(IWineJSDispatchHost *host_obj, IWineJSDispatch **ret); + HRESULT InitHostObject(IWineJSDispatchHost *host_obj, IWineJSDispatch *prototype, IWineJSDispatch **ret); } diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 2ee0bea7986..2d61848a296 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -45,6 +45,8 @@ static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg = }; static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
+static HRESULT get_prototype(HTMLInnerWindow *script_global, prototype_id_t id, DispatchEx **ret); + typedef struct { IID iid; VARIANT default_value; @@ -1749,7 +1751,7 @@ static dispex_data_t *ensure_dispex_info(dispex_static_data_t *desc, compat_mode return desc->info_cache[compat_mode]; }
-static void init_host_object(DispatchEx *dispex, HTMLInnerWindow *script_global) +static void init_host_object(DispatchEx *dispex, HTMLInnerWindow *script_global, DispatchEx *prototype) { HRESULT hres;
@@ -1760,7 +1762,7 @@ static void init_host_object(DispatchEx *dispex, HTMLInnerWindow *script_global) initialize_script_global(script_global); if(script_global->jscript && !dispex->jsdisp) { hres = IWineJScript_InitHostObject(script_global->jscript, &dispex->IWineJSDispatchHost_iface, - &dispex->jsdisp); + prototype ? prototype->jsdisp : NULL, &dispex->jsdisp); if(FAILED(hres)) ERR("Failed to initialize jsdisp: %08lx\n", hres); } @@ -1769,14 +1771,24 @@ static void init_host_object(DispatchEx *dispex, HTMLInnerWindow *script_global) static BOOL ensure_real_info(DispatchEx *dispex) { HTMLInnerWindow *script_global; + DispatchEx *prototype = NULL;
if(dispex->info != dispex->info->desc->delayed_init_info) return TRUE;
script_global = dispex->info->vtbl->get_script_global(dispex); + + if(dispex->info->compat_mode >= COMPAT_MODE_IE9 && dispex->info->desc->id) { + HRESULT hres = get_prototype(script_global, dispex->info->desc->id, &prototype); + if(FAILED(hres)) { + ERR("could not get prototype: %08lx\n", hres); + return FALSE; + } + } + if (!(dispex->info = ensure_dispex_info(dispex->info->desc, script_global->doc->document_mode))) return FALSE; - init_host_object(dispex, script_global); + init_host_object(dispex, script_global, prototype); return TRUE; }
@@ -2667,7 +2679,7 @@ const void *dispex_get_vtbl(DispatchEx *dispex) return dispex->info->vtbl; }
-static void init_dispatch_from_desc(DispatchEx *dispex, dispex_data_t *info, HTMLInnerWindow *script_global) +static void init_dispatch_from_desc(DispatchEx *dispex, dispex_data_t *info, HTMLInnerWindow *script_global, DispatchEx *prototype) { dispex->IWineJSDispatchHost_iface.lpVtbl = &JSDispatchHostVtbl; dispex->dynamic_data = NULL; @@ -2675,11 +2687,12 @@ static void init_dispatch_from_desc(DispatchEx *dispex, dispex_data_t *info, HTM dispex->info = info; ccref_init(&dispex->ccref, 1); if(info != info->desc->delayed_init_info) - init_host_object(dispex, script_global); + init_host_object(dispex, script_global, prototype); }
void init_dispatch(DispatchEx *dispex, dispex_static_data_t *data, HTMLInnerWindow *script_global, compat_mode_t compat_mode) { + DispatchEx *prototype = NULL; dispex_data_t *info;
assert(compat_mode < COMPAT_MODE_CNT); @@ -2700,10 +2713,16 @@ void init_dispatch(DispatchEx *dispex, dispex_static_data_t *data, HTMLInnerWind } info = data->delayed_init_info; }else { + if(compat_mode >= COMPAT_MODE_IE9 && data->id) { + HRESULT hres = get_prototype(script_global, data->id, &prototype); + if(FAILED(hres)) + ERR("could not get prototype: %08lx\n", hres); + } + info = ensure_dispex_info(data, compat_mode); }
- init_dispatch_from_desc(dispex, info, script_global); + init_dispatch_from_desc(dispex, info, script_global, prototype); }
void init_dispatch_with_owner(DispatchEx *dispex, dispex_static_data_t *desc, DispatchEx *owner) @@ -2714,6 +2733,83 @@ void init_dispatch_with_owner(DispatchEx *dispex, dispex_static_data_t *desc, Di IHTMLWindow2_Release(&script_global->base.IHTMLWindow2_iface); }
+dispex_static_data_t *object_descriptors[] = { + NULL, +#define X(name) &name ## _dispex, + ALL_PROTOTYPES +#undef X +}; + +static void prototype_destructor(DispatchEx *dispex) +{ + free(dispex); +} + +static HRESULT prototype_find_dispid(DispatchEx *dispex, const WCHAR *name, DWORD flags, DISPID *dispid) +{ + HTMLInnerWindow *script_global; + DispatchEx *constructor; + HRESULT hres; + + if(wcscmp(name, L"constructor")) + return DISP_E_UNKNOWNNAME; + + script_global = get_script_global(dispex); + if(!script_global) + return DISP_E_UNKNOWNNAME; + + hres = get_constructor(script_global, dispex->info->desc->id, &constructor); + if(SUCCEEDED(hres)) { + VARIANT v; + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch *)&constructor->IWineJSDispatchHost_iface; + hres = dispex_define_property(dispex, L"constructor", PROPF_WRITABLE | PROPF_CONFIGURABLE, &v, dispid); + } + IHTMLWindow2_Release(&script_global->base.IHTMLWindow2_iface); + return hres; +} + +static const dispex_static_data_vtbl_t prototype_dispex_vtbl = { + .destructor = prototype_destructor, + .find_dispid = prototype_find_dispid, +}; + +static HRESULT get_prototype(HTMLInnerWindow *script_global, prototype_id_t id, DispatchEx **ret) +{ + compat_mode_t compat_mode = dispex_compat_mode(&script_global->event_target.dispex); + dispex_static_data_t *desc; + DispatchEx *prototype; + dispex_data_t *info; + + if(script_global->prototypes[id]) { + *ret = script_global->prototypes[id]; + return S_OK; + } + + desc = object_descriptors[id]; + info = desc->prototype_info[compat_mode - COMPAT_MODE_IE9]; + if(!info) { + EnterCriticalSection(&cs_dispex_static_data); + info = desc->prototype_info[compat_mode - COMPAT_MODE_IE9]; + if(!info) { + info = preprocess_dispex_data(desc, compat_mode); + if(info) { + info->vtbl = &prototype_dispex_vtbl; + desc->prototype_info[compat_mode - COMPAT_MODE_IE9] = info; + } + } + LeaveCriticalSection(&cs_dispex_static_data); + if(!info) + return E_OUTOFMEMORY; + } + + if(!(prototype = calloc(sizeof(*prototype), 1))) + return E_OUTOFMEMORY; + init_dispatch_from_desc(prototype, info, script_global, NULL); + *ret = script_global->prototypes[id] = prototype; + return S_OK; +} + struct constructor { DispatchEx dispex; @@ -2734,14 +2830,26 @@ static void constructor_destructor(DispatchEx *dispex) static HRESULT constructor_find_dispid(DispatchEx *dispex, const WCHAR *name, DWORD flags, DISPID *dispid) { struct constructor *constr = constr_from_DispatchEx(dispex); - VARIANT v; + HTMLInnerWindow *script_global; + DispatchEx *prototype; + HRESULT hres;
if(wcscmp(name, L"prototype")) return DISP_E_UNKNOWNNAME;
- FIXME("prototype not implemented\n"); - V_VT(&v) = VT_EMPTY; - return dispex_define_property(&constr->dispex, name, 0, &v, dispid); + script_global = get_script_global(&constr->dispex); + if(!script_global) + return DISP_E_UNKNOWNNAME; + + hres = get_prototype(script_global, constr->id, &prototype); + if(SUCCEEDED(hres)) { + VARIANT v; + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch *)&prototype->IWineJSDispatchHost_iface; + hres = dispex_define_property(&constr->dispex, name, 0, &v, dispid); + } + IHTMLWindow2_Release(&script_global->base.IHTMLWindow2_iface); + return hres; }
static const dispex_static_data_vtbl_t constructor_dispex_vtbl = { diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index ac427ded3ba..3569b9caad1 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -3683,6 +3683,10 @@ static void HTMLWindow_traverse(DispatchEx *dispex, nsCycleCollectionTraversalCa traverse_event_target(&This->event_target, cb); LIST_FOR_EACH_ENTRY(child, &This->children, HTMLOuterWindow, sibling_entry) note_cc_edge((nsISupports*)&child->base.IHTMLWindow2_iface, "child", cb); + for(i = 0; i < ARRAYSIZE(This->prototypes); i++) { + if(This->prototypes[i]) + note_cc_edge((nsISupports*)&This->prototypes[i]->IWineJSDispatchHost_iface, "prototypes", cb); + } for(i = 0; i < ARRAYSIZE(This->constructors); i++) { if(This->constructors[i]) note_cc_edge((nsISupports*)&This->constructors[i]->IWineJSDispatchHost_iface, "constructors", cb); @@ -3724,6 +3728,8 @@ static void HTMLWindow_unlink(DispatchEx *dispex) unlink_ref(&This->console); detach_inner_window(This);
+ for(i = 0; i < ARRAYSIZE(This->prototypes); i++) + unlink_ref(&This->prototypes[i]); for(i = 0; i < ARRAYSIZE(This->constructors); i++) unlink_ref(&This->constructors[i]);
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 207fcd7cafa..eb00a7b3629 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -424,9 +424,17 @@ typedef struct { const tid_t* const iface_tids; void (*init_info)(dispex_data_t*,compat_mode_t); dispex_data_t *info_cache[COMPAT_MODE_CNT]; + dispex_data_t *prototype_info[COMPAT_MODE_CNT - COMPAT_MODE_IE9]; dispex_data_t *delayed_init_info; + prototype_id_t id; } dispex_static_data_t;
+#define X(name) extern dispex_static_data_t name ## _dispex; +ALL_PROTOTYPES +#undef X + +extern dispex_static_data_t *object_descriptors[PROT_LAST]; + typedef HRESULT (*dispex_hook_invoke_t)(DispatchEx*,WORD,DISPPARAMS*,VARIANT*, EXCEPINFO*,IServiceProvider*);
@@ -695,6 +703,7 @@ struct HTMLInnerWindow { ULONG navigation_type; ULONG redirect_count;
+ DispatchEx *prototypes[PROT_LAST]; DispatchEx *constructors[PROT_LAST];
ULONGLONG navigation_start_time; diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index 900c4ee0ca7..93a48309e2e 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -202,7 +202,7 @@ static void HTMLDOMImplementation_destructor(DispatchEx *dispex) free(This); }
-static const dispex_static_data_vtbl_t HTMLDOMImplementation_dispex_vtbl = { +static const dispex_static_data_vtbl_t DOMImplementation_dispex_vtbl = { .query_interface = HTMLDOMImplementation_query_interface, .destructor = HTMLDOMImplementation_destructor, .traverse = HTMLDOMImplementation_traverse, @@ -219,12 +219,13 @@ static const tid_t HTMLDOMImplementation_iface_tids[] = { IHTMLDOMImplementation_tid, 0 }; -static dispex_static_data_t HTMLDOMImplementation_dispex = { - "DOMImplementation", - &HTMLDOMImplementation_dispex_vtbl, - DispHTMLDOMImplementation_tid, - HTMLDOMImplementation_iface_tids, - HTMLDOMImplementation_init_dispex_info +dispex_static_data_t DOMImplementation_dispex = { + .name = "DOMImplementation", + .id = PROT_DOMImplementation, + .vtbl = &DOMImplementation_dispex_vtbl, + .disp_tid = DispHTMLDOMImplementation_tid, + .iface_tids = HTMLDOMImplementation_iface_tids, + .init_info = HTMLDOMImplementation_init_dispex_info, };
HRESULT create_dom_implementation(HTMLDocumentNode *doc_node, IHTMLDOMImplementation **ret) @@ -243,7 +244,7 @@ HRESULT create_dom_implementation(HTMLDocumentNode *doc_node, IHTMLDOMImplementa dom_implementation->IHTMLDOMImplementation2_iface.lpVtbl = &HTMLDOMImplementation2Vtbl; dom_implementation->doc = doc_node;
- init_dispatch(&dom_implementation->dispex, &HTMLDOMImplementation_dispex, doc_node->script_global, doc_node->document_mode); + init_dispatch(&dom_implementation->dispex, &DOMImplementation_dispex, doc_node->script_global, doc_node->document_mode);
nsres = nsIDOMDocument_GetImplementation(doc_node->dom_document, &dom_implementation->implementation); if(NS_FAILED(nsres)) { diff --git a/dlls/mshtml/script.c b/dlls/mshtml/script.c index e985912c526..2b6abe6d8e2 100644 --- a/dlls/mshtml/script.c +++ b/dlls/mshtml/script.c @@ -209,6 +209,7 @@ static BOOL init_script_engine(ScriptHost *script_host, IActiveScript *script)
hres = IWineJScript_InitHostObject(jscript, &script_host->window->event_target.dispex.IWineJSDispatchHost_iface, + NULL, &script_host->window->event_target.dispex.jsdisp); if(FAILED(hres)) ERR("Could not initialize script global: %08lx\n", hres); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 2aec564f77a..294b9fed490 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2732,7 +2732,24 @@ sync_test("prototypes", function() { todo_wine. ok(DOMImplementation == "[object DOMImplementation]", "DOMImplementation = " + DOMImplementation);
+ var proto = constr.prototype; + todo_wine. + ok(proto == "[object DOMImplementationPrototype]", "DOMImplementation.prototype = " + proto); + ok(Object.getPrototypeOf(document.implementation) === proto, + "Object.getPrototypeOf(document.implementation) = " + Object.getPrototypeOf(document.implementation)); + ok(Object.getPrototypeOf(proto) === Object.prototype, "Object.getPrototypeOf(proto) = " + Object.getPrototypeOf(proto)); + test_own_data_prop_desc(constr, "prototype", false, false, false); + test_own_data_prop_desc(proto, "constructor", true, false, true); + ok(proto.hasOwnProperty("createHTMLDocument"), "prototype has no own createHTMLDocument property"); + todo_wine. + ok(!document.implementation.hasOwnProperty("createHTMLDocument"), + "prototype has own createHTMLDocument property"); + + ok(proto.constructor === constr, "proto.constructor = " + proto.constructor); + proto.constructor = 1; + ok(proto.constructor === 1, "proto.constructor = " + proto.constructor + " expected 1"); + proto.constructor = constr;
DOMImplementation = 1; ok(DOMImplementation === 1, "DOMImplementation = " + DOMImplementation + " expected 1");