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

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2021-03-25 19:28:36 +01:00 committed by Alexandre Julliard
parent 34dd3dd217
commit e5da8dbfb7
4 changed files with 77 additions and 0 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
})();