-- v2: mshtml: Implement toJSON() for Performance. mshtml: Implement toJSON() for PerformanceNavigation. mshtml: Implement toJSON() for PerformanceTiming. mshtml: Don't expose toString from Performance* objects' prototypes in mshtml: Expose toJSON only in IE9+ modes.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/omnavigator.c | 46 ++++++++++++++-------- dlls/mshtml/tests/documentmode.js | 63 +++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 15 deletions(-)
diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index 942bd5bbe70..1bd370bcffd 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -24,6 +24,7 @@ #include "winbase.h" #include "winuser.h" #include "ole2.h" +#include "mshtmdid.h"
#include "wine/debug.h"
@@ -1708,15 +1709,20 @@ static const dispex_static_data_vtbl_t HTMLPerformanceTiming_dispex_vtbl = { .unlink = HTMLPerformanceTiming_unlink };
-static const tid_t PerformanceTiming_iface_tids[] = { - IHTMLPerformanceTiming_tid, - 0 -}; +static void PerformanceTiming_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +{ + static const dispex_hook_t hooks[] = { + {DISPID_IHTMLPERFORMANCETIMING_TOJSON}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IHTMLPerformanceTiming_tid, mode < COMPAT_MODE_IE9 ? hooks : NULL); +} + dispex_static_data_t PerformanceTiming_dispex = { .id = OBJID_PerformanceTiming, .vtbl = &HTMLPerformanceTiming_dispex_vtbl, .disp_tid = IHTMLPerformanceTiming_tid, - .iface_tids = PerformanceTiming_iface_tids, + .init_info = PerformanceTiming_init_dispex_info, };
typedef struct { @@ -1829,15 +1835,20 @@ static const dispex_static_data_vtbl_t HTMLPerformanceNavigation_dispex_vtbl = { .unlink = HTMLPerformanceNavigation_unlink };
-static const tid_t PerformanceNavigation_iface_tids[] = { - IHTMLPerformanceNavigation_tid, - 0 -}; +static void PerformanceNavigation_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +{ + static const dispex_hook_t hooks[] = { + {DISPID_IHTMLPERFORMANCENAVIGATION_TOJSON}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IHTMLPerformanceNavigation_tid, mode < COMPAT_MODE_IE9 ? hooks : NULL); +} + dispex_static_data_t PerformanceNavigation_dispex = { .id = OBJID_PerformanceNavigation, .vtbl = &HTMLPerformanceNavigation_dispex_vtbl, .disp_tid = IHTMLPerformanceNavigation_tid, - .iface_tids = PerformanceNavigation_iface_tids, + .init_info = PerformanceNavigation_init_dispex_info, };
typedef struct { @@ -1992,15 +2003,20 @@ static const dispex_static_data_vtbl_t HTMLPerformance_dispex_vtbl = { .unlink = HTMLPerformance_unlink };
-static const tid_t Performance_iface_tids[] = { - IHTMLPerformance_tid, - 0 -}; +static void Performance_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +{ + static const dispex_hook_t hooks[] = { + {DISPID_IHTMLPERFORMANCE_TOJSON}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IHTMLPerformance_tid, mode < COMPAT_MODE_IE9 ? hooks : NULL); +} + dispex_static_data_t Performance_dispex = { .id = OBJID_Performance, .vtbl = &HTMLPerformance_dispex_vtbl, .disp_tid = IHTMLPerformance_tid, - .iface_tids = Performance_iface_tids, + .init_info = Performance_init_dispex_info, };
HRESULT create_performance(HTMLInnerWindow *window, IHTMLPerformance **ret) diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index dd228ee1bae..bfec46b37d2 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1008,6 +1008,56 @@ sync_test("domimpl_props", function() { test_exposed("createHTMLDocument", v >= 9); });
+sync_test("perf_props", function() { + var obj = window.performance, name = "Performance"; + var v = document.documentMode; + + function test_exposed(prop, expect) { + if(expect) + ok(prop in obj, prop + " not found in " + name + "."); + else + ok(!(prop in obj), prop + " found in " + name + "."); + } + + test_exposed("navigation", true); + test_exposed("timing", true); + test_exposed("toJSON", v >= 9); + test_exposed("toString", true); + + obj = window.performance.navigation, name = "PerformanceNavigation"; + + test_exposed("redirectCount", true); + test_exposed("type", true); + test_exposed("toJSON", v >= 9); + test_exposed("toString", true); + + obj = window.performance.timing, name = "PerformanceTiming"; + + test_exposed("connectEnd", true); + test_exposed("connectStart", true); + test_exposed("domComplete", true); + test_exposed("domContentLoadedEventEnd", true); + test_exposed("domContentLoadedEventStart", true); + test_exposed("domInteractive", true); + test_exposed("domLoading", true); + test_exposed("domainLookupEnd", true); + test_exposed("domainLookupStart", true); + test_exposed("fetchStart", true); + test_exposed("loadEventEnd", true); + test_exposed("loadEventStart", true); + test_exposed("msFirstPaint", true); + test_exposed("navigationStart", true); + test_exposed("redirectEnd", true); + test_exposed("redirectStart", true); + test_exposed("requestStart", true); + test_exposed("responseEnd", true); + test_exposed("responseStart", true); + test_exposed("unloadEventEnd", true); + test_exposed("unloadEventStart", true); + test_exposed("toJSON", v >= 9); + test_exposed("toString", true); +}); + sync_test("xhr_props", function() { var xhr = new XMLHttpRequest();
@@ -4412,6 +4462,19 @@ sync_test("prototype props", function() { ]); if(v >= 11) check(PageTransitionEvent, [ "persisted" ]); + check(Performance, [ + "clearMarks", "clearMeasures", "clearResourceTimings", "getEntries", "getEntriesByName", "getEntriesByType", "getMarks", + "getMeasures", "mark", "measure", "navigation", ["now",10], "setResourceTimingBufferSize", "timing", "toJSON" + ], [ + "clearMarks", "clearMeasures", "clearResourceTimings", "getEntries", "getEntriesByName", "getEntriesByType", "getMarks", + "getMeasures", "mark", "measure", ["now",10], "setResourceTimingBufferSize", "toString" + ]); + check(PerformanceNavigation, [ "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED", "redirectCount", "toJSON", "type" ], [ "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED", "toString" ]); + check(PerformanceTiming, [ + "connectEnd", "connectStart", "domComplete", "domContentLoadedEventEnd", "domContentLoadedEventStart", "domInteractive", "domLoading", + "domainLookupEnd", "domainLookupStart", "fetchStart", "loadEventEnd", "loadEventStart", "msFirstPaint", "navigationStart", "redirectEnd", + "redirectStart", "requestStart", "responseEnd", "responseStart", "toJSON", "unloadEventEnd", "unloadEventStart" + ], [ "toString" ]); if(v >= 10) check(ProgressEvent, [ "initProgressEvent", "lengthComputable", "loaded", "total" ]); check(StorageEvent, [ "initStorageEvent", "key", "newValue", "oldValue", "storageArea", "url" ]);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/omnavigator.c | 18 +++++++++++++++--- dlls/mshtml/tests/documentmode.js | 6 +++--- 2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index 1bd370bcffd..a49387f1ed0 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -1715,7 +1715,11 @@ static void PerformanceTiming_init_dispex_info(dispex_data_t *info, compat_mode_ {DISPID_IHTMLPERFORMANCETIMING_TOJSON}, {DISPID_UNKNOWN} }; - dispex_info_add_interface(info, IHTMLPerformanceTiming_tid, mode < COMPAT_MODE_IE9 ? hooks : NULL); + static const dispex_hook_t ie9_hooks[] = { + {DISPID_IHTMLPERFORMANCETIMING_TOSTRING}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IHTMLPerformanceTiming_tid, mode < COMPAT_MODE_IE9 ? hooks : ie9_hooks); }
dispex_static_data_t PerformanceTiming_dispex = { @@ -1841,7 +1845,11 @@ static void PerformanceNavigation_init_dispex_info(dispex_data_t *info, compat_m {DISPID_IHTMLPERFORMANCENAVIGATION_TOJSON}, {DISPID_UNKNOWN} }; - dispex_info_add_interface(info, IHTMLPerformanceNavigation_tid, mode < COMPAT_MODE_IE9 ? hooks : NULL); + static const dispex_hook_t ie9_hooks[] = { + {DISPID_IHTMLPERFORMANCENAVIGATION_TOSTRING}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IHTMLPerformanceNavigation_tid, mode < COMPAT_MODE_IE9 ? hooks : ie9_hooks); }
dispex_static_data_t PerformanceNavigation_dispex = { @@ -2009,7 +2017,11 @@ static void Performance_init_dispex_info(dispex_data_t *info, compat_mode_t mode {DISPID_IHTMLPERFORMANCE_TOJSON}, {DISPID_UNKNOWN} }; - dispex_info_add_interface(info, IHTMLPerformance_tid, mode < COMPAT_MODE_IE9 ? hooks : NULL); + static const dispex_hook_t ie9_hooks[] = { + {DISPID_IHTMLPERFORMANCE_TOSTRING}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IHTMLPerformance_tid, mode < COMPAT_MODE_IE9 ? hooks : ie9_hooks); }
dispex_static_data_t Performance_dispex = { diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index bfec46b37d2..faac18c45a3 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -4467,14 +4467,14 @@ sync_test("prototype props", function() { "getMeasures", "mark", "measure", "navigation", ["now",10], "setResourceTimingBufferSize", "timing", "toJSON" ], [ "clearMarks", "clearMeasures", "clearResourceTimings", "getEntries", "getEntriesByName", "getEntriesByType", "getMarks", - "getMeasures", "mark", "measure", ["now",10], "setResourceTimingBufferSize", "toString" + "getMeasures", "mark", "measure", ["now",10], "setResourceTimingBufferSize" ]); - check(PerformanceNavigation, [ "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED", "redirectCount", "toJSON", "type" ], [ "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED", "toString" ]); + check(PerformanceNavigation, [ "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED", "redirectCount", "toJSON", "type" ], [ "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED" ]); check(PerformanceTiming, [ "connectEnd", "connectStart", "domComplete", "domContentLoadedEventEnd", "domContentLoadedEventStart", "domInteractive", "domLoading", "domainLookupEnd", "domainLookupStart", "fetchStart", "loadEventEnd", "loadEventStart", "msFirstPaint", "navigationStart", "redirectEnd", "redirectStart", "requestStart", "responseEnd", "responseStart", "toJSON", "unloadEventEnd", "unloadEventStart" - ], [ "toString" ]); + ]); if(v >= 10) check(ProgressEvent, [ "initProgressEvent", "lengthComputable", "loaded", "total" ]); check(StorageEvent, [ "initStorageEvent", "key", "newValue", "oldValue", "storageArea", "url" ]);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/jscript.c | 13 ++++++++ dlls/jscript/jsdisp.idl | 1 + dlls/mshtml/dispex.c | 36 ++++++++++++++++++++++ dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/omnavigator.c | 6 ++-- dlls/mshtml/tests/dom.c | 60 ++++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 48 +++++++++++++++++++++++++++++ 7 files changed, 163 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index a7f468a677a..b65b95b1e0f 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -1465,6 +1465,18 @@ static HRESULT WINAPI WineJScript_InitHostConstructor(IWineJScript *iface, IWine return init_host_constructor(This->ctx, constr, method_name, ret); }
+static HRESULT WINAPI WineJScript_CreateObject(IWineJScript *iface, IWineJSDispatch **ret) +{ + JScript *This = impl_from_IWineJScript(iface); + jsdisp_t *jsdisp; + HRESULT hres; + + hres = create_object(This->ctx, NULL, &jsdisp); + if(SUCCEEDED(hres)) + *ret = &jsdisp->IWineJSDispatch_iface; + return hres; +} + static HRESULT WINAPI WineJScript_FillGlobals(IWineJScript *iface, IWineJSDispatchHost *script_global) { JScript *This = impl_from_IWineJScript(iface); @@ -1477,6 +1489,7 @@ static const IWineJScriptVtbl WineJScriptVtbl = { WineJScript_Release, WineJScript_InitHostObject, WineJScript_InitHostConstructor, + WineJScript_CreateObject, WineJScript_FillGlobals, };
diff --git a/dlls/jscript/jsdisp.idl b/dlls/jscript/jsdisp.idl index abef1f32f61..22349ecb50c 100644 --- a/dlls/jscript/jsdisp.idl +++ b/dlls/jscript/jsdisp.idl @@ -87,5 +87,6 @@ interface IWineJScript : IUnknown { HRESULT InitHostObject(IWineJSDispatchHost *host_obj, IWineJSDispatch *prototype, UINT32 flags, IWineJSDispatch **ret); HRESULT InitHostConstructor(IWineJSDispatchHost *constr, const WCHAR *method_name, IWineJSDispatch **ret); + HRESULT CreateObject(IWineJSDispatch **ret); HRESULT FillGlobals(IWineJSDispatchHost *script_global); } diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index d281929e09e..d7c8b667724 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -2901,6 +2901,42 @@ static IWineJSDispatchHostVtbl JSDispatchHostVtbl = { JSDispatchHost_ToString, };
+HRESULT dispex_builtin_props_to_json(DispatchEx *dispex, HTMLInnerWindow *window, VARIANT *ret) +{ + func_info_t *func, *end; + IWineJSDispatch *json; + HRESULT hres; + VARIANT var; + DISPPARAMS dp = { 0 }; + + if(!window->jscript) + return E_UNEXPECTED; + + hres = IWineJScript_CreateObject(window->jscript, &json); + if(FAILED(hres)) + return hres; + + for(func = dispex->info->funcs, end = func + dispex->info->func_cnt; func < end; func++) { + if(func->func_disp_idx != -1) + continue; + hres = builtin_propget(dispex, func, &dp, &var, NULL, NULL); + if(SUCCEEDED(hres)) { + hres = IWineJSDispatch_DefineProperty(json, func->name, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, &var); + VariantClear(&var); + } + if(FAILED(hres)) { + IWineJSDispatch_Release(json); + return hres; + } + } + + if(ret) { + V_VT(ret) = VT_DISPATCH; + V_DISPATCH(ret) = (IDispatch*)json; + } + return hres; +} + static nsresult NSAPI dispex_traverse(void *ccp, void *p, nsCycleCollectionTraversalCallback *cb) { DispatchEx *This = impl_from_IWineJSDispatchHost(p); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 7d61ba0d74f..d014c34ba7c 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -638,6 +638,7 @@ HRESULT remove_attribute(DispatchEx*,DISPID,VARIANT_BOOL*); BOOL is_builtin_attribute(DispatchEx*,DISPID); BOOL is_builtin_value(DispatchEx*,DISPID); HRESULT dispex_get_dynid(DispatchEx*,const WCHAR*,BOOL,DISPID*); +HRESULT dispex_builtin_props_to_json(DispatchEx*,HTMLInnerWindow*,VARIANT*); void release_typelib(void); HRESULT get_class_typeinfo(const CLSID*,ITypeInfo**); const void *dispex_get_vtbl(DispatchEx*); diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index a49387f1ed0..2316f539702 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -1627,8 +1627,10 @@ static HRESULT WINAPI HTMLPerformanceTiming_toString(IHTMLPerformanceTiming *ifa static HRESULT WINAPI HTMLPerformanceTiming_toJSON(IHTMLPerformanceTiming *iface, VARIANT *p) { HTMLPerformanceTiming *This = impl_from_IHTMLPerformanceTiming(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + + TRACE("(%p)->(%p)\n", This, p); + + return dispex_builtin_props_to_json(&This->dispex, This->window, p); }
static const IHTMLPerformanceTimingVtbl HTMLPerformanceTimingVtbl = { diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 17b4f63711d..f93da5fc159 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -52,6 +52,16 @@ static enum { static const char doc_blank[] = "<html></html>";
+static const char doc_blank_ie8[] = + "<!DOCTYPE html>\n" + "<html>" + " <head>" + " <meta http-equiv="x-ua-compatible" content="IE=8" />" + " </head>" + " <body>" + " </body>" + "</html>"; + static const char doc_blank_ie9[] = "<!DOCTYPE html>\n" "<html>" @@ -12219,6 +12229,54 @@ static void test_quirks_mode_offsetHeight(IHTMLDocument2 *doc) IHTMLElement_Release(elem); }
+static void test_quirks_mode_perf_toJSON(IHTMLDocument2 *doc) +{ + IHTMLPerformanceTiming *timing; + IHTMLPerformance *perf; + DISPPARAMS dp = { 0 }; + IHTMLWindow2 *window; + IDispatchEx *dispex; + DISPID dispid; + HRESULT hres; + VARIANT var; + BSTR bstr; + + hres = IHTMLDocument2_get_parentWindow(doc, &window); + ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres); + + hres = IHTMLWindow2_QueryInterface(window, &IID_IDispatchEx, (void**)&dispex); + ok(hres == S_OK, "QueryInterface(IID_IDispatchEx) failed: %08lx\n", hres); + IHTMLWindow2_Release(window); + + bstr = SysAllocString(L"performance"); + hres = IDispatchEx_GetDispID(dispex, bstr, fdexNameCaseSensitive, &dispid); + ok(hres == S_OK, "GetDispID(performance) failed: %08lx\n", hres); + SysFreeString(bstr); + + V_VT(&var) = VT_EMPTY; + hres = IDispatchEx_InvokeEx(dispex, dispid, 0, DISPATCH_PROPERTYGET, &dp, &var, NULL, NULL); + ok(hres == S_OK, "InvokeEx(performance) failed: %08lx\n", hres); + ok(V_VT(&var) == VT_DISPATCH, "V_VT(performance) = %d\n", V_VT(&var)); + ok(V_DISPATCH(&var) != NULL, "V_DISPATCH(performance) = NULL\n"); + IDispatchEx_Release(dispex); + + hres = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IHTMLPerformance, (void**)&perf); + ok(hres == S_OK, "QueryInterface(IID_IHTMLPerformance) failed: %08lx\n", hres); + ok(perf != NULL, "performance is NULL\n"); + VariantClear(&var); + + hres = IHTMLPerformance_get_timing(perf, &timing); + ok(hres == S_OK, "get_timing failed: %08lx\n", hres); + ok(timing != NULL, "performance.timing is NULL\n"); + + hres = IHTMLPerformanceTiming_toJSON(timing, &var); + ok(hres == E_UNEXPECTED, "timing.toJSON() failed: %08lx\n", hres); + ok(V_VT(&var) == VT_EMPTY, "V_VT(timing.toJSON()) = %d\n", V_VT(&var)); + IHTMLPerformanceTiming_Release(timing); + + IHTMLPerformance_Release(perf); +} + static IHTMLDocument2 *notif_doc; static BOOL doc_complete;
@@ -13638,6 +13696,8 @@ START_TEST(dom) run_domtest(frameset_str, test_frameset); run_domtest(emptydiv_str, test_docfrag); run_domtest(doc_blank, test_replacechild_elems); + run_domtest(doc_blank, test_quirks_mode_perf_toJSON); + run_domtest(doc_blank_ie8, test_quirks_mode_perf_toJSON); run_domtest(doctype_str, test_doctype); run_domtest(case_insens_str, test_case_insens); if(is_ie9plus) { diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 3dfb6458655..62daa58a383 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2594,6 +2594,54 @@ sync_test("functions scope", function() { })(); });
+sync_test("perf toJSON", function() { + var json, objs = [ performance.timing ]; + var non_props = [ "constructor" ]; + + for(var i = 0; i < objs.length; i++) { + var desc, prop, proto = Object.getPrototypeOf(objs[i]), props = Object.getOwnPropertyNames(proto); + var name = Object.prototype.toString.call(objs[i]).slice(8, -1); + + Object.defineProperty(objs[i], "foobar", {writable: true, enumerable: true, configurable: true, value: 1}); + Object.defineProperty(proto, "barfoo", {writable: true, enumerable: true, configurable: true, value: 3}); + json = objs[i].toJSON(); + + ok(Object.getPrototypeOf(json) === Object.prototype, "prototype of " + name + ".toJSON() != Object.prototype"); + ok(typeof json === "object", "typeof " + name + ".toJSON() != object"); + + for(var j = 0; j < non_props.length; j++) { + var idx = props.indexOf(non_props[j]); + if(idx !== -1) + props.splice(idx, 1); + } + + for(var j = 0; j < props.length; j++) { + if(typeof(proto[props[j]]) === "function") + ok(!(props[j] in json), props[j] + " in " + name + ".toJSON()"); + else { + test_own_data_prop_desc(json, props[j], true, true, true); + prop = props[j]; + } + } + + for(var j = 0; j < non_props.length; j++) + ok(!json.hasOwnProperty(non_props[j]), non_props[j] + " in " + name + ".toJSON()"); + ok(!("foobar" in json), "foobar in " + name + ".toJSON()"); + ok(!("barfoo" in json), "barfoo in " + name + ".toJSON()"); + + delete objs[i].foobar; + delete proto.barfoo; + + // test delete a builtin from the prototype and toJSON after + desc = Object.getOwnPropertyDescriptor(proto, prop); + delete proto[prop]; + ok(!(prop in objs[i]), prop + " in " + name + " after delete"); + json = objs[i].toJSON(); + ok(json.hasOwnProperty(prop), name + ".toJSON() does not have " + prop + " after delete"); + Object.defineProperty(proto, prop, desc); + } +}); + sync_test("console", function() { var except
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/omnavigator.c | 6 ++++-- dlls/mshtml/tests/dom.c | 10 ++++++++++ dlls/mshtml/tests/es5.js | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index 2316f539702..9706643f776 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -1778,8 +1778,10 @@ static HRESULT WINAPI HTMLPerformanceNavigation_toString(IHTMLPerformanceNavigat static HRESULT WINAPI HTMLPerformanceNavigation_toJSON(IHTMLPerformanceNavigation *iface, VARIANT *p) { HTMLPerformanceNavigation *This = impl_from_IHTMLPerformanceNavigation(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + + TRACE("(%p)->(%p)\n", This, p); + + return dispex_builtin_props_to_json(&This->dispex, This->window, p); }
static const IHTMLPerformanceNavigationVtbl HTMLPerformanceNavigationVtbl = { diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index f93da5fc159..9778a5b1ad8 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -12231,6 +12231,7 @@ static void test_quirks_mode_offsetHeight(IHTMLDocument2 *doc)
static void test_quirks_mode_perf_toJSON(IHTMLDocument2 *doc) { + IHTMLPerformanceNavigation *nav; IHTMLPerformanceTiming *timing; IHTMLPerformance *perf; DISPPARAMS dp = { 0 }; @@ -12265,6 +12266,15 @@ static void test_quirks_mode_perf_toJSON(IHTMLDocument2 *doc) ok(perf != NULL, "performance is NULL\n"); VariantClear(&var);
+ hres = IHTMLPerformance_get_navigation(perf, &nav); + ok(hres == S_OK, "get_navigation failed: %08lx\n", hres); + ok(nav != NULL, "performance.navigation is NULL\n"); + + hres = IHTMLPerformanceNavigation_toJSON(nav, &var); + ok(hres == E_UNEXPECTED, "navigation.toJSON() failed: %08lx\n", hres); + ok(V_VT(&var) == VT_EMPTY, "V_VT(navigation.toJSON()) = %d\n", V_VT(&var)); + IHTMLPerformanceNavigation_Release(nav); + hres = IHTMLPerformance_get_timing(perf, &timing); ok(hres == S_OK, "get_timing failed: %08lx\n", hres); ok(timing != NULL, "performance.timing is NULL\n"); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 62daa58a383..347108e5810 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2595,8 +2595,8 @@ sync_test("functions scope", function() { });
sync_test("perf toJSON", function() { - var json, objs = [ performance.timing ]; - var non_props = [ "constructor" ]; + var json, objs = [ performance.navigation, performance.timing ]; + var non_props = [ "constructor", "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED" ];
for(var i = 0; i < objs.length; i++) { var desc, prop, proto = Object.getPrototypeOf(objs[i]), props = Object.getOwnPropertyNames(proto);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 17 ++++++++++++++++- dlls/mshtml/omnavigator.c | 6 ++++-- dlls/mshtml/tests/dom.c | 4 ++++ dlls/mshtml/tests/es5.js | 8 +++++++- 4 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index d7c8b667724..a94f770da3f 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -2903,10 +2903,12 @@ static IWineJSDispatchHostVtbl JSDispatchHostVtbl = {
HRESULT dispex_builtin_props_to_json(DispatchEx *dispex, HTMLInnerWindow *window, VARIANT *ret) { + IWineJSDispatchHost *subdispex_iface; func_info_t *func, *end; IWineJSDispatch *json; HRESULT hres; VARIANT var; + DISPID id; DISPPARAMS dp = { 0 };
if(!window->jscript) @@ -2921,7 +2923,20 @@ HRESULT dispex_builtin_props_to_json(DispatchEx *dispex, HTMLInnerWindow *window continue; hres = builtin_propget(dispex, func, &dp, &var, NULL, NULL); if(SUCCEEDED(hres)) { - hres = IWineJSDispatch_DefineProperty(json, func->name, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, &var); + if(V_VT(&var) == VT_DISPATCH && + SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IWineJSDispatchHost, (void**)&subdispex_iface))) { + if(subdispex_iface->lpVtbl == &JSDispatchHostVtbl) { + DispatchEx *subdispex = impl_from_IWineJSDispatchHost(subdispex_iface); + + if(SUCCEEDED(dispex_get_chain_builtin_id(subdispex, L"toJSON", fdexNameCaseSensitive, &id))) { + VariantClear(&var); + hres = dispex_call_builtin(subdispex, id, &dp, &var, NULL, NULL); + } + } + IWineJSDispatchHost_Release(subdispex_iface); + } + if(SUCCEEDED(hres)) + hres = IWineJSDispatch_DefineProperty(json, func->name, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, &var); VariantClear(&var); } if(FAILED(hres)) { diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index 9706643f776..5d2babd2e09 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -1946,8 +1946,10 @@ static HRESULT WINAPI HTMLPerformance_toString(IHTMLPerformance *iface, BSTR *st static HRESULT WINAPI HTMLPerformance_toJSON(IHTMLPerformance *iface, VARIANT *var) { HTMLPerformance *This = impl_from_IHTMLPerformance(iface); - FIXME("(%p)->(%p)\n", This, var); - return E_NOTIMPL; + + TRACE("(%p)->(%p)\n", This, var); + + return dispex_builtin_props_to_json(&This->dispex, This->window, var); }
static const IHTMLPerformanceVtbl HTMLPerformanceVtbl = { diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 9778a5b1ad8..7250ffea636 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -12266,6 +12266,10 @@ static void test_quirks_mode_perf_toJSON(IHTMLDocument2 *doc) ok(perf != NULL, "performance is NULL\n"); VariantClear(&var);
+ hres = IHTMLPerformance_toJSON(perf, &var); + ok(hres == E_UNEXPECTED, "toJSON() returned: %08lx\n", hres); + ok(V_VT(&var) == VT_EMPTY, "V_VT(toJSON()) = %d\n", V_VT(&var)); + hres = IHTMLPerformance_get_navigation(perf, &nav); ok(hres == S_OK, "get_navigation failed: %08lx\n", hres); ok(nav != NULL, "performance.navigation is NULL\n"); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 347108e5810..6a3f24a265f 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2595,7 +2595,7 @@ sync_test("functions scope", function() { });
sync_test("perf toJSON", function() { - var json, objs = [ performance.navigation, performance.timing ]; + var json, objs = [ performance, performance.navigation, performance.timing ]; var non_props = [ "constructor", "TYPE_BACK_FORWARD", "TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED" ];
for(var i = 0; i < objs.length; i++) { @@ -2640,6 +2640,12 @@ sync_test("perf toJSON", function() { ok(json.hasOwnProperty(prop), name + ".toJSON() does not have " + prop + " after delete"); Object.defineProperty(proto, prop, desc); } + + json = performance.toJSON(); + ok(typeof json.navigation === "object", "JSON'd performance's navigation type = " + typeof json.navigation); + ok(typeof json.navigation.redirectCount === "number", "JSON'd performance's navigation.redirectCount type = " + typeof json.navigation.redirectCount); + ok(Object.getPrototypeOf(json.navigation) === Object.prototype, "JSON'd performance's navigation prototype != Object.prototype"); + ok(!("toJSON" in json.navigation), "toJSON in JSON'd performance's navigation"); });
sync_test("console", function() {
Jacek Caban (@jacek) commented about dlls/mshtml/dispex.c:
if(SUCCEEDED(hres)) {
hres = IWineJSDispatch_DefineProperty(json, func->name, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, &var);
if(V_VT(&var) == VT_DISPATCH &&
SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IWineJSDispatchHost, (void**)&subdispex_iface))) {
if(subdispex_iface->lpVtbl == &JSDispatchHostVtbl) {
DispatchEx *subdispex = impl_from_IWineJSDispatchHost(subdispex_iface);
if(SUCCEEDED(dispex_get_chain_builtin_id(subdispex, L"toJSON", fdexNameCaseSensitive, &id))) {
VariantClear(&var);
hres = dispex_call_builtin(subdispex, id, &dp, &var, NULL, NULL);
}
}
IWineJSDispatchHost_Release(subdispex_iface);
}
if(SUCCEEDED(hres))
hres = IWineJSDispatch_DefineProperty(json, func->name, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, &var);
Do you expect more cases like this? It feels a bit like forcing our way through abstraction layers with things like vtbl checks. We could perhaps do that, but I wonder if we could just skip `VT_DISPATCH` here and have `HTMLPerformance_toJSON` perform those `toJSON` and `DefineProperty` calls for object properties instead.
On Sun Sep 7 12:12:12 2025 +0000, Jacek Caban wrote:
Do you expect more cases like this? It feels a bit like forcing our way through abstraction layers with things like vtbl checks. We could perhaps do that, but I wonder if we could just skip `VT_DISPATCH` here and have `HTMLPerformance_toJSON` perform those `toJSON` and `DefineProperty` calls for object properties instead.
I'm not aware of anything else that will need this, and yeah I hardcoded it in `HTMLPerformance_toJSON`. I also fixed it leaking when the return value was not used.