mshtml: Added history update notification support.
[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 #define NO_SHLWAPI_REG
36 #include "shlwapi.h"
37
38 #include "wine/debug.h"
39
40 #include "mshtml_private.h"
41 #include "htmlevent.h"
42 #include "binding.h"
43 #include "resource.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
46
47 /* Undocumented notification, see tests */
48 #define CMDID_EXPLORER_UPDATEHISTORY 38
49
50 typedef struct {
51     task_t header;
52     HTMLDocumentObj *doc;
53     BOOL set_download;
54     LPOLESTR url;
55 } download_proc_task_t;
56
57 static BOOL use_gecko_script(HTMLWindow *window)
58 {
59     DWORD zone, scheme;
60     HRESULT hres;
61
62     hres = IInternetSecurityManager_MapUrlToZone(window->secmgr, window->url, &zone, 0);
63     if(FAILED(hres)) {
64         WARN("Could not map %s to zone: %08x\n", debugstr_w(window->url), hres);
65         return TRUE;
66     }
67
68     TRACE("zone %d\n", zone);
69     if(zone == URLZONE_LOCAL_MACHINE || zone == URLZONE_TRUSTED || !window->uri)
70         return FALSE;
71
72     hres = IUri_GetScheme(window->uri, &scheme);
73     return FAILED(hres) || scheme != URL_SCHEME_ABOUT;
74 }
75
76 static void notify_travellog_update(HTMLDocumentObj *doc)
77 {
78     IOleCommandTarget *cmdtrg;
79     HRESULT hres;
80
81     if(!doc->is_webbrowser)
82         return;
83
84     /* Don't notify if we were in about: page */
85     if(doc->basedoc.window->uri) {
86         DWORD scheme;
87
88         hres = IUri_GetScheme(doc->basedoc.window->uri, &scheme);
89         if(SUCCEEDED(hres) && scheme == URL_SCHEME_ABOUT)
90             return;
91     }
92
93     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&cmdtrg);
94     if(SUCCEEDED(hres)) {
95         VARIANT vin;
96
97         V_VT(&vin) = VT_I4;
98         V_I4(&vin) = 0;
99
100         IOleCommandTarget_Exec(cmdtrg, &CGID_Explorer, CMDID_EXPLORER_UPDATEHISTORY, 0, &vin, NULL);
101         IOleCommandTarget_Release(cmdtrg);
102     }
103 }
104
105 void set_current_uri(HTMLWindow *window, IUri *uri)
106 {
107     if(window->uri) {
108         IUri_Release(window->uri);
109         window->uri = NULL;
110     }
111
112     SysFreeString(window->url);
113     window->url = NULL;
114
115     if(!uri)
116         return;
117
118     IUri_AddRef(uri);
119     window->uri = uri;
120
121     IUri_GetDisplayUri(uri, &window->url);
122 }
123
124 void set_current_mon(HTMLWindow *This, IMoniker *mon)
125 {
126     IUriContainer *uri_container;
127     IUri *uri = NULL;
128     HRESULT hres;
129
130     if(This->mon) {
131         if(This->doc_obj)
132             notify_travellog_update(This->doc_obj);
133         IMoniker_Release(This->mon);
134         This->mon = NULL;
135     }
136
137     if(!mon)
138         return;
139
140     IMoniker_AddRef(mon);
141     This->mon = mon;
142
143     hres = IMoniker_QueryInterface(mon, &IID_IUriContainer, (void**)&uri_container);
144     if(SUCCEEDED(hres)) {
145         hres = IUriContainer_GetIUri(uri_container, &uri);
146         IUriContainer_Release(uri_container);
147         if(hres != S_OK) {
148             WARN("GetIUri failed: %08x\n", hres);
149             uri = NULL;
150         }
151     }
152
153     if(!uri) {
154         WCHAR *url;
155
156         hres = IMoniker_GetDisplayName(mon, NULL, NULL, &url);
157         if(SUCCEEDED(hres)) {
158             hres = CreateUri(url, 0, 0, &uri);
159             if(FAILED(hres)) {
160                 WARN("CrateUri failed: %08x\n", hres);
161                 set_current_uri(This, NULL);
162                 This->url = SysAllocString(url);
163                 CoTaskMemFree(url);
164                 return;
165             }
166             CoTaskMemFree(url);
167         }else {
168             WARN("GetDisplayName failed: %08x\n", hres);
169         }
170     }
171
172     set_current_uri(This, uri);
173     if(uri)
174         IUri_Release(uri);
175     set_script_mode(This, use_gecko_script(This) ? SCRIPTMODE_GECKO : SCRIPTMODE_ACTIVESCRIPT);
176 }
177
178 void set_download_state(HTMLDocumentObj *doc, int state)
179 {
180     if(doc->client) {
181         IOleCommandTarget *olecmd;
182         HRESULT hres;
183
184         hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
185         if(SUCCEEDED(hres)) {
186             VARIANT var;
187
188             V_VT(&var) = VT_I4;
189             V_I4(&var) = state;
190
191             IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETDOWNLOADSTATE,
192                     OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL);
193             IOleCommandTarget_Release(olecmd);
194         }
195     }
196
197     doc->download_state = state;
198 }
199
200 static void set_progress_proc(task_t *_task)
201 {
202     docobj_task_t *task = (docobj_task_t*)_task;
203     IOleCommandTarget *olecmd = NULL;
204     HTMLDocumentObj *doc = task->doc;
205     HRESULT hres;
206
207     TRACE("(%p)\n", doc);
208
209     if(doc->client)
210         IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
211
212     if(olecmd) {
213         VARIANT progress_max, progress;
214
215         V_VT(&progress_max) = VT_I4;
216         V_I4(&progress_max) = 0; /* FIXME */
217         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSMAX, OLECMDEXECOPT_DONTPROMPTUSER,
218                                &progress_max, NULL);
219
220         V_VT(&progress) = VT_I4;
221         V_I4(&progress) = 0; /* FIXME */
222         IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSPOS, OLECMDEXECOPT_DONTPROMPTUSER,
223                                &progress, NULL);
224         IOleCommandTarget_Release(olecmd);
225     }
226
227     if(doc->usermode == EDITMODE && doc->hostui) {
228         DOCHOSTUIINFO hostinfo;
229
230         memset(&hostinfo, 0, sizeof(DOCHOSTUIINFO));
231         hostinfo.cbSize = sizeof(DOCHOSTUIINFO);
232         hres = IDocHostUIHandler_GetHostInfo(doc->hostui, &hostinfo);
233         if(SUCCEEDED(hres))
234             /* FIXME: use hostinfo */
235             TRACE("hostinfo = {%u %08x %08x %s %s}\n",
236                     hostinfo.cbSize, hostinfo.dwFlags, hostinfo.dwDoubleClick,
237                     debugstr_w(hostinfo.pchHostCss), debugstr_w(hostinfo.pchHostNS));
238     }
239 }
240
241 static void set_downloading_proc(task_t *_task)
242 {
243     download_proc_task_t *task = (download_proc_task_t*)_task;
244     HTMLDocumentObj *doc = task->doc;
245     HRESULT hres;
246
247     TRACE("(%p)\n", doc);
248
249     set_statustext(doc, IDS_STATUS_DOWNLOADINGFROM, task->url);
250
251     if(task->set_download)
252         set_download_state(doc, 1);
253
254     if(!doc->client)
255         return;
256
257     if(doc->view_sink)
258         IAdviseSink_OnViewChange(doc->view_sink, DVASPECT_CONTENT, -1);
259
260     if(doc->hostui) {
261         IDropTarget *drop_target = NULL;
262
263         hres = IDocHostUIHandler_GetDropTarget(doc->hostui, NULL /* FIXME */, &drop_target);
264         if(SUCCEEDED(hres)) {
265             FIXME("Use IDropTarget\n");
266             IDropTarget_Release(drop_target);
267         }
268     }
269 }
270
271 static void set_downloading_task_destr(task_t *_task)
272 {
273     download_proc_task_t *task = (download_proc_task_t*)_task;
274
275     CoTaskMemFree(task->url);
276     heap_free(task);
277 }
278
279 void prepare_for_binding(HTMLDocument *This, IMoniker *mon, BOOL navigated_binding)
280 {
281     HRESULT hres;
282
283     if(This->doc_obj->client) {
284         VARIANT silent, offline;
285
286         hres = get_client_disp_property(This->doc_obj->client, DISPID_AMBIENT_SILENT, &silent);
287         if(SUCCEEDED(hres)) {
288             if(V_VT(&silent) != VT_BOOL)
289                 WARN("V_VT(silent) = %d\n", V_VT(&silent));
290             else if(V_BOOL(&silent))
291                 FIXME("silent == true\n");
292         }
293
294         hres = get_client_disp_property(This->doc_obj->client,
295                 DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, &offline);
296         if(SUCCEEDED(hres)) {
297             if(V_VT(&offline) != VT_BOOL)
298                 WARN("V_VT(offline) = %d\n", V_VT(&offline));
299             else if(V_BOOL(&offline))
300                 FIXME("offline == true\n");
301         }
302     }
303
304     if(This->window->mon) {
305         update_doc(This, UPDATE_TITLE|UPDATE_UI);
306     }else {
307         update_doc(This, UPDATE_TITLE);
308         set_current_mon(This->window, mon);
309     }
310
311     if(This->doc_obj->client) {
312         IOleCommandTarget *cmdtrg = NULL;
313
314         hres = IOleClientSite_QueryInterface(This->doc_obj->client, &IID_IOleCommandTarget,
315                 (void**)&cmdtrg);
316         if(SUCCEEDED(hres)) {
317             VARIANT var, out;
318
319             if(!navigated_binding) {
320                 V_VT(&var) = VT_I4;
321                 V_I4(&var) = 0;
322                 IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 37, 0, &var, NULL);
323             }else {
324                 V_VT(&var) = VT_UNKNOWN;
325                 V_UNKNOWN(&var) = (IUnknown*)&This->window->IHTMLWindow2_iface;
326                 V_VT(&out) = VT_EMPTY;
327                 hres = IOleCommandTarget_Exec(cmdtrg, &CGID_ShellDocView, 63, 0, &var, &out);
328                 if(SUCCEEDED(hres))
329                     VariantClear(&out);
330             }
331
332             IOleCommandTarget_Release(cmdtrg);
333         }
334     }
335 }
336
337 HRESULT set_moniker(HTMLDocument *This, IMoniker *mon, IBindCtx *pibc, nsChannelBSC *async_bsc, BOOL set_download)
338 {
339     download_proc_task_t *download_task;
340     nsChannelBSC *bscallback;
341     nsWineURI *nsuri;
342     LPOLESTR url;
343     HRESULT hres;
344
345     hres = IMoniker_GetDisplayName(mon, pibc, NULL, &url);
346     if(FAILED(hres)) {
347         WARN("GetDiaplayName failed: %08x\n", hres);
348         return hres;
349     }
350
351     TRACE("got url: %s\n", debugstr_w(url));
352
353     set_ready_state(This->window, READYSTATE_LOADING);
354
355     hres = create_doc_uri(This->window, url, &nsuri);
356     if(SUCCEEDED(hres)) {
357         if(async_bsc)
358             bscallback = async_bsc;
359         else
360             hres = create_channelbsc(mon, NULL, NULL, 0, &bscallback);
361     }
362
363     if(SUCCEEDED(hres)) {
364         remove_target_tasks(This->task_magic);
365         abort_document_bindings(This->doc_node);
366
367         hres = load_nsuri(This->window, nsuri, bscallback, 0/*LOAD_INITIAL_DOCUMENT_URI*/);
368         nsISupports_Release((nsISupports*)nsuri); /* FIXME */
369         if(SUCCEEDED(hres))
370             set_window_bscallback(This->window, bscallback);
371         if(bscallback != async_bsc)
372             IUnknown_Release((IUnknown*)bscallback);
373     }
374
375     if(FAILED(hres)) {
376         CoTaskMemFree(url);
377         return hres;
378     }
379
380     HTMLDocument_LockContainer(This->doc_obj, TRUE);
381
382     if(This->doc_obj->frame) {
383         docobj_task_t *task;
384
385         task = heap_alloc(sizeof(docobj_task_t));
386         task->doc = This->doc_obj;
387         push_task(&task->header, set_progress_proc, NULL, This->doc_obj->basedoc.task_magic);
388     }
389
390     download_task = heap_alloc(sizeof(download_proc_task_t));
391     download_task->doc = This->doc_obj;
392     download_task->set_download = set_download;
393     download_task->url = url;
394     push_task(&download_task->header, set_downloading_proc, set_downloading_task_destr, This->doc_obj->basedoc.task_magic);
395
396     return S_OK;
397 }
398
399 void set_ready_state(HTMLWindow *window, READYSTATE readystate)
400 {
401     window->readystate = readystate;
402
403     if(window->doc_obj && window->doc_obj->basedoc.window == window)
404         call_property_onchanged(&window->doc_obj->basedoc.cp_propnotif, DISPID_READYSTATE);
405
406     fire_event(window->doc, EVENTID_READYSTATECHANGE, FALSE, window->doc->node.nsnode, NULL);
407
408     if(window->frame_element)
409         fire_event(window->frame_element->element.node.doc, EVENTID_READYSTATECHANGE,
410                    TRUE, window->frame_element->element.node.nsnode, NULL);
411 }
412
413 static HRESULT get_doc_string(HTMLDocumentNode *This, char **str)
414 {
415     nsIDOMNode *nsnode;
416     LPCWSTR strw;
417     nsAString nsstr;
418     nsresult nsres;
419     HRESULT hres;
420
421     if(!This->nsdoc) {
422         WARN("NULL nsdoc\n");
423         return E_UNEXPECTED;
424     }
425
426     nsres = nsIDOMHTMLDocument_QueryInterface(This->nsdoc, &IID_nsIDOMNode, (void**)&nsnode);
427     if(NS_FAILED(nsres)) {
428         ERR("Could not get nsIDOMNode failed: %08x\n", nsres);
429         return E_FAIL;
430     }
431
432     nsAString_Init(&nsstr, NULL);
433     hres = nsnode_to_nsstring(nsnode, &nsstr);
434     nsIDOMNode_Release(nsnode);
435     if(FAILED(hres)) {
436         nsAString_Finish(&nsstr);
437         return hres;
438     }
439
440     nsAString_GetData(&nsstr, &strw);
441     TRACE("%s\n", debugstr_w(strw));
442
443     *str = heap_strdupWtoA(strw);
444
445     nsAString_Finish(&nsstr);
446
447     if(!*str)
448         return E_OUTOFMEMORY;
449     return S_OK;
450 }
451
452
453 /**********************************************************
454  * IPersistMoniker implementation
455  */
456
457 static inline HTMLDocument *impl_from_IPersistMoniker(IPersistMoniker *iface)
458 {
459     return CONTAINING_RECORD(iface, HTMLDocument, IPersistMoniker_iface);
460 }
461
462 static HRESULT WINAPI PersistMoniker_QueryInterface(IPersistMoniker *iface, REFIID riid, void **ppv)
463 {
464     HTMLDocument *This = impl_from_IPersistMoniker(iface);
465     return htmldoc_query_interface(This, riid, ppv);
466 }
467
468 static ULONG WINAPI PersistMoniker_AddRef(IPersistMoniker *iface)
469 {
470     HTMLDocument *This = impl_from_IPersistMoniker(iface);
471     return htmldoc_addref(This);
472 }
473
474 static ULONG WINAPI PersistMoniker_Release(IPersistMoniker *iface)
475 {
476     HTMLDocument *This = impl_from_IPersistMoniker(iface);
477     return htmldoc_release(This);
478 }
479
480 static HRESULT WINAPI PersistMoniker_GetClassID(IPersistMoniker *iface, CLSID *pClassID)
481 {
482     HTMLDocument *This = impl_from_IPersistMoniker(iface);
483     return IPersist_GetClassID(&This->IPersistFile_iface, pClassID);
484 }
485
486 static HRESULT WINAPI PersistMoniker_IsDirty(IPersistMoniker *iface)
487 {
488     HTMLDocument *This = impl_from_IPersistMoniker(iface);
489
490     TRACE("(%p)\n", This);
491
492     return IPersistStreamInit_IsDirty(&This->IPersistStreamInit_iface);
493 }
494
495 static HRESULT WINAPI PersistMoniker_Load(IPersistMoniker *iface, BOOL fFullyAvailable,
496         IMoniker *pimkName, LPBC pibc, DWORD grfMode)
497 {
498     HTMLDocument *This = impl_from_IPersistMoniker(iface);
499     HRESULT hres;
500
501     TRACE("(%p)->(%x %p %p %08x)\n", This, fFullyAvailable, pimkName, pibc, grfMode);
502
503     if(pibc) {
504         IUnknown *unk = NULL;
505
506         /* FIXME:
507          * Use params:
508          * "__PrecreatedObject"
509          * "BIND_CONTEXT_PARAM"
510          * "__HTMLLOADOPTIONS"
511          * "__DWNBINDINFO"
512          * "URL Context"
513          * "_ITransData_Object_"
514          * "_EnumFORMATETC_"
515          */
516
517         IBindCtx_GetObjectParam(pibc, (LPOLESTR)SZ_HTML_CLIENTSITE_OBJECTPARAM, &unk);
518         if(unk) {
519             IOleClientSite *client = NULL;
520
521             hres = IUnknown_QueryInterface(unk, &IID_IOleClientSite, (void**)&client);
522             if(SUCCEEDED(hres)) {
523                 TRACE("Got client site %p\n", client);
524                 IOleObject_SetClientSite(&This->IOleObject_iface, client);
525                 IOleClientSite_Release(client);
526             }
527
528             IUnknown_Release(unk);
529         }
530     }
531
532     prepare_for_binding(This, pimkName, FALSE);
533     hres = set_moniker(This, pimkName, pibc, NULL, TRUE);
534     if(FAILED(hres))
535         return hres;
536
537     return start_binding(This->window, NULL, (BSCallback*)This->window->bscallback, pibc);
538 }
539
540 static HRESULT WINAPI PersistMoniker_Save(IPersistMoniker *iface, IMoniker *pimkName,
541         LPBC pbc, BOOL fRemember)
542 {
543     HTMLDocument *This = impl_from_IPersistMoniker(iface);
544     FIXME("(%p)->(%p %p %x)\n", This, pimkName, pbc, fRemember);
545     return E_NOTIMPL;
546 }
547
548 static HRESULT WINAPI PersistMoniker_SaveCompleted(IPersistMoniker *iface, IMoniker *pimkName, LPBC pibc)
549 {
550     HTMLDocument *This = impl_from_IPersistMoniker(iface);
551     FIXME("(%p)->(%p %p)\n", This, pimkName, pibc);
552     return E_NOTIMPL;
553 }
554
555 static HRESULT WINAPI PersistMoniker_GetCurMoniker(IPersistMoniker *iface, IMoniker **ppimkName)
556 {
557     HTMLDocument *This = impl_from_IPersistMoniker(iface);
558
559     TRACE("(%p)->(%p)\n", This, ppimkName);
560
561     if(!This->window || !This->window->mon)
562         return E_UNEXPECTED;
563
564     IMoniker_AddRef(This->window->mon);
565     *ppimkName = This->window->mon;
566     return S_OK;
567 }
568
569 static const IPersistMonikerVtbl PersistMonikerVtbl = {
570     PersistMoniker_QueryInterface,
571     PersistMoniker_AddRef,
572     PersistMoniker_Release,
573     PersistMoniker_GetClassID,
574     PersistMoniker_IsDirty,
575     PersistMoniker_Load,
576     PersistMoniker_Save,
577     PersistMoniker_SaveCompleted,
578     PersistMoniker_GetCurMoniker
579 };
580
581 /**********************************************************
582  * IMonikerProp implementation
583  */
584
585 static inline HTMLDocument *impl_from_IMonikerProp(IMonikerProp *iface)
586 {
587     return CONTAINING_RECORD(iface, HTMLDocument, IMonikerProp_iface);
588 }
589
590 static HRESULT WINAPI MonikerProp_QueryInterface(IMonikerProp *iface, REFIID riid, void **ppv)
591 {
592     HTMLDocument *This = impl_from_IMonikerProp(iface);
593     return htmldoc_query_interface(This, riid, ppv);
594 }
595
596 static ULONG WINAPI MonikerProp_AddRef(IMonikerProp *iface)
597 {
598     HTMLDocument *This = impl_from_IMonikerProp(iface);
599     return htmldoc_addref(This);
600 }
601
602 static ULONG WINAPI MonikerProp_Release(IMonikerProp *iface)
603 {
604     HTMLDocument *This = impl_from_IMonikerProp(iface);
605     return htmldoc_release(This);
606 }
607
608 static HRESULT WINAPI MonikerProp_PutProperty(IMonikerProp *iface, MONIKERPROPERTY mkp, LPCWSTR val)
609 {
610     HTMLDocument *This = impl_from_IMonikerProp(iface);
611
612     TRACE("(%p)->(%d %s)\n", This, mkp, debugstr_w(val));
613
614     switch(mkp) {
615     case MIMETYPEPROP:
616         heap_free(This->doc_obj->mime);
617         This->doc_obj->mime = heap_strdupW(val);
618         break;
619
620     case CLASSIDPROP:
621         break;
622
623     default:
624         FIXME("mkp %d\n", mkp);
625         return E_NOTIMPL;
626     }
627
628     return S_OK;
629 }
630
631 static const IMonikerPropVtbl MonikerPropVtbl = {
632     MonikerProp_QueryInterface,
633     MonikerProp_AddRef,
634     MonikerProp_Release,
635     MonikerProp_PutProperty
636 };
637
638 /**********************************************************
639  * IPersistFile implementation
640  */
641
642 static inline HTMLDocument *impl_from_IPersistFile(IPersistFile *iface)
643 {
644     return CONTAINING_RECORD(iface, HTMLDocument, IPersistFile_iface);
645 }
646
647 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
648 {
649     HTMLDocument *This = impl_from_IPersistFile(iface);
650     return htmldoc_query_interface(This, riid, ppv);
651 }
652
653 static ULONG WINAPI PersistFile_AddRef(IPersistFile *iface)
654 {
655     HTMLDocument *This = impl_from_IPersistFile(iface);
656     return htmldoc_addref(This);
657 }
658
659 static ULONG WINAPI PersistFile_Release(IPersistFile *iface)
660 {
661     HTMLDocument *This = impl_from_IPersistFile(iface);
662     return htmldoc_release(This);
663 }
664
665 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *iface, CLSID *pClassID)
666 {
667     HTMLDocument *This = impl_from_IPersistFile(iface);
668
669     TRACE("(%p)->(%p)\n", This, pClassID);
670
671     if(!pClassID)
672         return E_INVALIDARG;
673
674     *pClassID = CLSID_HTMLDocument;
675     return S_OK;
676 }
677
678 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *iface)
679 {
680     HTMLDocument *This = impl_from_IPersistFile(iface);
681
682     TRACE("(%p)\n", This);
683
684     return IPersistStreamInit_IsDirty(&This->IPersistStreamInit_iface);
685 }
686
687 static HRESULT WINAPI PersistFile_Load(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
688 {
689     HTMLDocument *This = impl_from_IPersistFile(iface);
690     FIXME("(%p)->(%s %08x)\n", This, debugstr_w(pszFileName), dwMode);
691     return E_NOTIMPL;
692 }
693
694 static HRESULT WINAPI PersistFile_Save(IPersistFile *iface, LPCOLESTR pszFileName, BOOL fRemember)
695 {
696     HTMLDocument *This = impl_from_IPersistFile(iface);
697     char *str;
698     DWORD written=0;
699     HANDLE file;
700     HRESULT hres;
701
702     TRACE("(%p)->(%s %x)\n", This, debugstr_w(pszFileName), fRemember);
703
704     file = CreateFileW(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
705                        FILE_ATTRIBUTE_NORMAL, NULL);
706     if(file == INVALID_HANDLE_VALUE) {
707         WARN("Could not create file: %u\n", GetLastError());
708         return E_FAIL;
709     }
710
711     hres = get_doc_string(This->doc_node, &str);
712     if(SUCCEEDED(hres))
713         WriteFile(file, str, strlen(str), &written, NULL);
714
715     CloseHandle(file);
716     return hres;
717 }
718
719 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
720 {
721     HTMLDocument *This = impl_from_IPersistFile(iface);
722     FIXME("(%p)->(%s)\n", This, debugstr_w(pszFileName));
723     return E_NOTIMPL;
724 }
725
726 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *iface, LPOLESTR *pszFileName)
727 {
728     HTMLDocument *This = impl_from_IPersistFile(iface);
729     FIXME("(%p)->(%p)\n", This, pszFileName);
730     return E_NOTIMPL;
731 }
732
733 static const IPersistFileVtbl PersistFileVtbl = {
734     PersistFile_QueryInterface,
735     PersistFile_AddRef,
736     PersistFile_Release,
737     PersistFile_GetClassID,
738     PersistFile_IsDirty,
739     PersistFile_Load,
740     PersistFile_Save,
741     PersistFile_SaveCompleted,
742     PersistFile_GetCurFile
743 };
744
745 static inline HTMLDocument *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
746 {
747     return CONTAINING_RECORD(iface, HTMLDocument, IPersistStreamInit_iface);
748 }
749
750 static HRESULT WINAPI PersistStreamInit_QueryInterface(IPersistStreamInit *iface,
751                                                        REFIID riid, void **ppv)
752 {
753     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
754     return htmldoc_query_interface(This, riid, ppv);
755 }
756
757 static ULONG WINAPI PersistStreamInit_AddRef(IPersistStreamInit *iface)
758 {
759     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
760     return htmldoc_addref(This);
761 }
762
763 static ULONG WINAPI PersistStreamInit_Release(IPersistStreamInit *iface)
764 {
765     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
766     return htmldoc_release(This);
767 }
768
769 static HRESULT WINAPI PersistStreamInit_GetClassID(IPersistStreamInit *iface, CLSID *pClassID)
770 {
771     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
772     return IPersist_GetClassID(&This->IPersistFile_iface, pClassID);
773 }
774
775 static HRESULT WINAPI PersistStreamInit_IsDirty(IPersistStreamInit *iface)
776 {
777     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
778
779     TRACE("(%p)\n", This);
780
781     if(This->doc_obj->usermode == EDITMODE)
782         return editor_is_dirty(This);
783
784     return S_FALSE;
785 }
786
787 static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, LPSTREAM pStm)
788 {
789     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
790     IMoniker *mon;
791     HRESULT hres;
792
793     static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};
794
795     TRACE("(%p)->(%p)\n", This, pStm);
796
797     hres = CreateURLMoniker(NULL, about_blankW, &mon);
798     if(FAILED(hres)) {
799         WARN("CreateURLMoniker failed: %08x\n", hres);
800         return hres;
801     }
802
803     prepare_for_binding(This, mon, FALSE);
804     hres = set_moniker(This, mon, NULL, NULL, TRUE);
805     IMoniker_Release(mon);
806     if(FAILED(hres))
807         return hres;
808
809     return channelbsc_load_stream(This->window->bscallback, pStm);
810 }
811
812 static HRESULT WINAPI PersistStreamInit_Save(IPersistStreamInit *iface, LPSTREAM pStm,
813                                              BOOL fClearDirty)
814 {
815     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
816     char *str;
817     DWORD written=0;
818     HRESULT hres;
819
820     TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
821
822     hres = get_doc_string(This->doc_node, &str);
823     if(FAILED(hres))
824         return hres;
825
826     hres = IStream_Write(pStm, str, strlen(str), &written);
827     if(FAILED(hres))
828         FIXME("Write failed: %08x\n", hres);
829
830     heap_free(str);
831
832     if(fClearDirty)
833         set_dirty(This, VARIANT_FALSE);
834
835     return S_OK;
836 }
837
838 static HRESULT WINAPI PersistStreamInit_GetSizeMax(IPersistStreamInit *iface,
839                                                    ULARGE_INTEGER *pcbSize)
840 {
841     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
842     FIXME("(%p)->(%p)\n", This, pcbSize);
843     return E_NOTIMPL;
844 }
845
846 static HRESULT WINAPI PersistStreamInit_InitNew(IPersistStreamInit *iface)
847 {
848     HTMLDocument *This = impl_from_IPersistStreamInit(iface);
849     IMoniker *mon;
850     HRESULT hres;
851
852     static const WCHAR about_blankW[] = {'a','b','o','u','t',':','b','l','a','n','k',0};
853
854     TRACE("(%p)\n", This);
855
856     hres = CreateURLMoniker(NULL, about_blankW, &mon);
857     if(FAILED(hres)) {
858         WARN("CreateURLMoniker failed: %08x\n", hres);
859         return hres;
860     }
861
862     prepare_for_binding(This, mon, FALSE);
863     hres = set_moniker(This, mon, NULL, NULL, FALSE);
864     IMoniker_Release(mon);
865     if(FAILED(hres))
866         return hres;
867
868     return channelbsc_load_stream(This->window->bscallback, NULL);
869 }
870
871 static const IPersistStreamInitVtbl PersistStreamInitVtbl = {
872     PersistStreamInit_QueryInterface,
873     PersistStreamInit_AddRef,
874     PersistStreamInit_Release,
875     PersistStreamInit_GetClassID,
876     PersistStreamInit_IsDirty,
877     PersistStreamInit_Load,
878     PersistStreamInit_Save,
879     PersistStreamInit_GetSizeMax,
880     PersistStreamInit_InitNew
881 };
882
883 /**********************************************************
884  * IPersistHistory implementation
885  */
886
887 static inline HTMLDocument *impl_from_IPersistHistory(IPersistHistory *iface)
888 {
889     return CONTAINING_RECORD(iface, HTMLDocument, IPersistHistory_iface);
890 }
891
892 static HRESULT WINAPI PersistHistory_QueryInterface(IPersistHistory *iface, REFIID riid, void **ppv)
893 {
894     HTMLDocument *This = impl_from_IPersistHistory(iface);
895     return htmldoc_query_interface(This, riid, ppv);
896 }
897
898 static ULONG WINAPI PersistHistory_AddRef(IPersistHistory *iface)
899 {
900     HTMLDocument *This = impl_from_IPersistHistory(iface);
901     return htmldoc_addref(This);
902 }
903
904 static ULONG WINAPI PersistHistory_Release(IPersistHistory *iface)
905 {
906     HTMLDocument *This = impl_from_IPersistHistory(iface);
907     return htmldoc_release(This);
908 }
909
910 static HRESULT WINAPI PersistHistory_GetClassID(IPersistHistory *iface, CLSID *pClassID)
911 {
912     HTMLDocument *This = impl_from_IPersistHistory(iface);
913     return IPersist_GetClassID(&This->IPersistFile_iface, pClassID);
914 }
915
916 static HRESULT WINAPI PersistHistory_LoadHistory(IPersistHistory *iface, IStream *pStream, IBindCtx *pbc)
917 {
918     HTMLDocument *This = impl_from_IPersistHistory(iface);
919     FIXME("(%p)->(%p %p)\n", This, pStream, pbc);
920     return E_NOTIMPL;
921 }
922
923 static HRESULT WINAPI PersistHistory_SaveHistory(IPersistHistory *iface, IStream *pStream)
924 {
925     HTMLDocument *This = impl_from_IPersistHistory(iface);
926     FIXME("(%p)->(%p)\n", This, pStream);
927     return E_NOTIMPL;
928 }
929
930 static HRESULT WINAPI PersistHistory_SetPositionCookie(IPersistHistory *iface, DWORD dwPositioncookie)
931 {
932     HTMLDocument *This = impl_from_IPersistHistory(iface);
933     FIXME("(%p)->(%x)\n", This, dwPositioncookie);
934     return E_NOTIMPL;
935 }
936
937 static HRESULT WINAPI PersistHistory_GetPositionCookie(IPersistHistory *iface, DWORD *pdwPositioncookie)
938 {
939     HTMLDocument *This = impl_from_IPersistHistory(iface);
940     FIXME("(%p)->(%p)\n", This, pdwPositioncookie);
941     return E_NOTIMPL;
942 }
943
944 static const IPersistHistoryVtbl PersistHistoryVtbl = {
945     PersistHistory_QueryInterface,
946     PersistHistory_AddRef,
947     PersistHistory_Release,
948     PersistHistory_GetClassID,
949     PersistHistory_LoadHistory,
950     PersistHistory_SaveHistory,
951     PersistHistory_SetPositionCookie,
952     PersistHistory_GetPositionCookie
953 };
954
955 void HTMLDocument_Persist_Init(HTMLDocument *This)
956 {
957     This->IPersistMoniker_iface.lpVtbl = &PersistMonikerVtbl;
958     This->IPersistFile_iface.lpVtbl = &PersistFileVtbl;
959     This->IMonikerProp_iface.lpVtbl = &MonikerPropVtbl;
960     This->IPersistStreamInit_iface.lpVtbl = &PersistStreamInitVtbl;
961     This->IPersistHistory_iface.lpVtbl = &PersistHistoryVtbl;
962 }