navigate.c 26.2 KB
Newer Older
1
/*
2
 * Copyright 2006-2007 Jacek Caban for CodeWeavers
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * 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
16
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 18
 */

19 20 21
#define NONAMELESSUNION
#define NONAMELESSSTRUCT

22 23 24
#include "wine/debug.h"

#include "shdocvw.h"
25 26
#include "mshtml.h"
#include "exdispid.h"
27 28
#include "shellapi.h"
#include "winreg.h"
29 30
#include "shlwapi.h"
#include "wininet.h"
31 32 33

WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);

34 35
static const WCHAR emptyW[] = {0};

36 37 38 39 40 41
typedef struct {
    const IBindStatusCallbackVtbl  *lpBindStatusCallbackVtbl;
    const IHttpNegotiateVtbl       *lpHttpNegotiateVtbl;

    LONG ref;

42 43
    DocHost *doc_host;

44
    LPWSTR url;
45
    HGLOBAL post_data;
46
    BSTR headers;
47 48 49 50 51 52
    ULONG post_data_len;
} BindStatusCallback;

#define BINDSC(x)  ((IBindStatusCallback*) &(x)->lpBindStatusCallbackVtbl)
#define HTTPNEG(x) ((IHttpNegotiate*)      &(x)->lpHttpNegotiateVtbl)

53 54
static void dump_BINDINFO(BINDINFO *bi)
{
55
    static const char * const BINDINFOF_str[] = {
56 57 58 59 60
        "#0",
        "BINDINFOF_URLENCODESTGMEDDATA",
        "BINDINFOF_URLENCODEDEXTRAINFO"
    };

61
    static const char * const BINDVERB_str[] = {
62 63 64 65 66 67 68 69
        "BINDVERB_GET",
        "BINDVERB_POST",
        "BINDVERB_PUT",
        "BINDVERB_CUSTOM"
    };

    TRACE("\n"
            "BINDINFO = {\n"
70 71
            "    %d, %s,\n"
            "    {%d, %p, %p},\n"
72 73 74
            "    %s,\n"
            "    %s,\n"
            "    %s,\n"
75 76
            "    %d, %08x, %d, %d\n"
            "    {%d %p %x},\n"
77
            "    %s\n"
78
            "    %p, %d\n"
79 80 81 82 83 84 85 86 87
            "}\n",

            bi->cbSize, debugstr_w(bi->szExtraInfo),
            bi->stgmedData.tymed, bi->stgmedData.u.hGlobal, bi->stgmedData.pUnkForRelease,
            bi->grfBindInfoF > BINDINFOF_URLENCODEDEXTRAINFO
                ? "unknown" : BINDINFOF_str[bi->grfBindInfoF],
            bi->dwBindVerb > BINDVERB_CUSTOM
                ? "unknown" : BINDVERB_str[bi->dwBindVerb],
            debugstr_w(bi->szCustomVerb),
88
            bi->cbstgmedData, bi->dwOptions, bi->dwOptionsFlags, bi->dwCodePage,
89 90 91 92 93 94 95 96
            bi->securityAttributes.nLength,
            bi->securityAttributes.lpSecurityDescriptor,
            bi->securityAttributes.bInheritHandle,
            debugstr_guid(&bi->iid),
            bi->pUnk, bi->dwReserved
            );
}

