1 /* 2 * Copyright 2006 Jacek Caban for CodeWeavers 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include "mshtml_private.h" 20 21 #define WM_PROCESSTASK 0x8008 22 #define TIMER_ID 0x3000 23 24 typedef struct { 25 HTMLInnerWindow *window; 26 DWORD id; 27 DWORD time; 28 DWORD interval; 29 IDispatch *disp; 30 31 struct list entry; 32 } task_timer_t; 33 34 static void default_task_destr(task_t *task) 35 { 36 heap_free(task); 37 } 38 39 HRESULT push_task(task_t *task, task_proc_t proc, task_proc_t destr, LONG magic) 40 { 41 thread_data_t *thread_data; 42 43 thread_data = get_thread_data(TRUE); 44 if(!thread_data) { 45 if(destr) 46 destr(task); 47 else 48 heap_free(task); 49 return E_OUTOFMEMORY; 50 } 51 52 task->target_magic = magic; 53 task->proc = proc; 54 task->destr = destr ? destr : default_task_destr; 55 56 list_add_tail(&thread_data->task_list, &task->entry); 57 58 PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0); 59 return S_OK; 60 } 61 62 static task_t *pop_task(void) 63 { 64 thread_data_t *thread_data; 65 task_t *task; 66 67 thread_data = get_thread_data(FALSE); 68 if(!thread_data) 69 return NULL; 70 71 if(list_empty(&thread_data->task_list)) 72 return NULL; 73 74 task = LIST_ENTRY(thread_data->task_list.next, task_t, entry); 75 list_remove(&task->entry); 76 return task; 77 } 78 79 static void release_task_timer(HWND thread_hwnd, task_timer_t *timer) 80 { 81 list_remove(&timer->entry); 82 83 IDispatch_Release(timer->disp); 84 85 heap_free(timer); 86 } 87 88 void flush_pending_tasks(LONG target) 89 { 90 thread_data_t *thread_data = get_thread_data(FALSE); 91 struct list *liter, *ltmp; 92 task_t *task; 93 94 if(!thread_data) 95 return; 96 97 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) { 98 task = LIST_ENTRY(liter, task_t, entry); 99 if(task->target_magic == target) { 100 list_remove(&task->entry); 101 task->proc(task); 102 task->destr(task); 103 } 104 } 105 } 106 107 void remove_target_tasks(LONG target) 108 { 109 thread_data_t *thread_data = get_thread_data(FALSE); 110 struct list *liter, *ltmp; 111 task_timer_t *timer; 112 task_t *task; 113 114 if(!thread_data) 115 return; 116 117 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) { 118 timer = LIST_ENTRY(liter, task_timer_t, entry); 119 if(timer->window->task_magic == target) 120 release_task_timer(thread_data->thread_hwnd, timer); 121 } 122 123 if(!list_empty(&thread_data->timer_list)) { 124 DWORD tc = GetTickCount(); 125 126 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); 127 SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL); 128 } 129 130 LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->task_list) { 131 task = LIST_ENTRY(liter, task_t, entry); 132 if(task->target_magic == target) { 133 list_remove(&task->entry); 134 task->destr(task); 135 } 136 } 137 } 138 139 LONG get_task_target_magic(void) 140 { 141 static LONG magic = 0x10000000; 142 return InterlockedIncrement(&magic); 143 } 144 145 static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer) 146 { 147 task_timer_t *iter; 148 149 list_remove(&timer->entry); 150 151 if(list_empty(&thread_data->timer_list) 152 || LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry)->time > timer->time) { 153 154 list_add_head(&thread_data->timer_list, &timer->entry); 155 return TRUE; 156 } 157 158 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) { 159 if(iter->time > timer->time) { 160 list_add_tail(&iter->entry, &timer->entry); 161 return FALSE; 162 } 163 } 164 165 list_add_tail(&thread_data->timer_list, &timer->entry); 166 return FALSE; 167 } 168 169 HRESULT set_task_timer(HTMLInnerWindow *window, DWORD msec, BOOL interval, IDispatch *disp, LONG *id) 170 { 171 thread_data_t *thread_data; 172 task_timer_t *timer; 173 DWORD tc = GetTickCount(); 174 175 static DWORD id_cnt = 0x20000000; 176 177 thread_data = get_thread_data(TRUE); 178 if(!thread_data) 179 return E_OUTOFMEMORY; 180 181 timer = heap_alloc(sizeof(task_timer_t)); 182 if(!timer) 183 return E_OUTOFMEMORY; 184 185 timer->id = id_cnt++; 186 timer->window = window; 187 timer->time = tc + msec; 188 timer->interval = interval ? msec : 0; 189 list_init(&timer->entry); 190 191 IDispatch_AddRef(disp); 192 timer->disp = disp; 193 194 if(queue_timer(thread_data, timer)) 195 SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL); 196 197 *id = timer->id; 198 return S_OK; 199 } 200 201 HRESULT clear_task_timer(HTMLInnerWindow *window, BOOL interval, DWORD id) 202 { 203 thread_data_t *thread_data = get_thread_data(FALSE); 204 task_timer_t *iter; 205 206 if(!thread_data) 207 return S_OK; 208 209 LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) { 210 if(iter->id == id && iter->window == window && !iter->interval == !interval) { 211 release_task_timer(thread_data->thread_hwnd, iter); 212 return S_OK; 213 } 214 } 215 216 WARN("timet not found\n"); 217 return S_OK; 218 } 219 220 static void call_timer_disp(IDispatch *disp) 221 { 222 DISPPARAMS dp = {NULL, NULL, 0, 0}; 223 EXCEPINFO ei; 224 VARIANT res; 225 HRESULT hres; 226 227 V_VT(&res) = VT_EMPTY; 228 memset(&ei, 0, sizeof(ei)); 229 230 TRACE(">>>\n"); 231 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL); 232 if(hres == S_OK) 233 TRACE("<<<\n"); 234 else 235 WARN("<<< %08x\n", hres); 236 237 VariantClear(&res); 238 } 239 240 static LRESULT process_timer(void) 241 { 242 thread_data_t *thread_data; 243 IDispatch *disp; 244 DWORD tc; 245 task_timer_t *timer=NULL, *last_timer; 246 247 TRACE("\n"); 248 249 thread_data = get_thread_data(FALSE); 250 assert(thread_data != NULL); 251 252 if(list_empty(&thread_data->timer_list)) { 253 KillTimer(thread_data->thread_hwnd, TIMER_ID); 254 return 0; 255 } 256 257 last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry); 258 do { 259 tc = GetTickCount(); 260 if(timer == last_timer) { 261 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); 262 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time>tc ? timer->time-tc : 0, NULL); 263 return 0; 264 } 265 266 timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); 267 268 if(timer->time > tc) { 269 SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL); 270 return 0; 271 } 272 273 disp = timer->disp; 274 IDispatch_AddRef(disp); 275 276 if(timer->interval) { 277 timer->time += timer->interval; 278 queue_timer(thread_data, timer); 279 }else { 280 release_task_timer(thread_data->thread_hwnd, timer); 281 } 282 283 call_timer_disp(disp); 284 285 IDispatch_Release(disp); 286 }while(!list_empty(&thread_data->timer_list)); 287 288 KillTimer(thread_data->thread_hwnd, TIMER_ID); 289 return 0; 290 } 291 292 static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 293 { 294 switch(msg) { 295 case WM_PROCESSTASK: 296 while(1) { 297 task_t *task = pop_task(); 298 if(!task) 299 break; 300 301 task->proc(task); 302 task->destr(task); 303 } 304 305 return 0; 306 case WM_TIMER: 307 return process_timer(); 308 } 309 310 if(msg > WM_USER) 311 FIXME("(%p %d %lx %lx)\n", hwnd, msg, wParam, lParam); 312 313 return DefWindowProcW(hwnd, msg, wParam, lParam); 314 } 315 316 static HWND create_thread_hwnd(void) 317 { 318 static ATOM hidden_wnd_class = 0; 319 static const WCHAR wszInternetExplorer_Hidden[] = {'I','n','t','e','r','n','e','t', 320 ' ','E','x','p','l','o','r','e','r','_','H','i','d','d','e','n',0}; 321 322 if(!hidden_wnd_class) { 323 WNDCLASSEXW wndclass = { 324 sizeof(WNDCLASSEXW), 0, 325 hidden_proc, 326 0, 0, hInst, NULL, NULL, NULL, NULL, 327 wszInternetExplorer_Hidden, 328 NULL 329 }; 330 331 hidden_wnd_class = RegisterClassExW(&wndclass); 332 } 333 334 return CreateWindowExW(0, wszInternetExplorer_Hidden, NULL, WS_POPUP, 335 0, 0, 0, 0, NULL, NULL, hInst, NULL); 336 } 337 338 HWND get_thread_hwnd(void) 339 { 340 thread_data_t *thread_data; 341 342 thread_data = get_thread_data(TRUE); 343 if(!thread_data) 344 return NULL; 345 346 if(!thread_data->thread_hwnd) 347 thread_data->thread_hwnd = create_thread_hwnd(); 348 349 return thread_data->thread_hwnd; 350 } 351 352 thread_data_t *get_thread_data(BOOL create) 353 { 354 thread_data_t *thread_data; 355 356 if(mshtml_tls == TLS_OUT_OF_INDEXES) { 357 DWORD tls; 358 359 if(!create) 360 return NULL; 361 362 tls = TlsAlloc(); 363 if(tls == TLS_OUT_OF_INDEXES) 364 return NULL; 365 366 tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES); 367 if(tls != mshtml_tls) 368 TlsFree(tls); 369 } 370 371 thread_data = TlsGetValue(mshtml_tls); 372 if(!thread_data && create) { 373 thread_data = heap_alloc_zero(sizeof(thread_data_t)); 374 if(!thread_data) 375 return NULL; 376 377 TlsSetValue(mshtml_tls, thread_data); 378 list_init(&thread_data->task_list); 379 list_init(&thread_data->timer_list); 380 } 381 382 return thread_data; 383 } 384