Commit 6a31e4ac authored by Gabriel Ivăncescu's avatar Gabriel Ivăncescu Committed by Alexandre Julliard

mshtml: Implement synchronous XMLHttpRequest.

parent 51a68184
...@@ -3294,6 +3294,8 @@ static HRESULT WINAPI window_private_postMessage(IWineHTMLWindowPrivate *iface, ...@@ -3294,6 +3294,8 @@ static HRESULT WINAPI window_private_postMessage(IWineHTMLWindowPrivate *iface,
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
/* Because message events can be sent to different windows, they get blocked by any context */
task->header.thread_blocked = TRUE;
task->event = event; task->event = event;
return push_event_task(&task->header, window, post_message_proc, post_message_destr, window->task_magic); return push_event_task(&task->header, window, post_message_proc, post_message_destr, window->task_magic);
} }
......
...@@ -607,6 +607,7 @@ struct HTMLInnerWindow { ...@@ -607,6 +607,7 @@ struct HTMLInnerWindow {
VARIANT performance; VARIANT performance;
HTMLPerformanceTiming *performance_timing; HTMLPerformanceTiming *performance_timing;
unsigned blocking_depth;
unsigned parser_callback_cnt; unsigned parser_callback_cnt;
struct list script_queue; struct list script_queue;
...@@ -1288,6 +1289,7 @@ typedef void (*event_task_proc_t)(event_task_t*); ...@@ -1288,6 +1289,7 @@ typedef void (*event_task_proc_t)(event_task_t*);
struct event_task_t { struct event_task_t {
LONG target_magic; LONG target_magic;
BOOL thread_blocked;
event_task_proc_t proc; event_task_proc_t proc;
event_task_proc_t destr; event_task_proc_t destr;
struct list entry; struct list entry;
...@@ -1304,11 +1306,14 @@ typedef struct { ...@@ -1304,11 +1306,14 @@ typedef struct {
struct list task_list; struct list task_list;
struct list event_task_list; struct list event_task_list;
struct list timer_list; struct list timer_list;
struct list *pending_xhr_events_tail;
struct wine_rb_tree session_storage_map; struct wine_rb_tree session_storage_map;
void *blocking_xhr;
} thread_data_t; } thread_data_t;
thread_data_t *get_thread_data(BOOL) DECLSPEC_HIDDEN; thread_data_t *get_thread_data(BOOL) DECLSPEC_HIDDEN;
HWND get_thread_hwnd(void) DECLSPEC_HIDDEN; HWND get_thread_hwnd(void) DECLSPEC_HIDDEN;
void unblock_tasks_and_timers(thread_data_t*) DECLSPEC_HIDDEN;
int session_storage_map_cmp(const void*,const struct wine_rb_entry*) DECLSPEC_HIDDEN; int session_storage_map_cmp(const void*,const struct wine_rb_entry*) DECLSPEC_HIDDEN;
void destroy_session_storage(thread_data_t*) DECLSPEC_HIDDEN; void destroy_session_storage(thread_data_t*) DECLSPEC_HIDDEN;
......
...@@ -96,6 +96,13 @@ HRESULT push_event_task(event_task_t *task, HTMLInnerWindow *window, event_task_ ...@@ -96,6 +96,13 @@ HRESULT push_event_task(event_task_t *task, HTMLInnerWindow *window, event_task_
return S_OK; return S_OK;
} }
static void unlink_event_task(event_task_t *task, thread_data_t *thread_data)
{
if(thread_data->pending_xhr_events_tail == &task->entry)
thread_data->pending_xhr_events_tail = task->entry.prev;
list_remove(&task->entry);
}
static void release_task_timer(HWND thread_hwnd, task_timer_t *timer) static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
{ {
list_remove(&timer->entry); list_remove(&timer->entry);
...@@ -140,7 +147,7 @@ void remove_target_tasks(LONG target) ...@@ -140,7 +147,7 @@ void remove_target_tasks(LONG target)
LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->event_task_list) { LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->event_task_list) {
event_task_t *task = LIST_ENTRY(liter, event_task_t, entry); event_task_t *task = LIST_ENTRY(liter, event_task_t, entry);
if(task->target_magic == target) { if(task->target_magic == target) {
list_remove(&task->entry); unlink_event_task(task, thread_data);
release_event_task(task); release_event_task(task);
} }
} }
...@@ -303,7 +310,7 @@ static LRESULT process_timer(void) ...@@ -303,7 +310,7 @@ static LRESULT process_timer(void)
thread_data = get_thread_data(FALSE); thread_data = get_thread_data(FALSE);
assert(thread_data != NULL); assert(thread_data != NULL);
if(list_empty(&thread_data->timer_list)) { if(list_empty(&thread_data->timer_list) || thread_data->blocking_xhr) {
KillTimer(thread_data->thread_hwnd, TIMER_ID); KillTimer(thread_data->thread_hwnd, TIMER_ID);
return 0; return 0;
} }
...@@ -338,7 +345,7 @@ static LRESULT process_timer(void) ...@@ -338,7 +345,7 @@ static LRESULT process_timer(void)
call_timer_disp(disp, timer_type); call_timer_disp(disp, timer_type);
IDispatch_Release(disp); IDispatch_Release(disp);
}while(!list_empty(&thread_data->timer_list)); }while(!list_empty(&thread_data->timer_list) && !thread_data->blocking_xhr);
KillTimer(thread_data->thread_hwnd, TIMER_ID); KillTimer(thread_data->thread_hwnd, TIMER_ID);
return 0; return 0;
...@@ -366,15 +373,18 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa ...@@ -366,15 +373,18 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
continue; continue;
} }
head = list_head(&thread_data->event_task_list); head = &thread_data->event_task_list;
if(head) { while((head = list_next(&thread_data->event_task_list, head))) {
event_task_t *task = LIST_ENTRY(head, event_task_t, entry); event_task_t *task = LIST_ENTRY(head, event_task_t, entry);
list_remove(&task->entry);
if((!task->thread_blocked || !thread_data->blocking_xhr) && !task->window->blocking_depth) {
unlink_event_task(task, thread_data);
task->proc(task); task->proc(task);
release_event_task(task); release_event_task(task);
continue; break;
} }
}
if(!head)
break; break;
} }
return 0; return 0;
...@@ -451,6 +461,7 @@ thread_data_t *get_thread_data(BOOL create) ...@@ -451,6 +461,7 @@ thread_data_t *get_thread_data(BOOL create)
list_init(&thread_data->task_list); list_init(&thread_data->task_list);
list_init(&thread_data->event_task_list); list_init(&thread_data->event_task_list);
list_init(&thread_data->timer_list); list_init(&thread_data->timer_list);
thread_data->pending_xhr_events_tail = &thread_data->event_task_list;
wine_rb_init(&thread_data->session_storage_map, session_storage_map_cmp); wine_rb_init(&thread_data->session_storage_map, session_storage_map_cmp);
} }
...@@ -467,3 +478,16 @@ ULONGLONG get_time_stamp(void) ...@@ -467,3 +478,16 @@ ULONGLONG get_time_stamp(void)
GetSystemTimeAsFileTime(&time); GetSystemTimeAsFileTime(&time);
return (((ULONGLONG)time.dwHighDateTime << 32) + time.dwLowDateTime) / 10000 - time_epoch; return (((ULONGLONG)time.dwHighDateTime << 32) + time.dwLowDateTime) / 10000 - time_epoch;
} }
void unblock_tasks_and_timers(thread_data_t *thread_data)
{
if(!list_empty(&thread_data->event_task_list))
PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
if(!thread_data->blocking_xhr && !list_empty(&thread_data->timer_list)) {
task_timer_t *timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
DWORD tc = GetTickCount();
SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time > tc ? timer->time - tc : 0, NULL);
}
}
...@@ -88,6 +88,9 @@ doc_with_prop_ie9.html HTML "doc_with_prop_ie9.html" ...@@ -88,6 +88,9 @@ doc_with_prop_ie9.html HTML "doc_with_prop_ie9.html"
/* @makedep: iframe.html */ /* @makedep: iframe.html */
iframe.html HTML "iframe.html" iframe.html HTML "iframe.html"
/* @makedep: xhr_iframe.html */
xhr_iframe.html HTML "xhr_iframe.html"
/* For res: protocol test: */ /* For res: protocol test: */
/* @makedep: jstest.html */ /* @makedep: jstest.html */
......
...@@ -3675,6 +3675,7 @@ typedef struct { ...@@ -3675,6 +3675,7 @@ typedef struct {
IInternetProtocolSink *sink; IInternetProtocolSink *sink;
BINDINFO bind_info; BINDINFO bind_info;
HANDLE delay_event;
BSTR content_type; BSTR content_type;
IStream *stream; IStream *stream;
char *data; char *data;
...@@ -3685,11 +3686,16 @@ typedef struct { ...@@ -3685,11 +3686,16 @@ typedef struct {
IUri *uri; IUri *uri;
} ProtocolHandler; } ProtocolHandler;
static ProtocolHandler *delay_with_signal_handler;
static DWORD WINAPI delay_proc(void *arg) static DWORD WINAPI delay_proc(void *arg)
{ {
PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC}; PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC};
ProtocolHandler *protocol_handler = arg; ProtocolHandler *protocol_handler = arg;
if(protocol_handler->delay_event)
WaitForSingleObject(protocol_handler->delay_event, INFINITE);
else
Sleep(protocol_handler->delay); Sleep(protocol_handler->delay);
protocol_handler->delay = -1; protocol_handler->delay = -1;
IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data); IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data);
...@@ -3735,7 +3741,7 @@ static void report_data(ProtocolHandler *This) ...@@ -3735,7 +3741,7 @@ static void report_data(ProtocolHandler *This)
IHttpNegotiate_Release(http_negotiate); IHttpNegotiate_Release(http_negotiate);
if(This->delay) { if(This->delay || This->delay_event) {
IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface); IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
QueueUserWorkItem(delay_proc, This, 0); QueueUserWorkItem(delay_proc, This, 0);
return; return;
...@@ -3880,6 +3886,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface) ...@@ -3880,6 +3886,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface)
LONG ref = InterlockedDecrement(&This->ref); LONG ref = InterlockedDecrement(&This->ref);
if(!ref) { if(!ref) {
if(This->delay_event)
CloseHandle(This->delay_event);
if(This->sink) if(This->sink)
IInternetProtocolSink_Release(This->sink); IInternetProtocolSink_Release(This->sink);
if(This->stream) if(This->stream)
...@@ -4061,8 +4069,20 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri, ...@@ -4061,8 +4069,20 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
hres = IUri_GetQuery(uri, &query); hres = IUri_GetQuery(uri, &query);
if(SUCCEEDED(hres)) { if(SUCCEEDED(hres)) {
if(!lstrcmpW(query, L"?delay")) if(!wcscmp(query, L"?delay"))
This->delay = 1000; This->delay = 1000;
else if(!wcscmp(query, L"?delay_with_signal")) {
if(delay_with_signal_handler) {
ProtocolHandler *delayed = delay_with_signal_handler;
delay_with_signal_handler = NULL;
SetEvent(delayed->delay_event);
This->delay = 30;
}else {
delay_with_signal_handler = This;
This->delay_event = CreateEventW(NULL, FALSE, FALSE, NULL);
ok(This->delay_event != NULL, "CreateEvent failed: %08lx\n", GetLastError());
}
}
else if(!wcsncmp(query, L"?content-type=", sizeof("?content-type=")-1)) else if(!wcsncmp(query, L"?content-type=", sizeof("?content-type=")-1))
This->content_type = SysAllocString(query + sizeof("?content-type=")-1); This->content_type = SysAllocString(query + sizeof("?content-type=")-1);
SysFreeString(query); SysFreeString(query);
......
...@@ -76,6 +76,145 @@ function test_xhr() { ...@@ -76,6 +76,145 @@ function test_xhr() {
xhr.send(xml); xhr.send(xml);
} }
function test_sync_xhr() {
var async_xhr, async_xhr2, sync_xhr, sync_xhr_in_async, sync_xhr_nested, a = [ 0 ];
var async_xhr_clicked = false, doc_dblclicked = false;
function onmsg(e) { a.push("msg" + e.data); }
document.ondblclick = function() { doc_dblclicked = true; };
window.addEventListener("message", onmsg);
window.postMessage("1", "*");
window.setTimeout(function() { a.push("timeout"); }, 0);
window.postMessage("2", "*");
a.push(1);
async_xhr = new XMLHttpRequest();
async_xhr.open("POST", "echo.php", true);
async_xhr.onreadystatechange = function() {
if(async_xhr.readyState < 3)
return;
a.push("async_xhr(" + async_xhr.readyState + ")");
ok(async_xhr2.readyState === 1, "async_xhr2.readyState = " + async_xhr2.readyState);
if(async_xhr.readyState == 4) {
window.postMessage("_async", "*");
sync_xhr_in_async = new XMLHttpRequest();
sync_xhr_in_async.open("POST", "echo.php", false);
sync_xhr_in_async.onreadystatechange = function() { if(sync_xhr_in_async.readyState == 4) a.push("sync_xhr_in_async"); };
sync_xhr_in_async.setRequestHeader("X-Test", "True");
sync_xhr_in_async.send("sync_in_async");
}
};
async_xhr.addEventListener("click", function() { async_xhr_clicked = true; });
async_xhr.setRequestHeader("X-Test", "True");
async_xhr.send("1234");
a.push(2);
async_xhr2 = new XMLHttpRequest();
async_xhr2.open("POST", "echo.php?delay_with_signal", true);
async_xhr2.onreadystatechange = function() {
if(async_xhr2.readyState < 3)
return;
a.push("async_xhr2(" + async_xhr2.readyState + ")");
ok(async_xhr.readyState === 4, "async_xhr.readyState = " + async_xhr.readyState);
};
async_xhr2.setRequestHeader("X-Test", "True");
async_xhr2.send("foobar");
a.push(3);
sync_xhr = new XMLHttpRequest();
sync_xhr.open("POST", "echo.php?delay_with_signal", false);
sync_xhr.onreadystatechange = function() {
a.push("sync_xhr(" + sync_xhr.readyState + ")");
ok(async_xhr.readyState === 1, "async_xhr.readyState in sync_xhr handler = " + async_xhr.readyState);
ok(async_xhr2.readyState === 1, "async_xhr2.readyState in sync_xhr handler = " + async_xhr2.readyState);
if(sync_xhr.readyState < 4)
return;
window.setTimeout(function() { a.push("timeout_sync"); }, 0);
window.postMessage("_sync", "*");
sync_xhr_nested = new XMLHttpRequest();
sync_xhr_nested.open("POST", "echo.php", false);
sync_xhr_nested.onreadystatechange = function() {
a.push("nested(" + sync_xhr_nested.readyState + ")");
if(sync_xhr_nested.readyState == 4) {
window.setTimeout(function() { a.push("timeout_nested"); }, 0);
window.postMessage("_nested", "*");
var e = document.createEvent("Event");
e.initEvent("click", true, false);
ok(async_xhr_clicked === false, "async_xhr click fired before dispatch");
async_xhr.dispatchEvent(e);
ok(async_xhr_clicked === true, "async_xhr click not fired immediately");
if(document.fireEvent) {
ok(doc_dblclicked === false, "document dblclick fired before dispatch");
document.fireEvent("ondblclick", document.createEventObject());
ok(doc_dblclicked === true, "document dblclick not fired immediately");
}
}
};
sync_xhr_nested.setRequestHeader("X-Test", "True");
sync_xhr_nested.send("nested");
};
sync_xhr.setRequestHeader("X-Test", "True");
sync_xhr.send("abcd");
a.push(4);
window.setTimeout(function() {
var r = a.join(",");
todo_wine_if(document.documentMode < 10).
ok(r === "0,1,2,3," + (document.documentMode < 10 ? "sync_xhr(1),sync_xhr(2),sync_xhr(3)," : "") +
"sync_xhr(4)," + (document.documentMode < 10 ? "nested(1),nested(2),nested(3)," : "") +
"nested(4),4,async_xhr(3),async_xhr(4),sync_xhr_in_async,async_xhr2(3),async_xhr2(4)," +
"msg1,msg2,msg_sync,msg_nested,msg_async,timeout,timeout_sync,timeout_nested",
"unexpected order: " + r);
window.removeEventListener("message", onmsg);
document.ondblclick = null;
a = [ 0 ];
// Events dispatched to other iframes are not blocked by a send() in another context,
// except for async XHR events (which are a special case again), messages, and timeouts.
var iframe = document.createElement("iframe"), iframe2 = document.createElement("iframe");
iframe.onload = function() {
iframe2.onload = function() {
function onmsg(e) {
a.push(e.data);
if(e.data === "echo")
iframe2.contentWindow.postMessage("sync_xhr", "*");
};
window.setTimeout(function() {
var r = a.join(",");
ok(r === "0,1,async_xhr,echo,sync_xhr(pre-send),sync_xhr(DONE),sync_xhr,async_xhr(DONE)",
"[iframes 1] unexpected order: " + r);
a = [ 0 ];
window.setTimeout(function() {
var r = a.join(",");
ok(r === "0,1,echo,blank(DONE),sync_xhr(pre-send),sync_xhr(DONE),sync_xhr",
"[iframes 2] unexpected order: " + r);
window.removeEventListener("message", onmsg);
next_test();
}, 0);
iframe.onload = function() { a.push("blank(DONE)"); };
iframe.src = "blank.html?delay_with_signal";
iframe2.contentWindow.postMessage("echo", "*");
a.push(1);
}, 0);
window.addEventListener("message", onmsg);
iframe.contentWindow.postMessage("async_xhr", "*");
iframe2.contentWindow.postMessage("echo", "*");
a.push(1);
};
iframe2.src = "xhr_iframe.html";
document.body.appendChild(iframe2);
};
iframe.src = "xhr_iframe.html";
document.body.appendChild(iframe);
}, 0);
}
function test_content_types() { function test_content_types() {
var xhr = new XMLHttpRequest(), types, i = 0, override = false; var xhr = new XMLHttpRequest(), types, i = 0, override = false;
var v = document.documentMode; var v = document.documentMode;
...@@ -291,6 +430,7 @@ function test_response() { ...@@ -291,6 +430,7 @@ function test_response() {
var tests = [ var tests = [
test_xhr, test_xhr,
test_sync_xhr,
test_content_types, test_content_types,
test_abort, test_abort,
test_timeout, test_timeout,
......
<html><head><script type="text/javascript">window.onmessage = function(e)
{
if(e.data === "echo")
parent.postMessage("echo", "*");
else if(e.data === "async_xhr") {
var async_xhr = new XMLHttpRequest();
async_xhr.open("POST", "echo.php?delay_with_signal", true);
async_xhr.onreadystatechange = function() { if(async_xhr.readyState == 4) parent.postMessage("async_xhr(DONE)", "*"); };
async_xhr.setRequestHeader("X-Test", "True");
async_xhr.send("foo");
parent.postMessage("async_xhr", "*");
}
else if(e.data === "sync_xhr") {
var sync_xhr = new XMLHttpRequest();
sync_xhr.open("POST", "echo.php?delay_with_signal", false);
sync_xhr.onreadystatechange = function() { if(sync_xhr.readyState == 4) parent.postMessage("sync_xhr(DONE)", "*"); };
sync_xhr.setRequestHeader("X-Test", "True");
parent.postMessage("sync_xhr(pre-send)", "*");
sync_xhr.send("bar");
parent.postMessage("sync_xhr", "*");
}
}
</script><body></body></html>
...@@ -673,18 +673,12 @@ static void test_sync_xhr(IHTMLDocument2 *doc, const WCHAR *xml_url, const WCHAR ...@@ -673,18 +673,12 @@ static void test_sync_xhr(IHTMLDocument2 *doc, const WCHAR *xml_url, const WCHAR
SET_EXPECT(xmlhttprequest_onreadystatechange_opened); SET_EXPECT(xmlhttprequest_onreadystatechange_opened);
hres = IHTMLXMLHttpRequest_open(xhr, method, url, vbool, vempty, vempty); hres = IHTMLXMLHttpRequest_open(xhr, method, url, vbool, vempty, vempty);
todo_wine ok(hres == S_OK, "open failed: %08lx\n", hres); /* Gecko 30+ only supports async */ ok(hres == S_OK, "open failed: %08lx\n", hres);
todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_opened); CHECK_CALLED(xmlhttprequest_onreadystatechange_opened);
SysFreeString(method); SysFreeString(method);
SysFreeString(url); SysFreeString(url);
if(FAILED(hres)) {
IHTMLXMLHttpRequest_Release(xhr);
xhr = NULL;
return;
}
text = (BSTR)0xdeadbeef; text = (BSTR)0xdeadbeef;
hres = IHTMLXMLHttpRequest_getAllResponseHeaders(xhr, &text); hres = IHTMLXMLHttpRequest_getAllResponseHeaders(xhr, &text);
ok(hres == E_FAIL, "got %08lx\n", hres); ok(hres == E_FAIL, "got %08lx\n", hres);
...@@ -718,11 +712,11 @@ static void test_sync_xhr(IHTMLDocument2 *doc, const WCHAR *xml_url, const WCHAR ...@@ -718,11 +712,11 @@ static void test_sync_xhr(IHTMLDocument2 *doc, const WCHAR *xml_url, const WCHAR
loading_cnt = 0; loading_cnt = 0;
hres = IHTMLXMLHttpRequest_send(xhr, vempty); hres = IHTMLXMLHttpRequest_send(xhr, vempty);
ok(hres == S_OK, "send failed: %08lx\n", hres); ok(hres == S_OK, "send failed: %08lx\n", hres);
CHECK_CALLED(xmlhttprequest_onreadystatechange_opened); todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_opened);
CHECK_CALLED(xmlhttprequest_onreadystatechange_headers_received); todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_headers_received);
CHECK_CALLED(xmlhttprequest_onreadystatechange_loading); todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_loading);
CHECK_CALLED(xmlhttprequest_onreadystatechange_done); CHECK_CALLED(xmlhttprequest_onreadystatechange_done);
ok(loading_cnt == 1, "loading_cnt = %d\n", loading_cnt); todo_wine ok(loading_cnt == 1, "loading_cnt = %d\n", loading_cnt);
text = NULL; text = NULL;
hres = IHTMLXMLHttpRequest_getResponseHeader(xhr, content_type, &text); hres = IHTMLXMLHttpRequest_getResponseHeader(xhr, content_type, &text);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment