[PATCH 04/11] mshtml: Implement the private jscript proxy interface.

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Sep 20 09:46:10 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         | 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;
-- 
2.31.1




More information about the wine-devel mailing list