From e5da8dbfb78c0f19b22af4f4406ad837b43b9f2e Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Thu, 25 Mar 2021 19:28:36 +0100 Subject: [PATCH] mshtml: Block load event until dynamically created script elements that are part of the document are loaded. Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- 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); +})();