From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/task.c | 25 ++++++++++++++++++------- dlls/mshtml/xmlhttprequest.c | 12 ++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 582cc109d31..178abc9a559 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1493,6 +1493,7 @@ typedef struct { struct list *pending_xhr_events_tail; struct wine_rb_tree session_storage_map; void *blocking_xhr; + unsigned tasks_locked; BOOL timer_blocked; } thread_data_t;
diff --git a/dlls/mshtml/task.c b/dlls/mshtml/task.c index 245f8771ab2..955567eaabb 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -142,7 +142,7 @@ void remove_target_tasks(LONG target) release_task_timer(thread_data->thread_hwnd, timer); }
- if(!list_empty(&thread_data->timer_list)) { + if(!list_empty(&thread_data->timer_list) && !thread_data->tasks_locked && !thread_data->blocking_xhr) { DWORD tc = GetTickCount();
timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); @@ -227,7 +227,7 @@ HRESULT set_task_timer(HTMLInnerWindow *window, LONG msec, enum timer_type timer timer->disp = disp;
if(queue_timer(thread_data, timer)) { - if(thread_data->blocking_xhr) + if(thread_data->tasks_locked || thread_data->blocking_xhr) thread_data->timer_blocked = TRUE; else SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL); @@ -328,27 +328,28 @@ static LRESULT process_timer(void) thread_data = get_thread_data(FALSE); assert(thread_data != NULL);
- if(list_empty(&thread_data->timer_list) || thread_data->blocking_xhr) { + if(list_empty(&thread_data->timer_list) || thread_data->tasks_locked || thread_data->blocking_xhr) { if(!list_empty(&thread_data->timer_list)) thread_data->timer_blocked = TRUE; KillTimer(thread_data->thread_hwnd, TIMER_ID); return 0; }
+ thread_data->tasks_locked++; last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry); do { tc = GetTickCount(); if(timer == last_timer) { timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time>tc ? timer->time-tc : 0, NULL); - return 0; + goto done; }
timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
if(timer->time > tc) { SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL); - return 0; + goto done; }
disp = timer->disp; @@ -370,6 +371,10 @@ static LRESULT process_timer(void) if(!list_empty(&thread_data->timer_list)) thread_data->timer_blocked = TRUE; KillTimer(thread_data->thread_hwnd, TIMER_ID); + +done: + if(!--thread_data->tasks_locked && (!list_empty(&thread_data->task_list) || !list_empty(&thread_data->event_task_list))) + PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0); return 0; }
@@ -383,13 +388,16 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa if(!thread_data) return 0;
- while(1) { + while(!thread_data->tasks_locked) { struct list *head = list_head(&thread_data->task_list);
if(head) { task_t *task = LIST_ENTRY(head, task_t, entry); list_remove(&task->entry); + thread_data->tasks_locked++; task->proc(task); + if(!--thread_data->tasks_locked) + unblock_timers(thread_data); task->destr(task); free(task); continue; @@ -401,7 +409,10 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
if((!task->thread_blocked || !thread_data->blocking_xhr) && !task->window->blocking_depth) { unlink_event_task(task, thread_data); + thread_data->tasks_locked++; task->proc(task); + if(!--thread_data->tasks_locked) + unblock_timers(thread_data); release_event_task(task); break; } @@ -503,7 +514,7 @@ ULONGLONG get_time_stamp(void)
void unblock_tasks_and_timers(thread_data_t *thread_data) { - if(!list_empty(&thread_data->event_task_list)) + if(!list_empty(&thread_data->task_list) || !list_empty(&thread_data->event_task_list)) PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
unblock_timers(thread_data); diff --git a/dlls/mshtml/xmlhttprequest.c b/dlls/mshtml/xmlhttprequest.c index fffc9fab51b..4d0d629326f 100644 --- a/dlls/mshtml/xmlhttprequest.c +++ b/dlls/mshtml/xmlhttprequest.c @@ -269,10 +269,12 @@ 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; + unsigned prev_tasks_locked; nsresult nsres;
if(!thread_data) return NS_ERROR_OUT_OF_MEMORY; + prev_tasks_locked = thread_data->tasks_locked; prev_blocking_xhr = thread_data->blocking_xhr;
/* Note: Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), @@ -297,12 +299,22 @@ static nsresult sync_xhr_send(HTMLXMLHttpRequest *xhr, nsIVariant *nsbody) * to figure out a way to handle it... * * For details (and bunch of problems to consider) see: https://bugzil.la/697151 + * + * FIXME: Since Gecko uses a message loop to implement sync XHR, and it requires that + * all the async tasks are executed (or else it hangs indefinitely waiting for them), + * we have to enable processing of all tasks, even if we're coming from a nested loop + * that wouldn't otherwise process tasks. This isn't correct but it's niche enough. */ + if(thread_data->tasks_locked) { + thread_data->tasks_locked = 0; + unblock_tasks_and_timers(thread_data); + } 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; + thread_data->tasks_locked = prev_tasks_locked; window->base.outer_window->readystate_locked--;
if(!--window->blocking_depth)