97 98
static void set_status_text(BindStatusCallback *This, LPCWSTR str)
{
99 100 101 102
    VARIANTARG arg;
    DISPPARAMS dispparams = {&arg, NULL, 1, 0};

    if(!This->doc_host)
103 104
        return;

105 106 107 108 109 110 111
    V_VT(&arg) = VT_BSTR;
    V_BSTR(&arg) = str ? SysAllocString(str) : NULL;
    call_sink(This->doc_host->cps.wbe2, DISPID_STATUSTEXTCHANGE, &dispparams);
    VariantClear(&arg);

    if(This->doc_host->frame)
        IOleInPlaceFrame_SetStatusText(This->doc_host->frame, str);
112 113
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
#define BINDSC_THIS(iface) DEFINE_THIS(BindStatusCallback, BindStatusCallback, iface)

static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
                                                        REFIID riid, void **ppv)
{
    BindStatusCallback *This = BINDSC_THIS(iface);

    *ppv = NULL;

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = BINDSC(This);
    }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
        TRACE("(%p)->(IID_IBindStatusCallback %p)\n", This, ppv);
        *ppv = BINDSC(This);
    }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
        TRACE("(%p)->(IID_IHttpNegotiate %p)\n", This, ppv);
        *ppv = HTTPNEG(This);
    }

    if(*ppv) {
        IBindStatusCallback_AddRef(BINDSC(This));
        return S_OK;
    }

    WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
    return E_NOINTERFACE;
}

static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
    LONG ref = InterlockedIncrement(&This->ref);

148
    TRACE("(%p) ref=%d\n", This, ref);
149 150 151 152 153 154 155 156 157

    return ref;
}

static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
    LONG ref = InterlockedDecrement(&This->ref);

158
    TRACE("(%p) ref=%d\n", This, ref);
159 160

    if(!ref) {
161 162
        if(This->doc_host)
            IOleClientSite_Release(CLIENTSITE(This->doc_host));
163 164
        if(This->post_data)
            GlobalFree(This->post_data);
165 166
        if(This->headers)
            SysFreeString(This->headers);
167
        heap_free(This->url);
168
        heap_free(This);
169 170 171 172 173 174 175 176 177
    }

    return ref;
}

static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
       DWORD dwReserved, IBinding *pbind)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
178 179 180 181

    TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind);

    return S_OK;
182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface,
       LONG *pnPriority)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
    FIXME("(%p)->(%p)\n", This, pnPriority);
    return E_NOTIMPL;
}

static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface,
       DWORD reserved)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
196
    FIXME("(%p)->(%d)\n", This, reserved);
197 198 199 200 201 202 203
    return E_NOTIMPL;
}

static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface,
        ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
204 205

    TRACE("(%p)->(%d %d %d %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
206
          debugstr_w(szStatusText));
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

    switch(ulStatusCode) {
    case BINDSTATUS_BEGINDOWNLOADDATA:
        set_status_text(This, szStatusText); /* FIXME: "Start downloading from site: %s" */
        return S_OK;
    case BINDSTATUS_ENDDOWNLOADDATA:
        set_status_text(This, szStatusText); /* FIXME: "Downloading from site: %s" */
        return S_OK;
    case BINDSTATUS_CLASSIDAVAILABLE:
    case BINDSTATUS_MIMETYPEAVAILABLE:
    case BINDSTATUS_BEGINSYNCOPERATION:
    case BINDSTATUS_ENDSYNCOPERATION:
        return S_OK;
    default:
        FIXME("status code %u\n", ulStatusCode);
    }

224 225 226 227 228 229 230
    return E_NOTIMPL;
}

static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
        HRESULT hresult, LPCWSTR szError)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
231

232 233 234
    TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));

    set_status_text(This, emptyW);
235 236 237 238 239 240

    if(This->doc_host) {
        IOleClientSite_Release(CLIENTSITE(This->doc_host));
        This->doc_host = NULL;
    }

241
    return S_OK;
242 243 244 245 246 247 248
}

static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
        DWORD *grfBINDF, BINDINFO *pbindinfo)
{
    BindStatusCallback *This = BINDSC_THIS(iface);

249
    TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
250

251
    *grfBINDF = BINDF_ASYNCHRONOUS;
252 253 254 255 256 257

    if(This->post_data) {
        pbindinfo->dwBindVerb = BINDVERB_POST;

        pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
        pbindinfo->stgmedData.u.hGlobal = This->post_data;
258
        pbindinfo->cbstgmedData = This->post_data_len;
259 260 261 262 263 264 265 266 267 268 269
        pbindinfo->stgmedData.pUnkForRelease = (IUnknown*)BINDSC(This);
        IBindStatusCallback_AddRef(BINDSC(This));
    }

    return S_OK;
}

