task.c 9.38 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * 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>
21
#include <assert.h>
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

#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
37 38 39
#define TIMER_ID 0x3000

typedef struct {
40
    HTMLInnerWindow *window;
41 42
    DWORD id;
    DWORD time;
43
    DWORD interval;
44 45 46 47
    IDispatch *disp;

    struct list entry;
} task_timer_t;
48

49 50 51 52 53
static void default_task_destr(task_t *task)
{
    heap_free(task);
}

54
HRESULT push_task(task_t *task, task_proc_t proc, task_proc_t destr, LONG magic)
55
{
56 57 58 59 60 61 62 63 64 65
    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;
    }
66

67 68
    task->target_magic = magic;
    task->proc = proc;
69
    task->destr = destr ? destr : default_task_destr;
70

71
    list_add_tail(&thread_data->task_list, &task->entry);
72 73

    PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
74
    return S_OK;
75 76 77 78
}

static task_t *pop_task(void)
{
79 80
    thread_data_t *thread_data;
    task_t *task;
81

82 83 84 85
    thread_data = get_thread_data(FALSE);
    if(!thread_data)
        return NULL;

86
    if(list_empty(&thread_data->task_list))
87 88
        return NULL;

89 90
    task = LIST_ENTRY(thread_data->task_list.next, task_t, entry);
    list_remove(&task->entry);
91 92 93
    return task;
}

94 95 96 97 98 99 100 101 102
static void release_task_timer(HWND thread_hwnd, task_timer_t *timer)
{
    list_remove(&timer->entry);

    IDispatch_Release(timer->disp);

    heap_free(timer);
}

103
void remove_target_tasks(LONG target)
104 105
{
    thread_data_t *thread_data = get_thread_data(FALSE);
106 107
    struct list *liter, *ltmp;
    task_timer_t *timer;
108
    task_t *task;
109

110 111 112
    if(!thread_data)
        return;

113 114
    LIST_FOR_EACH_SAFE(liter, ltmp, &thread_data->timer_list) {
        timer = LIST_ENTRY(liter, task_timer_t, entry);
115
        if(timer->window->task_magic == target)
116 117 118 119
            release_task_timer(thread_data->thread_hwnd, timer);
    }

    if(!list_empty(&thread_data->timer_list)) {
120 121
        DWORD tc = GetTickCount();

122
        timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry);
123
        SetTimer(thread_data->thread_hwnd, TIMER_ID, max( (int)(timer->time - tc), 0 ), NULL);
124 125
    }

126 127 128 129 130
    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);
131 132 133 134
        }
    }
}

135 136 137 138 139 140
LONG get_task_target_magic(void)
{
    static LONG magic = 0x10000000;
    return InterlockedIncrement(&magic);
}

141 142 143 144
static BOOL queue_timer(thread_data_t *thread_data, task_timer_t *timer)
{
    task_timer_t *iter;

145 146
    list_remove(&timer->entry);

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    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;
}

165
HRESULT set_task_timer(HTMLInnerWindow *window, LONG msec, BOOL interval, IDispatch *disp, LONG *id)
166
{
167
    thread_data_t *thread_data;
168 169 170 171 172
    task_timer_t *timer;
    DWORD tc = GetTickCount();

    static DWORD id_cnt = 0x20000000;

173 174 175 176
    thread_data = get_thread_data(TRUE);
    if(!thread_data)
        return E_OUTOFMEMORY;

177
    timer = heap_alloc(sizeof(task_timer_t));
178 179 180
    if(!timer)
        return E_OUTOFMEMORY;

181 182 183
    if(msec < 1)
        msec = 1;

184
    timer->id = id_cnt++;
185
    timer->window = window;
186
    timer->time = tc + msec;
187
    timer->interval = interval ? msec : 0;
188
    list_init(&timer->entry);
189 190 191 192

    IDispatch_AddRef(disp);
    timer->disp = disp;

193
    if(queue_timer(thread_data, timer))
194 195
        SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);

196 197
    *id = timer->id;
    return S_OK;
198 199
}

200
HRESULT clear_task_timer(HTMLInnerWindow *window, DWORD id)
201 202 203 204 205 206 207 208
{
    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) {
209
        if(iter->id == id && iter->window == window) {
210 211 212 213 214 215 216 217 218
            release_task_timer(thread_data->thread_hwnd, iter);
            return S_OK;
        }
    }

    WARN("timet not found\n");
    return S_OK;
}

