Add new wxEVT_COMMAND_WEB_VIEW_TITLE_CHANGED event. Implement for all backends, exten...
[wxWidgets.git] / src / common / webview.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: webview.cpp
3 // Purpose: Common interface and events for web view component
4 // Author: Marianne Gagnon
5 // Id: $Id$
6 // Copyright: (c) 2010 Marianne Gagnon, 2011 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 wxUSE_WEB
14
15 #if defined(__BORLANDC__)
16 #pragma hdrstop
17 #endif
18
19 #include "wx/webview.h"
20
21 #include "wx/osx/webview_webkit.h"
22 #include "wx/gtk/webview_webkit.h"
23 #include "wx/msw/webview_ie.h"
24 #include "wx/filesys.h"
25 #include "wx/tokenzr.h"
26
27 // DLL options compatibility check:
28 #include "wx/app.h"
29 WX_CHECK_BUILD_OPTIONS("wxWEB")
30
31 extern WXDLLIMPEXP_DATA_WEB(const char) wxWebViewNameStr[] = "wxWebView";
32 extern WXDLLIMPEXP_DATA_WEB(const char) wxWebViewDefaultURLStr[] = "about:blank";
33
34 IMPLEMENT_DYNAMIC_CLASS(wxWebNavigationEvent, wxCommandEvent)
35
36 wxDEFINE_EVENT( wxEVT_COMMAND_WEB_VIEW_NAVIGATING, wxWebNavigationEvent );
37 wxDEFINE_EVENT( wxEVT_COMMAND_WEB_VIEW_NAVIGATED, wxWebNavigationEvent );
38 wxDEFINE_EVENT( wxEVT_COMMAND_WEB_VIEW_LOADED, wxWebNavigationEvent );
39 wxDEFINE_EVENT( wxEVT_COMMAND_WEB_VIEW_ERROR, wxWebNavigationEvent );
40 wxDEFINE_EVENT( wxEVT_COMMAND_WEB_VIEW_NEWWINDOW, wxWebNavigationEvent );
41 wxDEFINE_EVENT( wxEVT_COMMAND_WEB_VIEW_TITLE_CHANGED, wxWebNavigationEvent );
42
43 //Taken from wx/filesys.cpp
44 static wxString EscapeFileNameCharsInURL(const char *in)
45 {
46 wxString s;
47
48 for ( const unsigned char *p = (const unsigned char*)in; *p; ++p )
49 {
50 const unsigned char c = *p;
51
52 if ( c == '/' || c == '-' || c == '.' || c == '_' || c == '~' ||
53 (c >= '0' && c <= '9') ||
54 (c >= 'a' && c <= 'z') ||
55 (c >= 'A' && c <= 'Z') )
56 {
57 s << c;
58 }
59 else
60 {
61 s << wxString::Format("%%%02x", c);
62 }
63 }
64
65 return s;
66 }
67
68 wxWebFileProtocolHandler::wxWebFileProtocolHandler()
69 {
70 m_protocol = "test";
71 m_fileSystem = new wxFileSystem();
72 }
73
74 wxFSFile* wxWebFileProtocolHandler::GetFile(const wxString &uri)
75 {
76 size_t pos = uri.find('?');
77 //There is no query string so we can load the file directly
78 if(pos == wxString::npos)
79 {
80 size_t doubleslash = uri.find("//");
81 //The path is incorrectly formed without // after the first protocol
82 if(doubleslash == wxString::npos)
83 return NULL;
84
85 wxString fspath = "file:" +
86 EscapeFileNameCharsInURL(uri.substr(doubleslash + 2));
87 return m_fileSystem->OpenFile(fspath);
88 }
89 //Otherwise we have a query string of some kind that we need to extract
90 else{
91 //First we extract the query string, this should have two parameters,
92 //protocol=type and path=path
93 wxString query = uri.substr(pos + 1), protocol, path;
94 //We also trim the query off the end as we handle it alone
95 wxString lefturi = uri.substr(0, pos);
96 wxStringTokenizer tokenizer(query, ";");
97 while(tokenizer.HasMoreTokens() && (protocol == "" || path == ""))
98 {
99 wxString token = tokenizer.GetNextToken();
100 if(token.substr(0, 9) == "protocol=")
101 {
102 protocol = token.substr(9);
103 }
104 else if(token.substr(0, 5) == "path=")
105 {
106 path = token.substr(5);
107 }
108 }
109 if(protocol == "" || path == "")
110 return NULL;
111
112 //We now have the path and the protocol and so can format a correct uri
113 //to pass to wxFileSystem to get a wxFSFile
114 size_t doubleslash = uri.find("//");
115 //The path is incorrectly formed without // after the first protocol
116 if(doubleslash == wxString::npos)
117 return NULL;
118
119 wxString fspath = "file:" +
120 EscapeFileNameCharsInURL(lefturi.substr(doubleslash + 2))
121 + "#" + protocol +":" + path;
122 return m_fileSystem->OpenFile(fspath);
123 }
124 }
125
126 wxString wxWebFileProtocolHandler::CombineURIs(const wxString &baseuri,
127 const wxString &newuri)
128 {
129 //If there is a colon in the path then we just return it
130 if(newuri.find(':') != wxString::npos)
131 {
132 return newuri;
133 }
134 //We have an absolute path and no query string
135 else if(newuri.substr(0, 1) == "/" && baseuri.find('?') == wxString::npos)
136 {
137 //By finding the next / after file:// we get to the end of the
138 //(optional) hostname
139 size_t pos = baseuri.find('/', 7);
140 //So we return up to the end of the hostname, plus the newuri
141 return baseuri.substr(0, pos) + newuri;
142 }
143 //We have an absolute path and a query string
144 else if(newuri.substr(0, 1) == "/" && baseuri.find('?') != wxString::npos)
145 {
146 wxString query = baseuri.substr(baseuri.find('?') + 1);
147 wxString newquery;
148 wxStringTokenizer tokenizer(query, ";");
149 while(tokenizer.HasMoreTokens())
150 {
151 wxString token = tokenizer.GetNextToken();
152 if(token.substr(0, 5) == "path=")
153 {
154 //As the path is absolue simply replace the old path with the
155 //new one
156 newquery = newquery + "path=" + newuri;
157 }
158 else
159 {
160 newquery += token;
161 }
162 //We need to add the separators back
163 if(tokenizer.HasMoreTokens())
164 newquery += ';';
165 }
166 return baseuri.substr(0, baseuri.find('?')) + "?" + newquery;
167 }
168 //We have a relative path and no query string
169 else if(baseuri.find('?') == wxString::npos)
170 {
171 //By finding the next / after file:// we get to the end of the
172 //(optional) hostname
173 size_t pos = baseuri.find('/', 7);
174 wxString path = baseuri.substr(pos);
175 //Then we remove the last filename
176 path = path.BeforeLast('/') + '/';
177 //Ensure that we have the leading / so we can normalise properly
178 if(path.substr(0, 1) != "/")
179 path = "/" + path;
180
181 //If we have a colon in the path (i.e. we are on windows) we need to
182 //handle it specially
183 if(path.find(':') != wxString::npos)
184 {
185 wxFileName fn(path.AfterFirst('/').AfterFirst('/') + newuri);
186 fn.Normalize(wxPATH_NORM_DOTS, "", wxPATH_UNIX);
187 return baseuri.substr(0, pos) + '/' +
188 path.AfterFirst('/').BeforeFirst('/') + '/' +
189 fn.GetFullPath(wxPATH_UNIX);
190 }
191 else
192 {
193 //We can now use wxFileName to perform the normalisation
194 wxFileName fn(path + newuri);
195 fn.Normalize(wxPATH_NORM_DOTS, "", wxPATH_UNIX);
196 return baseuri.substr(0, pos) + fn.GetFullPath(wxPATH_UNIX);
197 }
198 }
199 //We have a relative path and a query string
200 else
201 {
202 wxString query = baseuri.substr(baseuri.find('?') + 1);
203 wxString newquery;
204 wxStringTokenizer tokenizer(query, ";");
205 while(tokenizer.HasMoreTokens())
206 {
207 wxString token = tokenizer.GetNextToken();
208 if(token.substr(0, 5) == "path=")
209 {
210 wxString path = token.substr(6);
211 //Then we remove the last filename
212 path = path.BeforeLast('/') + '/';
213 //Ensure that we have the leading / so we can normalise properly
214 //if(path.substr(0, 1) != "/")
215 // path = "/" + path;
216
217 //We can now use wxFileName to perform the normalisation
218 wxFileName fn(path + newuri);
219 fn.Normalize(wxPATH_NORM_DOTS, "", wxPATH_UNIX);
220 newquery = newquery + "path=" + fn.GetFullPath(wxPATH_UNIX);
221 }
222 else
223 {
224 newquery += token;
225 }
226 //We need to add the separators back
227 if(tokenizer.HasMoreTokens())
228 newquery += ';';
229 }
230 return baseuri.substr(0, baseuri.find('?')) + "?" + newquery;
231 }
232 }
233
234 // static
235 wxWebView* wxWebView::New(wxWebViewBackend backend)
236 {
237 switch (backend)
238 {
239 #if defined(wxUSE_WEBVIEW_WEBKIT) && \
240 (defined(__WXGTK__) || defined(__WXOSX__))
241 case wxWEB_VIEW_BACKEND_WEBKIT:
242 return new wxWebViewWebKit();
243 #endif
244
245 #if wxUSE_WEBVIEW_IE
246 case wxWEB_VIEW_BACKEND_IE:
247 return new wxWebViewIE();
248 #endif
249
250 case wxWEB_VIEW_BACKEND_DEFAULT:
251
252 #if defined(wxUSE_WEBVIEW_WEBKIT) && \
253 (defined(__WXGTK__) || defined(__WXOSX__))
254 return new wxWebViewWebKit();
255 #endif
256
257 #if wxUSE_WEBVIEW_IE
258 return new wxWebViewIE();
259 #endif
260
261 // fall-through intended
262 default:
263 return NULL;
264 }
265 }
266
267 // static
268 wxWebView* wxWebView::New(wxWindow* parent,
269 wxWindowID id,
270 const wxString& url,
271 const wxPoint& pos,
272 const wxSize& size,
273 wxWebViewBackend backend,
274 long style,
275 const wxString& name)
276 {
277 switch (backend)
278 {
279 #if defined(wxUSE_WEBVIEW_WEBKIT) && \
280 (defined(__WXGTK__) || defined(__WXOSX__))
281 case wxWEB_VIEW_BACKEND_WEBKIT:
282 return new wxWebViewWebKit(parent, id, url, pos, size, style, name);
283 #endif
284
285 #if wxUSE_WEBVIEW_IE
286 case wxWEB_VIEW_BACKEND_IE:
287 return new wxWebViewIE(parent, id, url, pos, size, style, name);
288 #endif
289
290 case wxWEB_VIEW_BACKEND_DEFAULT:
291
292 #if defined(wxUSE_WEBVIEW_WEBKIT) && \
293 (defined(__WXGTK__) || defined(__WXOSX__))
294 return new wxWebViewWebKit(parent, id, url, pos, size, style, name);
295 #endif
296
297 #if wxUSE_WEBVIEW_IE
298 return new wxWebViewIE(parent, id, url, pos, size, style, name);
299 #endif
300
301 // fall-through intended
302 default:
303 return NULL;
304 }
305 }
306
307 #endif // wxUSE_WEB