static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
        DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
270
    FIXME("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed);
271 272 273
    return E_NOTIMPL;
}

274 275 276 277 278
static void object_available_proc(DocHost *This, task_header_t *task)
{
    object_available(This);
}

279 280 281 282
static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
        REFIID riid, IUnknown *punk)
{
    BindStatusCallback *This = BINDSC_THIS(iface);
283
    task_header_t *task;
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
    IOleObject *oleobj;
    HRESULT hres;

    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk);

    IUnknown_AddRef(punk);
    This->doc_host->document = punk;

    hres = IUnknown_QueryInterface(punk, &IID_IOleObject, (void**)&oleobj);
    if(SUCCEEDED(hres)) {
        CLSID clsid;

        hres = IOleObject_GetUserClassID(oleobj, &clsid);
        if(SUCCEEDED(hres))
            TRACE("Got clsid %s\n",
                  IsEqualGUID(&clsid, &CLSID_HTMLDocument) ? "CLSID_HTMLDocument" : debugstr_guid(&clsid));

        hres = IOleObject_SetClientSite(oleobj, CLIENTSITE(This->doc_host));
        if(FAILED(hres))
            FIXME("SetClientSite failed: %08x\n", hres);

305
        IOleObject_Release(oleobj);
306 307 308 309
    }else {
        FIXME("Could not get IOleObject iface: %08x\n", hres);
    }

310 311 312
    /* FIXME: Call SetAdvise */
    /* FIXME: Call Invoke(DISPID_READYSTATE) */

313 314
    task = heap_alloc(sizeof(*task));
    push_dochost_task(This->doc_host, task, object_available_proc, FALSE);
315 316

    return S_OK;
317 318
}

319 320 321 322 323 324 325 326 327 328 329 330
#undef BSC_THIS

static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
    BindStatusCallback_QueryInterface,
    BindStatusCallback_AddRef,
    BindStatusCallback_Release,
    BindStatusCallback_OnStartBinding,
    BindStatusCallback_GetPriority,
    BindStatusCallback_OnLowResource,
    BindStatusCallback_OnProgress,
    BindStatusCallback_OnStopBinding,
    BindStatusCallback_GetBindInfo,
331 332
    BindStatusCallback_OnDataAvailable,
    BindStatusCallback_OnObjectAvailable
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
};

#define HTTPNEG_THIS(iface) DEFINE_THIS(BindStatusCallback, HttpNegotiate, iface)

static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate *iface,
                                                   REFIID riid, void **ppv)
{
    BindStatusCallback *This = HTTPNEG_THIS(iface);
    return IBindStatusCallback_QueryInterface(BINDSC(This), riid, ppv);
}

static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate *iface)
{
    BindStatusCallback *This = HTTPNEG_THIS(iface);
    return IBindStatusCallback_AddRef(BINDSC(This));
}

static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate *iface)
{
    BindStatusCallback *This = HTTPNEG_THIS(iface);
    return IBindStatusCallback_Release(BINDSC(This));
}

static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate *iface,
        LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
{
    BindStatusCallback *This = HTTPNEG_THIS(iface);

361
    FIXME("(%p)->(%s %s %d %p)\n", This, debugstr_w(szURL), debugstr_w(szHeaders),
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
          dwReserved, pszAdditionalHeaders);

    if(This->headers) {
        int size = (strlenW(This->headers)+1)*sizeof(WCHAR);
        *pszAdditionalHeaders = CoTaskMemAlloc(size);
        memcpy(*pszAdditionalHeaders, This->headers, size);
    }

    return S_OK;
}

