persist.c 20.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright 2005 Jacek Caban
 *
 * 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 22 23 24
 */

#include "config.h"

#include <stdarg.h>
#include <stdio.h>

#define COBJMACROS
25 26
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
27 28 29 30 31

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
32
#include "shlguid.h"
33
#include "idispids.h"
34 35

#include "wine/debug.h"
36
#include "wine/unicode.h"
37 38 39 40 41

#include "mshtml_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(mshtml);

42 43 44 45 46
#define USER_AGENT "User-Agent:"
#define CONTENT_TYPE "Content-Type:"

static int fix_headers(char *buf, DWORD post_len)
{
47
    char *ptr = buf, *ptr2;
48

49 50
    while(*ptr && (ptr[0] != '\r' || ptr[1] != '\n')) {
        for(ptr2=ptr+1; *ptr2 && (ptr2[0] != '\r' || ptr2[1] != '\n'); ptr2++);
51

52 53
        if(*ptr2)
            ptr2 += 2;
54

55 56
        if(!strncasecmp(ptr, USER_AGENT, sizeof(USER_AGENT)-1)) {
            FIXME("Ignoring User-Agent header\n");
57
            memmove(ptr, ptr2, strlen(ptr2)+1);
58
        }else if(!post_len && !strncasecmp(ptr, CONTENT_TYPE, sizeof(CONTENT_TYPE)-1)) {
59
            TRACE("Ignoring Content-Type header\n");
60 61 62
            memmove(ptr, ptr2, strlen(ptr2)+1);
        }else {
            ptr = ptr2;
63 64 65
        }
    }

66 67
    *ptr = 0;
    return ptr-buf;
68 69
}

70 71 72
static nsIInputStream *get_post_data_stream(IBindCtx *bctx)
{
    nsIInputStream *ret = NULL;
73
    IUnknown *unk;
74
    IBindStatusCallback *callback;
75
    IServiceProvider *service_provider;
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
    BINDINFO bindinfo;
    DWORD bindf = 0;
    DWORD post_len = 0, headers_len = 0;
    LPWSTR headers = NULL;
    WCHAR emptystr[] = {0};
    char *data;
    HRESULT hres;

    static WCHAR _BSCB_Holder_[] =
        {'_','B','S','C','B','_','H','o','l','d','e','r','_',0};


    /* FIXME: This should be done in URLMoniker */
    if(!bctx)
        return NULL;

92
    hres = IBindCtx_GetObjectParam(bctx, _BSCB_Holder_, &unk);
93 94 95
    if(FAILED(hres))
        return NULL;

96 97 98 99 100 101 102 103
    hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)&callback);
    if(FAILED(hres)) {
        IUnknown_Release(unk);
        return NULL;
    }

    hres = IUnknown_QueryInterface(unk, &IID_IServiceProvider, (void**)&service_provider);
    IUnknown_Release(unk);
104
    if(SUCCEEDED(hres)) {
105 106 107 108 109 110 111 112 113 114 115 116
        IHttpNegotiate *http_negotiate;

        hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate,
                                             (void**)&http_negotiate);
        if(SUCCEEDED(hres)) {
            hres = IHttpNegotiate_BeginningTransaction(http_negotiate, emptystr,
                                                       emptystr, 0, &headers);
            IHttpNegotiate_Release(http_negotiate);

            if(SUCCEEDED(hres) && headers)
                headers_len = WideCharToMultiByte(CP_ACP, 0, headers, -1, NULL, 0, NULL, NULL);
        }
117

118
        IServiceProvider_Release(service_provider);
119 120 121 122 123 124 125 126
    }

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

    hres = IBindStatusCallback_GetBindInfo(callback, &bindf, &bindinfo);

    if(SUCCEEDED(hres) && bindinfo.dwBindVerb == BINDVERB_POST)
127
        post_len = bindinfo.cbstgmedData;
128 129

    if(headers_len || post_len) {
130
        int len = 0;
131

132
        static const char content_length[] = "Content-Length: %u\r\n\r\n";
133

134
        data = heap_alloc(headers_len+post_len+sizeof(content_length)+8);
135 136

        if(headers_len) {
137
            WideCharToMultiByte(CP_ACP, 0, headers, -1, data, headers_len, NULL, NULL);
138
            len = fix_headers(data, post_len);
139 140 141
        }

        if(post_len) {
142
            sprintf(data+len, content_length, post_len);
143
            len += strlen(data+len);
144 145 146 147 148 149

            memcpy(data+len, bindinfo.stgmedData.u.hGlobal, post_len);
        }

        TRACE("data = %s\n", debugstr_an(data, len+post_len));

150 151
        if(len)
            ret = create_nsstream(data, len+post_len);
152 153
    }

