/* * Copyright 2006 Jacek Caban for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include <stdio.h> #include <assert.h> #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winuser.h" #include "ole2.h" #include "wine/debug.h" #include "mshtml_private.h" WINE_DEFAULT_DEBUG_CHANNEL(mshtml); #define WM_PROCESSTASK 0x8008 #define TIMER_ID 0x3000 typedef struct { HTMLInnerWindow *window; DWORD id; DWORD time; DWORD interval; IDispatch *disp; struct list entry; } task_timer_t; static void default_task_destr(task_t *task) { heap_free(task); } HRESULT push_task(task_t *task, task_proc_t proc, task_proc_t destr, LONG magic) { thread_data_t *thread_data; thread_data = get_thread_data(TRUE); if(!thread_data) { if(destr) destr(task); else heap_free(task); return E_OUTOFMEMORY; } task->target_magic = magic; task->proc = proc; task->destr = destr ? destr : default_task_destr; list_add_tail(&thread_data->task_list, &task->entry); PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0); return S_OK; } static task_t *pop_task(void) { thread_data_t *thread_data; task_t *task; thread_data = get_thread_data(FALSE); if(!thread_data) return NULL; if(list_empty(&thread_data->task_list)) return NULL; task = LIST_ENTRY(thread_data->task_list.next, task_t, entry); list_remove(&task->entry); return task; } static void release_task_timer(HWND thread_hwnd, task_timer_t *timer) { list_remove(&timer->entry); IDispatch_Release(timer->disp); heap_free(timer); } void remove_target_tasks(LONG target) { thread_data_t *thread_data = get_thread_data(FALSE); struct list *liter, *ltmp; task_timer_t *timer; task_t *task; if(!thread_data) return; LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) { timer = LIST_ENTRY(liter, task_timer_t, entry); if(timer->window->task_magic == target) release_task_timer(thread_data->thread_hwnd, timer); } if(!list_empty(&thread_data->timer_list)) { DWORD tc = GetTickCount(); timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL); } LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) { task = LIST_ENTRY(liter, task_t, entry); if(task->target_magic == target) { list_remove(&task->entry); task->destr(task); } } } LONG get_task_target_magic(void) { static LONG magic = 0x10000000; return InterlockedIncrement(&magic); } static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer) { task_timer_t *iter; list_remove(&timer->entry); if(list_empty(&thread_data->timer_list) || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) { list_add_head(&thread_data->timer_list, &timer->entry); return TRUE; } LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) { if(iter->time > timer->time) { list_add_tail(&iter->entry, &timer->entry); return FALSE; } } list_add_tail(&thread_data->timer_list, &timer->entry); return FALSE; } HRESULT set_task_timer(HTMLInnerWindow *window, LONG msec, BOOL interval, IDispatch *disp, LONG *id) { thread_data_t *thread_data; task_timer_t *timer; DWORD tc = GetTickCount(); static DWORD id_cnt = 0x20000000; thread_data = get_thread_data(TRUE); if(!thread_data) return E_OUTOFMEMORY; timer = heap_alloc(sizeof(task_timer_t)); if(!timer) return E_OUTOFMEMORY; if(msec < 1) msec = 1; timer->id = id_cnt++; timer->window = window; timer->time = tc + msec; timer->interval = interval ? msec : 0; list_init(&timer->entry); IDispatch_AddRef(disp); timer->disp = disp; if(queue_timer(thread_data, timer)) SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL); *id = timer->id; return S_OK; } HRESULT clear_task_timer(HTMLInnerWindow *window, DWORD id) { thread_data_t *thread_data = get_thread_data(FALSE); task_timer_t *iter; if(!thread_data) return S_OK; LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) { if(iter->id == id && iter->window == window) { release_task_timer(thread_data->thread_hwnd, iter); return S_OK; } } WARN("timet not found\n"); return S_OK; } static void call_timer_disp(IDispatch *disp) { DISPPARAMS dp = {NULL, NULL, 0, 0}; EXCEPINFO ei; VARIANT res; HRESULT hres; V_VT(&res) = VT_EMPTY; memset(&ei, 0, sizeof(ei)); TRACE(">>>\n"); hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL); if(hres == S_OK) TRACE("<<<\n"); else WARN("<<< %08x\n", hres); VariantClear(&res); } static LRESULT process_timer(void) { thread_data_t *thread_data; IDispatch *disp; DWORD tc; task_timer_t *timer=NULL, *last_timer; TRACE("\n"); thread_data = get_thread_data(FALSE); assert(thread_data != NULL); if(list_empty(&thread_data->timer_list)) { KillTimer(thread_data->thread_hwnd, TIMER_ID); return 0; } 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; } 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; } disp = timer->disp; IDispatch_AddRef(disp); if(timer->interval) { timer->time += timer->interval; queue_timer(thread_data, timer); }else { release_task_timer(thread_data->thread_hwnd, timer); } call_timer_disp(disp); IDispatch_Release(disp); }while(!list_empty(&thread_data->timer_list)); KillTimer(thread_data->thread_hwnd, TIMER_ID); return 0; } static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_PROCESSTASK: while(1) { task_t *task = pop_task(); if(!task) break; task->proc(task); task->destr(task); } return 0; case WM_TIMER: return process_timer(); } if(msg > WM_USER) FIXME("(%p %d %lx %lx)\n", hwnd, msg, wParam, lParam); return DefWindowProcW(hwnd, msg, wParam, lParam); } static HWND create_thread_hwnd(void) { static ATOM hidden_wnd_class = 0; if(!hidden_wnd_class) { WNDCLASSEXW wndclass = { sizeof(WNDCLASSEXW), 0, hidden_proc, 0, 0, hInst, NULL, NULL, NULL, NULL, L"Internet Explorer_Hidden", NULL }; hidden_wnd_class = RegisterClassExW(&wndclass); } return CreateWindowExW(0, L"Internet Explorer_Hidden", NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInst, NULL); } HWND get_thread_hwnd(void) { thread_data_t *thread_data; thread_data = get_thread_data(TRUE); if(!thread_data) return NULL; if(!thread_data->thread_hwnd) thread_data->thread_hwnd = create_thread_hwnd(); return thread_data->thread_hwnd; } thread_data_t *get_thread_data(BOOL create) { thread_data_t *thread_data; if(mshtml_tls == TLS_OUT_OF_INDEXES) { DWORD tls; if(!create) return NULL; tls = TlsAlloc(); if(tls == TLS_OUT_OF_INDEXES) return NULL; tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES); if(tls != mshtml_tls) TlsFree(tls); } thread_data = TlsGetValue(mshtml_tls); if(!thread_data && create) { thread_data = heap_alloc_zero(sizeof(thread_data_t)); if(!thread_data) return NULL; TlsSetValue(mshtml_tls, thread_data); list_init(&thread_data->task_list); list_init(&thread_data->timer_list); } return thread_data; }