static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate *iface,
        DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders,
        LPWSTR *pszAdditionalRequestHeaders)
{
    BindStatusCallback *This = HTTPNEG_THIS(iface);
378
    FIXME("(%p)->(%d %s %s %p)\n", This, dwResponseCode, debugstr_w(szResponseHeaders),
379 380 381 382 383 384 385 386 387 388 389 390 391 392
          debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
    return E_NOTIMPL;
}

#undef HTTPNEG_THIS

static const IHttpNegotiateVtbl HttpNegotiateVtbl = {
    HttpNegotiate_QueryInterface,
    HttpNegotiate_AddRef,
    HttpNegotiate_Release,
    HttpNegotiate_BeginningTransaction,
    HttpNegotiate_OnResponse
};

393 394
static BindStatusCallback *create_callback(DocHost *doc_host, LPCWSTR url, PBYTE post_data,
        ULONG post_data_len, LPCWSTR headers)
395
{
396
    BindStatusCallback *ret = heap_alloc(sizeof(BindStatusCallback));
397 398 399 400 401

    ret->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl;
    ret->lpHttpNegotiateVtbl      = &HttpNegotiateVtbl;

    ret->ref = 1;
402
    ret->url = heap_strdupW(url);
403 404
    ret->post_data = NULL;
    ret->post_data_len = post_data_len;
405
    ret->headers = headers ? SysAllocString(headers) : NULL;
406

407 408 409
    ret->doc_host = doc_host;
    IOleClientSite_AddRef(CLIENTSITE(doc_host));

410 411 412 413 414
    if(post_data) {
        ret->post_data = GlobalAlloc(0, post_data_len);
        memcpy(ret->post_data, post_data, post_data_len);
    }

415
    return ret;
416
}
417

418 419
static void on_before_navigate2(DocHost *This, LPCWSTR url, const BYTE *post_data,
                                ULONG post_data_len, LPWSTR headers, VARIANT_BOOL *cancel)
420 421 422 423 424 425 426 427 428 429
{
    VARIANT var_url, var_flags, var_frame_name, var_post_data, var_post_data2, var_headers;
    DISPPARAMS dispparams;
    VARIANTARG params[7];

    dispparams.cArgs = 7;
    dispparams.cNamedArgs = 0;
    dispparams.rgdispidNamedArgs = NULL;
    dispparams.rgvarg = params;

430 431
    This->busy = VARIANT_TRUE;

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
    V_VT(params) = VT_BOOL|VT_BYREF;
    V_BOOLREF(params) = cancel;

    V_VT(params+1) = (VT_BYREF|VT_VARIANT);
    V_VARIANTREF(params+1) = &var_headers;
    V_VT(&var_headers) = VT_BSTR;
    V_BSTR(&var_headers) = headers;

    V_VT(params+2) = (VT_BYREF|VT_VARIANT);
    V_VARIANTREF(params+2) = &var_post_data2;
    V_VT(&var_post_data2) = (VT_BYREF|VT_VARIANT);
    V_VARIANTREF(&var_post_data2) = &var_post_data;
    VariantInit(&var_post_data);

    if(post_data_len) {
        SAFEARRAYBOUND bound = {post_data_len, 0};
        void *data;

        V_VT(&var_post_data) = VT_UI1|VT_ARRAY;
        V_ARRAY(&var_post_data) = SafeArrayCreate(VT_UI1, 1, &bound);

        SafeArrayAccessData(V_ARRAY(&var_post_data), &data);
        memcpy(data, post_data, post_data_len);
        SafeArrayUnaccessData(V_ARRAY(&var_post_data));
    }

    V_VT(params+3) = (VT_BYREF|VT_VARIANT);
    V_VARIANTREF(params+3) = &var_frame_name;
    V_VT(&var_frame_name) = VT_BSTR;
    V_BSTR(&var_frame_name) = NULL;

    V_VT(params+4) = (VT_BYREF|VT_VARIANT);
    V_VARIANTREF(params+4) = &var_flags;
    V_VT(&var_flags) = VT_I4;
    V_I4(&var_flags) = 0;

    V_VT(params+5) = (VT_BYREF|VT_VARIANT);
    V_VARIANTREF(params+5) = &var_url;
    V_VT(&var_url) = VT_BSTR;
471
    V_BSTR(&var_url) = SysAllocString(url);
472 473

    V_VT(params+6) = (VT_DISPATCH);
474
    V_DISPATCH(params+6) = This->disp;
475

476
    call_sink(This->cps.wbe2, DISPID_BEFORENAVIGATE2, &dispparams);
477

478
    SysFreeString(V_BSTR(&var_url));
479 480 481 482
    if(post_data_len)
        SafeArrayDestroy(V_ARRAY(&var_post_data));
}

483
/* FIXME: urlmon should handle it */
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
static BOOL try_application_url(LPCWSTR url)
{
    SHELLEXECUTEINFOW exec_info;
    WCHAR app[64];
    HKEY hkey;
    DWORD res, type;
    HRESULT hres;

    static const WCHAR wszURLProtocol[] = {'U','R','L',' ','P','r','o','t','o','c','o','l',0};

    hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, app, sizeof(app)/sizeof(WCHAR), NULL, 0);
    if(FAILED(hres))
        return FALSE;

    res = RegOpenKeyW(HKEY_CLASSES_ROOT, app, &hkey);
    if(res != ERROR_SUCCESS)
        return FALSE;

    res = RegQueryValueExW(hkey, wszURLProtocol, NULL, &type, NULL, NULL);
    RegCloseKey(hkey);
    if(res != ERROR_SUCCESS || type != REG_SZ)
        return FALSE;

    TRACE("openning application %s\n", debugstr_w(app));
 
    memset(&exec_info, 0, sizeof(exec_info));
    exec_info.cbSize = sizeof(exec_info);
    exec_info.lpFile = url;
    exec_info.nShow = SW_SHOW;

    return ShellExecuteExW(&exec_info);
}

