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);
}
}
...@@ -111,6 +111,8 @@ DEFINE_EXPECT(window1_onstorage); ...@@ -111,6 +111,8 @@ DEFINE_EXPECT(window1_onstorage);
DEFINE_EXPECT(doc2_onstorage); DEFINE_EXPECT(doc2_onstorage);
DEFINE_EXPECT(doc2_onstoragecommit); DEFINE_EXPECT(doc2_onstoragecommit);
DEFINE_EXPECT(window2_onstorage); DEFINE_EXPECT(window2_onstorage);
DEFINE_EXPECT(async_xhr_done);
DEFINE_EXPECT(sync_xhr_done);
static HWND container_hwnd = NULL; static HWND container_hwnd = NULL;
static IHTMLWindow2 *window; static IHTMLWindow2 *window;
...@@ -3730,6 +3732,60 @@ static HRESULT WINAPI window2_onstorage(IDispatchEx *iface, DISPID id, LCID lcid ...@@ -3730,6 +3732,60 @@ static HRESULT WINAPI window2_onstorage(IDispatchEx *iface, DISPID id, LCID lcid
EVENT_HANDLER_FUNC_OBJ(window2_onstorage); EVENT_HANDLER_FUNC_OBJ(window2_onstorage);
static HRESULT WINAPI async_xhr(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
{
IHTMLXMLHttpRequest *xhr;
LONG ready_state;
HRESULT hres;
ok(pdp != NULL, "pdp == NULL\n");
ok(pdp->cArgs == (document_mode < 9 ? 1 : 2), "pdp->cArgs = %d\n", pdp->cArgs);
ok(pdp->cNamedArgs == 1, "pdp->cNamedArgs = %d\n", pdp->cNamedArgs);
ok(pdp->rgdispidNamedArgs[0] == DISPID_THIS, "pdp->rgdispidNamedArgs[0] = %ld\n", pdp->rgdispidNamedArgs[0]);
ok(V_VT(pdp->rgvarg) == VT_DISPATCH, "V_VT(this) = %d\n", V_VT(pdp->rgvarg));
ok(V_DISPATCH(pdp->rgvarg) != NULL, "V_DISPATCH(this) == NULL\n");
hres = IDispatch_QueryInterface(V_DISPATCH(pdp->rgvarg), &IID_IHTMLXMLHttpRequest, (void**)&xhr);
ok(hres == S_OK, "Could not get IHTMLXMLHttpRequest: %08lx\n", hres);
hres = IHTMLXMLHttpRequest_get_readyState(xhr, &ready_state);
if(SUCCEEDED(hres) && ready_state == 4)
CHECK_EXPECT(async_xhr_done);
IHTMLXMLHttpRequest_Release(xhr);
return S_OK;
}
EVENT_HANDLER_FUNC_OBJ(async_xhr);
static HRESULT WINAPI sync_xhr(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
{
IHTMLXMLHttpRequest *xhr;
LONG ready_state;
HRESULT hres;
ok(pdp != NULL, "pdp == NULL\n");
ok(pdp->cArgs == (document_mode < 9 ? 1 : 2), "pdp->cArgs = %d\n", pdp->cArgs);
ok(pdp->cNamedArgs == 1, "pdp->cNamedArgs = %d\n", pdp->cNamedArgs);
ok(pdp->rgdispidNamedArgs[0] == DISPID_THIS, "pdp->rgdispidNamedArgs[0] = %ld\n", pdp->rgdispidNamedArgs[0]);
ok(V_VT(pdp->rgvarg) == VT_DISPATCH, "V_VT(this) = %d\n", V_VT(pdp->rgvarg));
ok(V_DISPATCH(pdp->rgvarg) != NULL, "V_DISPATCH(this) == NULL\n");
hres = IDispatch_QueryInterface(V_DISPATCH(pdp->rgvarg), &IID_IHTMLXMLHttpRequest, (void**)&xhr);
ok(hres == S_OK, "Could not get IHTMLXMLHttpRequest: %08lx\n", hres);
hres = IHTMLXMLHttpRequest_get_readyState(xhr, &ready_state);
if(SUCCEEDED(hres) && ready_state == 4)
CHECK_EXPECT(sync_xhr_done);
IHTMLXMLHttpRequest_Release(xhr);
return S_OK;
}
EVENT_HANDLER_FUNC_OBJ(sync_xhr);
static HRESULT QueryInterface(REFIID,void**); static HRESULT QueryInterface(REFIID,void**);
static HRESULT browserservice_qi(REFIID,void**); static HRESULT browserservice_qi(REFIID,void**);
static HRESULT wb_qi(REFIID,void**); static HRESULT wb_qi(REFIID,void**);
...@@ -5130,11 +5186,31 @@ typedef struct { ...@@ -5130,11 +5186,31 @@ typedef struct {
IInternetProtocolSink *sink; IInternetProtocolSink *sink;
IUri *uri; IUri *uri;
BOOL replied;
ULONG size; ULONG size;
const char *data; const char *data;
const char *ptr; const char *ptr;
HANDLE delay_event;
ULONG delay;
} ProtocolHandler; } ProtocolHandler;
static ProtocolHandler *delay_with_signal_handler;
static DWORD WINAPI delay_proc(void *arg)
{
PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC};
ProtocolHandler *protocol_handler = arg;
if(protocol_handler->delay_event)
WaitForSingleObject(protocol_handler->delay_event, INFINITE);
else
Sleep(protocol_handler->delay);
protocol_handler->delay = 0;
IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data);
return 0;
}
static DWORD WINAPI async_switch_proc(void *arg) static DWORD WINAPI async_switch_proc(void *arg)
{ {
PROTOCOLDATA protocol_data = { PI_FORCE_ASYNC }; PROTOCOLDATA protocol_data = { PI_FORCE_ASYNC };
...@@ -5177,6 +5253,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface) ...@@ -5177,6 +5253,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->uri) if(This->uri)
...@@ -5203,6 +5281,7 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA ...@@ -5203,6 +5281,7 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
HRESULT hres; HRESULT hres;
BSTR bstr; BSTR bstr;
if(!This->replied) {
hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider); hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider);
ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres); ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres);
...@@ -5223,6 +5302,15 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA ...@@ -5223,6 +5302,15 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
IHttpNegotiate_Release(http_negotiate); IHttpNegotiate_Release(http_negotiate);
SysFreeString(bstr); SysFreeString(bstr);
This->replied = TRUE;
if(This->delay || This->delay_event) {
IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
QueueUserWorkItem(delay_proc, This, 0);
return S_OK;
}
}
hres = IInternetProtocolSink_ReportData(This->sink, BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION, hres = IInternetProtocolSink_ReportData(This->sink, BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION,
This->size, This->size); This->size, This->size);
ok(hres == S_OK, "ReportData failed: %08lx\n", hres); ok(hres == S_OK, "ReportData failed: %08lx\n", hres);
...@@ -5298,6 +5386,8 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri, ...@@ -5298,6 +5386,8 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE *dwReserved) IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE *dwReserved)
{ {
ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
HRESULT hres;
BSTR query;
This->data = protocol_doc_str; This->data = protocol_doc_str;
This->size = strlen(This->data); This->size = strlen(This->data);
...@@ -5306,6 +5396,23 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri, ...@@ -5306,6 +5396,23 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
IUri_AddRef(This->uri = uri); IUri_AddRef(This->uri = uri);
This->ptr = This->data; This->ptr = This->data;
hres = IUri_GetQuery(uri, &query);
if(hres == S_OK) {
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());
}
}
SysFreeString(query);
}
IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface); IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
QueueUserWorkItem(async_switch_proc, This, 0); QueueUserWorkItem(async_switch_proc, This, 0);
return E_PENDING; return E_PENDING;
...@@ -6008,6 +6115,95 @@ done: ...@@ -6008,6 +6115,95 @@ done:
IHTMLDocument2_Release(doc[1]); IHTMLDocument2_Release(doc[1]);
} }
static void test_sync_xhr_events(const char *doc_str)
{
IHTMLXMLHttpRequest *xhr[2];
IHTMLDocument2 *doc[2];
IHTMLDocument6 *doc6;
VARIANT var, vempty;
HRESULT hres;
unsigned i;
for(i = 0; i < ARRAY_SIZE(doc); i++)
doc[i] = create_document_with_origin(doc_str);
document_mode = 0;
V_VT(&vempty) = VT_EMPTY;
hres = IHTMLDocument2_QueryInterface(doc[0], &IID_IHTMLDocument6, (void**)&doc6);
if(SUCCEEDED(hres)) {
hres = IHTMLDocument6_get_documentMode(doc6, &var);
ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres);
ok(V_VT(&var) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&var));
document_mode = V_R4(&var);
IHTMLDocument6_Release(doc6);
}
for(i = 0; i < ARRAY_SIZE(doc); i++) {
IHTMLXMLHttpRequestFactory *ctor;
IHTMLWindow5 *window5;
IHTMLWindow2 *window;
BSTR bstr, method;
hres = IHTMLDocument2_get_parentWindow(doc[i], &window);
ok(hres == S_OK, "[%u] get_parentWindow failed: %08lx\n", i, hres);
ok(window != NULL, "[%u] window == NULL\n", i);
hres = IHTMLWindow2_QueryInterface(window, &IID_IHTMLWindow5, (void**)&window5);
ok(hres == S_OK, "[%u] Could not get IHTMLWindow5: %08lx\n", i, hres);
IHTMLWindow2_Release(window);
hres = IHTMLWindow5_get_XMLHttpRequest(window5, &var);
ok(hres == S_OK, "[%u] get_XMLHttpRequest failed: %08lx\n", i, hres);
ok(V_VT(&var) == VT_DISPATCH, "[%u] V_VT(XMLHttpRequest) == %d\n", i, V_VT(&var));
ok(V_DISPATCH(&var) != NULL, "[%u] V_DISPATCH(XMLHttpRequest) == NULL\n", i);
IHTMLWindow5_Release(window5);
hres = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IHTMLXMLHttpRequestFactory, (void**)&ctor);
ok(hres == S_OK, "[%u] Could not get IHTMLXMLHttpRequestFactory: %08lx\n", i, hres);
IDispatch_Release(V_DISPATCH(&var));
hres = IHTMLXMLHttpRequestFactory_create(ctor, &xhr[i]);
ok(hres == S_OK, "[%u] create failed: %08lx\n", i, hres);
IHTMLXMLHttpRequestFactory_Release(ctor);
V_VT(&var) = VT_BOOL;
V_BOOL(&var) = i ? VARIANT_FALSE : VARIANT_TRUE;
method = SysAllocString(L"GET");
bstr = SysAllocString(L"blank.html?delay_with_signal");
hres = IHTMLXMLHttpRequest_open(xhr[i], method, bstr, var, vempty, vempty);
ok(hres == S_OK, "[%u] open failed: %08lx\n", i, hres);
SysFreeString(method);
SysFreeString(bstr);
V_VT(&var) = VT_DISPATCH;
V_DISPATCH(&var) = (IDispatch*)(i ? &sync_xhr_obj : &async_xhr_obj);
hres = IHTMLXMLHttpRequest_put_onreadystatechange(xhr[i], var);
ok(hres == S_OK, "[%u] put_onreadystatechange failed: %08lx\n", i, hres);
}
/* async xhr */
hres = IHTMLXMLHttpRequest_send(xhr[0], vempty);
ok(hres == S_OK, "async xhr send failed: %08lx\n", hres);
/* sync xhr */
SET_EXPECT(sync_xhr_done);
hres = IHTMLXMLHttpRequest_send(xhr[1], vempty);
ok(hres == S_OK, "sync xhr send failed: %08lx\n", hres);
CHECK_CALLED(sync_xhr_done);
SET_EXPECT(async_xhr_done);
pump_msgs(&called_async_xhr_done);
CHECK_CALLED(async_xhr_done);
for(i = 0; i < ARRAY_SIZE(xhr); i++)
IHTMLXMLHttpRequest_Release(xhr[i]);
set_client_site(doc[0], FALSE);
set_client_site(doc[1], FALSE);
IHTMLDocument2_Release(doc[0]);
IHTMLDocument2_Release(doc[1]);
}
static BOOL check_ie(void) static BOOL check_ie(void)
{ {
IHTMLDocument2 *doc; IHTMLDocument2 *doc;
...@@ -6069,8 +6265,11 @@ START_TEST(events) ...@@ -6069,8 +6265,11 @@ START_TEST(events)
test_empty_document(); test_empty_document();
test_storage_events(empty_doc_str); test_storage_events(empty_doc_str);
if(is_ie9plus) test_sync_xhr_events(empty_doc_str);
if(is_ie9plus) {
test_storage_events(empty_doc_ie9_str); test_storage_events(empty_doc_ie9_str);
test_sync_xhr_events(empty_doc_ie9_str);
}
DestroyWindow(container_hwnd); DestroyWindow(container_hwnd);
}else { }else {
......
...@@ -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);
......
...@@ -137,10 +137,16 @@ struct HTMLXMLHttpRequest { ...@@ -137,10 +137,16 @@ struct HTMLXMLHttpRequest {
IWineXMLHttpRequestPrivate IWineXMLHttpRequestPrivate_iface; IWineXMLHttpRequestPrivate IWineXMLHttpRequestPrivate_iface;
IProvideClassInfo2 IProvideClassInfo2_iface; IProvideClassInfo2 IProvideClassInfo2_iface;
LONG ref; LONG ref;
LONG task_magic;
LONG ready_state; LONG ready_state;
response_type_t response_type; response_type_t response_type;
BOOLEAN synchronous;
DWORD magic;
DWORD pending_events_magic;
HTMLInnerWindow *window;
nsIXMLHttpRequest *nsxhr; nsIXMLHttpRequest *nsxhr;
XMLHttpReqEventListener *event_listener; XMLHttpReqEventListener *event_listener;
DOMEvent *pending_progress_event;
}; };
static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener) static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener)
...@@ -166,6 +172,157 @@ static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener) ...@@ -166,6 +172,157 @@ static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener)
nsIDOMEventListener_Release(&event_listener->nsIDOMEventListener_iface); nsIDOMEventListener_Release(&event_listener->nsIDOMEventListener_iface);
} }
static void synthesize_pending_events(HTMLXMLHttpRequest *xhr)
{
DWORD magic = xhr->pending_events_magic;
UINT16 ready_state = xhr->ready_state;
BOOLEAN send_load, send_loadend;
DOMEvent *event;
HRESULT hres;
if(xhr->magic != magic)
return;
/* Make sure further events are synthesized with a new task */
xhr->pending_events_magic = magic - 1;
/* Synthesize the necessary events that led us to this current state */
nsIXMLHttpRequest_GetReadyState(xhr->nsxhr, &ready_state);
if(ready_state == READYSTATE_UNINITIALIZED)
return;
/* Synchronous XHRs only send readyState changes before DONE in IE9 and below */
if(xhr->synchronous && dispex_compat_mode(&xhr->event_target.dispex) > COMPAT_MODE_IE9) {
if(ready_state < READYSTATE_INTERACTIVE) {
xhr->ready_state = ready_state;
return;
}
xhr->ready_state = max(xhr->ready_state, READYSTATE_INTERACTIVE);
}
IHTMLXMLHttpRequest_AddRef(&xhr->IHTMLXMLHttpRequest_iface);
send_loadend = send_load = (xhr->ready_state != ready_state && ready_state == READYSTATE_COMPLETE);
for(;;) {
if(xhr->pending_progress_event &&
xhr->ready_state == (xhr->pending_progress_event->event_id == EVENTID_PROGRESS ? READYSTATE_INTERACTIVE : READYSTATE_COMPLETE))
{
DOMEvent *pending_progress_event = xhr->pending_progress_event;
xhr->pending_progress_event = NULL;
if(pending_progress_event->event_id != EVENTID_PROGRESS) {
send_load = FALSE;
send_loadend = TRUE;
}
dispatch_event(&xhr->event_target, pending_progress_event);
IDOMEvent_Release(&pending_progress_event->IDOMEvent_iface);
if(xhr->magic != magic)
goto ret;
}
if(xhr->ready_state >= ready_state)
break;
xhr->ready_state++;
hres = create_document_event(xhr->window->doc, EVENTID_READYSTATECHANGE, &event);
if(SUCCEEDED(hres)) {
dispatch_event(&xhr->event_target, event);
IDOMEvent_Release(&event->IDOMEvent_iface);
if(xhr->magic != magic)
goto ret;
}
}
if(send_load) {
hres = create_document_event(xhr->window->doc, EVENTID_LOAD, &event);
if(SUCCEEDED(hres)) {
dispatch_event(&xhr->event_target, event);
IDOMEvent_Release(&event->IDOMEvent_iface);
if(xhr->magic != magic)
goto ret;
}
}
if(send_loadend) {
hres = create_document_event(xhr->window->doc, EVENTID_LOADEND, &event);
if(SUCCEEDED(hres)) {
dispatch_event(&xhr->event_target, event);
IDOMEvent_Release(&event->IDOMEvent_iface);
if(xhr->magic != magic)
goto ret;
}
}
ret:
IHTMLXMLHttpRequest_Release(&xhr->IHTMLXMLHttpRequest_iface);
}
static nsresult sync_xhr_send(HTMLXMLHttpRequest *xhr, nsIVariant *nsbody)
{
thread_data_t *thread_data = get_thread_data(TRUE);
HTMLXMLHttpRequest *prev_blocking_xhr;
HTMLInnerWindow *window = xhr->window;
nsresult nsres;
if(!thread_data)
return NS_ERROR_OUT_OF_MEMORY;
prev_blocking_xhr = thread_data->blocking_xhr;
/* Note: Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27),
* synchronous requests on the main thread have been deprecated due to the negative
* effects to the user experience. However, they still work. The larger issue is that
* it is broken because it still dispatches async XHR and some other events, while all
* other major browsers don't, including IE, so we have to filter them out during Send.
*
* They will need to be queued and dispatched later, after Send returns, otherwise it
* breaks JavaScript single-threaded expectations (JS code will switch from blocking in
* Send to executing some event handler, then returning back to Send, messing its state).
*
* Of course we can't just delay dispatching the events, because the state won't match
* for each event later on, to what it's supposed to be (most notably, XHR's readyState).
* We'll keep snapshots and synthesize them when unblocked for async XHR events.
*
* Note that while queuing an event this way would not work correctly with their default
* behavior in Gecko (preventDefault() can't be called because we need to *delay* the
* default, rather than prevent it completely), Gecko does suppress events reaching the
* document during the sync XHR event loop, so those we do not handle manually. If we
* find an event that has defaults on Gecko's side and isn't delayed by Gecko, we need
* to figure out a way to handle it...
*
* For details (and bunch of problems to consider) see: https://bugzil.la/697151
*/
window->base.outer_window->readystate_locked++;
window->blocking_depth++;
thread_data->blocking_xhr = xhr;
nsres = nsIXMLHttpRequest_Send(xhr->nsxhr, nsbody);
thread_data->blocking_xhr = prev_blocking_xhr;
window->base.outer_window->readystate_locked--;
if(!--window->blocking_depth)
unblock_tasks_and_timers(thread_data);
/* Process any pending events now since they were part of the blocked send() above */
synthesize_pending_events(xhr);
return nsres;
}
struct pending_xhr_events_task {
event_task_t header;
HTMLXMLHttpRequest *xhr;
};
static void pending_xhr_events_proc(event_task_t *_task)
{
struct pending_xhr_events_task *task = (struct pending_xhr_events_task*)_task;
synthesize_pending_events(task->xhr);
}
static void pending_xhr_events_destr(event_task_t *_task)
{
}
static inline XMLHttpReqEventListener *impl_from_nsIDOMEventListener(nsIDOMEventListener *iface) static inline XMLHttpReqEventListener *impl_from_nsIDOMEventListener(nsIDOMEventListener *iface)
{ {
...@@ -221,23 +378,92 @@ static nsrefcnt NSAPI XMLHttpReqEventListener_Release(nsIDOMEventListener *iface ...@@ -221,23 +378,92 @@ static nsrefcnt NSAPI XMLHttpReqEventListener_Release(nsIDOMEventListener *iface
static nsresult NSAPI XMLHttpReqEventListener_HandleEvent(nsIDOMEventListener *iface, nsIDOMEvent *nsevent) static nsresult NSAPI XMLHttpReqEventListener_HandleEvent(nsIDOMEventListener *iface, nsIDOMEvent *nsevent)
{ {
XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface);
UINT16 ready_state; HTMLXMLHttpRequest *blocking_xhr = NULL;
thread_data_t *thread_data;
LONG ready_state;
DOMEvent *event; DOMEvent *event;
HRESULT hres; HRESULT hres;
UINT16 val;
TRACE("(%p)\n", This); TRACE("(%p)\n", This);
if(!This->xhr) if(!This->xhr)
return NS_OK; return NS_OK;
if(NS_SUCCEEDED(nsIXMLHttpRequest_GetReadyState(This->xhr->nsxhr, &ready_state))) ready_state = This->xhr->ready_state;
This->xhr->ready_state = ready_state; if(NS_SUCCEEDED(nsIXMLHttpRequest_GetReadyState(This->xhr->nsxhr, &val)))
ready_state = val;
if((thread_data = get_thread_data(FALSE)))
blocking_xhr = thread_data->blocking_xhr;
hres = create_event_from_nsevent(nsevent, dispex_compat_mode(&This->xhr->event_target.dispex), &event); hres = create_event_from_nsevent(nsevent, dispex_compat_mode(&This->xhr->event_target.dispex), &event);
if(SUCCEEDED(hres) ){ if(FAILED(hres)) {
dispatch_event(&This->xhr->event_target, event); if(!blocking_xhr || This->xhr == blocking_xhr)
This->xhr->ready_state = ready_state;
return NS_ERROR_OUT_OF_MEMORY;
}
if(blocking_xhr) {
BOOL has_pending_events = (This->xhr->magic == This->xhr->pending_events_magic);
if(has_pending_events || This->xhr != blocking_xhr) {
switch(event->event_id) {
case EVENTID_PROGRESS:
case EVENTID_ABORT:
case EVENTID_ERROR:
case EVENTID_TIMEOUT:
if(This->xhr->pending_progress_event)
IDOMEvent_Release(&This->xhr->pending_progress_event->IDOMEvent_iface);
This->xhr->pending_progress_event = event;
break;
default:
IDOMEvent_Release(&event->IDOMEvent_iface); IDOMEvent_Release(&event->IDOMEvent_iface);
break;
} }
if(!has_pending_events) {
if(!This->xhr->synchronous) {
struct pending_xhr_events_task *task;
remove_target_tasks(This->xhr->task_magic);
if(!(task = malloc(sizeof(*task))))
return NS_ERROR_OUT_OF_MEMORY;
task->header.target_magic = This->xhr->task_magic;
task->header.thread_blocked = TRUE;
task->header.proc = pending_xhr_events_proc;
task->header.destr = pending_xhr_events_destr;
task->header.window = This->xhr->window;
task->xhr = This->xhr;
IHTMLWindow2_AddRef(&This->xhr->window->base.IHTMLWindow2_iface);
list_add_after(thread_data->pending_xhr_events_tail, &task->header.entry);
thread_data->pending_xhr_events_tail = &task->header.entry;
}
This->xhr->pending_events_magic = This->xhr->magic;
return NS_OK;
}
/* Synthesize pending events that a nested sync XHR might have blocked us on */
if(This->xhr == blocking_xhr)
synthesize_pending_events(This->xhr);
return NS_OK;
}
/* Workaround weird Gecko behavior with nested sync XHRs, where it sends readyState changes
for OPENED (or possibly other states than DONE), unlike IE10+ and non-nested sync XHRs... */
if(ready_state < READYSTATE_COMPLETE && event->event_id == EVENTID_READYSTATECHANGE) {
IDOMEvent_Release(&event->IDOMEvent_iface);
This->xhr->ready_state = ready_state;
return NS_OK;
}
}
This->xhr->ready_state = ready_state;
dispatch_event(&This->xhr->event_target, event);
IDOMEvent_Release(&event->IDOMEvent_iface);
return NS_OK; return NS_OK;
} }
...@@ -299,7 +525,11 @@ static ULONG WINAPI HTMLXMLHttpRequest_Release(IHTMLXMLHttpRequest *iface) ...@@ -299,7 +525,11 @@ static ULONG WINAPI HTMLXMLHttpRequest_Release(IHTMLXMLHttpRequest *iface)
TRACE("(%p) ref=%ld\n", This, ref); TRACE("(%p) ref=%ld\n", This, ref);
if(!ref) { if(!ref) {
remove_target_tasks(This->task_magic);
detach_xhr_event_listener(This->event_listener); detach_xhr_event_listener(This->event_listener);
if(This->pending_progress_event)
IDOMEvent_Release(&This->pending_progress_event->IDOMEvent_iface);
IHTMLWindow2_Release(&This->window->base.IHTMLWindow2_iface);
release_event_target(&This->event_target); release_event_target(&This->event_target);
release_dispex(&This->event_target.dispex); release_dispex(&This->event_target.dispex);
nsIXMLHttpRequest_Release(This->nsxhr); nsIXMLHttpRequest_Release(This->nsxhr);
...@@ -510,14 +740,17 @@ static HRESULT WINAPI HTMLXMLHttpRequest_get_onreadystatechange(IHTMLXMLHttpRequ ...@@ -510,14 +740,17 @@ static HRESULT WINAPI HTMLXMLHttpRequest_get_onreadystatechange(IHTMLXMLHttpRequ
static HRESULT WINAPI HTMLXMLHttpRequest_abort(IHTMLXMLHttpRequest *iface) static HRESULT WINAPI HTMLXMLHttpRequest_abort(IHTMLXMLHttpRequest *iface)
{ {
HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface);
DWORD prev_magic = This->magic;
UINT16 ready_state; UINT16 ready_state;
nsresult nsres; nsresult nsres;
TRACE("(%p)->()\n", This); TRACE("(%p)->()\n", This);
This->magic++;
nsres = nsIXMLHttpRequest_SlowAbort(This->nsxhr); nsres = nsIXMLHttpRequest_SlowAbort(This->nsxhr);
if(NS_FAILED(nsres)) { if(NS_FAILED(nsres)) {
ERR("nsIXMLHttpRequest_SlowAbort failed: %08lx\n", nsres); ERR("nsIXMLHttpRequest_SlowAbort failed: %08lx\n", nsres);
This->magic = prev_magic;
return E_FAIL; return E_FAIL;
} }
...@@ -553,9 +786,11 @@ static HRESULT HTMLXMLHttpRequest_open_hook(DispatchEx *dispex, WORD flags, ...@@ -553,9 +786,11 @@ static HRESULT HTMLXMLHttpRequest_open_hook(DispatchEx *dispex, WORD flags,
static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR bstrMethod, BSTR bstrUrl, VARIANT varAsync, VARIANT varUser, VARIANT varPassword) static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR bstrMethod, BSTR bstrUrl, VARIANT varAsync, VARIANT varUser, VARIANT varPassword)
{ {
HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface);
BOOLEAN prev_synchronous;
nsAString user, password; nsAString user, password;
nsACString method, url; nsACString method, url;
unsigned opt_argc = 1; unsigned opt_argc = 1;
DWORD prev_magic;
nsresult nsres; nsresult nsres;
HRESULT hres; HRESULT hres;
...@@ -570,15 +805,6 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b ...@@ -570,15 +805,6 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
} }
} }
/* Note: Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27),
* synchronous requests on the main thread have been deprecated due to the negative
* effects to the user experience.
*/
if(!V_BOOL(&varAsync)) {
FIXME("Synchronous request is not supported yet\n");
return E_FAIL;
}
hres = variant_to_nsastr(varUser, &user); hres = variant_to_nsastr(varUser, &user);
if(FAILED(hres)) if(FAILED(hres))
return hres; return hres;
...@@ -602,6 +828,12 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b ...@@ -602,6 +828,12 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
return hres; return hres;
} }
/* Set this here, Gecko dispatches nested sync XHR readyState changes for OPENED (see HandleEvent) */
prev_magic = This->magic;
prev_synchronous = This->synchronous;
This->synchronous = !V_BOOL(&varAsync);
This->magic++;
if(V_VT(&varPassword) != VT_EMPTY && V_VT(&varPassword) != VT_ERROR) if(V_VT(&varPassword) != VT_EMPTY && V_VT(&varPassword) != VT_ERROR)
opt_argc += 2; opt_argc += 2;
else if(V_VT(&varUser) != VT_EMPTY && V_VT(&varUser) != VT_ERROR) else if(V_VT(&varUser) != VT_EMPTY && V_VT(&varUser) != VT_ERROR)
...@@ -615,6 +847,8 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b ...@@ -615,6 +847,8 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
if(NS_FAILED(nsres)) { if(NS_FAILED(nsres)) {
ERR("nsIXMLHttpRequest_Open failed: %08lx\n", nsres); ERR("nsIXMLHttpRequest_Open failed: %08lx\n", nsres);
This->magic = prev_magic;
This->synchronous = prev_synchronous;
return E_FAIL; return E_FAIL;
} }
...@@ -651,13 +885,18 @@ static HRESULT WINAPI HTMLXMLHttpRequest_send(IHTMLXMLHttpRequest *iface, VARIAN ...@@ -651,13 +885,18 @@ static HRESULT WINAPI HTMLXMLHttpRequest_send(IHTMLXMLHttpRequest *iface, VARIAN
return E_NOTIMPL; return E_NOTIMPL;
} }
if(NS_SUCCEEDED(nsres)) if(NS_SUCCEEDED(nsres)) {
if(This->synchronous)
nsres = sync_xhr_send(This, (nsIVariant*)nsbody);
else
nsres = nsIXMLHttpRequest_Send(This->nsxhr, (nsIVariant*)nsbody); nsres = nsIXMLHttpRequest_Send(This->nsxhr, (nsIVariant*)nsbody);
}
if(nsbody) if(nsbody)
nsIWritableVariant_Release(nsbody); nsIWritableVariant_Release(nsbody);
if(NS_FAILED(nsres)) { if(NS_FAILED(nsres)) {
ERR("nsIXMLHttpRequest_Send failed: %08lx\n", nsres); ERR("nsIXMLHttpRequest_Send failed: %08lx\n", nsres);
return E_FAIL; return map_nsresult(nsres);
} }
return S_OK; return S_OK;
...@@ -1445,6 +1684,9 @@ static HRESULT WINAPI HTMLXMLHttpRequestFactory_create(IHTMLXMLHttpRequestFactor ...@@ -1445,6 +1684,9 @@ static HRESULT WINAPI HTMLXMLHttpRequestFactory_create(IHTMLXMLHttpRequestFactor
} }
ret->nsxhr = nsxhr; ret->nsxhr = nsxhr;
ret->window = This->window;
ret->task_magic = get_task_target_magic();
IHTMLWindow2_AddRef(&This->window->base.IHTMLWindow2_iface);
ret->IHTMLXMLHttpRequest_iface.lpVtbl = &HTMLXMLHttpRequestVtbl; ret->IHTMLXMLHttpRequest_iface.lpVtbl = &HTMLXMLHttpRequestVtbl;
ret->IHTMLXMLHttpRequest2_iface.lpVtbl = &HTMLXMLHttpRequest2Vtbl; ret->IHTMLXMLHttpRequest2_iface.lpVtbl = &HTMLXMLHttpRequest2Vtbl;
......
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