Jacek Caban : mshtml: Block load event until dynamically created script elements that are part of the document are loaded.

Alexandre Julliard julliard at winehq.org
Thu Mar 25 16:49:23 CDT 2021


Module: wine
Branch: master
Commit: e5da8dbfb78c0f19b22af4f4406ad837b43b9f2e
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=e5da8dbfb78c0f19b22af4f4406ad837b43b9f2e

Author: Jacek Caban <jacek at codeweavers.com>
Date:   Thu Mar 25 19:28:36 2021 +0100

mshtml: Block load event until dynamically created script elements that are part of the document are loaded.

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/mshtml/binding.h                |  1 +
 dlls/mshtml/nsio.c                   | 18 ++++++++++++++++
 dlls/mshtml/script.c                 | 42 ++++++++++++++++++++++++++++++++++++
 dlls/mshtml/tests/asyncscriptload.js | 16 ++++++++++++++
 4 files changed, 77 insertions(+)

diff --git a/dlls/mshtml/binding.h b/dlls/mshtml/binding.h
index 3d1ea6ecc8d..26a337fe470 100644
--- a/dlls/mshtml/binding.h
+++ b/dlls/mshtml/binding.h
@@ -154,6 +154,7 @@ HRESULT create_channelbsc(IMoniker*,const WCHAR*,BYTE*,DWORD,BOOL,nsChannelBSC**
 HRESULT channelbsc_load_stream(HTMLInnerWindow*,IMoniker*,IStream*) DECLSPEC_HIDDEN;
 void channelbsc_set_channel(nsChannelBSC*,nsChannel*,nsIStreamListener*,nsISupports*) DECLSPEC_HIDDEN;
 IUri *nsuri_get_uri(nsWineURI*) DECLSPEC_HIDDEN;
+nsresult create_onload_blocker_request(nsIRequest**) DECLSPEC_HIDDEN;
 
 HRESULT read_stream(BSCallback*,IStream*,void*,DWORD,DWORD*) DECLSPEC_HIDDEN;
 
diff --git a/dlls/mshtml/nsio.c b/dlls/mshtml/nsio.c
index 1ffc6325725..ea8eae2007d 100644
--- a/dlls/mshtml/nsio.c
+++ b/dlls/mshtml/nsio.c
@@ -3980,3 +3980,21 @@ void release_nsio(void)
         nsio = NULL;
     }
 }
+
+nsresult create_onload_blocker_request(nsIRequest **ret)
+{
+    nsIChannel *channel;
+    nsACString spec;
+    nsresult nsres;
+
+    nsACString_InitDepend(&spec, "about:wine-script-onload-blocker");
+    nsres = nsIIOService_NewChannel(nsio, &spec, NULL, NULL, &channel);
+    nsACString_Finish(&spec);
+    if(NS_FAILED(nsres)) {
+        ERR("Failed to create channel: %08x\n", nsres);
+        return nsres;
+    }
+
+    *ret = (nsIRequest *)channel;
+    return NS_OK;
+}
diff --git a/dlls/mshtml/script.c b/dlls/mshtml/script.c
index c52c62f56d9..ec739c4777f 100644
--- a/dlls/mshtml/script.c
+++ b/dlls/mshtml/script.c
@@ -822,6 +822,8 @@ typedef struct {
     BSCallback bsc;
 
     HTMLScriptElement *script_elem;
+    nsILoadGroup *load_group;
+    nsIRequest *request;
     DWORD scheme;
 
     DWORD size;
@@ -945,6 +947,13 @@ static void ScriptBSC_destroy(BSCallback *bsc)
         This->script_elem = NULL;
     }
 
+    if(This->request) {
+        ERR("Unfinished request\n");
+        nsIRequest_Release(This->request);
+    }
+    if(This->load_group)
+        nsILoadGroup_Release(This->load_group);
+
     heap_free(This->buf);
     heap_free(This);
 }