517
static HRESULT http_load_hack(DocHost *This, IMoniker *mon, IBindStatusCallback *callback, IBindCtx *bindctx)
518 519
{
    IPersistMoniker *persist;
520
    IUnknown *doc;
521 522 523 524 525 526 527 528 529 530
    HRESULT hres;

    /*
     * FIXME:
     * We should use URLMoniker's BindToObject instead creating HTMLDocument here.
     * This should be fixed when mshtml.dll and urlmon.dll will be good enough.
     */

    hres = CoCreateInstance(&CLSID_HTMLDocument, NULL,
                            CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
531
                            &IID_IUnknown, (void**)&doc);
532 533

    if(FAILED(hres)) {
534
        ERR("Could not create HTMLDocument: %08x\n", hres);
535 536 537
        return hres;
    }

538
    hres = IUnknown_QueryInterface(doc, &IID_IPersistMoniker, (void**)&persist);
539 540
    if(FAILED(hres)) {
        IUnknown_Release(doc);
541
        return hres;
542
    }
543 544 545

    hres = IPersistMoniker_Load(persist, FALSE, mon, bindctx, 0);
    IPersistMoniker_Release(persist);
546

547 548 549 550 551 552 553
    if(SUCCEEDED(hres))
        hres = IBindStatusCallback_OnObjectAvailable(callback, &IID_IUnknown, doc);
    else
        WARN("Load failed: %08x\n", hres);

    IUnknown_Release(doc);

554
    return IBindStatusCallback_OnStopBinding(callback, hres, NULL);
555 556
}

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
static HRESULT create_moniker(LPCWSTR url, IMoniker **mon)
{
    WCHAR new_url[INTERNET_MAX_URL_LENGTH];
    DWORD size;
    HRESULT hres;

    if(PathIsURLW(url))
        return CreateURLMoniker(NULL, url, mon);

    if(url[1] == ':') {
        size = sizeof(new_url);
        hres = UrlCreateFromPathW(url, new_url, &size, 0);
        if(FAILED(hres)) {
            WARN("UrlCreateFromPathW failed: %08x\n", hres);
            return hres;
        }
    }else {
        size = sizeof(new_url);
        hres = UrlApplySchemeW(url, new_url, &size, URL_APPLY_GUESSSCHEME);
        TRACE("got %s\n", debugstr_w(new_url));
        if(FAILED(hres)) {
            WARN("UrlApplyScheme failed: %08x\n", hres);
            return hres;
        }
    }

    return CreateURLMoniker(NULL, new_url, mon);
}

