Initial implementation of wxWebProtocolHandler and wxWebFileProtocolHandler for the...
[wxWidgets.git] / src / msw / webview_ie.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/webview_ie.cpp
3 // Purpose: wxMSW wxWebViewIE class implementation for web view component
4 // Author: Marianne Gagnon
5 // Id: $Id$
6 // Copyright: (c) 2010 Marianne Gagnon, Steven Lamerton
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if defined(__BORLANDC__)
14 #pragma hdrstop
15 #endif
16
17 #include "wx/msw/webview_ie.h"
18
19 #if wxUSE_WEBVIEW_IE
20
21 #include <olectl.h>
22 #include <oleidl.h>
23 #include <exdispid.h>
24 #include <exdisp.h>
25 #include <mshtml.h>
26 #include "wx/msw/registry.h"
27 #include "wx/msw/missing.h"
28 #include "wx/filesys.h"
29 #include "wx/tokenzr.h"
30
31 //We link to urlmon as it is required for CoInternetGetSession
32 #pragma comment(lib, "urlmon")
33
34 //Taken from wx/filesys.cpp
35 static wxString EscapeFileNameCharsInURL(const char *in)
36 {
37 wxString s;
38
39 for ( const unsigned char *p = (const unsigned char*)in; *p; ++p )
40 {
41 const unsigned char c = *p;
42
43 if ( c == '/' || c == '-' || c == '.' || c == '_' || c == '~' ||
44 (c >= '0' && c <= '9') ||
45 (c >= 'a' && c <= 'z') ||
46 (c >= 'A' && c <= 'Z') )
47 {
48 s << c;
49 }
50 else
51 {
52 s << wxString::Format("%%%02x", c);
53 }
54 }
55
56 return s;
57 }
58
59 wxWebFileProtocolHandler::wxWebFileProtocolHandler()
60 {
61 m_protocol = "test";
62 m_fileSystem = new wxFileSystem();
63 }
64
65 wxFSFile* wxWebFileProtocolHandler::GetFile(const wxString &uri)
66 {
67 size_t pos = uri.find('?');
68 //There is no query string so we can load the file directly
69 if(pos == wxString::npos)
70 {
71 size_t doubleslash = uri.find("//");
72 //The path is incorrectly formed without // after the first protocol
73 if(doubleslash == wxString::npos)
74 return NULL;
75
76 wxString fspath = "file:" +
77 EscapeFileNameCharsInURL(uri.substr(doubleslash + 2));
78 return m_fileSystem->OpenFile(fspath);
79 }
80 //Otherwise we have a query string of some kind that we need to extract
81 else{
82 //First we extract the query string, this should have two parameters,
83 //protocol=type and path=path
84 wxString query = uri.substr(pos + 1), protocol, path;
85 //We also trim the query off the end as we handle it alone
86 wxString lefturi = uri.substr(0, pos);
87 wxStringTokenizer tokenizer(query, ";");
88 while(tokenizer.HasMoreTokens() && (protocol == "" || path == ""))
89 {
90 wxString token = tokenizer.GetNextToken();
91 if(token.substr(0, 9) == "protocol=")
92 {
93 protocol = token.substr(9);
94 }
95 else if(token.substr(0, 5) == "path=")
96 {
97 path = token.substr(5);
98 }
99 }
100 if(protocol == "" || path == "")
101 return NULL;
102
103 //We now have the path and the protocol and so can format a correct uri
104 //to pass to wxFileSystem to get a wxFSFile
105 size_t doubleslash = uri.find("//");
106 //The path is incorrectly formed without // after the first protocol
107 if(doubleslash == wxString::npos)
108 return NULL;
109
110 wxString fspath = "file:" +
111 EscapeFileNameCharsInURL(lefturi.substr(doubleslash + 2))
112 + "#" + protocol +":" + path;
113 return m_fileSystem->OpenFile(fspath);
114 }
115 }
116
117 wxString wxWebFileProtocolHandler::CombineURIs(const wxString &baseuri,
118 const wxString &newuri)
119 {
120 //Still need to be implemented correctly
121 return newuri;
122 }
123
124 BEGIN_EVENT_TABLE(wxWebViewIE, wxControl)
125 EVT_ACTIVEX(wxID_ANY, wxWebViewIE::onActiveXEvent)
126 EVT_ERASE_BACKGROUND(wxWebViewIE::onEraseBg)
127 END_EVENT_TABLE()
128
129 bool wxWebViewIE::Create(wxWindow* parent,
130 wxWindowID id,
131 const wxString& url,
132 const wxPoint& pos,
133 const wxSize& size,
134 long style,
135 const wxString& name)
136 {
137 if (!wxControl::Create(parent, id, pos, size, style,
138 wxDefaultValidator, name))
139 {
140 return false;
141 }
142
143 m_webBrowser = NULL;
144 m_isBusy = false;
145 m_historyLoadingFromList = false;
146 m_historyEnabled = true;
147 m_historyPosition = -1;
148 m_zoomType = wxWEB_VIEW_ZOOM_TYPE_TEXT;
149
150 if (::CoCreateInstance(CLSID_WebBrowser, NULL,
151 CLSCTX_INPROC_SERVER, // CLSCTX_INPROC,
152 IID_IWebBrowser2 , (void**)&m_webBrowser) != 0)
153 {
154 wxLogError("Failed to initialize IE, CoCreateInstance returned an error");
155 return false;
156 }
157
158 m_ie.SetDispatchPtr(m_webBrowser); // wxAutomationObject will release itself
159
160 m_webBrowser->put_RegisterAsBrowser(VARIANT_TRUE);
161 m_webBrowser->put_RegisterAsDropTarget(VARIANT_TRUE);
162
163 //For testing purposes
164 RegisterProtocol(new wxWebFileProtocolHandler());
165
166 m_container = new wxActiveXContainer(this, IID_IWebBrowser2, m_webBrowser);
167
168 SetBackgroundStyle(wxBG_STYLE_PAINT);
169 SetDoubleBuffered(true);
170 LoadUrl(url);
171 return true;
172 }
173
174
175 void wxWebViewIE::LoadUrl(const wxString& url)
176 {
177 m_ie.CallMethod("Navigate", (BSTR) url.wc_str(), NULL, NULL, NULL, NULL);
178 }
179
180 void wxWebViewIE::SetPage(const wxString& html, const wxString& baseUrl)
181 {
182 BSTR bstr = SysAllocString(html.wc_str());
183
184 // Creates a new one-dimensional array
185 SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
186 if (psaStrings != NULL)
187 {
188 VARIANT *param;
189
190 HRESULT hr = SafeArrayAccessData(psaStrings, (LPVOID*)&param);
191 param->vt = VT_BSTR;
192 param->bstrVal = bstr;
193 hr = SafeArrayUnaccessData(psaStrings);
194
195 IHTMLDocument2* document = GetDocument();
196 document->write(psaStrings);
197 document->Release();
198
199 // SafeArrayDestroy calls SysFreeString for each BSTR
200 SafeArrayDestroy(psaStrings);
201
202 //We send the events when we are done to mimic webkit
203 //Navigated event
204 wxWebNavigationEvent event(wxEVT_COMMAND_WEB_VIEW_NAVIGATED,
205 GetId(), baseUrl, "", false);
206 event.SetEventObject(this);
207 HandleWindowEvent(event);
208
209 //Document complete event
210 event.SetEventType(wxEVT_COMMAND_WEB_VIEW_LOADED);
211 event.SetEventObject(this);
212 HandleWindowEvent(event);
213 }
214 else
215 {
216 wxLogError("wxWebViewIE::SetPage() : psaStrings is NULL");
217 }
218
219 }
220
221 wxString wxWebViewIE::GetPageSource()
222 {
223 IHTMLDocument2* document = GetDocument();
224 IHTMLElement *bodyTag = NULL;
225 IHTMLElement *htmlTag = NULL;
226 wxString source;
227 HRESULT hr = document->get_body(&bodyTag);
228 if(SUCCEEDED(hr))
229 {
230 hr = bodyTag->get_parentElement(&htmlTag);
231 if(SUCCEEDED(hr))
232 {
233 BSTR bstr;
234 htmlTag->get_outerHTML(&bstr);
235 source = wxString(bstr);
236 htmlTag->Release();
237 }
238 bodyTag->Release();
239 }
240
241 document->Release();
242 return source;
243 }
244
245 wxWebViewZoom wxWebViewIE::GetZoom()
246 {
247 if(m_zoomType == wxWEB_VIEW_ZOOM_TYPE_LAYOUT)
248 return GetIEOpticalZoom();
249 else if(m_zoomType == wxWEB_VIEW_ZOOM_TYPE_TEXT)
250 return GetIETextZoom();
251 else
252 wxFAIL;
253
254 //Dummy return to stop compiler warnings
255 return wxWEB_VIEW_ZOOM_MEDIUM;
256
257 }
258
259 void wxWebViewIE::SetZoom(wxWebViewZoom zoom)
260 {
261 if(m_zoomType == wxWEB_VIEW_ZOOM_TYPE_LAYOUT)
262 SetIEOpticalZoom(zoom);
263 else if(m_zoomType == wxWEB_VIEW_ZOOM_TYPE_TEXT)
264 SetIETextZoom(zoom);
265 else
266 wxFAIL;
267 }
268
269 void wxWebViewIE::SetIETextZoom(wxWebViewZoom level)
270 {
271 //We do not use OLECMDID_OPTICAL_GETZOOMRANGE as the docs say the range
272 //is 0 to 4 so the check is unnecessary, these match exactly with the
273 //enum values
274 VARIANT zoomVariant;
275 VariantInit (&zoomVariant);
276 V_VT(&zoomVariant) = VT_I4;
277 V_I4(&zoomVariant) = level;
278
279 HRESULT result = m_webBrowser->ExecWB(OLECMDID_ZOOM,
280 OLECMDEXECOPT_DONTPROMPTUSER,
281 &zoomVariant, NULL);
282 wxASSERT(result == S_OK);
283 }
284
285 wxWebViewZoom wxWebViewIE::GetIETextZoom()
286 {
287 VARIANT zoomVariant;
288 VariantInit (&zoomVariant);
289 V_VT(&zoomVariant) = VT_I4;
290
291 HRESULT result = m_webBrowser->ExecWB(OLECMDID_ZOOM,
292 OLECMDEXECOPT_DONTPROMPTUSER,
293 NULL, &zoomVariant);
294 wxASSERT(result == S_OK);
295
296 //We can safely cast here as we know that the range matches our enum
297 return static_cast<wxWebViewZoom>(V_I4(&zoomVariant));
298 }
299
300 void wxWebViewIE::SetIEOpticalZoom(wxWebViewZoom level)
301 {
302 //We do not use OLECMDID_OPTICAL_GETZOOMRANGE as the docs say the range
303 //is 10 to 1000 so the check is unnecessary
304 VARIANT zoomVariant;
305 VariantInit (&zoomVariant);
306 V_VT(&zoomVariant) = VT_I4;
307
308 //We make a somewhat arbitray map here, taken from values used by webkit
309 switch(level)
310 {
311 case wxWEB_VIEW_ZOOM_TINY:
312 V_I4(&zoomVariant) = 60;
313 break;
314 case wxWEB_VIEW_ZOOM_SMALL:
315 V_I4(&zoomVariant) = 80;
316 break;
317 case wxWEB_VIEW_ZOOM_MEDIUM:
318 V_I4(&zoomVariant) = 100;
319 break;
320 case wxWEB_VIEW_ZOOM_LARGE:
321 V_I4(&zoomVariant) = 130;
322 break;
323 case wxWEB_VIEW_ZOOM_LARGEST:
324 V_I4(&zoomVariant) = 160;
325 break;
326 default:
327 wxFAIL;
328 }
329
330 HRESULT result = m_webBrowser->ExecWB((OLECMDID)OLECMDID_OPTICAL_ZOOM,
331 OLECMDEXECOPT_DODEFAULT,
332 &zoomVariant,
333 NULL);
334 wxASSERT(result == S_OK);
335 }
336
337 wxWebViewZoom wxWebViewIE::GetIEOpticalZoom()
338 {
339 VARIANT zoomVariant;
340 VariantInit (&zoomVariant);
341 V_VT(&zoomVariant) = VT_I4;
342
343 HRESULT result = m_webBrowser->ExecWB((OLECMDID)OLECMDID_OPTICAL_ZOOM,
344 OLECMDEXECOPT_DODEFAULT, NULL,
345 &zoomVariant);
346 wxASSERT(result == S_OK);
347
348 const int zoom = V_I4(&zoomVariant);
349
350 //We make a somewhat arbitray map here, taken from values used by webkit
351 if (zoom <= 65)
352 {
353 return wxWEB_VIEW_ZOOM_TINY;
354 }
355 else if (zoom > 65 && zoom <= 90)
356 {
357 return wxWEB_VIEW_ZOOM_SMALL;
358 }
359 else if (zoom > 90 && zoom <= 115)
360 {
361 return wxWEB_VIEW_ZOOM_MEDIUM;
362 }
363 else if (zoom > 115 && zoom <= 145)
364 {
365 return wxWEB_VIEW_ZOOM_LARGE;
366 }
367 else /*if (zoom > 145) */ //Using else removes a compiler warning
368 {
369 return wxWEB_VIEW_ZOOM_LARGEST;
370 }
371 }
372
373 void wxWebViewIE::SetZoomType(wxWebViewZoomType type)
374 {
375 m_zoomType = type;
376 }
377
378 wxWebViewZoomType wxWebViewIE::GetZoomType() const
379 {
380 return m_zoomType;
381 }
382
383 bool wxWebViewIE::CanSetZoomType(wxWebViewZoomType type) const
384 {
385 //IE 6 and below only support text zoom, so check the registry to see what
386 //version we actually have
387 wxRegKey key(wxRegKey::HKLM, "Software\\Microsoft\\Internet Explorer");
388 wxString value;
389 key.QueryValue("Version", value);
390
391 long version = wxAtoi(value.Left(1));
392 if(version <= 6 && type == wxWEB_VIEW_ZOOM_TYPE_LAYOUT)
393 return false;
394 else
395 return true;
396 }
397
398 void wxWebViewIE::Print()
399 {
400 m_webBrowser->ExecWB(OLECMDID_PRINTPREVIEW,
401 OLECMDEXECOPT_DODEFAULT, NULL, NULL);
402 }
403
404 bool wxWebViewIE::CanGoBack()
405 {
406 if(m_historyEnabled)
407 return m_historyPosition > 0;
408 else
409 return false;
410 }
411
412 bool wxWebViewIE::CanGoForward()
413 {
414 if(m_historyEnabled)
415 return m_historyPosition != static_cast<int>(m_historyList.size()) - 1;
416 else
417 return false;
418 }
419
420 void wxWebViewIE::LoadHistoryItem(wxSharedPtr<wxWebHistoryItem> item)
421 {
422 int pos = -1;
423 for(unsigned int i = 0; i < m_historyList.size(); i++)
424 {
425 //We compare the actual pointers to find the correct item
426 if(m_historyList[i].get() == item.get())
427 pos = i;
428 }
429 wxASSERT_MSG(pos != static_cast<int>(m_historyList.size()),
430 "invalid history item");
431 m_historyLoadingFromList = true;
432 LoadUrl(item->GetUrl());
433 m_historyPosition = pos;
434 }
435
436 wxVector<wxSharedPtr<wxWebHistoryItem> > wxWebViewIE::GetBackwardHistory()
437 {
438 wxVector<wxSharedPtr<wxWebHistoryItem> > backhist;
439 //As we don't have std::copy or an iterator constructor in the wxwidgets
440 //native vector we construct it by hand
441 for(int i = 0; i < m_historyPosition; i++)
442 {
443 backhist.push_back(m_historyList[i]);
444 }
445 return backhist;
446 }
447
448 wxVector<wxSharedPtr<wxWebHistoryItem> > wxWebViewIE::GetForwardHistory()
449 {
450 wxVector<wxSharedPtr<wxWebHistoryItem> > forwardhist;
451 //As we don't have std::copy or an iterator constructor in the wxwidgets
452 //native vector we construct it by hand
453 for(int i = m_historyPosition + 1; i < static_cast<int>(m_historyList.size()); i++)
454 {
455 forwardhist.push_back(m_historyList[i]);
456 }
457 return forwardhist;
458 }
459
460 void wxWebViewIE::GoBack()
461 {
462 LoadHistoryItem(m_historyList[m_historyPosition - 1]);
463 }
464
465 void wxWebViewIE::GoForward()
466 {
467 LoadHistoryItem(m_historyList[m_historyPosition + 1]);
468 }
469
470 void wxWebViewIE::Stop()
471 {
472 m_ie.CallMethod("Stop");
473 }
474
475 void wxWebViewIE::ClearHistory()
476 {
477 m_historyList.clear();
478 m_historyPosition = -1;
479 }
480
481 void wxWebViewIE::EnableHistory(bool enable)
482 {
483 m_historyEnabled = enable;
484 m_historyList.clear();
485 m_historyPosition = -1;
486 }
487
488 void wxWebViewIE::Reload(wxWebViewReloadFlags flags)
489 {
490 VARIANTARG level;
491 VariantInit(&level);
492 V_VT(&level) = VT_I2;
493
494 switch(flags)
495 {
496 case wxWEB_VIEW_RELOAD_DEFAULT:
497 V_I2(&level) = REFRESH_NORMAL;
498 break;
499 case wxWEB_VIEW_RELOAD_NO_CACHE:
500 V_I2(&level) = REFRESH_COMPLETELY;
501 break;
502 default:
503 wxFAIL_MSG("Unexpected reload type");
504 }
505
506 m_webBrowser->Refresh2(&level);
507 }
508
509 bool wxWebViewIE::IsOfflineMode()
510 {
511 wxVariant out = m_ie.GetProperty("Offline");
512
513 wxASSERT(out.GetType() == "bool");
514
515 return out.GetBool();
516 }
517
518 void wxWebViewIE::SetOfflineMode(bool offline)
519 {
520 // FIXME: the wxWidgets docs do not really document what the return
521 // parameter of PutProperty is
522 const bool success = m_ie.PutProperty("Offline", (offline ?
523 VARIANT_TRUE :
524 VARIANT_FALSE));
525 wxASSERT(success);
526 }
527
528 bool wxWebViewIE::IsBusy()
529 {
530 if (m_isBusy) return true;
531
532 wxVariant out = m_ie.GetProperty("Busy");
533
534 wxASSERT(out.GetType() == "bool");
535
536 return out.GetBool();
537 }
538
539 wxString wxWebViewIE::GetCurrentURL()
540 {
541 wxVariant out = m_ie.GetProperty("LocationURL");
542
543 wxASSERT(out.GetType() == "string");
544 return out.GetString();
545 }
546
547 wxString wxWebViewIE::GetCurrentTitle()
548 {
549 IHTMLDocument2* document = GetDocument();
550 BSTR title;
551
552 document->get_nameProp(&title);
553 document->Release();
554 return wxString(title);
555 }
556
557 bool wxWebViewIE::CanCut()
558 {
559 return CanExecCommand("Cut");
560 }
561
562 bool wxWebViewIE::CanCopy()
563 {
564 return CanExecCommand("Copy");
565 }
566 bool wxWebViewIE::CanPaste()
567 {
568 return CanExecCommand("Paste");
569 }
570
571 void wxWebViewIE::Cut()
572 {
573 ExecCommand("Cut");
574 }
575
576 void wxWebViewIE::Copy()
577 {
578 ExecCommand("Copy");
579 }
580
581 void wxWebViewIE::Paste()
582 {
583 ExecCommand("Paste");
584 }
585
586 bool wxWebViewIE::CanUndo()
587 {
588 return CanExecCommand("Undo");
589 }
590 bool wxWebViewIE::CanRedo()
591 {
592 return CanExecCommand("Redo");
593 }
594
595 void wxWebViewIE::Undo()
596 {
597 ExecCommand("Undo");
598 }
599
600 void wxWebViewIE::Redo()
601 {
602 ExecCommand("Redo");
603 }
604
605 void wxWebViewIE::SetEditable(bool enable)
606 {
607 IHTMLDocument2* document = GetDocument();
608 if( enable )
609 document->put_designMode(SysAllocString(L"On"));
610 else
611 document->put_designMode(SysAllocString(L"Off"));
612
613 document->Release();
614 }
615
616 bool wxWebViewIE::IsEditable()
617 {
618 IHTMLDocument2* document = GetDocument();
619 BSTR mode;
620 document->get_designMode(&mode);
621 document->Release();
622 if(wxString(mode) == "On")
623 return true;
624 else
625 return false;
626 }
627
628 void wxWebViewIE::SelectAll()
629 {
630 ExecCommand("SelectAll");
631 }
632
633 bool wxWebViewIE::HasSelection()
634 {
635 IHTMLDocument2* document = GetDocument();
636 IHTMLSelectionObject* selection;
637 wxString sel;
638 HRESULT hr = document->get_selection(&selection);
639 if(SUCCEEDED(hr))
640 {
641 BSTR type;
642 selection->get_type(&type);
643 sel = wxString(type);
644 selection->Release();
645 }
646 document->Release();
647 return sel != "None";
648 }
649
650 void wxWebViewIE::DeleteSelection()
651 {
652 ExecCommand("Delete");
653 }
654
655 wxString wxWebViewIE::GetSelectedText()
656 {
657 IHTMLDocument2* document = GetDocument();
658 IHTMLSelectionObject* selection;
659 wxString selected;
660 HRESULT hr = document->get_selection(&selection);
661 if(SUCCEEDED(hr))
662 {
663 IDispatch* disrange;
664 hr = selection->createRange(&disrange);
665 if(SUCCEEDED(hr))
666 {
667 IHTMLTxtRange* range;
668 hr = disrange->QueryInterface(IID_IHTMLTxtRange, (void**)&range);
669 if(SUCCEEDED(hr))
670 {
671 BSTR text;
672 range->get_text(&text);
673 selected = wxString(text);
674 range->Release();
675 }
676 disrange->Release();
677 }
678 selection->Release();
679 }
680 document->Release();
681 return selected;
682 }
683
684 wxString wxWebViewIE::GetSelectedSource()
685 {
686 IHTMLDocument2* document = GetDocument();
687 IHTMLSelectionObject* selection;
688 wxString selected;
689 HRESULT hr = document->get_selection(&selection);
690 if(SUCCEEDED(hr))
691 {
692 IDispatch* disrange;
693 hr = selection->createRange(&disrange);
694 if(SUCCEEDED(hr))
695 {
696 IHTMLTxtRange* range;
697 hr = disrange->QueryInterface(IID_IHTMLTxtRange, (void**)&range);
698 if(SUCCEEDED(hr))
699 {
700 BSTR text;
701 range->get_htmlText(&text);
702 selected = wxString(text);
703 range->Release();
704 }
705 disrange->Release();
706 }
707 selection->Release();
708 }
709 document->Release();
710 return selected;
711 }
712
713 void wxWebViewIE::ClearSelection()
714 {
715 IHTMLDocument2* document = GetDocument();
716 IHTMLSelectionObject* selection;
717 wxString selected;
718 HRESULT hr = document->get_selection(&selection);
719 if(SUCCEEDED(hr))
720 {
721 selection->empty();
722 selection->Release();
723 }
724 document->Release();
725 }
726
727 wxString wxWebViewIE::GetPageText()
728 {
729 IHTMLDocument2* document = GetDocument();
730 wxString text;
731 IHTMLElement* body;
732 HRESULT hr = document->get_body(&body);
733 if(SUCCEEDED(hr))
734 {
735 BSTR out;
736 body->get_innerText(&out);
737 text = wxString(out);
738 body->Release();
739 }
740 document->Release();
741 return text;
742 }
743
744 void wxWebViewIE::RunScript(const wxString& javascript)
745 {
746 IHTMLDocument2* document = GetDocument();
747 IHTMLWindow2* window;
748 wxString language = "javascript";
749 HRESULT hr = document->get_parentWindow(&window);
750 if(SUCCEEDED(hr))
751 {
752 VARIANT level;
753 VariantInit(&level);
754 V_VT(&level) = VT_EMPTY;
755 window->execScript(SysAllocString(javascript), SysAllocString(language), &level);
756 }
757 document->Release();
758 }
759
760 void wxWebViewIE::RegisterProtocol(wxWebProtocolHandler* handler)
761 {
762 ClassFactory* cf = new ClassFactory(handler);
763 IInternetSession* session;
764 if(FAILED(CoInternetGetSession(0, &session, 0)))
765 {
766 wxFAIL_MSG("Could not retrive internet session");
767 }
768
769 HRESULT hr = session->RegisterNameSpace(cf, CLSID_FileProtocol, handler->GetProtocol(), 0, NULL, 0);
770 if(FAILED(hr))
771 {
772 wxFAIL_MSG("Could not register protocol");
773 }
774 }
775
776 bool wxWebViewIE::CanExecCommand(wxString command)
777 {
778 IHTMLDocument2* document = GetDocument();
779 VARIANT_BOOL enabled;
780
781 document->queryCommandEnabled(SysAllocString(command.wc_str()), &enabled);
782 document->Release();
783
784 return (enabled == VARIANT_TRUE);
785 }
786
787 void wxWebViewIE::ExecCommand(wxString command)
788 {
789 IHTMLDocument2* document = GetDocument();
790 document->execCommand(SysAllocString(command.wc_str()), VARIANT_FALSE, VARIANT(), NULL);
791 document->Release();
792 }
793
794 IHTMLDocument2* wxWebViewIE::GetDocument()
795 {
796 wxVariant variant = m_ie.GetProperty("Document");
797 IHTMLDocument2* document = (IHTMLDocument2*)variant.GetVoidPtr();
798
799 wxASSERT(document);
800
801 return document;
802 }
803
804 void wxWebViewIE::onActiveXEvent(wxActiveXEvent& evt)
805 {
806 if (m_webBrowser == NULL) return;
807
808 switch (evt.GetDispatchId())
809 {
810 case DISPID_BEFORENAVIGATE2:
811 {
812 m_isBusy = true;
813
814 wxString url = evt[1].GetString();
815 wxString target = evt[3].GetString();
816
817 wxWebNavigationEvent event(wxEVT_COMMAND_WEB_VIEW_NAVIGATING,
818 GetId(), url, target, true);
819 event.SetEventObject(this);
820 HandleWindowEvent(event);
821
822 if (event.IsVetoed())
823 {
824 wxActiveXEventNativeMSW* nativeParams =
825 evt.GetNativeParameters();
826 *V_BOOLREF(&nativeParams->pDispParams->rgvarg[0]) = VARIANT_TRUE;
827 }
828
829 // at this point, either the navigation event has been cancelled
830 // and we're not busy, either it was accepted and IWebBrowser2's
831 // Busy property will be true; so we don't need our override
832 // flag anymore.
833 m_isBusy = false;
834
835 break;
836 }
837
838 case DISPID_NAVIGATECOMPLETE2:
839 {
840 wxString url = evt[1].GetString();
841 // TODO: set target parameter if possible
842 wxString target = wxEmptyString;
843 wxWebNavigationEvent event(wxEVT_COMMAND_WEB_VIEW_NAVIGATED,
844 GetId(), url, target, false);
845 event.SetEventObject(this);
846 HandleWindowEvent(event);
847 break;
848 }
849
850 case DISPID_PROGRESSCHANGE:
851 {
852 // download progress
853 break;
854 }
855
856 case DISPID_DOCUMENTCOMPLETE:
857 {
858 //Only send a complete even if we are actually finished, this brings
859 //the event in to line with webkit
860 READYSTATE rs;
861 m_webBrowser->get_ReadyState( &rs );
862 if(rs != READYSTATE_COMPLETE)
863 break;
864
865 wxString url = evt[1].GetString();
866
867 //As we are complete we also add to the history list, but not if the
868 //page is not the main page, ie it is a subframe
869 if(m_historyEnabled && !m_historyLoadingFromList && url == GetCurrentURL())
870 {
871 //If we are not at the end of the list, then erase everything
872 //between us and the end before adding the new page
873 if(m_historyPosition != static_cast<int>(m_historyList.size()) - 1)
874 {
875 m_historyList.erase(m_historyList.begin() + m_historyPosition + 1,
876 m_historyList.end());
877 }
878 wxSharedPtr<wxWebHistoryItem> item(new wxWebHistoryItem(url, GetCurrentTitle()));
879 m_historyList.push_back(item);
880 m_historyPosition++;
881 }
882 //Reset as we are done now
883 m_historyLoadingFromList = false;
884 // TODO: set target parameter if possible
885 wxString target = wxEmptyString;
886 wxWebNavigationEvent event(wxEVT_COMMAND_WEB_VIEW_LOADED, GetId(),
887 url, target, false);
888 event.SetEventObject(this);
889 HandleWindowEvent(event);
890 break;
891 }
892
893 case DISPID_STATUSTEXTCHANGE:
894 {
895 break;
896 }
897
898 case DISPID_TITLECHANGE:
899 {
900 break;
901 }
902
903 case DISPID_NAVIGATEERROR:
904 {
905 wxWebNavigationError errorType = wxWEB_NAV_ERR_OTHER;
906 wxString errorCode = "?";
907 switch (evt[3].GetLong())
908 {
909 case INET_E_INVALID_URL: // (0x800C0002L or -2146697214)
910 errorCode = "INET_E_INVALID_URL";
911 errorType = wxWEB_NAV_ERR_REQUEST;
912 break;
913 case INET_E_NO_SESSION: // (0x800C0003L or -2146697213)
914 errorCode = "INET_E_NO_SESSION";
915 errorType = wxWEB_NAV_ERR_CONNECTION;
916 break;
917 case INET_E_CANNOT_CONNECT: // (0x800C0004L or -2146697212)
918 errorCode = "INET_E_CANNOT_CONNECT";
919 errorType = wxWEB_NAV_ERR_CONNECTION;
920 break;
921 case INET_E_RESOURCE_NOT_FOUND: // (0x800C0005L or -2146697211)
922 errorCode = "INET_E_RESOURCE_NOT_FOUND";
923 errorType = wxWEB_NAV_ERR_NOT_FOUND;
924 break;
925 case INET_E_OBJECT_NOT_FOUND: // (0x800C0006L or -2146697210)
926 errorCode = "INET_E_OBJECT_NOT_FOUND";
927 errorType = wxWEB_NAV_ERR_NOT_FOUND;
928 break;
929 case INET_E_DATA_NOT_AVAILABLE: // (0x800C0007L or -2146697209)
930 errorCode = "INET_E_DATA_NOT_AVAILABLE";
931 errorType = wxWEB_NAV_ERR_NOT_FOUND;
932 break;
933 case INET_E_DOWNLOAD_FAILURE: // (0x800C0008L or -2146697208)
934 errorCode = "INET_E_DOWNLOAD_FAILURE";
935 errorType = wxWEB_NAV_ERR_CONNECTION;
936 break;
937 case INET_E_AUTHENTICATION_REQUIRED: // (0x800C0009L or -2146697207)
938 errorCode = "INET_E_AUTHENTICATION_REQUIRED";
939 errorType = wxWEB_NAV_ERR_AUTH;
940 break;
941 case INET_E_NO_VALID_MEDIA: // (0x800C000AL or -2146697206)
942 errorCode = "INET_E_NO_VALID_MEDIA";
943 errorType = wxWEB_NAV_ERR_REQUEST;
944 break;
945 case INET_E_CONNECTION_TIMEOUT: // (0x800C000BL or -2146697205)
946 errorCode = "INET_E_CONNECTION_TIMEOUT";
947 errorType = wxWEB_NAV_ERR_CONNECTION;
948 break;
949 case INET_E_INVALID_REQUEST: // (0x800C000CL or -2146697204)
950 errorCode = "INET_E_INVALID_REQUEST";
951 errorType = wxWEB_NAV_ERR_REQUEST;
952 break;
953 case INET_E_UNKNOWN_PROTOCOL: // (0x800C000DL or -2146697203)
954 errorCode = "INET_E_UNKNOWN_PROTOCOL";
955 errorType = wxWEB_NAV_ERR_REQUEST;
956 break;
957 case INET_E_SECURITY_PROBLEM: // (0x800C000EL or -2146697202)
958 errorCode = "INET_E_SECURITY_PROBLEM";
959 errorType = wxWEB_NAV_ERR_SECURITY;
960 break;
961 case INET_E_CANNOT_LOAD_DATA: // (0x800C000FL or -2146697201)
962 errorCode = "INET_E_CANNOT_LOAD_DATA";
963 errorType = wxWEB_NAV_ERR_OTHER;
964 break;
965 case INET_E_CANNOT_INSTANTIATE_OBJECT:
966 // CoCreateInstance will return an error code if this happens,
967 // we'll handle this above.
968 return;
969 break;
970 case INET_E_REDIRECT_FAILED: // (0x800C0014L or -2146697196)
971 errorCode = "INET_E_REDIRECT_FAILED";
972 errorType = wxWEB_NAV_ERR_OTHER;
973 break;
974 case INET_E_REDIRECT_TO_DIR: // (0x800C0015L or -2146697195)
975 errorCode = "INET_E_REDIRECT_TO_DIR";
976 errorType = wxWEB_NAV_ERR_REQUEST;
977 break;
978 case INET_E_CANNOT_LOCK_REQUEST: // (0x800C0016L or -2146697194)
979 errorCode = "INET_E_CANNOT_LOCK_REQUEST";
980 errorType = wxWEB_NAV_ERR_OTHER;
981 break;
982 case INET_E_USE_EXTEND_BINDING: // (0x800C0017L or -2146697193)
983 errorCode = "INET_E_USE_EXTEND_BINDING";
984 errorType = wxWEB_NAV_ERR_OTHER;
985 break;
986 case INET_E_TERMINATED_BIND: // (0x800C0018L or -2146697192)
987 errorCode = "INET_E_TERMINATED_BIND";
988 errorType = wxWEB_NAV_ERR_OTHER;
989 break;
990 case INET_E_INVALID_CERTIFICATE: // (0x800C0019L or -2146697191)
991 errorCode = "INET_E_INVALID_CERTIFICATE";
992 errorType = wxWEB_NAV_ERR_CERTIFICATE;
993 break;
994 case INET_E_CODE_DOWNLOAD_DECLINED: // (0x800C0100L or -2146696960)
995 errorCode = "INET_E_CODE_DOWNLOAD_DECLINED";
996 errorType = wxWEB_NAV_ERR_USER_CANCELLED;
997 break;
998 case INET_E_RESULT_DISPATCHED: // (0x800C0200L or -2146696704)
999 // cancel request cancelled...
1000 errorCode = "INET_E_RESULT_DISPATCHED";
1001 errorType = wxWEB_NAV_ERR_OTHER;
1002 break;
1003 case INET_E_CANNOT_REPLACE_SFP_FILE: // (0x800C0300L or -2146696448)
1004 errorCode = "INET_E_CANNOT_REPLACE_SFP_FILE";
1005 errorType = wxWEB_NAV_ERR_SECURITY;
1006 break;
1007 case INET_E_CODE_INSTALL_BLOCKED_BY_HASH_POLICY:
1008 errorCode = "INET_E_CODE_INSTALL_BLOCKED_BY_HASH_POLICY";
1009 errorType = wxWEB_NAV_ERR_SECURITY;
1010 break;
1011 case INET_E_CODE_INSTALL_SUPPRESSED:
1012 errorCode = "INET_E_CODE_INSTALL_SUPPRESSED";
1013 errorType = wxWEB_NAV_ERR_SECURITY;
1014 break;
1015 }
1016
1017 wxString url = evt[1].GetString();
1018 wxString target = evt[2].GetString();
1019 wxWebNavigationEvent event(wxEVT_COMMAND_WEB_VIEW_ERROR, GetId(),
1020 url, target, false);
1021 event.SetEventObject(this);
1022 event.SetInt(errorType);
1023 event.SetString(errorCode);
1024 HandleWindowEvent(event);
1025 break;
1026 }
1027 case DISPID_NEWWINDOW3:
1028 {
1029 wxString url = evt[4].GetString();
1030
1031 wxWebNavigationEvent event(wxEVT_COMMAND_WEB_VIEW_NEWWINDOW,
1032 GetId(), url, wxEmptyString, true);
1033 event.SetEventObject(this);
1034 HandleWindowEvent(event);
1035
1036 //We always cancel this event otherwise an Internet Exporer window
1037 //is opened for the url
1038 wxActiveXEventNativeMSW* nativeParams = evt.GetNativeParameters();
1039 *V_BOOLREF(&nativeParams->pDispParams->rgvarg[3]) = VARIANT_TRUE;
1040 break;
1041 }
1042 }
1043
1044 evt.Skip();
1045 }
1046
1047 VirtualProtocol::VirtualProtocol(wxWebProtocolHandler *handler)
1048 {
1049 m_refCount = 0;
1050 m_file = NULL;
1051 m_handler = handler;
1052 }
1053
1054 VirtualProtocol::~VirtualProtocol()
1055 {
1056 }
1057
1058 ULONG VirtualProtocol::AddRef()
1059 {
1060 m_refCount++;
1061 return m_refCount;
1062 }
1063
1064 HRESULT VirtualProtocol::QueryInterface(REFIID riid, void **ppvObject)
1065 {
1066 if(riid == IID_IUnknown || riid == IID_IInternetProtocolRoot ||
1067 riid == IID_IInternetProtocol)
1068 {
1069 *ppvObject = (IInternetProtocol*)this;
1070 AddRef();
1071 return S_OK;
1072 }
1073 else if(riid == IID_IInternetProtocolInfo)
1074 {
1075 *ppvObject = (IInternetProtocolInfo*)this;
1076 AddRef();
1077 return S_OK;
1078 }
1079 else
1080 {
1081 *ppvObject = NULL;
1082 return E_POINTER;
1083 }
1084 }
1085
1086 ULONG VirtualProtocol::Release()
1087 {
1088 m_refCount--;
1089 if (m_refCount > 0)
1090 {
1091 return m_refCount;
1092 }
1093 else
1094 {
1095 delete this;
1096 return 0;
1097 }
1098 }
1099
1100 HRESULT VirtualProtocol::Start(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink,
1101 IInternetBindInfo *pOIBindInfo, DWORD grfPI,
1102 HANDLE_PTR dwReserved)
1103 {
1104 wxUnusedVar(szUrl);
1105 wxUnusedVar(pOIBindInfo);
1106 wxUnusedVar(grfPI);
1107 wxUnusedVar(dwReserved);
1108 m_protocolSink = pOIProtSink;
1109
1110 //We get the file itself from the protocol handler
1111 m_file = m_handler->GetFile(szUrl);
1112
1113
1114 if(!m_file)
1115 return INET_E_RESOURCE_NOT_FOUND;
1116
1117 //We return the stream length for current and total size as we can always
1118 //read the whole file from the stream
1119 wxFileOffset length = m_file->GetStream()->GetLength();
1120 m_protocolSink->ReportData(BSCF_FIRSTDATANOTIFICATION |
1121 BSCF_DATAFULLYAVAILABLE |
1122 BSCF_LASTDATANOTIFICATION,
1123 length, length);
1124 return S_OK;
1125 }
1126
1127 HRESULT VirtualProtocol::Read(void *pv, ULONG cb, ULONG *pcbRead)
1128 {
1129 //If the file is null we return false to indicte it is finished
1130 if(!m_file)
1131 return S_FALSE;
1132
1133 wxStreamError err = m_file->GetStream()->Read(pv, cb).GetLastError();
1134 *pcbRead = m_file->GetStream()->LastRead();
1135
1136 if(err == wxSTREAM_NO_ERROR)
1137 {
1138 if(*pcbRead < cb)
1139 {
1140 wxDELETE(m_file);
1141 m_protocolSink->ReportResult(S_OK, 0, NULL);
1142 }
1143 //As we are not eof there is more data
1144 return S_OK;
1145 }
1146 else if(err == wxSTREAM_EOF)
1147 {
1148 wxDELETE(m_file);
1149 m_protocolSink->ReportResult(S_OK, 0, NULL);
1150 //We are eof and so finished
1151 return S_OK;
1152 }
1153 else if(err == wxSTREAM_READ_ERROR)
1154 {
1155 wxDELETE(m_file);
1156 return INET_E_DOWNLOAD_FAILURE;
1157 }
1158 else
1159 {
1160 //Dummy return to surpress a compiler warning
1161 wxFAIL;
1162 return INET_E_DOWNLOAD_FAILURE;
1163 }
1164 }
1165
1166 HRESULT VirtualProtocol::CombineUrl(LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl,
1167 DWORD dwCombineFlags, LPWSTR pwzResult,
1168 DWORD cchResult, DWORD *pcchResult,
1169 DWORD dwReserved)
1170 {
1171 return INET_E_DEFAULT_ACTION;
1172 }
1173
1174 HRESULT VirtualProtocol::ParseUrl(LPCWSTR pwzUrl, PARSEACTION ParseAction,
1175 DWORD dwParseFlags, LPWSTR pwzResult,
1176 DWORD cchResult, DWORD *pcchResult,
1177 DWORD dwReserved)
1178 {
1179 //return INET_E_DEFAULT_ACTION;
1180 wcscpy(pwzResult, pwzUrl);
1181 *pcchResult = wcslen(pwzResult);
1182 return S_OK;
1183 }
1184
1185 HRESULT VirtualProtocol::QueryInfo(LPCWSTR pwzUrl, QUERYOPTION OueryOption,
1186 DWORD dwQueryFlags, LPVOID pBuffer,
1187 DWORD cbBuffer, DWORD *pcbBuf,
1188 DWORD dwReserved)
1189 {
1190 return INET_E_DEFAULT_ACTION;
1191 }
1192
1193 HRESULT ClassFactory::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
1194 void ** ppvObject)
1195 {
1196 if (pUnkOuter)
1197 return CLASS_E_NOAGGREGATION;
1198 VirtualProtocol* vp = new VirtualProtocol(m_handler);
1199 vp->AddRef();
1200 HRESULT hr = vp->QueryInterface(riid, ppvObject);
1201 vp->Release();
1202 return hr;
1203
1204 }
1205
1206 STDMETHODIMP ClassFactory::LockServer(BOOL fLock)
1207 {
1208 wxUnusedVar(fLock);
1209 return S_OK;
1210 }
1211
1212 ULONG ClassFactory::AddRef(void)
1213 {
1214 m_refCount++;
1215 return m_refCount;
1216 }
1217
1218 HRESULT ClassFactory::QueryInterface(REFIID riid, void **ppvObject)
1219 {
1220 if ((riid == IID_IUnknown) || (riid == IID_IClassFactory))
1221 {
1222 *ppvObject = this;
1223 AddRef();
1224 return S_OK;
1225 }
1226 else
1227 {
1228 *ppvObject = NULL;
1229 return E_POINTER;
1230 }
1231
1232 }
1233
1234 ULONG ClassFactory::Release(void)
1235 {
1236 m_refCount--;
1237 if (m_refCount > 0)
1238 {
1239 return m_refCount;
1240 }
1241 else
1242 {
1243 delete this;
1244 return 0;
1245 }
1246
1247 }
1248
1249 #endif