@@ -957,9 +966,19 @@ static HRESULT ScriptBSC_init_bindinfo(BSCallback *bsc)
 static HRESULT ScriptBSC_start_binding(BSCallback *bsc)
 {
     ScriptBSC *This = impl_from_BSCallback(bsc);
+    nsresult nsres;
 
     This->script_elem->binding = &This->bsc;
 
+    if(This->load_group) {
+        nsres = create_onload_blocker_request(&This->request);
+        if(NS_SUCCEEDED(nsres)) {
+            nsres = nsILoadGroup_AddRequest(This->load_group, This->request, NULL);
+            if(NS_FAILED(nsres))
+                ERR("AddRequest failed: %08x\n", nsres);
+        }
+    }
+
     /* FIXME: We should find a better to decide if 'loading' state is supposed to be used by the protocol. */
     if(This->scheme == URL_SCHEME_HTTPS || This->scheme == URL_SCHEME_HTTP)
         set_script_elem_readystate(This->script_elem, READYSTATE_LOADING);
@@ -970,6 +989,7 @@ static HRESULT ScriptBSC_start_binding(BSCallback *bsc)
 static HRESULT ScriptBSC_stop_binding(BSCallback *bsc, HRESULT result)
 {
     ScriptBSC *This = impl_from_BSCallback(bsc);
+    nsresult nsres;
 
     if(SUCCEEDED(result) && !This->script_elem)
         result = E_UNEXPECTED;
@@ -989,6 +1009,14 @@ static HRESULT ScriptBSC_stop_binding(BSCallback *bsc, HRESULT result)
         This->size = 0;
     }
 
+    if(This->request) {
+        nsres = nsILoadGroup_RemoveRequest(This->load_group, This->request, NULL, NS_OK);
+        if(NS_FAILED(nsres))
+            ERR("RemoveRequest failed: %08x\n", nsres);
+        nsIRequest_Release(This->request);
+        This->request = NULL;
+    }
+
     IHTMLScriptElement_Release(&This->script_elem->IHTMLScriptElement_iface);
     This->script_elem = NULL;
     return S_OK;
@@ -1099,6 +1127,20 @@ HRESULT load_script(HTMLScriptElement *script_elem, const WCHAR *src, BOOL async
     IHTMLScriptElement_AddRef(&script_elem->IHTMLScriptElement_iface);
     bsc->script_elem = script_elem;
 
+    if(window->bscallback && window->bscallback->nschannel &&
+       window->bscallback->nschannel->load_group) {
+        cpp_bool contains;
+        nsresult nsres;
+
+        nsres = nsIDOMNode_Contains(script_elem->element.node.doc->node.nsnode,
+                                    script_elem->element.node.nsnode, &contains);
+        if(NS_SUCCEEDED(nsres) && contains) {
+            TRACE("script %p will block load event\n", script_elem);
+            bsc->load_group = window->bscallback->nschannel->load_group;
+            nsILoadGroup_AddRef(bsc->load_group);
+        }
+    }
+
     hres = start_binding(window, &bsc->bsc, NULL);
 
     IBindStatusCallback_Release(&bsc->bsc.IBindStatusCallback_iface);
diff --git a/dlls/mshtml/tests/asyncscriptload.js b/dlls/mshtml/tests/asyncscriptload.js
index 6dc333afdf4..207df133e91 100644
--- a/dlls/mshtml/tests/asyncscriptload.js
+++ b/dlls/mshtml/tests/asyncscriptload.js
@@ -90,3 +90,19 @@ async_test("append_script", function() {
     elem.src = "jsstream.php?simple";
     external.writeStream("simple", " ");
 });
+
+function unexpected_load(e) {
+    ok(false, "onload event before executing script");
+}
+
+guard(function() {
+    var elem = document.createElement("script");
+    document.head.appendChild(elem);
+    elem.src = "jsstream.php?blockload";
+
+    window.addEventListener("load", unexpected_load, true);
+
+    setTimeout(guard(function() {
+        external.writeStream("blockload", "window.removeEventListener('load', unexpected_load, true);");
+    }), 100);
+})();




More information about the wine-cvs mailing list