586 587 588
static HRESULT bind_to_object(DocHost *This, IMoniker *mon, LPCWSTR url, IBindCtx *bindctx,
                              IBindStatusCallback *callback)
{
589 590
    WCHAR schema[30];
    DWORD schema_len;
591 592
    HRESULT hres;

593 594 595 596
    static const WCHAR httpW[] = {'h','t','t','p',0};
    static const WCHAR httpsW[] = {'h','t','t','p','s',0};
    static const WCHAR ftpW[]= {'f','t','p',0};

597 598 599
    if(mon) {
        IMoniker_AddRef(mon);
    }else {
600 601
        hres = create_moniker(url, &mon);
        if(FAILED(hres))
602 603 604 605 606 607 608 609
            return hres;
    }

    CoTaskMemFree(This->url);
    hres = IMoniker_GetDisplayName(mon, 0, NULL, &This->url);
    if(FAILED(hres))
        FIXME("GetDisplayName failed: %08x\n", hres);

610 611 612
    IBindCtx_RegisterObjectParam(bindctx, (LPOLESTR)SZ_HTML_CLIENTSITE_OBJECTPARAM,
                                 (IUnknown*)CLIENTSITE(This));

613
    hres = CoInternetParseUrl(This->url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
614 615 616 617 618 619 620 621 622 623 624 625
            &schema_len, 0);
    if(SUCCEEDED(hres) &&
       (!strcmpW(schema, httpW) || !strcmpW(schema, httpsW) || !strcmpW(schema, ftpW))) {
        hres = http_load_hack(This, mon, callback, bindctx);
    }else {
        IUnknown *unk = NULL;

        hres = IMoniker_BindToObject(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk);
        if(SUCCEEDED(hres)) {
            hres = S_OK;
            if(unk)
                IUnknown_Release(unk);
626
        }else if(try_application_url(url)) {
627
            hres = S_OK;
628 629 630 631
        }else {
            FIXME("BindToObject failed: %08x\n", hres);
        }
    }
632

633
    IMoniker_Release(mon);
634 635 636
    return S_OK;
}

