[PATCH v2 3/8] mshtml: Implement the private jscript proxy interface.

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Oct 13 09:11:53 CDT 2021


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 at 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              | 91 ++++++++++++++++++++++++++++++-
 dlls/mshtml/htmlwindow.c          | 55 ++++++++++++++++++-
 dlls/mshtml/mshtml_private.h      | 28 ++++++++++
 dlls/mshtml/tests/documentmode.js |  2 +-
 4 files changed, 171 insertions(+), 5 deletions(-)

diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c
index 96a776d..6314087 100644
--- a/dlls/mshtml/dispex.c
+++ b/dlls/mshtml/dispex.c
@@ -1886,7 +1886,75 @@ 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 HRESULT WINAPI WineDispatchProxyPrivate_ToString(IWineDispatchProxyPrivate *iface, BSTR *string)
+{
+    DispatchEx *This = impl_from_IWineDispatchProxyPrivate(iface);
+    return dispex_to_string(This, string);
+}
+
+static IWineDispatchProxyPrivateVtbl WineDispatchProxyPrivateVtbl = {
+    {
     DispatchEx_QueryInterface,
     DispatchEx_AddRef,
     DispatchEx_Release,
@@ -1902,6 +1970,13 @@ static IDispatchExVtbl DispatchExVtbl = {
     DispatchEx_GetMemberName,
     DispatchEx_GetNextDispID,
     DispatchEx_GetNameSpaceParent
+    },
+
+    /* IWineDispatchProxyPrivate extension */
+    WineDispatchProxyPrivate_GetProxyFieldRef,
+    WineDispatchProxyPrivate_GetPropFlags,
+    WineDispatchProxyPrivate_DeleteProp,
+    WineDispatchProxyPrivate_ToString
 };
 
 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
@@ -1910,6 +1985,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))
@@ -1945,6 +2022,15 @@ void dispex_unlink(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;
 
@@ -1998,8 +2084,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 0b25e71..0a10244 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)) {
@@ -3541,7 +3543,49 @@ 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 HRESULT WINAPI WindowWineDispProxyPrivate_ToString(IWineDispatchProxyPrivate *iface, BSTR *string)
+{
+    HTMLWindow *This = impl_from_IWineDispatchProxyPrivate(iface);
+    IWineDispatchProxyPrivate *itf = (IWineDispatchProxyPrivate*)&This->inner_window->event_target.dispex.IDispatchEx_iface;
+
+    return itf->lpVtbl->ToString(itf, string);
+}
+
+static const IWineDispatchProxyPrivateVtbl WindowDispExVtbl = {
+    {
     WindowDispEx_QueryInterface,
     WindowDispEx_AddRef,
     WindowDispEx_Release,
@@ -3557,6 +3601,13 @@ static const IDispatchExVtbl WindowDispExVtbl = {
     WindowDispEx_GetMemberName,
     WindowDispEx_GetNextDispID,
     WindowDispEx_GetNameSpaceParent
+    },
+
+    /* IWineDispatchProxyPrivate extension */
+    WindowWineDispProxyPrivate_GetProxyFieldRef,
+    WindowWineDispProxyPrivate_GetPropFlags,
+    WindowWineDispProxyPrivate_DeleteProp,
+    WindowWineDispProxyPrivate_ToString
 };
 
 static inline HTMLWindow *impl_from_IServiceProvider(IServiceProvider *iface)
@@ -3801,7 +3852,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 5cf53bb..ebadd62 100644
--- a/dlls/mshtml/mshtml_private.h
+++ b/dlls/mshtml/mshtml_private.h
@@ -44,6 +44,33 @@
 
 #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);
+    HRESULT (STDMETHODCALLTYPE *ToString)(IWineDispatchProxyPrivate *This, BSTR *string);
+} 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) \
@@ -349,6 +376,7 @@ struct DispatchEx {
     IDispatchEx IDispatchEx_iface;
 
     IUnknown *outer;
+    void *proxy;
 
     dispex_data_t *info;
     dispex_dynamic_data_t *dynamic_data;
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js
index 3307f2c..4356b93 100644
--- a/dlls/mshtml/tests/documentmode.js
+++ b/dlls/mshtml/tests/documentmode.js
@@ -154,7 +154,7 @@ sync_test("builtin_toString", function() {
             ok(s === (tostr ? tostr : (v < 9 ? "[object]" : "[object " + name + "]")), msg + " toString returned " + s);
         }
         s = Object.prototype.toString.call(obj);
-        todo_wine_if(v >= 9 && name != "Object").
+        todo_wine_if(name !== "HTMLElement" && s === "[object HTMLElement]").
         ok(s === (v < 9 ? "[object Object]" : "[object " + name + "]"), msg + " Object.toString returned " + s);
     }
 
-- 
2.31.1




More information about the wine-devel mailing list