154
    CoTaskMemFree(headers);
155 156 157 158 159 160
    ReleaseBindInfo(&bindinfo);
    IBindStatusCallback_Release(callback);

    return ret;
}

161 162 163 164 165 166
static BOOL use_gecko_script(LPCWSTR url)
{
    static const WCHAR fileW[] = {'f','i','l','e',':'};
    return strncmpiW(fileW, url, sizeof(fileW)/sizeof(WCHAR));
}

167 168
void set_current_mon(HTMLDocument *This, IMoniker *mon)
{
169 170 171
    HRESULT hres;

    if(This->mon) {
172
        IMoniker_Release(This->mon);
173 174 175 176
        This->mon = NULL;
    }

    if(This->url) {
177
        CoTaskMemFree(This->url);
178 179 180 181 182 183 184
        This->url = NULL;
    }

    if(!mon)
        return;

    IMoniker_AddRef(mon);
185
    This->mon = mon;
186 187 188 189

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

    set_script_mode(This, use_gecko_script(This->url) ? SCRIPTMODE_GECKO : SCRIPTMODE_ACTIVESCRIPT);
192 193
}

194
static HRESULT set_moniker(HTMLDocument *This, IMoniker *mon, IBindCtx *pibc, BOOL *bind_complete)
195
{
196
    nsChannelBSC *bscallback;
197
    LPOLESTR url = NULL;
198
    task_t *task;
199 200 201
    HRESULT hres;
    nsresult nsres;

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    if(pibc) {
        IUnknown *unk = NULL;

        /* FIXME:
         * Use params:
         * "__PrecreatedObject"
         * "BIND_CONTEXT_PARAM"
         * "__HTMLLOADOPTIONS"
         * "__DWNBINDINFO"
         * "URL Context"
         * "CBinding Context"
         * "_ITransData_Object_"
         * "_EnumFORMATETC_"
         */

217
        IBindCtx_GetObjectParam(pibc, (LPOLESTR)SZ_HTML_CLIENTSITE_OBJECTPARAM, &unk);
218 219 220 221 222 223 224 225 226 227 228 229 230
        if(unk) {
            IOleClientSite *client = NULL;

            hres = IUnknown_QueryInterface(unk, &IID_IOleClientSite, (void**)&client);
            if(SUCCEEDED(hres)) {
                TRACE("Got client site %p\n", client);
                IOleObject_SetClientSite(OLEOBJ(This), client);
                IOleClientSite_Release(client);
            }

            IUnknown_Release(unk);
        }
    }
231

232
    This->readystate = READYSTATE_LOADING;
233
    call_property_onchanged(&This->cp_propnotif, DISPID_READYSTATE);
234
    update_doc(This, UPDATE_TITLE);
235

236
    HTMLDocument_LockContainer(This, TRUE);
237
    
238
    hres = IMoniker_GetDisplayName(mon, pibc, NULL, &url);
239
    if(FAILED(hres)) {
240
        WARN("GetDiaplayName failed: %08x\n", hres);
241 242
        return hres;
    }
243

244 245
    TRACE("got url: %s\n", debugstr_w(url));

246 247
    set_current_mon(This, mon);

248 249
    if(This->client) {
        VARIANT silent, offline;
250
        IOleCommandTarget *cmdtrg = NULL;
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

        hres = get_client_disp_property(This->client, DISPID_AMBIENT_SILENT, &silent);
        if(SUCCEEDED(hres)) {
            if(V_VT(&silent) != VT_BOOL)
                WARN("V_VT(silent) = %d\n", V_VT(&silent));
            else if(V_BOOL(&silent))
                FIXME("silent == true\n");
        }

        hres = get_client_disp_property(This->client,
                DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, &offline);
        if(SUCCEEDED(hres)) {
            if(V_VT(&silent) != VT_BOOL)
                WARN("V_VT(offline) = %d\n", V_VT(&silent));
            else if(V_BOOL(&silent))
                FIXME("offline == true\n");
        }
268 269 270 271 272 273 274 275 276 277 278 279

        hres = IOleClientSite_QueryInterface(This->client, &IID_IOleCommandTarget,
                (void**)&cmdtrg);
        if(SUCCEEDED(hres)) {
            VARIANT var;

            V_VT(&var) = VT_I4;
            V_I4(&var) = 0;
            IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 37, 0, &var, NULL);

            IOleCommandTarget_Release(cmdtrg);
        }
280 281
    }

