mshtml: Added nsINetUtil interface to nsIOService.
[wine] / dlls / mshtml / persist.c
1 /*
2  * Copyright 2005 Jacek Caban
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 "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "shlguid.h"
33 #include "idispids.h"
34
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37
38 #include "mshtml_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
41
42 #define USER_AGENT "User-Agent:"
43 #define CONTENT_TYPE "Content-Type:"
44
45 static int fix_headers(char *buf, DWORD post_len)
46 {
47     char *ptr = NULL;
48
49     if(!strncasecmp(USER_AGENT, buf, sizeof(USER_AGENT)-1)) {
50         ptr = buf;
51     }else {
52         ptr = strstr(buf, "\r\n" USER_AGENT);
53         if(ptr)
54             ptr += 2;
55     }
56
57     if(ptr) {
58         const char *ptr2;
59
60         FIXME("Ignoring User-Agent header\n");
61
62         ptr2 = strstr(ptr, "\r\n");
63         if(ptr2)
64             memmove(ptr, ptr2, strlen(ptr2)+1);
65     }
66
67     if(!post_len) {
68         if(!strncasecmp(CONTENT_TYPE, buf, sizeof(CONTENT_TYPE)-1)) {
69             ptr = buf;
70         }else {
71             ptr = strstr(buf, "\r\n" CONTENT_TYPE);
72             if(ptr)
73                 ptr += 2;
74         }
75
76         if(ptr) {
77             const char *ptr2;
78
79             TRACE("Ignoring Content-Type header\n");
80
81             ptr2 = strstr(ptr, "\r\n");
82             if(ptr2)
83                 memmove(ptr, ptr2, strlen(ptr2)+1);
84         }
85     }
86
87     return strlen(buf);
88 }
89
90 static nsIInputStream *get_post_data_stream(IBindCtx *bctx)
91 {
92     nsIInputStream *ret = NULL;
93     IUnknown *unk;
94     IBindStatusCallback *callback;
95     IServiceProvider *service_provider;
96     BINDINFO bindinfo;
97     DWORD bindf = 0;
98     DWORD post_len = 0, headers_len = 0;
99     LPWSTR headers = NULL;
100     WCHAR emptystr[] = {0};
101     char *data;
102     HRESULT hres;
103
104     static WCHAR _BSCB_Holder_[] =
105         {'_','B','S','C','B','_','H','o','l','d','e','r','_',0};
106
107
108     /* FIXME: This should be done in URLMoniker */
109     if(!bctx)
110         return NULL;
111
112     hres = IBindCtx_GetObjectParam(bctx, _BSCB_Holder_, &unk);
113     if(FAILED(hres))
114         return NULL;
115
116     hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)&callback);
117     if(FAILED(hres)) {
118         IUnknown_Release(unk);
119         return NULL;
120     }
121
122     hres = IUnknown_QueryInterface(unk, &IID_IServiceProvider, (void**)&service_provider);
123     IUnknown_Release(unk);
124     if(SUCCEEDED(hres)) {
125         IHttpNegotiate *http_negotiate;
126
127         hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate,
128                                              (void**)&http_negotiate);
129         if(SUCCEEDED(hres)) {
130             hres = IHttpNegotiate_BeginningTransaction(http_negotiate, emptystr,
131                                                        emptystr, 0, &headers);
132             IHttpNegotiate_Release(http_negotiate);
133
134             if(SUCCEEDED(hres) && headers)
135                 headers_len = WideCharToMultiByte(CP_ACP, 0, headers, -1, NULL, 0, NULL, NULL);
136         }
137
138         IServiceProvider_Release(service_provider);
139     }
140
141     memset(&bindinfo, 0, sizeof(bindinfo));
142     bindinfo.cbSize = sizeof(bindinfo);
143
144     hres = IBindStatusCallback_GetBindInfo(callback, &bindf, &bindinfo);
145
146     if(SUCCEEDED(hres) && bindinfo.dwBindVerb == BINDVERB_POST)
147         post_len = bindinfo.cbstgmedData;
148
149     if(headers_len || post_len) {
150         int len = 0;
151
152         static const char content_length[] = "Content-Length: %u\r\n\r\n";
153
154         data = heap_alloc(headers_len+post_len+sizeof(content_length)+8);
155
156         if(headers_len) {
157             WideCharToMultiByte(CP_ACP, 0, headers, -1, data, -1, NULL, NULL);
158             len = fix_headers(data, post_len);
159         }
160
161         if(post_len) {
162             if(len >= 4 && !strcmp(data+len-4, "\r\n\r\n"))
163                 len -= 2;
164
165             sprintf(data+len, content_length, post_len);
166             len = strlen(data);
167
168             memcpy(data+len, bindinfo.stgmedData.u.hGlobal, post_len);
169         }
170
171         TRACE("data = %s\n", debugstr_an(data, len+post_len));
172
173         ret = create_nsstream(data, len+post_len);
174     }
175
176     CoTaskMemFree(headers);
177     ReleaseBindInfo(&bindinfo);
178     IBindStatusCallback_Release(callback);
179
180     return ret;
181 }
182
183 void set_current_mon(HTMLDocument *This, IMoniker *mon)
184 {
185     HRESULT hres;
186
187     if(This->mon) {
188         IMoniker_Release(This->mon);
189         This->mon = NULL;
190     }
191
192     if(This->url) {
193         CoTaskMemFree(This->url);
194         This->url = NULL;
195     }
196
197     if(!mon)
198         return;
199
200     IMoniker_AddRef(mon);
201     This->mon = mon;
202
203     hres = IMoniker_GetDisplayName(mon, NULL, NULL, &This->url);
204     if(FAILED(hres))
205         WARN("GetDisplayName failed: %08x\n", hres);
206 }
207
208 static HRESULT set_moniker(HTMLDocument *This, IMoniker *mon, IBindCtx *pibc, BOOL *bind_complete)
209 {
210     BSCallback *bscallback;
211     LPOLESTR url = NULL;
212     task_t *task;
213     HRESULT hres;
214     nsresult nsres;
215
216     if(pibc) {
217         IUnknown *unk = NULL;
218
219         /* FIXME:
220          * Use params:
221          * "__PrecreatedObject"
222          * "BIND_CONTEXT_PARAM"
223          * "__HTMLLOADOPTIONS"
224          * "__DWNBINDINFO"
225          * "URL Context"
226          * "CBinding Context"
227          * "_ITransData_Object_"
228          * "_EnumFORMATETC_"
229          */
230
231         IBindCtx_GetObjectParam(pibc, (LPOLESTR)SZ_HTML_CLIENTSITE_OBJECTPARAM, &unk);
232         if(unk) {
233             IOleClientSite *client = NULL;
234
235             hres = IUnknown_QueryInterface(unk, &IID_IOleClientSite, (void**)&client);
236             if(SUCCEEDED(hres)) {
237                 TRACE("Got client site %p\n", client);
238                 IOleObject_SetClientSite(OLEOBJ(This), client);
239                 IOleClientSite_Release(client);
240             }
241
242             IUnknown_Release(unk);
243         }
244     }
245
246     This->readystate = READYSTATE_LOADING;
247     call_property_onchanged(&This->cp_propnotif, DISPID_READYSTATE);
248     update_doc(This, UPDATE_TITLE);
249
250     HTMLDocument_LockContainer(This, TRUE);
251     
252     hres = IMoniker_GetDisplayName(mon, pibc, NULL, &url);
253     if(FAILED(hres)) {
254         WARN("GetDiaplayName failed: %08x\n", hres);
255         return hres;
256     }
257
258     TRACE("got url: %s\n", debugstr_w(url));
259
260     set_current_mon(This, mon);
261
262     if(This->client) {
263         VARIANT silent, offline;
264         IOleCommandTarget *cmdtrg = NULL;
265
266         hres = get_client_disp_property(This->client, DISPID_AMBIENT_SILENT, &silent);
267         if(SUCCEEDED(hres)) {
268             if(V_VT(&silent) != VT_BOOL)
269                 WARN("V_VT(silent) = %d\n", V_VT(&silent));
270             else if(V_BOOL(&silent))
271                 FIXME("silent == true\n");
272         }
273
274         hres = get_client_disp_property(This->client,
275                 DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, &offline);
276         if(SUCCEEDED(hres)) {
277             if(V_VT(&silent) != VT_BOOL)
278                 WARN("V_VT(offline) = %d\n", V_VT(&silent));
279             else if(V_BOOL(&silent))
280                 FIXME("offline == true\n");
281         }
282
283         hres = IOleClientSite_QueryInterface(This->client, &IID_IOleCommandTarget,
284                 (void**)&cmdtrg);
285         if(SUCCEEDED(hres)) {
286             VARIANT var;
287
288             V_VT(&var) = VT_I4;
289             V_I4(&var) = 0;
290             IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 37, 0, &var, NULL);
291
292             IOleCommandTarget_Release(cmdtrg);
293         }
294     }
295
296     bscallback = create_bscallback(mon);
297
298     if(This->frame) {
299         task = heap_alloc(sizeof(task_t));
300
301         task->doc = This;
302         task->task_id = TASK_SETPROGRESS;
303         task->next = NULL;
304
305         push_task(task);
306     }
307
308     task = heap_alloc(sizeof(task_t));
309
310     task->doc = This;
311     task->task_id = TASK_SETDOWNLOADSTATE;
312     task->next = NULL;
313
314     push_task(task);
315
316     if(This->nscontainer) {
317         nsIInputStream *post_data_stream = get_post_data_stream(pibc);
318
319         This->nscontainer->bscallback = bscallback;
320         nsres = nsIWebNavigation_LoadURI(This->nscontainer->navigation, url,
321                 LOAD_FLAGS_NONE, NULL, post_data_stream, NULL);
322         This->nscontainer->bscallback = NULL;
323
324         if(post_data_stream)
325             nsIInputStream_Release(post_data_stream);
326
327         if(NS_SUCCEEDED(nsres)) {
328             /* FIXME: don't return here (URL Moniker needs to be good enough) */
329
330             IBindStatusCallback_Release(STATUSCLB(bscallback));
331             CoTaskMemFree(url);
332
333             if(bind_complete)
334                 *bind_complete = TRUE;
335             return S_OK;
336         }else if(nsres != WINE_NS_LOAD_FROM_MONIKER) {
337             WARN("LoadURI failed: %08x\n", nsres);
338         }
339     }
340
341     set_document_bscallback(This, bscallback);
342     IBindStatusCallback_Release(STATUSCLB(bscallback));
343     CoTaskMemFree(url);
344
345     if(bind_complete)
346         *bind_complete = FALSE;
347     return S_OK;
348 }
349
350 static HRESULT get_doc_string(HTMLDocument *This, char **str, DWORD *len)
351 {
352     nsIDOMDocument *nsdoc;
353     nsIDOMNode *nsnode;
354     LPCWSTR strw;
355     nsAString nsstr;
356     nsresult nsres;
357
358     if(!This->nscontainer) {
359         WARN("no nscontainer, returning NULL\n");
360         return S_OK;
361     }
362
363     nsres = nsIWebNavigation_GetDocument(This->nscontainer->navigation, &nsdoc);
364     if(NS_FAILED(nsres)) {
365         ERR("GetDocument failed: %08x\n", nsres);
366         return E_FAIL;
367     }
368
369     nsres = nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDOMNode, (void**)&nsnode);
370     nsIDOMDocument_Release(nsdoc);
371     if(NS_FAILED(nsres)) {
372         ERR("Could not get nsIDOMNode failed: %08x\n", nsres);
373         return E_FAIL;
374     }
375
376     nsAString_Init(&nsstr, NULL);
377     nsnode_to_nsstring(nsnode, &nsstr);
378     nsIDOMNode_Release(nsnode);
379
380     nsAString_GetData(&nsstr, &strw);
381     TRACE("%s\n", debugstr_w(strw));
382
383     *len = WideCharToMultiByte(CP_ACP, 0, strw, -1, NULL, 0, NULL, NULL);
384     *str = heap_alloc(*len);
385     WideCharToMultiByte(CP_ACP, 0, strw, -1, *str, *len, NULL, NULL);
386
387     nsAString_Finish(&nsstr);
388
389     return S_OK;
390 }
391
392
393 /**********************************************************
394  * IPersistMoniker implementation
395  */
396
397 #define PERSISTMON_THIS(iface) DEFINE_THIS(HTMLDocument, PersistMoniker, iface)
398
399 static HRESULT WINAPI PersistMoniker_QueryInterface(IPersistMoniker *iface, REFIID riid,
400                                                             void **ppvObject)
401 {
402     HTMLDocument *This = PERSISTMON_THIS(iface);
403     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
404 }
405
406 static ULONG WINAPI PersistMoniker_AddRef(IPersistMoniker *iface)
407 {
408     HTMLDocument *This = PERSISTMON_THIS(iface);
409     return IHTMLDocument2_AddRef(HTMLDOC(This));
410 }
411
412 static ULONG WINAPI PersistMoniker_Release(IPersistMoniker *iface)
413 {
414     HTMLDocument *This = PERSISTMON_THIS(iface);
415     return IHTMLDocument2_Release(HTMLDOC(This));
416 }
417
418 static HRESULT WINAPI PersistMoniker_GetClassID(IPersistMoniker *iface, CLSID *pClassID)
419 {
420     HTMLDocument *This = PERSISTMON_THIS(iface);
421     return IPersist_GetClassID(PERSIST(This), pClassID);
422 }
423
424 static HRESULT WINAPI PersistMoniker_IsDirty(IPersistMoniker *iface)
425 {
426     HTMLDocument *This = PERSISTMON_THIS(iface);
427
428     TRACE("(%p)\n", This);
429
430     return IPersistStreamInit_IsDirty(PERSTRINIT(This));
431 }
432
433 static HRESULT WINAPI PersistMoniker_Load(IPersistMoniker *iface, BOOL fFullyAvailable,
434         IMoniker *pimkName, LPBC pibc, DWORD grfMode)
435 {
436     HTMLDocument *This = PERSISTMON_THIS(iface);
437     BOOL bind_complete = FALSE;
438     HRESULT hres;
439
440     TRACE("(%p)->(%x %p %p %08x)\n", This, fFullyAvailable, pimkName, pibc, grfMode);
441
442     hres = set_moniker(This, pimkName, pibc, &bind_complete);
443     if(FAILED(hres))
444         return hres;
445
446     if(!bind_complete)
447         return start_binding(This, This->bscallback, pibc);
448
449     return S_OK;
450 }
451
452 static HRESULT WINAPI PersistMoniker_Save(IPersistMoniker *iface, IMoniker *pimkName,
453         LPBC pbc, BOOL fRemember)
454 {
455     HTMLDocument *This = PERSISTMON_THIS(iface);
456     FIXME("(%p)->(%p %p %x)\n", This, pimkName, pbc, fRemember);
457     return E_NOTIMPL;
458 }
459
460 static HRESULT WINAPI PersistMoniker_SaveCompleted(IPersistMoniker *iface, IMoniker *pimkName, LPBC pibc)
461 {
462     HTMLDocument *This = PERSISTMON_THIS(iface);
463     FIXME("(%p)->(%p %p)\n", This, pimkName, pibc);
464     return E_NOTIMPL;
465 }
466
467 static HRESULT WINAPI PersistMoniker_GetCurMoniker(IPersistMoniker *iface, IMoniker **ppimkName)
468 {
469     HTMLDocument *This = PERSISTMON_THIS(iface);
470
471     TRACE("(%p)->(%p)\n", This, ppimkName);
472
473     if(!This->mon)
474         return E_UNEXPECTED;
475
476     IMoniker_AddRef(This->mon);
477     *ppimkName = This->mon;
478     return S_OK;
479 }
480
481 static const IPersistMonikerVtbl PersistMonikerVtbl = {
482     PersistMoniker_QueryInterface,
483     PersistMoniker_AddRef,
484     PersistMoniker_Release,
485     PersistMoniker_GetClassID,
486     PersistMoniker_IsDirty,
487     PersistMoniker_Load,
488     PersistMoniker_Save,
489     PersistMoniker_SaveCompleted,
490     PersistMoniker_GetCurMoniker
491 };
492
493 /**********************************************************
494  * IMonikerProp implementation
495  */
496
497 #define MONPROP_THIS(iface) DEFINE_THIS(HTMLDocument, MonikerProp, iface)
498
499 static HRESULT WINAPI MonikerProp_QueryInterface(IMonikerProp *iface, REFIID riid, void **ppvObject)
500 {
501     HTMLDocument *This = MONPROP_THIS(iface);
502     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
503 }
504
505 static ULONG WINAPI MonikerProp_AddRef(IMonikerProp *iface)
506 {
507     HTMLDocument *This = MONPROP_THIS(iface);
508     return IHTMLDocument2_AddRef(HTMLDOC(This));
509 }
510
511 static ULONG WINAPI MonikerProp_Release(IMonikerProp *iface)
512 {
513     HTMLDocument *This = MONPROP_THIS(iface);
514     return IHTMLDocument_Release(HTMLDOC(This));
515 }
516
517 static HRESULT WINAPI MonikerProp_PutProperty(IMonikerProp *iface, MONIKERPROPERTY mkp, LPCWSTR val)
518 {
519     HTMLDocument *This = MONPROP_THIS(iface);
520
521     TRACE("(%p)->(%d %s)\n", This, mkp, debugstr_w(val));
522
523     switch(mkp) {
524     case MIMETYPEPROP:
525         heap_free(This->mime);
526         This->mime = heap_strdupW(val);
527         break;
528
529     case CLASSIDPROP:
530         break;
531
532     default:
533         FIXME("mkp %d\n", mkp);
534         return E_NOTIMPL;
535     }
536
537     return S_OK;
538 }
539
540 static const IMonikerPropVtbl MonikerPropVtbl = {
541     MonikerProp_QueryInterface,
542     MonikerProp_AddRef,
543     MonikerProp_Release,
544     MonikerProp_PutProperty
545 };
546
547 /**********************************************************
548  * IPersistFile implementation
549  */
550
551 #define PERSISTFILE_THIS(iface) DEFINE_THIS(HTMLDocument, PersistFile, iface)
552
553 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *iface, REFIID riid, void **ppvObject)
554 {
555     HTMLDocument *This = PERSISTFILE_THIS(iface);
556     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppvObject);
557 }
558
559 static ULONG WINAPI PersistFile_AddRef(IPersistFile *iface)
560 {
561     HTMLDocument *This = PERSISTFILE_THIS(iface);
562     return IHTMLDocument2_AddRef(HTMLDOC(This));
563 }
564
565 static ULONG WINAPI PersistFile_Release(IPersistFile *iface)
566 {
567     HTMLDocument *This = PERSISTFILE_THIS(iface);
568     return IHTMLDocument2_Release(HTMLDOC(This));
569 }
570
571 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *iface, CLSID *pClassID)
572 {
573     HTMLDocument *This = PERSISTFILE_THIS(iface);
574
575     TRACE("(%p)->(%p)\n", This, pClassID);
576
577     if(!pClassID)
578         return E_INVALIDARG;
579
580     memcpy(pClassID, &CLSID_HTMLDocument, sizeof(CLSID));
581     return S_OK;
582 }
583
584 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *iface)
585 {
586     HTMLDocument *This = PERSISTFILE_THIS(iface);
587
588     TRACE("(%p)\n", This);
589
590     return IPersistStreamInit_IsDirty(PERSTRINIT(This));
591 }
592
593 static HRESULT WINAPI PersistFile_Load(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
594 {
595     HTMLDocument *This = PERSISTFILE_THIS(iface);
596     FIXME("(%p)->(%s %08x)\n", This, debugstr_w(pszFileName), dwMode);
597     return E_NOTIMPL;
598 }
599
600 static HRESULT WINAPI PersistFile_Save(IPersistFile *iface, LPCOLESTR pszFileName, BOOL fRemember)
601 {
602     HTMLDocument *This = PERSISTFILE_THIS(iface);
603     char *str;
604     DWORD len, written=0;
605     HANDLE file;
606     HRESULT hres;
607
608     TRACE("(%p)->(%s %x)\n", This, debugstr_w(pszFileName), fRemember);
609
610     file = CreateFileW(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
611                        FILE_ATTRIBUTE_NORMAL, NULL);
612     if(file == INVALID_HANDLE_VALUE) {
613         WARN("Could not create file: %u\n", GetLastError());
614         return E_FAIL;
615     }
616
617     hres = get_doc_string(This, &str, &len);
618     if(FAILED(hres))
619         return hres;
620
621     WriteFile(file, str, len, &written, NULL);
622     CloseHandle(file);
623     return S_OK;
624 }
625
626 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
627 {
628     HTMLDocument *This = PERSISTFILE_THIS(iface);
629     FIXME("(%p)->(%s)\n", This, debugstr_w(pszFileName));
630     return E_NOTIMPL;
631 }
632
633 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *iface, LPOLESTR *pszFileName)
634 {
635     HTMLDocument *This = PERSISTFILE_THIS(iface);
636     FIXME("(%p)->(%p)\n", This, pszFileName);
637     return E_NOTIMPL;
638 }
639
640 static const IPersistFileVtbl PersistFileVtbl = {
641     PersistFile_QueryInterface,
642     PersistFile_AddRef,
643     PersistFile_Release,
644     PersistFile_GetClassID,
645     PersistFile_IsDirty,
646     PersistFile_Load,
647     PersistFile_Save,
648     PersistFile_SaveCompleted,
649     PersistFile_GetCurFile
650 };
651
652 #define PERSTRINIT_THIS(iface) DEFINE_THIS(HTMLDocument, PersistStreamInit, iface)
653
654 static HRESULT WINAPI PersistStreamInit_QueryInterface(IPersistStreamInit *iface,
655                                                        REFIID riid, void **ppv)
656 {
657     HTMLDocument *This = PERSTRINIT_THIS(iface);
658     return IHTMLDocument2_QueryInterface(HTMLDOC(This), riid, ppv);
659 }
660
661 static ULONG WINAPI PersistStreamInit_AddRef(IPersistStreamInit *iface)
662 {
663     HTMLDocument *This = PERSTRINIT_THIS(iface);
664     return IHTMLDocument2_AddRef(HTMLDOC(This));
665 }
666
667 static ULONG WINAPI PersistStreamInit_Release(IPersistStreamInit *iface)
668 {
669     HTMLDocument *This = PERSTRINIT_THIS(iface);
670     return IHTMLDocument2_Release(HTMLDOC(This));
671 }
672
673 static HRESULT WINAPI PersistStreamInit_GetClassID(IPersistStreamInit *iface, CLSID *pClassID)
674 {
675     HTMLDocument *This = PERSTRINIT_THIS(iface);
676     return IPersist_GetClassID(PERSIST(This), pClassID);
677 }
678
679 static HRESULT WINAPI PersistStreamInit_IsDirty(IPersistStreamInit *iface)
680 {
681     HTMLDocument *This = PERSTRINIT_THIS(iface);
682
683     TRACE("(%p)\n", This);
684
685     if(This->usermode == EDITMODE)
686         return editor_is_dirty(This);
687
688     return S_FALSE;
689 }
690
691 static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, LPSTREAM pStm)
692 {
693     HTMLDocument *This = PERSTRINIT_THIS(iface);
694     IMoniker *mon;
695     HRESULT hres;
696
697     static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};
698
699     TRACE("(%p)->(%p)\n", This, pStm);
700
701     hres = CreateURLMoniker(NULL, about_blankW, &mon);
702     if(FAILED(hres)) {
703         WARN("CreateURLMoniker failed: %08x\n", hres);
704         return hres;
705     }
706
707     hres = set_moniker(This, mon, NULL, NULL);
708     IMoniker_Release(mon);
709     if(FAILED(hres))
710         return hres;
711
712     return load_stream(This->bscallback, pStm);
713 }
714
715 static HRESULT WINAPI PersistStreamInit_Save(IPersistStreamInit *iface, LPSTREAM pStm,
716                                              BOOL fClearDirty)
717 {
718     HTMLDocument *This = PERSTRINIT_THIS(iface);
719     char *str;
720     DWORD len, written=0;
721     HRESULT hres;
722
723     TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
724
725     hres = get_doc_string(This, &str, &len);
726     if(FAILED(hres))
727         return hres;
728
729     hres = IStream_Write(pStm, str, len, &written);
730     if(FAILED(hres))
731         FIXME("Write failed: %08x\n", hres);
732
733     heap_free(str);
734
735     if(fClearDirty)
736         set_dirty(This, VARIANT_FALSE);
737
738     return S_OK;
739 }
740
741 static HRESULT WINAPI PersistStreamInit_GetSizeMax(IPersistStreamInit *iface,
742                                                    ULARGE_INTEGER *pcbSize)
743 {
744     HTMLDocument *This = PERSTRINIT_THIS(iface);
745     FIXME("(%p)->(%p)\n", This, pcbSize);
746     return E_NOTIMPL;
747 }
748
749 static HRESULT WINAPI PersistStreamInit_InitNew(IPersistStreamInit *iface)
750 {
751     HTMLDocument *This = PERSTRINIT_THIS(iface);
752     FIXME("(%p)\n", This);
753     return E_NOTIMPL;
754 }
755
756 #undef PERSTRINIT_THIS
757
758 static const IPersistStreamInitVtbl PersistStreamInitVtbl = {
759     PersistStreamInit_QueryInterface,
760     PersistStreamInit_AddRef,
761     PersistStreamInit_Release,
762     PersistStreamInit_GetClassID,
763     PersistStreamInit_IsDirty,
764     PersistStreamInit_Load,
765     PersistStreamInit_Save,
766     PersistStreamInit_GetSizeMax,
767     PersistStreamInit_InitNew
768 };
769
770 void HTMLDocument_Persist_Init(HTMLDocument *This)
771 {
772     This->lpPersistMonikerVtbl = &PersistMonikerVtbl;
773     This->lpPersistFileVtbl = &PersistFileVtbl;
774     This->lpMonikerPropVtbl = &MonikerPropVtbl;
775     This->lpPersistStreamInitVtbl = &PersistStreamInitVtbl;
776
777     This->bscallback = NULL;
778     This->mon = NULL;
779     This->url = NULL;
780     This->mime = NULL;
781 }