mshtml: Added asynchronous script loading tests.
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
b1f4bf1d7d
commit
82d867a676
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2016 Jacek Caban for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
|
||||
/* Dynamically created script element is downloaded as soon as src property is set,
|
||||
* but it doesn't block document onload event. */
|
||||
var detached_elem_executed = false;
|
||||
var detached_elem = document.createElement("script");
|
||||
detached_elem.src = "jsstream.php?detached_script";
|
||||
|
||||
function test_detached_script_elem() {
|
||||
var oncomplete_called = false;
|
||||
detached_elem.onreadystatechange = guard(function() {
|
||||
if(detached_elem.readyState == "complete") {
|
||||
ok(detached_elem_executed, "detached element not executed before readyState complete");
|
||||
oncomplete_called = true;
|
||||
next_test();
|
||||
return;
|
||||
}
|
||||
|
||||
ok(!detached_elem_executed, "detached element executed");
|
||||
if(detached_elem.readyState == "loaded") {
|
||||
head.appendChild(detached_elem);
|
||||
ok(detached_elem_executed, "detached element not yet executed");
|
||||
ok(detached_elem.readyState == "complete", "detached_elem.readyState = " + detached_elem.readyState + " expected complete");
|
||||
ok(!oncomplete_called, "oncomplete not called");
|
||||
}
|
||||
});
|
||||
|
||||
external.writeStream("detached_script", 'detached_elem_executed = true;');
|
||||
}
|
||||
|
||||
/* Dynamically created script elements are evaluated as soon as they are loaded, no matter
|
||||
* how they are ordered in the tree. */
|
||||
var attached_elem1_executed = false;
|
||||
var attached_elem1 = document.createElement("script");
|
||||
attached_elem1.src = "jsstream.php?attached_script1";
|
||||
head.appendChild(attached_elem1);
|
||||
|
||||
var attached_elem2_executed = false;
|
||||
var attached_elem2 = document.createElement("script");
|
||||
attached_elem2.src = "jsstream.php?attached_script2";
|
||||
head.appendChild(attached_elem2);
|
||||
|
||||
function test_attached_script_elem() {
|
||||
attached_elem1.onreadystatechange = guard(function() {
|
||||
ok(attached_elem1.readyState == "loaded", "attached_elem1.readyState = " + attached_elem2.readyState);
|
||||
ok(attached_elem1_executed, "attached element 1 not executed before readyState complete");
|
||||
next_test();
|
||||
});
|
||||
|
||||
attached_elem2.onreadystatechange = guard(function() {
|
||||
ok(attached_elem2.readyState == "loaded", "attached_elem2.readyState = " + attached_elem2.readyState);
|
||||
ok(attached_elem2_executed, "attached element 2 not executed before readyState complete");
|
||||
|
||||
external.writeStream("attached_script1", 'attached_elem1_executed = true;');
|
||||
});
|
||||
|
||||
external.writeStream("attached_script2", 'attached_elem2_executed = true;');
|
||||
}
|
||||
|
||||
function test_dynamic_element() {
|
||||
var elem = document.createElement("script");
|
||||
var ready_states = "";
|
||||
|
||||
elem.onreadystatechange = guard(function() {
|
||||
ready_states += elem.readyState + ",";
|
||||
if(elem.readyState == "loaded")
|
||||
next_test();
|
||||
});
|
||||
|
||||
document.body.appendChild(elem);
|
||||
elem.src = "jsstream.php?simple";
|
||||
external.writeStream("simple", " ");
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_detached_script_elem,
|
||||
test_attached_script_elem,
|
||||
test_dynamic_element
|
||||
];
|
|
@ -22,6 +22,9 @@ winetest.js HTML "winetest.js"
|
|||
/* @makedep: exectest.html */
|
||||
exectest.html HTML "exectest.html"
|
||||
|
||||
/* @makedep: asyncscriptload.js */
|
||||
asyncscriptload.js HTML "asyncscriptload.js"
|
||||
|
||||
/* @makedep: jstest.html */
|
||||
jstest.html HTML "jstest.html"
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ DEFINE_EXPECT(ChangeType);
|
|||
#define DISPID_EXTERNAL_TODO_WINE_OK 0x300003
|
||||
#define DISPID_EXTERNAL_BROKEN 0x300004
|
||||
#define DISPID_EXTERNAL_WIN_SKIP 0x300005
|
||||
#define DISPID_EXTERNAL_WRITESTREAM 0x300006
|
||||
|
||||
static const GUID CLSID_TestScript =
|
||||
{0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}};
|
||||
|
@ -604,11 +605,17 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName,
|
|||
*pid = DISPID_EXTERNAL_WIN_SKIP;
|
||||
return S_OK;
|
||||
}
|
||||
if(!strcmp_wa(bstrName, "writeStream")) {
|
||||
*pid = DISPID_EXTERNAL_WRITESTREAM;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName));
|
||||
return DISP_E_UNKNOWNNAME;
|
||||
}
|
||||
|
||||
static void stream_write(const WCHAR*,const WCHAR*);
|
||||
|
||||
static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
|
||||
VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
|
||||
{
|
||||
|
@ -713,6 +720,22 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID
|
|||
|
||||
return S_OK;
|
||||
|
||||
case DISPID_EXTERNAL_WRITESTREAM:
|
||||
ok(wFlags == INVOKE_FUNC, "wFlags = %x\n", wFlags);
|
||||
ok(pdp != NULL, "pdp == NULL\n");
|
||||
ok(pdp->rgvarg != NULL, "rgvarg == NULL\n");
|
||||
ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n");
|
||||
ok(pdp->cArgs == 2, "cArgs = %d\n", pdp->cArgs);
|
||||
ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs);
|
||||
ok(!pvarRes, "pvarRes != NULL\n");
|
||||
ok(pei != NULL, "pei == NULL\n");
|
||||
|
||||
ok(V_VT(pdp->rgvarg) == VT_BSTR, "V_VT(psp->rgvargs) = %d\n", V_VT(pdp->rgvarg));
|
||||
ok(V_VT(pdp->rgvarg+1) == VT_BSTR, "V_VT(psp->rgvargs) = %d\n", V_VT(pdp->rgvarg));
|
||||
|
||||
stream_write(V_BSTR(pdp->rgvarg+1), V_BSTR(pdp->rgvarg));
|
||||
return S_OK;
|
||||
|
||||
default:
|
||||
ok(0, "unexpected call\n");
|
||||
return E_NOTIMPL;
|
||||
|
@ -2822,6 +2845,99 @@ static void report_data(ProtocolHandler *This)
|
|||
ok(hres == S_OK, "ReportResult failed: %08x\n", hres);
|
||||
}
|
||||
|
||||
typedef struct js_stream_t {
|
||||
struct js_stream_t *next;
|
||||
ProtocolHandler *protocol_handler;
|
||||
WCHAR name[1];
|
||||
} js_stream_t;
|
||||
|
||||
static js_stream_t *registered_stream_list;
|
||||
|
||||
static void register_stream(ProtocolHandler *protocol_handler, const WCHAR *name)
|
||||
{
|
||||
js_stream_t *stream;
|
||||
size_t len;
|
||||
|
||||
len = lstrlenW(name)+1;
|
||||
stream = HeapAlloc(GetProcessHeap(), 0, offsetof(js_stream_t, name[len+1]));
|
||||
|
||||
IInternetProtocolEx_AddRef(&protocol_handler->IInternetProtocolEx_iface);
|
||||
stream->protocol_handler = protocol_handler;
|
||||
memcpy(stream->name, name, len*sizeof(WCHAR));
|
||||
|
||||
stream->next = registered_stream_list;
|
||||
registered_stream_list = stream;
|
||||
}
|
||||
|
||||
static void free_registered_streams(void)
|
||||
{
|
||||
js_stream_t *iter;
|
||||
|
||||
while((iter = registered_stream_list)) {
|
||||
ok(!iter->protocol_handler, "protocol handler still pending for %s\n", wine_dbgstr_w(iter->name));
|
||||
if(iter->protocol_handler)
|
||||
IInternetProtocolEx_Release(&iter->protocol_handler->IInternetProtocolEx_iface);
|
||||
|
||||
registered_stream_list = iter->next;
|
||||
HeapFree(GetProcessHeap(), 0, iter);
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD WINAPI async_switch_proc(void *arg)
|
||||
{
|
||||
PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC};
|
||||
ProtocolHandler *protocol_handler = arg;
|
||||
|
||||
IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void async_switch(ProtocolHandler *protocol_handler)
|
||||
{
|
||||
IInternetProtocolEx_AddRef(&protocol_handler->IInternetProtocolEx_iface);
|
||||
QueueUserWorkItem(async_switch_proc, protocol_handler, 0);
|
||||
}
|
||||
|
||||
static void stream_write(const WCHAR *name, const WCHAR *data)
|
||||
{
|
||||
ProtocolHandler *protocol_handler;
|
||||
LARGE_INTEGER large_zero;
|
||||
js_stream_t *stream;
|
||||
HRESULT hres;
|
||||
|
||||
static const BYTE bom_utf16[] = {0xff,0xfe};
|
||||
|
||||
for(stream = registered_stream_list; stream; stream = stream->next) {
|
||||
if(!lstrcmpW(stream->name, name))
|
||||
break;
|
||||
}
|
||||
ok(stream != NULL, "stream %s not found\n", wine_dbgstr_w(name));
|
||||
if(!stream)
|
||||
return;
|
||||
|
||||
protocol_handler = stream->protocol_handler;
|
||||
ok(protocol_handler != NULL, "protocol_handler is NULL\n");
|
||||
if(!protocol_handler)
|
||||
return;
|
||||
stream->protocol_handler = NULL;
|
||||
|
||||
hres = CreateStreamOnHGlobal(NULL, TRUE, &protocol_handler->stream);
|
||||
ok(hres == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hres);
|
||||
|
||||
hres = IStream_Write(protocol_handler->stream, bom_utf16, sizeof(bom_utf16), NULL);
|
||||
ok(hres == S_OK, "Write failed: %08x\n", hres);
|
||||
|
||||
hres = IStream_Write(protocol_handler->stream, data, lstrlenW(data)*sizeof(WCHAR), NULL);
|
||||
ok(hres == S_OK, "Write failed: %08x\n", hres);
|
||||
|
||||
U(large_zero).QuadPart = 0;
|
||||
hres = IStream_Seek(protocol_handler->stream, large_zero, STREAM_SEEK_SET, NULL);
|
||||
ok(hres == S_OK, "Seek failed: %08x\n", hres);
|
||||
|
||||
async_switch(protocol_handler);
|
||||
IInternetProtocolEx_Release(&protocol_handler->IInternetProtocolEx_iface);
|
||||
}
|
||||
|
||||
static char index_html_data[4096];
|
||||
|
||||
static inline ProtocolHandler *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
|
||||
|
@ -2882,12 +2998,13 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
|
|||
{
|
||||
ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
|
||||
report_data(This);
|
||||
IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI Protocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason, DWORD dwOptions)
|
||||
{
|
||||
ok(0, "unexpected call\n");
|
||||
trace("Abort(%08x %x)\n", hrReason, dwOptions);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
@ -2911,10 +3028,18 @@ static HRESULT WINAPI Protocol_Resume(IInternetProtocolEx *iface)
|
|||
static HRESULT WINAPI Protocol_Read(IInternetProtocolEx *iface, void *pv, ULONG cb, ULONG *pcbRead)
|
||||
{
|
||||
ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
|
||||
LONG read;
|
||||
ULONG read;
|
||||
HRESULT hres;
|
||||
|
||||
if(This->stream)
|
||||
return IStream_Read(This->stream, pv, cb, pcbRead);
|
||||
if(This->stream) {
|
||||
hres = IStream_Read(This->stream, pv, cb, &read);
|
||||
ok(SUCCEEDED(hres), "Read failed: %08x\n", hres);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
if(pcbRead)
|
||||
*pcbRead = read;
|
||||
return read ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
read = This->size - (This->ptr - This->data);
|
||||
if(read > cb)
|
||||
|
@ -2950,7 +3075,7 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
|
|||
IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE *dwReserved)
|
||||
{
|
||||
ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
|
||||
PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC};
|
||||
BOOL block = FALSE;
|
||||
DWORD bindf;
|
||||
BSTR path;
|
||||
HRSRC src;
|
||||
|
@ -2981,6 +3106,15 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
|
|||
default:
|
||||
ok(0, "unexpected tymed %d\n", This->bind_info.stgmedData.tymed);
|
||||
}
|
||||
}else if(!strcmp_wa(path, "/jsstream.php")) {
|
||||
BSTR query;
|
||||
|
||||
hres = IUri_GetQuery(uri, &query);
|
||||
ok(hres == S_OK, "GetQuery failed: %08x\n", hres);
|
||||
ok(SysStringLen(query) > 1 && *query == '?', "unexpected query %s\n", wine_dbgstr_w(query));
|
||||
register_stream(This, query+1);
|
||||
SysFreeString(query);
|
||||
block = TRUE;
|
||||
}else {
|
||||
src = FindResourceW(NULL, *path == '/' ? path+1 : path, (const WCHAR*)RT_HTML);
|
||||
ok(src != NULL, "Could not find resource for path %s\n", wine_dbgstr_w(path));
|
||||
|
@ -3001,7 +3135,8 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
|
|||
IUri_AddRef(This->uri = uri);
|
||||
|
||||
This->ptr = This->data;
|
||||
IInternetProtocolSink_Switch(This->sink, &protocol_data);
|
||||
if(!block)
|
||||
async_switch(This);
|
||||
return E_PENDING;
|
||||
}
|
||||
|
||||
|
@ -3225,6 +3360,7 @@ static void run_from_moniker(IMoniker *mon)
|
|||
|
||||
CHECK_CALLED(external_success);
|
||||
|
||||
free_registered_streams();
|
||||
set_client_site(doc, FALSE);
|
||||
IHTMLDocument2_Release(doc);
|
||||
}
|
||||
|
@ -3244,6 +3380,25 @@ static void run_js_script(const char *test_name)
|
|||
|
||||
hres = CreateURLMoniker(NULL, url, &mon);
|
||||
ok(hres == S_OK, "CreateURLMoniker failed: %08x\n", hres);
|
||||
run_from_moniker(mon);
|
||||
|
||||
IMoniker_Release(mon);
|
||||
}
|
||||
|
||||
static void run_from_path(const char *path, const char *opt)
|
||||
{
|
||||
char buf[255] = "http://winetest.example.org";
|
||||
IMoniker *mon;
|
||||
BSTR url;
|
||||
HRESULT hres;
|
||||
|
||||
strcat(buf, path);
|
||||
if(opt)
|
||||
strcat(buf, opt);
|
||||
url = a2bstr(buf);
|
||||
hres = CreateURLMoniker(NULL, url, &mon);
|
||||
SysFreeString(url);
|
||||
ok(hres == S_OK, "CreateUrlMoniker failed: %08x\n", hres);
|
||||
|
||||
run_from_moniker(mon);
|
||||
|
||||
|
@ -3252,11 +3407,6 @@ static void run_js_script(const char *test_name)
|
|||
|
||||
static void run_script_as_http_with_mode(const char *script, const char *opt, const char *document_mode)
|
||||
{
|
||||
char buf[255];
|
||||
IMoniker *mon;
|
||||
BSTR url;
|
||||
HRESULT hres;
|
||||
|
||||
trace("Running %s script in %s mode...\n", script, document_mode ? document_mode : "quirks");
|
||||
|
||||
sprintf(index_html_data,
|
||||
|
@ -3276,15 +3426,7 @@ static void run_script_as_http_with_mode(const char *script, const char *opt, co
|
|||
document_mode ? "\">" : "",
|
||||
script);
|
||||
|
||||
sprintf(buf, "http://winetest.org/index.html%s", opt ? opt : "");
|
||||
url = a2bstr(buf);
|
||||
hres = CreateURLMoniker(NULL, url, &mon);
|
||||
SysFreeString(url);
|
||||
ok(hres == S_OK, "CreateUrlMoniker failed: %08x\n", hres);
|
||||
|
||||
run_from_moniker(mon);
|
||||
|
||||
IMoniker_Release(mon);
|
||||
run_from_path("/index.html", opt);
|
||||
}
|
||||
|
||||
static void init_protocol_handler(void)
|
||||
|
@ -3329,6 +3471,8 @@ static void run_js_tests(void)
|
|||
run_script_as_http_with_mode("documentmode.js", "?9", "9");
|
||||
run_script_as_http_with_mode("documentmode.js", "?10", "10");
|
||||
run_script_as_http_with_mode("documentmode.js", "?11", "11");
|
||||
|
||||
run_script_as_http_with_mode("asyncscriptload.js", NULL, "9");
|
||||
}
|
||||
|
||||
static BOOL init_registry(BOOL init)
|
||||
|
|
|
@ -16,15 +16,19 @@
|
|||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
function next_test() {
|
||||
var test = tests.shift();
|
||||
window.setTimeout(function() {
|
||||
function guard(f) {
|
||||
return function() {
|
||||
try {
|
||||
test();
|
||||
f();
|
||||
}catch(e) {
|
||||
ok(false, "Got exception " + ("message" in e ? e.message : e));
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
}
|
||||
|
||||
function next_test() {
|
||||
var test = tests.shift();
|
||||
window.setTimeout(guard(test), 0);
|
||||
}
|
||||
|
||||
function run_tests() {
|
||||
|
|
Loading…
Reference in New Issue