282
    bscallback = create_channelbsc(mon);
283

284
    if(This->frame) {
285
        task = heap_alloc(sizeof(task_t));
286 287 288 289 290 291 292 293

        task->doc = This;
        task->task_id = TASK_SETPROGRESS;
        task->next = NULL;

        push_task(task);
    }

294
    task = heap_alloc(sizeof(task_t));
295 296 297 298 299 300 301

    task->doc = This;
    task->task_id = TASK_SETDOWNLOADSTATE;
    task->next = NULL;

    push_task(task);

302
    if(This->nscontainer) {
303
        nsIInputStream *post_data_stream = get_post_data_stream(pibc);
304

305
        This->nscontainer->bscallback = bscallback;
306
        nsres = nsIWebNavigation_LoadURI(This->nscontainer->navigation, url,
307
                LOAD_FLAGS_NONE, NULL, post_data_stream, NULL);
308
        This->nscontainer->bscallback = NULL;
309

310 311 312 313
        if(post_data_stream)
            nsIInputStream_Release(post_data_stream);

        if(NS_SUCCEEDED(nsres)) {
314 315
            /* FIXME: don't return here (URL Moniker needs to be good enough) */

316
            IUnknown_Release((IUnknown*)bscallback);
317
            CoTaskMemFree(url);
318 319 320

            if(bind_complete)
                *bind_complete = TRUE;
321
            return S_OK;
322
        }else if(nsres != WINE_NS_LOAD_FROM_MONIKER) {
323
            WARN("LoadURI failed: %08x\n", nsres);
324
        }
325
    }
326

327
    set_document_bscallback(This, bscallback);
328
    IUnknown_Release((IUnknown*)bscallback);
329 330
    CoTaskMemFree(url);

331 332
    if(bind_complete)
        *bind_complete = FALSE;
333 334 335 336 337 338 339 340 341 342
    return S_OK;
}

static HRESULT get_doc_string(HTMLDocument *This, char **str, DWORD *len)
{
    nsIDOMNode *nsnode;
    LPCWSTR strw;
    nsAString nsstr;
    nsresult nsres;

343 344 345
    if(!This->nsdoc) {
        WARN("NULL nsdoc\n");
        return E_UNEXPECTED;
346 347
    }

348
    nsres = nsIDOMHTMLDocument_QueryInterface(This->nsdoc, &IID_nsIDOMNode, (void**)&nsnode);
349 350 351 352 353 354 355 356 357
    if(NS_FAILED(nsres)) {
        ERR("Could not get nsIDOMNode failed: %08x\n", nsres);
        return E_FAIL;
    }

    nsAString_Init(&nsstr, NULL);
    nsnode_to_nsstring(nsnode, &nsstr);
    nsIDOMNode_Release(nsnode);

358
    nsAString_GetData(&nsstr, &strw);
359 360 361
    TRACE("%s\n", debugstr_w(strw));

    *len = WideCharToMultiByte(CP_ACP, 0, strw, -1, NULL, 0, NULL, NULL);
362
    *str = heap_alloc(*len);
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
    WideCharToMultiByte(CP_ACP, 0, strw, -1, *str, *len, NULL, NULL);

    nsAString_Finish(&nsstr);

    return S_OK;
}


/**********************************************************
 * IPersistMoniker implementation
 */

#define PERSISTMON_THIS(iface) DEFINE_THIS(HTMLDocument, PersistMoniker, iface)

static HRESULT WINAPI PersistMoniker_QueryInterface(IPersistMoniker *iface, REFIID riid,
                                                            void **ppvObject)
{
    HTMLDocument *This = PERSISTMON_THIS(iface);
    return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
}

static ULONG WINAPI PersistMoniker_AddRef(IPersistMoniker *iface)
{
    HTMLDocument *This = PERSISTMON_THIS(iface);
    return IHTMLDocument2_AddRef(HTMLDOC(This));
}