219 220 221 222 223 224 225 226 227
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));
228 229

    TRACE(">>>\n");
230
    hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, 0, DISPATCH_METHOD, &dp, &res, &ei, NULL);
231 232 233 234 235
    if(hres == S_OK)
        TRACE("<<<\n");
    else
        WARN("<<< %08x\n", hres);

236 237 238
    VariantClear(&res);
}

239 240
static LRESULT process_timer(void)
{
241
    thread_data_t *thread_data;
242
    IDispatch *disp;
243
    DWORD tc;
244
    task_timer_t *timer=NULL, *last_timer;
245

246 247
    TRACE("\n");

248 249 250
    thread_data = get_thread_data(FALSE);
    assert(thread_data != NULL);

251 252 253 254
    if(list_empty(&thread_data->timer_list)) {
        KillTimer(thread_data->thread_hwnd, TIMER_ID);
        return 0;
    }
255

256 257
    last_timer = LIST_ENTRY(list_tail(&thread_data->timer_list), task_timer_t, entry);
    do {
258
        tc = GetTickCount();
259 260 261 262 263 264 265 266
        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);

267 268 269 270 271
        if(timer->time > tc) {
            SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time-tc, NULL);
            return 0;
        }

272 273
        disp = timer->disp;
        IDispatch_AddRef(disp);
274

275 276 277 278 279 280
        if(timer->interval) {
            timer->time += timer->interval;
            queue_timer(thread_data, timer);
        }else {
            release_task_timer(thread_data->thread_hwnd, timer);
        }
281

282
        call_timer_disp(disp);
283 284

        IDispatch_Release(disp);
285
    }while(!list_empty(&thread_data->timer_list));
286 287 288 289 290

    KillTimer(thread_data->thread_hwnd, TIMER_ID);
    return 0;
}

291 292
static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
293 294 295 296 297 298 299
    switch(msg) {
    case WM_PROCESSTASK:
        while(1) {
            task_t *task = pop_task();
            if(!task)
                break;

300
            task->proc(task);
301
            task->destr(task);
302 303 304
        }

        return 0;
305 306
    case WM_TIMER:
        return process_timer();
307 308
    }

309
    if(msg > WM_USER)
310
        FIXME("(%p %d %lx %lx)\n", hwnd, msg, wParam, lParam);
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

static HWND create_thread_hwnd(void)
{
    static ATOM hidden_wnd_class = 0;
    static const WCHAR wszInternetExplorer_Hidden[] = {'I','n','t','e','r','n','e','t',
            ' ','E','x','p','l','o','r','e','r','_','H','i','d','d','e','n',0};

    if(!hidden_wnd_class) {
        WNDCLASSEXW wndclass = {
            sizeof(WNDCLASSEXW), 0,
            hidden_proc,
            0, 0, hInst, NULL, NULL, NULL, NULL,
            wszInternetExplorer_Hidden,
            NULL
        };

        hidden_wnd_class = RegisterClassExW(&wndclass);
    }

    return CreateWindowExW(0, wszInternetExplorer_Hidden, NULL, WS_POPUP,
                           0, 0, 0, 0, NULL, NULL, hInst, NULL);
}

HWND get_thread_hwnd(void)
{
339 340 341 342 343
    thread_data_t *thread_data;

    thread_data = get_thread_data(TRUE);
    if(!thread_data)
        return NULL;
344 345 346 347 348 349 350 351 352 353 354

    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;

355 356 357 358 359 360 361 362
    if(mshtml_tls == TLS_OUT_OF_INDEXES) {
        DWORD tls;

        if(!create)
            return NULL;

        tls = TlsAlloc();
        if(tls == TLS_OUT_OF_INDEXES)
363
            return NULL;
364 365 366 367

        tls = InterlockedCompareExchange((LONG*)&mshtml_tls, tls, TLS_OUT_OF_INDEXES);
        if(tls != mshtml_tls)
            TlsFree(tls);
368 369 370 371
    }

    thread_data = TlsGetValue(mshtml_tls);
    if(!thread_data && create) {
372
        thread_data = heap_alloc_zero(sizeof(thread_data_t));
373 374 375
        if(!thread_data)
            return NULL;

376
        TlsSetValue(mshtml_tls, thread_data);
377
        list_init(&thread_data->task_list);
378
        list_init(&thread_data->timer_list);
379 380 381 382
    }

    return thread_data;
}