mshtml: Implement synchronous XMLHttpRequest.
Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
parent
51a6818491
commit
6a31e4acc7
10 changed files with 713 additions and 61 deletions
|
@ -3294,6 +3294,8 @@ static HRESULT WINAPI window_private_postMessage(IWineHTMLWindowPrivate *iface,
|
|||
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;
|
||||
return push_event_task(&task->header, window, post_message_proc, post_message_destr, window->task_magic);
|
||||
}
|
||||
|
|
|
@ -607,6 +607,7 @@ struct HTMLInnerWindow {
|
|||
VARIANT performance;
|
||||
HTMLPerformanceTiming *performance_timing;
|
||||
|
||||
unsigned blocking_depth;
|
||||
unsigned parser_callback_cnt;
|
||||
struct list script_queue;
|
||||
|
||||
|
@ -1288,6 +1289,7 @@ typedef void (*event_task_proc_t)(event_task_t*);
|
|||
|
||||
struct event_task_t {
|
||||
LONG target_magic;
|
||||
BOOL thread_blocked;
|
||||
event_task_proc_t proc;
|
||||
event_task_proc_t destr;
|
||||
struct list entry;
|
||||
|
@ -1304,11 +1306,14 @@ typedef struct {
|
|||
struct list task_list;
|
||||
struct list event_task_list;
|
||||
struct list timer_list;
|
||||
struct list *pending_xhr_events_tail;
|
||||
struct wine_rb_tree session_storage_map;
|
||||
void *blocking_xhr;
|
||||
} thread_data_t;
|
||||
|
||||
thread_data_t *get_thread_data(BOOL) 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;
|
||||
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_
|
|||
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)
|
||||
{
|
||||
list_remove(&timer->entry);
|
||||
|
@ -140,7 +147,7 @@ void remove_target_tasks(LONG target)
|
|||
LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->event_task_list) {
|
||||
event_task_t *task = LIST_ENTRY(liter, event_task_t, entry);
|
||||
if(task->target_magic == target) {
|
||||
list_remove(&task->entry);
|
||||
unlink_event_task(task, thread_data);
|
||||
release_event_task(task);
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +310,7 @@ static LRESULT process_timer(void)
|
|||
thread_data = get_thread_data(FALSE);
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
@ -338,7 +345,7 @@ static LRESULT process_timer(void)
|
|||
call_timer_disp(disp, timer_type);
|
||||
|
||||
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);
|
||||
return 0;
|
||||
|
@ -366,16 +373,19 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
|
|||
continue;
|
||||
}
|
||||
|
||||
head = list_head(&thread_data->event_task_list);
|
||||
if(head) {
|
||||
head = &thread_data->event_task_list;
|
||||
while((head = list_next(&thread_data->event_task_list, head))) {
|
||||
event_task_t *task = LIST_ENTRY(head, event_task_t, entry);
|
||||
list_remove(&task->entry);
|
||||
task->proc(task);
|
||||
release_event_task(task);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
if((!task->thread_blocked || !thread_data->blocking_xhr) && !task->window->blocking_depth) {
|
||||
unlink_event_task(task, thread_data);
|
||||
task->proc(task);
|
||||
release_event_task(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!head)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case WM_TIMER:
|
||||
|
@ -451,6 +461,7 @@ thread_data_t *get_thread_data(BOOL create)
|
|||
list_init(&thread_data->task_list);
|
||||
list_init(&thread_data->event_task_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);
|
||||
}
|
||||
|
||||
|
@ -467,3 +478,16 @@ ULONGLONG get_time_stamp(void)
|
|||
GetSystemTimeAsFileTime(&time);
|
||||
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);
|
|||
DEFINE_EXPECT(doc2_onstorage);
|
||||
DEFINE_EXPECT(doc2_onstoragecommit);
|
||||
DEFINE_EXPECT(window2_onstorage);
|
||||
DEFINE_EXPECT(async_xhr_done);
|
||||
DEFINE_EXPECT(sync_xhr_done);
|
||||
|
||||
static HWND container_hwnd = NULL;
|
||||
static IHTMLWindow2 *window;
|
||||
|
@ -3730,6 +3732,60 @@ static HRESULT WINAPI window2_onstorage(IDispatchEx *iface, DISPID id, LCID lcid
|
|||
|
||||
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 browserservice_qi(REFIID,void**);
|
||||
static HRESULT wb_qi(REFIID,void**);
|
||||
|
@ -5130,11 +5186,31 @@ typedef struct {
|
|||
IInternetProtocolSink *sink;
|
||||
IUri *uri;
|
||||
|
||||
BOOL replied;
|
||||
ULONG size;
|
||||
const char *data;
|
||||
const char *ptr;
|
||||
|
||||
HANDLE delay_event;
|
||||
ULONG delay;
|
||||
} 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)
|
||||
{
|
||||
PROTOCOLDATA protocol_data = { PI_FORCE_ASYNC };
|
||||
|
@ -5177,6 +5253,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface)
|
|||
LONG ref = InterlockedDecrement(&This->ref);
|
||||
|
||||
if(!ref) {
|
||||
if(This->delay_event)
|
||||
CloseHandle(This->delay_event);
|
||||
if(This->sink)
|
||||
IInternetProtocolSink_Release(This->sink);
|
||||
if(This->uri)
|
||||
|
@ -5203,25 +5281,35 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
|
|||
HRESULT hres;
|
||||
BSTR bstr;
|
||||
|
||||
hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider);
|
||||
ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres);
|
||||
if(!This->replied) {
|
||||
hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider);
|
||||
ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres);
|
||||
|
||||
hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate, (void**)&http_negotiate);
|
||||
IServiceProvider_Release(service_provider);
|
||||
ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08lx\n", hres);
|
||||
hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate, (void**)&http_negotiate);
|
||||
IServiceProvider_Release(service_provider);
|
||||
ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08lx\n", hres);
|
||||
|
||||
hres = IUri_GetDisplayUri(This->uri, &bstr);
|
||||
ok(hres == S_OK, "Failed to get display uri: %08lx\n", hres);
|
||||
hres = IHttpNegotiate_BeginningTransaction(http_negotiate, bstr, L"", 0, &addl_headers);
|
||||
ok(hres == S_OK, "BeginningTransaction failed: %08lx\n", hres);
|
||||
CoTaskMemFree(addl_headers);
|
||||
SysFreeString(bstr);
|
||||
hres = IUri_GetDisplayUri(This->uri, &bstr);
|
||||
ok(hres == S_OK, "Failed to get display uri: %08lx\n", hres);
|
||||
hres = IHttpNegotiate_BeginningTransaction(http_negotiate, bstr, L"", 0, &addl_headers);
|
||||
ok(hres == S_OK, "BeginningTransaction failed: %08lx\n", hres);
|
||||
CoTaskMemFree(addl_headers);
|
||||
SysFreeString(bstr);
|
||||
|
||||
bstr = SysAllocString(L"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
|
||||
hres = IHttpNegotiate_OnResponse(http_negotiate, 200, bstr, NULL, NULL);
|
||||
ok(hres == S_OK, "OnResponse failed: %08lx\n", hres);
|
||||
IHttpNegotiate_Release(http_negotiate);
|
||||
SysFreeString(bstr);
|
||||
bstr = SysAllocString(L"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
|
||||
hres = IHttpNegotiate_OnResponse(http_negotiate, 200, bstr, NULL, NULL);
|
||||
ok(hres == S_OK, "OnResponse failed: %08lx\n", hres);
|
||||
IHttpNegotiate_Release(http_negotiate);
|
||||
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,
|
||||
This->size, This->size);
|
||||
|
@ -5298,6 +5386,8 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
|
|||
IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE *dwReserved)
|
||||
{
|
||||
ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
|
||||
HRESULT hres;
|
||||
BSTR query;
|
||||
|
||||
This->data = protocol_doc_str;
|
||||
This->size = strlen(This->data);
|
||||
|
@ -5306,6 +5396,23 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
|
|||
IUri_AddRef(This->uri = uri);
|
||||
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);
|
||||
QueueUserWorkItem(async_switch_proc, This, 0);
|
||||
return E_PENDING;
|
||||
|
@ -6008,6 +6115,95 @@ done:
|
|||
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)
|
||||
{
|
||||
IHTMLDocument2 *doc;
|
||||
|
@ -6069,8 +6265,11 @@ START_TEST(events)
|
|||
|
||||
test_empty_document();
|
||||
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_sync_xhr_events(empty_doc_ie9_str);
|
||||
}
|
||||
|
||||
DestroyWindow(container_hwnd);
|
||||
}else {
|
||||
|
|
|
@ -88,6 +88,9 @@ doc_with_prop_ie9.html HTML "doc_with_prop_ie9.html"
|
|||
/* @makedep: iframe.html */
|
||||
iframe.html HTML "iframe.html"
|
||||
|
||||
/* @makedep: xhr_iframe.html */
|
||||
xhr_iframe.html HTML "xhr_iframe.html"
|
||||
|
||||
/* For res: protocol test: */
|
||||
|
||||
/* @makedep: jstest.html */
|
||||
|
|
|
@ -3675,6 +3675,7 @@ typedef struct {
|
|||
IInternetProtocolSink *sink;
|
||||
BINDINFO bind_info;
|
||||
|
||||
HANDLE delay_event;
|
||||
BSTR content_type;
|
||||
IStream *stream;
|
||||
char *data;
|
||||
|
@ -3685,12 +3686,17 @@ typedef struct {
|
|||
IUri *uri;
|
||||
} ProtocolHandler;
|
||||
|
||||
static ProtocolHandler *delay_with_signal_handler;
|
||||
|
||||
static DWORD WINAPI delay_proc(void *arg)
|
||||
{
|
||||
PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC};
|
||||
ProtocolHandler *protocol_handler = arg;
|
||||
|
||||
Sleep(protocol_handler->delay);
|
||||
if(protocol_handler->delay_event)
|
||||
WaitForSingleObject(protocol_handler->delay_event, INFINITE);
|
||||
else
|
||||
Sleep(protocol_handler->delay);
|
||||
protocol_handler->delay = -1;
|
||||
IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data);
|
||||
return 0;
|
||||
|
@ -3735,7 +3741,7 @@ static void report_data(ProtocolHandler *This)
|
|||
|
||||
IHttpNegotiate_Release(http_negotiate);
|
||||
|
||||
if(This->delay) {
|
||||
if(This->delay || This->delay_event) {
|
||||
IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
|
||||
QueueUserWorkItem(delay_proc, This, 0);
|
||||
return;
|
||||
|
@ -3880,6 +3886,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface)
|
|||
LONG ref = InterlockedDecrement(&This->ref);
|
||||
|
||||
if(!ref) {
|
||||
if(This->delay_event)
|
||||
CloseHandle(This->delay_event);
|
||||
if(This->sink)
|
||||
IInternetProtocolSink_Release(This->sink);
|
||||
if(This->stream)
|
||||
|
@ -4061,8 +4069,20 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
|
|||
|
||||
hres = IUri_GetQuery(uri, &query);
|
||||
if(SUCCEEDED(hres)) {
|
||||
if(!lstrcmpW(query, L"?delay"))
|
||||
if(!wcscmp(query, L"?delay"))
|
||||
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))
|
||||
This->content_type = SysAllocString(query + sizeof("?content-type=")-1);
|
||||
SysFreeString(query);
|
||||
|
|
|
@ -76,6 +76,145 @@ function test_xhr() {
|
|||
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() {
|
||||
var xhr = new XMLHttpRequest(), types, i = 0, override = false;
|
||||
var v = document.documentMode;
|
||||
|
@ -291,6 +430,7 @@ function test_response() {
|
|||
|
||||
var tests = [
|
||||
test_xhr,
|
||||
test_sync_xhr,
|
||||
test_content_types,
|
||||
test_abort,
|
||||
test_timeout,
|
||||
|
|
23
dlls/mshtml/tests/xhr_iframe.html
Normal file
23
dlls/mshtml/tests/xhr_iframe.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<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
|
|||
|
||||
SET_EXPECT(xmlhttprequest_onreadystatechange_opened);
|
||||
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 */
|
||||
todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_opened);
|
||||
ok(hres == S_OK, "open failed: %08lx\n", hres);
|
||||
CHECK_CALLED(xmlhttprequest_onreadystatechange_opened);
|
||||
|
||||
SysFreeString(method);
|
||||
SysFreeString(url);
|
||||
|
||||
if(FAILED(hres)) {
|
||||
IHTMLXMLHttpRequest_Release(xhr);
|
||||
xhr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
text = (BSTR)0xdeadbeef;
|
||||
hres = IHTMLXMLHttpRequest_getAllResponseHeaders(xhr, &text);
|
||||
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
|
|||
loading_cnt = 0;
|
||||
hres = IHTMLXMLHttpRequest_send(xhr, vempty);
|
||||
ok(hres == S_OK, "send failed: %08lx\n", hres);
|
||||
CHECK_CALLED(xmlhttprequest_onreadystatechange_opened);
|
||||
CHECK_CALLED(xmlhttprequest_onreadystatechange_headers_received);
|
||||
CHECK_CALLED(xmlhttprequest_onreadystatechange_loading);
|
||||
todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_opened);
|
||||
todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_headers_received);
|
||||
todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_loading);
|
||||
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;
|
||||
hres = IHTMLXMLHttpRequest_getResponseHeader(xhr, content_type, &text);
|
||||
|
|
|
@ -137,10 +137,16 @@ struct HTMLXMLHttpRequest {
|
|||
IWineXMLHttpRequestPrivate IWineXMLHttpRequestPrivate_iface;
|
||||
IProvideClassInfo2 IProvideClassInfo2_iface;
|
||||
LONG ref;
|
||||
LONG task_magic;
|
||||
LONG ready_state;
|
||||
response_type_t response_type;
|
||||
BOOLEAN synchronous;
|
||||
DWORD magic;
|
||||
DWORD pending_events_magic;
|
||||
HTMLInnerWindow *window;
|
||||
nsIXMLHttpRequest *nsxhr;
|
||||
XMLHttpReqEventListener *event_listener;
|
||||
DOMEvent *pending_progress_event;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -221,23 +378,92 @@ static nsrefcnt NSAPI XMLHttpReqEventListener_Release(nsIDOMEventListener *iface
|
|||
static nsresult NSAPI XMLHttpReqEventListener_HandleEvent(nsIDOMEventListener *iface, nsIDOMEvent *nsevent)
|
||||
{
|
||||
XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface);
|
||||
UINT16 ready_state;
|
||||
HTMLXMLHttpRequest *blocking_xhr = NULL;
|
||||
thread_data_t *thread_data;
|
||||
LONG ready_state;
|
||||
DOMEvent *event;
|
||||
HRESULT hres;
|
||||
UINT16 val;
|
||||
|
||||
TRACE("(%p)\n", This);
|
||||
|
||||
if(!This->xhr)
|
||||
return NS_OK;
|
||||
|
||||
if(NS_SUCCEEDED(nsIXMLHttpRequest_GetReadyState(This->xhr->nsxhr, &ready_state)))
|
||||
This->xhr->ready_state = ready_state;
|
||||
ready_state = This->xhr->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);
|
||||
if(SUCCEEDED(hres) ){
|
||||
dispatch_event(&This->xhr->event_target, event);
|
||||
IDOMEvent_Release(&event->IDOMEvent_iface);
|
||||
if(FAILED(hres)) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -299,7 +525,11 @@ static ULONG WINAPI HTMLXMLHttpRequest_Release(IHTMLXMLHttpRequest *iface)
|
|||
TRACE("(%p) ref=%ld\n", This, ref);
|
||||
|
||||
if(!ref) {
|
||||
remove_target_tasks(This->task_magic);
|
||||
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_dispex(&This->event_target.dispex);
|
||||
nsIXMLHttpRequest_Release(This->nsxhr);
|
||||
|
@ -510,14 +740,17 @@ static HRESULT WINAPI HTMLXMLHttpRequest_get_onreadystatechange(IHTMLXMLHttpRequ
|
|||
static HRESULT WINAPI HTMLXMLHttpRequest_abort(IHTMLXMLHttpRequest *iface)
|
||||
{
|
||||
HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface);
|
||||
DWORD prev_magic = This->magic;
|
||||
UINT16 ready_state;
|
||||
nsresult nsres;
|
||||
|
||||
TRACE("(%p)->()\n", This);
|
||||
|
||||
This->magic++;
|
||||
nsres = nsIXMLHttpRequest_SlowAbort(This->nsxhr);
|
||||
if(NS_FAILED(nsres)) {
|
||||
ERR("nsIXMLHttpRequest_SlowAbort failed: %08lx\n", nsres);
|
||||
This->magic = prev_magic;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface);
|
||||
BOOLEAN prev_synchronous;
|
||||
nsAString user, password;
|
||||
nsACString method, url;
|
||||
unsigned opt_argc = 1;
|
||||
DWORD prev_magic;
|
||||
nsresult nsres;
|
||||
HRESULT hres;
|
||||
|
||||
|
@ -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);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
@ -602,6 +828,12 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
|
|||
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)
|
||||
opt_argc += 2;
|
||||
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
|
|||
|
||||
if(NS_FAILED(nsres)) {
|
||||
ERR("nsIXMLHttpRequest_Open failed: %08lx\n", nsres);
|
||||
This->magic = prev_magic;
|
||||
This->synchronous = prev_synchronous;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
@ -651,13 +885,18 @@ static HRESULT WINAPI HTMLXMLHttpRequest_send(IHTMLXMLHttpRequest *iface, VARIAN
|
|||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
if(NS_SUCCEEDED(nsres))
|
||||
nsres = nsIXMLHttpRequest_Send(This->nsxhr, (nsIVariant*)nsbody);
|
||||
if(NS_SUCCEEDED(nsres)) {
|
||||
if(This->synchronous)
|
||||
nsres = sync_xhr_send(This, (nsIVariant*)nsbody);
|
||||
else
|
||||
nsres = nsIXMLHttpRequest_Send(This->nsxhr, (nsIVariant*)nsbody);
|
||||
}
|
||||
|
||||
if(nsbody)
|
||||
nsIWritableVariant_Release(nsbody);
|
||||
if(NS_FAILED(nsres)) {
|
||||
ERR("nsIXMLHttpRequest_Send failed: %08lx\n", nsres);
|
||||
return E_FAIL;
|
||||
return map_nsresult(nsres);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
@ -1445,6 +1684,9 @@ static HRESULT WINAPI HTMLXMLHttpRequestFactory_create(IHTMLXMLHttpRequestFactor
|
|||
}
|
||||
|
||||
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->IHTMLXMLHttpRequest2_iface.lpVtbl = &HTMLXMLHttpRequest2Vtbl;
|
||||
|
|
Loading…
Add table
Reference in a new issue