static ULONG WINAPI PersistMoniker_Release(IPersistMoniker *iface)
{
    HTMLDocument *This = PERSISTMON_THIS(iface);
    return IHTMLDocument2_Release(HTMLDOC(This));
}

static HRESULT WINAPI PersistMoniker_GetClassID(IPersistMoniker *iface, CLSID *pClassID)
{
    HTMLDocument *This = PERSISTMON_THIS(iface);
    return IPersist_GetClassID(PERSIST(This), pClassID);
}

static HRESULT WINAPI PersistMoniker_IsDirty(IPersistMoniker *iface)
{
    HTMLDocument *This = PERSISTMON_THIS(iface);
405 406 407 408

    TRACE("(%p)\n", This);

    return IPersistStreamInit_IsDirty(PERSTRINIT(This));
409 410 411 412 413 414
}

static HRESULT WINAPI PersistMoniker_Load(IPersistMoniker *iface, BOOL fFullyAvailable,
        IMoniker *pimkName, LPBC pibc, DWORD grfMode)
{
    HTMLDocument *This = PERSISTMON_THIS(iface);
415
    BOOL bind_complete = FALSE;
416 417 418 419
    HRESULT hres;

    TRACE("(%p)->(%x %p %p %08x)\n", This, fFullyAvailable, pimkName, pibc, grfMode);

420
    hres = set_moniker(This, pimkName, pibc, &bind_complete);
421 422 423
    if(FAILED(hres))
        return hres;

424
    if(!bind_complete)
425
        return start_binding(This, (BSCallback*)This->bscallback, pibc);
426 427

    return S_OK;
428 429 430 431 432
}

static HRESULT WINAPI PersistMoniker_Save(IPersistMoniker *iface, IMoniker *pimkName,
        LPBC pbc, BOOL fRemember)
{
433 434
    HTMLDocument *This = PERSISTMON_THIS(iface);
    FIXME("(%p)->(%p %p %x)\n", This, pimkName, pbc, fRemember);
435 436 437 438 439
    return E_NOTIMPL;
}

static HRESULT WINAPI PersistMoniker_SaveCompleted(IPersistMoniker *iface, IMoniker *pimkName, LPBC pibc)
{
440 441
    HTMLDocument *This = PERSISTMON_THIS(iface);
    FIXME("(%p)->(%p %p)\n", This, pimkName, pibc);
442 443 444 445 446
    return E_NOTIMPL;
}

static HRESULT WINAPI PersistMoniker_GetCurMoniker(IPersistMoniker *iface, IMoniker **ppimkName)
{
447
    HTMLDocument *This = PERSISTMON_THIS(iface);
448 449 450 451 452 453 454 455 456

    TRACE("(%p)->(%p)\n", This, ppimkName);

    if(!This->mon)
        return E_UNEXPECTED;

    IMoniker_AddRef(This->mon);
    *ppimkName = This->mon;
    return S_OK;
457 458
}

459
static const IPersistMonikerVtbl PersistMonikerVtbl = {
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
    PersistMoniker_QueryInterface,
    PersistMoniker_AddRef,
    PersistMoniker_Release,
    PersistMoniker_GetClassID,
    PersistMoniker_IsDirty,
    PersistMoniker_Load,
    PersistMoniker_Save,
    PersistMoniker_SaveCompleted,
    PersistMoniker_GetCurMoniker
};

/**********************************************************
 * IMonikerProp implementation
 */

475
#define MONPROP_THIS(iface) DEFINE_THIS(HTMLDocument, MonikerProp, iface)
476 477 478

static HRESULT WINAPI MonikerProp_QueryInterface(IMonikerProp *iface, REFIID riid, void **ppvObject)
{
479
    HTMLDocument *This = MONPROP_THIS(iface);
480 481 482 483 484
    return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
}

static ULONG WINAPI MonikerProp_AddRef(IMonikerProp *iface)
{
485
    HTMLDocument *This = MONPROP_THIS(iface);
486 487 488 489 490
    return IHTMLDocument2_AddRef(HTMLDOC(This));
}

static ULONG WINAPI MonikerProp_Release(IMonikerProp *iface)
{
491
    HTMLDocument *This = MONPROP_THIS(iface);
492 493 494 495 496
    return IHTMLDocument_Release(HTMLDOC(This));
}