637
static HRESULT navigate_bsc(DocHost *This, BindStatusCallback *bsc, IMoniker *mon)
638 639
{
    IBindCtx *bindctx;
640
    VARIANT_BOOL cancel = VARIANT_FALSE;
641 642
    HRESULT hres;

643
    on_before_navigate2(This, bsc->url, bsc->post_data, bsc->post_data_len, bsc->headers, &cancel);
644 645 646 647
    if(cancel) {
        FIXME("Navigation canceled\n");
        return S_OK;
    }
648

649 650 651
    if(This->document)
        deactivate_document(This);

652
    CreateAsyncBindCtx(0, BINDSC(bsc), 0, &bindctx);
653

654 655 656 657 658 659 660
    if(This->frame)
        IOleInPlaceFrame_EnableModeless(This->frame, FALSE);

    hres = bind_to_object(This, mon, bsc->url, bindctx, BINDSC(bsc));

    if(This->frame)
        IOleInPlaceFrame_EnableModeless(This->frame, TRUE);
661

662
    IBindCtx_Release(bindctx);
663

664 665 666
    return hres;
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
typedef struct {
    task_header_t header;
    BindStatusCallback *bsc;
} task_navigate_bsc_t;

static void navigate_bsc_proc(DocHost *This, task_header_t *t)
{
    task_navigate_bsc_t *task = (task_navigate_bsc_t*)t;

    if(!This->hwnd)
        create_doc_view_hwnd(This);

    navigate_bsc(This, task->bsc, NULL);

    IBindStatusCallback_Release(BINDSC(task->bsc));
}


685
HRESULT navigate_url(DocHost *This, LPCWSTR url, const VARIANT *Flags,
686
                     const VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)
687
{
688
    task_navigate_bsc_t *task;
689 690 691 692 693 694 695 696
    PBYTE post_data = NULL;
    ULONG post_data_len = 0;
    LPWSTR headers = NULL;

    TRACE("navigating to %s\n", debugstr_w(url));

    if((Flags && V_VT(Flags) != VT_EMPTY) 
       || (TargetFrameName && V_VT(TargetFrameName) != VT_EMPTY))
697 698 699
        FIXME("Unsupported args (Flags %p:%d; TargetFrameName %p:%d)\n",
                Flags, Flags ? V_VT(Flags) : -1, TargetFrameName,
                TargetFrameName ? V_VT(TargetFrameName) : -1);
700

701 702
    if(PostData) {
        TRACE("PostData vt=%d\n", V_VT(PostData));
703

704 705 706 707
        if(V_VT(PostData) == (VT_ARRAY | VT_UI1)) {
            SafeArrayAccessData(V_ARRAY(PostData), (void**)&post_data);
            post_data_len = V_ARRAY(PostData)->rgsabound[0].cElements;
        }
708 709 710 711 712 713 714 715 716 717
    }

    if(Headers && V_VT(Headers) != VT_EMPTY && V_VT(Headers) != VT_ERROR) {
        if(V_VT(Headers) != VT_BSTR)
            return E_INVALIDARG;

        headers = V_BSTR(Headers);
        TRACE("Headers: %s\n", debugstr_w(headers));
    }

718 719
    task = heap_alloc(sizeof(*task));
    task->bsc = create_callback(This, url, post_data, post_data_len, headers);
720

721 722 723
    if(post_data)
        SafeArrayUnaccessData(V_ARRAY(PostData));

724
    push_dochost_task(This, &task->header, navigate_bsc_proc, This->url == NULL);
725 726

    return S_OK;
727 728
}

729 730
static HRESULT navigate_hlink(DocHost *This, IMoniker *mon, IBindCtx *bindctx,
                              IBindStatusCallback *callback)
731 732
{
    IHttpNegotiate *http_negotiate;
733
    BindStatusCallback *bsc;
734 735
    PBYTE post_data = NULL;
    ULONG post_data_len = 0;
736
    LPWSTR headers = NULL, url;
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
    BINDINFO bindinfo;
    DWORD bindf = 0;
    HRESULT hres;

    hres = IBindStatusCallback_QueryInterface(callback, &IID_IHttpNegotiate,
                                              (void**)&http_negotiate);
    if(SUCCEEDED(hres)) {
        static const WCHAR null_string[] = {0};

        IHttpNegotiate_BeginningTransaction(http_negotiate, null_string, null_string, 0,
                                            &headers);
        IHttpNegotiate_Release(http_negotiate);
    }

    memset(&bindinfo, 0, sizeof(bindinfo));
    bindinfo.cbSize = sizeof(bindinfo);

    hres = IBindStatusCallback_GetBindInfo(callback, &bindf, &bindinfo);
755
    dump_BINDINFO(&bindinfo);
756
    if(bindinfo.dwBindVerb == BINDVERB_POST) {
757
        post_data_len = bindinfo.cbstgmedData;
758 759 760 761
        if(post_data_len)
            post_data = bindinfo.stgmedData.u.hGlobal;
    }

762 763 764 765 766 767 768 769
    hres = IMoniker_GetDisplayName(mon, 0, NULL, &url);
    if(FAILED(hres))
        FIXME("GetDisplayName failed: %08x\n", hres);

    bsc = create_callback(This, url, post_data, post_data_len, headers);
    CoTaskMemFree(url);

    hres = navigate_bsc(This, bsc, mon);
770

771
    IBindStatusCallback_Release(BINDSC(bsc));
772 773
    CoTaskMemFree(headers);
    ReleaseBindInfo(&bindinfo);
774

775
    return hres;
776 777
}

778 779 780 781 782 783 784 785 786
HRESULT go_home(DocHost *This)
{
    static const WCHAR wszAboutBlank[] = {'a','b','o','u','t',':','b','l','a','n','k',0};

    FIXME("stub\n");

    return navigate_url(This, wszAboutBlank, NULL, NULL, NULL, NULL);
}

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
#define HLINKFRAME_THIS(iface) DEFINE_THIS(WebBrowser, HlinkFrame, iface)

static HRESULT WINAPI HlinkFrame_QueryInterface(IHlinkFrame *iface, REFIID riid, void **ppv)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
    return IWebBrowser2_QueryInterface(WEBBROWSER2(This), riid, ppv);
}

static ULONG WINAPI HlinkFrame_AddRef(IHlinkFrame *iface)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
    return IWebBrowser2_AddRef(WEBBROWSER2(This));
}

static ULONG WINAPI HlinkFrame_Release(IHlinkFrame *iface)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
    return IWebBrowser2_Release(WEBBROWSER2(This));
}

static HRESULT WINAPI HlinkFrame_SetBrowseContext(IHlinkFrame *iface,
                                                  IHlinkBrowseContext *pihlbc)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
    FIXME("(%p)->(%p)\n", This, pihlbc);
    return E_NOTIMPL;
}

static HRESULT WINAPI HlinkFrame_GetBrowseContext(IHlinkFrame *iface,
                                                  IHlinkBrowseContext **ppihlbc)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
    FIXME("(%p)->(%p)\n", This, ppihlbc);
    return E_NOTIMPL;
}

static HRESULT WINAPI HlinkFrame_Navigate(IHlinkFrame *iface, DWORD grfHLNF, LPBC pbc,
                                          IBindStatusCallback *pibsc, IHlink *pihlNavigate)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
827 828 829
    IMoniker *mon;
    LPWSTR location = NULL;

830
    TRACE("(%p)->(%08x %p %p %p)\n", This, grfHLNF, pbc, pibsc, pihlNavigate);
831 832

    if(grfHLNF)
833
        FIXME("unsupported grfHLNF=%08x\n", grfHLNF);
834 835 836 837 838 839 840 841 842 843 844 845

    /* Windows calls GetTargetFrameName here. */

    IHlink_GetMonikerReference(pihlNavigate, 1, &mon, &location);

    if(location) {
        FIXME("location = %s\n", debugstr_w(location));
        CoTaskMemFree(location);
    }

    /* Windows calls GetHlinkSite here */

846 847 848 849 850
    if(grfHLNF & HLNF_OPENINNEWWINDOW) {
        FIXME("Not supported HLNF_OPENINNEWWINDOW\n");
        return E_NOTIMPL;
    }

851
    return navigate_hlink(&This->doc_host, mon, pbc, pibsc);
852 853 854 855 856 857
}

static HRESULT WINAPI HlinkFrame_OnNavigate(IHlinkFrame *iface, DWORD grfHLNF,
        IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName, DWORD dwreserved)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
858
    FIXME("(%p)->(%08x %p %s %s %d)\n", This, grfHLNF, pimkTarget, debugstr_w(pwzLocation),
859 860 861 862 863 864 865 866
          debugstr_w(pwzFriendlyName), dwreserved);
    return E_NOTIMPL;
}

static HRESULT WINAPI HlinkFrame_UpdateHlink(IHlinkFrame *iface, ULONG uHLID,
        IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName)
{
    WebBrowser *This = HLINKFRAME_THIS(iface);
867
    FIXME("(%p)->(%u %p %s %s)\n", This, uHLID, pimkTarget, debugstr_w(pwzLocation),
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
          debugstr_w(pwzFriendlyName));
    return E_NOTIMPL;
}

#undef HLINKFRAME_THIS

static const IHlinkFrameVtbl HlinkFrameVtbl = {
    HlinkFrame_QueryInterface,
    HlinkFrame_AddRef,
    HlinkFrame_Release,
    HlinkFrame_SetBrowseContext,
    HlinkFrame_GetBrowseContext,
    HlinkFrame_Navigate,
    HlinkFrame_OnNavigate,
    HlinkFrame_UpdateHlink
};

void WebBrowser_HlinkFrame_Init(WebBrowser *This)
{
    This->lpHlinkFrameVtbl = &HlinkFrameVtbl;
}