static HRESULT WINAPI MonikerProp_PutProperty(IMonikerProp *iface, MONIKERPROPERTY mkp, LPCWSTR val)
{
497
    HTMLDocument *This = MONPROP_THIS(iface);
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515

    TRACE("(%p)->(%d %s)\n", This, mkp, debugstr_w(val));

    switch(mkp) {
    case MIMETYPEPROP:
        heap_free(This->mime);
        This->mime = heap_strdupW(val);
        break;

    case CLASSIDPROP:
        break;

    default:
        FIXME("mkp %d\n", mkp);
        return E_NOTIMPL;
    }

    return S_OK;
516 517
}

518
static const IMonikerPropVtbl MonikerPropVtbl = {
519 520 521 522 523 524 525 526 527 528
    MonikerProp_QueryInterface,
    MonikerProp_AddRef,
    MonikerProp_Release,
    MonikerProp_PutProperty
};

/**********************************************************
 * IPersistFile implementation
 */

529
#define PERSISTFILE_THIS(iface) DEFINE_THIS(HTMLDocument, PersistFile, iface)
530 531 532

static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *iface, REFIID riid, void **ppvObject)
{
533
    HTMLDocument *This = PERSISTFILE_THIS(iface);
534 535 536 537 538
    return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
}

static ULONG WINAPI PersistFile_AddRef(IPersistFile *iface)
{
539
    HTMLDocument *This = PERSISTFILE_THIS(iface);
540 541 542 543 544
    return IHTMLDocument2_AddRef(HTMLDOC(This));
}

static ULONG WINAPI PersistFile_Release(IPersistFile *iface)
{
545
    HTMLDocument *This = PERSISTFILE_THIS(iface);
546 547 548 549 550
    return IHTMLDocument2_Release(HTMLDOC(This));
}

static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *iface, CLSID *pClassID)
{
551 552 553
    HTMLDocument *This = PERSISTFILE_THIS(iface);

    TRACE("(%p)->(%p)\n", This, pClassID);
554 555 556 557

    if(!pClassID)
        return E_INVALIDARG;

558
    *pClassID = CLSID_HTMLDocument;
559 560 561 562 563
    return S_OK;
}

static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *iface)
{
564
    HTMLDocument *This = PERSISTFILE_THIS(iface);
565 566 567 568

    TRACE("(%p)\n", This);

    return IPersistStreamInit_IsDirty(PERSTRINIT(This));
569 570 571 572
}

static HRESULT WINAPI PersistFile_Load(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
{
573
    HTMLDocument *This = PERSISTFILE_THIS(iface);
574
    FIXME("(%p)->(%s %08x)\n", This, debugstr_w(pszFileName), dwMode);
575 576 577 578 579
    return E_NOTIMPL;
}

static HRESULT WINAPI PersistFile_Save(IPersistFile *iface, LPCOLESTR pszFileName, BOOL fRemember)
{
580
    HTMLDocument *This = PERSISTFILE_THIS(iface);
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
    char *str;
    DWORD len, written=0;
    HANDLE file;
    HRESULT hres;

    TRACE("(%p)->(%s %x)\n", This, debugstr_w(pszFileName), fRemember);

    file = CreateFileW(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                       FILE_ATTRIBUTE_NORMAL, NULL);
    if(file == INVALID_HANDLE_VALUE) {
        WARN("Could not create file: %u\n", GetLastError());
        return E_FAIL;
    }

    hres = get_doc_string(This, &str, &len);
596 597
    if(SUCCEEDED(hres))
        WriteFile(file, str, len, &written, NULL);
598 599

    CloseHandle(file);
600
    return hres;
601 602 603 604
}

static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
{
605 606
    HTMLDocument *This = PERSISTFILE_THIS(iface);
    FIXME("(%p)->(%s)\n", This, debugstr_w(pszFileName));
607 608 609 610 611
    return E_NOTIMPL;
}

static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *iface, LPOLESTR *pszFileName)
{
612 613
    HTMLDocument *This = PERSISTFILE_THIS(iface);
    FIXME("(%p)->(%p)\n", This, pszFileName);
614 615 616
    return E_NOTIMPL;
}

617
static const IPersistFileVtbl PersistFileVtbl = {
618 619 620 621 622 623 624 625 626 627 628
    PersistFile_QueryInterface,
    PersistFile_AddRef,
    PersistFile_Release,
    PersistFile_GetClassID,
    PersistFile_IsDirty,
    PersistFile_Load,
    PersistFile_Save,
    PersistFile_SaveCompleted,
    PersistFile_GetCurFile
};

629
#define PERSTRINIT_THIS(iface) DEFINE_THIS(HTMLDocument, PersistStreamInit, iface)
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646

static HRESULT WINAPI PersistStreamInit_QueryInterface(IPersistStreamInit *iface,
                                                       REFIID riid, void **ppv)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
    return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppv);
}

static ULONG WINAPI PersistStreamInit_AddRef(IPersistStreamInit *iface)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
    return IHTMLDocument2_AddRef(HTMLDOC(This));
}

static ULONG WINAPI PersistStreamInit_Release(IPersistStreamInit *iface)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
647
    return IHTMLDocument2_Release(HTMLDOC(This));
648 649 650 651 652 653 654 655 656 657 658
}

static HRESULT WINAPI PersistStreamInit_GetClassID(IPersistStreamInit *iface, CLSID *pClassID)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
    return IPersist_GetClassID(PERSIST(This), pClassID);
}

static HRESULT WINAPI PersistStreamInit_IsDirty(IPersistStreamInit *iface)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
659 660 661 662

    TRACE("(%p)\n", This);

    if(This->usermode == EDITMODE)
663
        return editor_is_dirty(This);
664 665

    return S_FALSE;
666 667 668 669 670
}

static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, LPSTREAM pStm)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
671 672 673 674 675 676 677 678 679 680 681 682 683
    IMoniker *mon;
    HRESULT hres;

    static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};

    TRACE("(%p)->(%p)\n", This, pStm);

    hres = CreateURLMoniker(NULL, about_blankW, &mon);
    if(FAILED(hres)) {
        WARN("CreateURLMoniker failed: %08x\n", hres);
        return hres;
    }

684
    hres = set_moniker(This, mon, NULL, NULL);
685 686 687 688
    IMoniker_Release(mon);
    if(FAILED(hres))
        return hres;

689
    return channelbsc_load_stream(This->bscallback, pStm);
690 691 692 693 694 695
}

static HRESULT WINAPI PersistStreamInit_Save(IPersistStreamInit *iface, LPSTREAM pStm,
                                             BOOL fClearDirty)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
696 697 698 699
    char *str;
    DWORD len, written=0;
    HRESULT hres;

700
    TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
701

702 703 704
    hres = get_doc_string(This, &str, &len);
    if(FAILED(hres))
        return hres;
705 706 707

    hres = IStream_Write(pStm, str, len, &written);
    if(FAILED(hres))
708
        FIXME("Write failed: %08x\n", hres);
709

710
    heap_free(str);
711 712 713 714

    if(fClearDirty)
        set_dirty(This, VARIANT_FALSE);

715
    return S_OK;
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
}

static HRESULT WINAPI PersistStreamInit_GetSizeMax(IPersistStreamInit *iface,
                                                   ULARGE_INTEGER *pcbSize)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
    FIXME("(%p)->(%p)\n", This, pcbSize);
    return E_NOTIMPL;
}

static HRESULT WINAPI PersistStreamInit_InitNew(IPersistStreamInit *iface)
{
    HTMLDocument *This = PERSTRINIT_THIS(iface);
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

#undef PERSTRINIT_THIS

static const IPersistStreamInitVtbl PersistStreamInitVtbl = {
    PersistStreamInit_QueryInterface,
    PersistStreamInit_AddRef,
    PersistStreamInit_Release,
    PersistStreamInit_GetClassID,
    PersistStreamInit_IsDirty,
    PersistStreamInit_Load,
    PersistStreamInit_Save,
    PersistStreamInit_GetSizeMax,
    PersistStreamInit_InitNew
};

747 748 749 750 751
void HTMLDocument_Persist_Init(HTMLDocument *This)
{
    This->lpPersistMonikerVtbl = &PersistMonikerVtbl;
    This->lpPersistFileVtbl = &PersistFileVtbl;
    This->lpMonikerPropVtbl = &MonikerPropVtbl;
752
    This->lpPersistStreamInitVtbl = &PersistStreamInitVtbl;
753 754

    This->bscallback = NULL;
755
    This->mon = NULL;
756
    This->url = NULL;
757
    This->mime = NULL;
758
}