KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wxmsw/webview.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <kiplatform/webview.h>
21
22#include <wx/webview.h>
23#include <wx/log.h>
24#include <wx/ffile.h>
25#include <wx/filename.h>
26#include <wx/utils.h>
27
28#include <json_common.h>
29
30#include <windows.h>
31
32// WebView2 does not exist when compiling on msys2/mingw. So disable cookies management
33// not buildable on msys2 (there are also compil issues with client.h and event.h)
34#if !defined(__MINGW32__)
35 #include <wrl/client.h>
36 #include <wrl/event.h>
37 #include <WebView2.h>
38
39using Microsoft::WRL::ComPtr;
40using Microsoft::WRL::Callback;
41#endif
42
43namespace KIPLATFORM::WEBVIEW
44{
45
46bool SaveCookies( wxWebView* aWebView, const wxString& aTargetFile )
47{
48 if( !aWebView )
49 return false;
50
51 void* nativeBackend = aWebView->GetNativeBackend();
52
53 if( !nativeBackend )
54 return false;
55#if defined(__MINGW32__)
56 return false;
57#else
58
59 ICoreWebView2* coreWebView = static_cast<ICoreWebView2*>( nativeBackend );
60
61 // We need ICoreWebView2_2 to access the CookieManager
62 ComPtr<ICoreWebView2_2> webView2;
63
64 if( FAILED( coreWebView->QueryInterface( IID_PPV_ARGS( &webView2 ) ) ) )
65 {
66 wxLogTrace( "webview", "Failed to get ICoreWebView2_2 interface. WebView2 runtime might be too old." );
67 return false;
68 }
69
70 ComPtr<ICoreWebView2CookieManager> cookieManager;
71
72 if( FAILED( webView2->get_CookieManager( &cookieManager ) ) )
73 {
74 wxLogTrace( "webview", "Failed to get cookie manager" );
75 return false;
76 }
77
78 nlohmann::json cookieArray = nlohmann::json::array();
79 bool callbackFinished = false;
80 bool success = false;
81
82 HRESULT hr = cookieManager->GetCookies(
83 nullptr, // Get all cookies
84 Callback<ICoreWebView2GetCookiesCompletedHandler>(
85 [&]( HRESULT result, ICoreWebView2CookieList* list ) -> HRESULT
86 {
87 if( FAILED( result ) || !list )
88 {
89 wxLogTrace( "webview", "GetCookies failed or returned null list" );
90 callbackFinished = true;
91 return S_OK;
92 }
93
94 UINT count = 0;
95 list->get_Count( &count );
96
97 for( UINT i = 0; i < count; ++i )
98 {
99 ComPtr<ICoreWebView2Cookie> cookie;
100 if( FAILED( list->GetValueAtIndex( i, &cookie ) ) )
101 continue;
102
103 nlohmann::json cookieObj;
104
105 LPWSTR name = nullptr;
106 cookie->get_Name( &name );
107
108 if( name )
109 {
110 cookieObj["name"] = std::string( wxString( name ).ToUTF8() );
111 CoTaskMemFree( name );
112 }
113
114 LPWSTR value = nullptr;
115 cookie->get_Value( &value );
116
117 if( value )
118 {
119 cookieObj["value"] = std::string( wxString( value ).ToUTF8() );
120 CoTaskMemFree( value );
121 }
122
123 LPWSTR domain = nullptr;
124 cookie->get_Domain( &domain );
125
126 if( domain )
127 {
128 cookieObj["domain"] = std::string( wxString( domain ).ToUTF8() );
129 CoTaskMemFree( domain );
130 }
131
132 LPWSTR path = nullptr;
133 cookie->get_Path( &path );
134
135 if( path )
136 {
137 cookieObj["path"] = std::string( wxString( path ).ToUTF8() );
138 CoTaskMemFree( path );
139 }
140
141 double expires = 0;
142 cookie->get_Expires( &expires );
143 cookieObj["expires"] = static_cast<int64_t>( expires );
144
145 BOOL secure = FALSE;
146 cookie->get_IsSecure( &secure );
147 cookieObj["secure"] = ( secure == TRUE );
148
149 BOOL httpOnly = FALSE;
150 cookie->get_IsHttpOnly( &httpOnly );
151 cookieObj["httpOnly"] = ( httpOnly == TRUE );
152
153 cookieArray.push_back( cookieObj );
154 }
155
156 success = true;
157 callbackFinished = true;
158 return S_OK;
159 } ).Get() );
160
161 if( FAILED( hr ) )
162 {
163 wxLogTrace( "webview", "Failed to initiate GetCookies" );
164 return false;
165 }
166
167 // Wait for callback with timeout (e.g., 5 seconds)
168 // We must pump messages because WebView2 callbacks run on the UI thread
169 long long startTime = wxGetLocalTimeMillis().GetValue();
170
171 while( !callbackFinished )
172 {
173 wxSafeYield();
174
175 if( ( wxGetLocalTimeMillis().GetValue() - startTime ) > 5000 )
176 {
177 wxLogTrace( "webview", "Timeout waiting for GetCookies callback" );
178 return false;
179 }
180
181 // Sleep a tiny bit to avoid 100% CPU if Yield returns immediately
182 wxMilliSleep( 10 );
183 }
184
185 if( !success )
186 return false;
187
188 // Write to file (binary mode to preserve UTF-8)
189 wxFFile file( aTargetFile, "wb" );
190
191 if( !file.IsOpened() )
192 {
193 wxLogTrace( "webview", "Failed to open cookie file for writing: %s", aTargetFile );
194 return false;
195 }
196
197 std::string jsonStr = cookieArray.dump( 2 );
198 file.Write( jsonStr.c_str(), jsonStr.size() );
199
200 return true;
201#endif
202}
203
204
205bool LoadCookies( wxWebView* aWebView, const wxString& aSourceFile )
206{
207 if( !aWebView )
208 return false;
209
210 if( !wxFileName::FileExists( aSourceFile ) )
211 return false;
212
213 void* nativeBackend = aWebView->GetNativeBackend();
214
215 if( !nativeBackend )
216 return false;
217
218#if defined(__MINGW32__)
219 return false;
220#else
221
222 ICoreWebView2* coreWebView = static_cast<ICoreWebView2*>( nativeBackend );
223
224 ComPtr<ICoreWebView2_2> webView2;
225
226 if( FAILED( coreWebView->QueryInterface( IID_PPV_ARGS( &webView2 ) ) ) )
227 {
228 wxLogTrace( "webview", "Failed to get ICoreWebView2_2 interface" );
229 return false;
230 }
231
232 ComPtr<ICoreWebView2CookieManager> cookieManager;
233
234 if( FAILED( webView2->get_CookieManager( &cookieManager ) ) )
235 {
236 wxLogDebug( "Failed to get cookie manager" );
237 return false;
238 }
239
240 // Read JSON file (binary mode)
241 wxFFile file( aSourceFile, "rb" );
242
243 if( !file.IsOpened() )
244 {
245 wxLogTrace( "webview", "Failed to open cookie file for reading: %s", aSourceFile );
246 return false;
247 }
248
249 wxFileOffset len = file.Length();
250
251 if( len == 0 )
252 return false;
253
254 std::string jsonStr;
255 jsonStr.resize( len );
256
257 if( file.Read( &jsonStr[0], len ) != len )
258 {
259 wxLogTrace( "webview", "Failed to read cookie file" );
260 return false;
261 }
262
263 nlohmann::json cookieArray;
264
265 try
266 {
267 cookieArray = nlohmann::json::parse( jsonStr );
268 }
269 catch( const nlohmann::json::exception& e )
270 {
271 wxLogTrace( "webview", "Failed to parse cookie JSON: %s", e.what() );
272 return false;
273 }
274
275 for( const auto& cookieObj : cookieArray )
276 {
277 std::string name = cookieObj.value( "name", "" );
278 std::string value = cookieObj.value( "value", "" );
279 std::string domain = cookieObj.value( "domain", "" );
280 std::string path = cookieObj.value( "path", "/" );
281 bool secure = cookieObj.value( "secure", false );
282 bool httpOnly = cookieObj.value( "httpOnly", false );
283 int64_t expires = cookieObj.value( "expires", 0 );
284
285 ComPtr<ICoreWebView2Cookie> cookie;
286 if( FAILED( cookieManager->CreateCookie(
287 wxString::FromUTF8( name.c_str() ).wc_str(),
288 wxString::FromUTF8( value.c_str() ).wc_str(),
289 wxString::FromUTF8( domain.c_str() ).wc_str(),
290 wxString::FromUTF8( path.c_str() ).wc_str(),
291 &cookie ) ) )
292 {
293 continue;
294 }
295
296 cookie->put_Expires( static_cast<double>( expires ) );
297 cookie->put_IsSecure( secure ? TRUE : FALSE );
298 cookie->put_IsHttpOnly( httpOnly ? TRUE : FALSE );
299
300 cookieManager->AddOrUpdateCookie( cookie.Get() );
301 }
302
303 return true;
304#endif
305}
306
307bool DeleteCookies( wxWebView* aWebView )
308{
309 if( !aWebView )
310 return false;
311
312 void* nativeBackend = aWebView->GetNativeBackend();
313
314 if( !nativeBackend )
315 return false;
316#if defined(__MINGW32__)
317 return false;
318#else
319
320 ICoreWebView2* coreWebView = static_cast<ICoreWebView2*>( nativeBackend );
321
322 ComPtr<ICoreWebView2_2> webView2;
323
324 if( FAILED( coreWebView->QueryInterface( IID_PPV_ARGS( &webView2 ) ) ) )
325 {
326 wxLogTrace( "webview", "Failed to get ICoreWebView2_2 interface. WebView2 runtime might be too old." );
327 return false;
328 }
329
330 ComPtr<ICoreWebView2CookieManager> cookieManager;
331
332 if( FAILED( webView2->get_CookieManager( &cookieManager ) ) )
333 {
334 wxLogTrace( "webview", "Failed to get cookie manager" );
335 return false;
336 }
337
338 if( FAILED( cookieManager->DeleteAllCookies() ) )
339 {
340 wxLogTrace( "webview", "Failed to delete all cookies" );
341 return false;
342 }
343
344 return true;
345#endif
346}
347
348} // namespace KIPLATFORM::WEBVIEW
const char * name
bool SaveCookies(wxWebView *aWebView, const wxString &aTargetFile)
Save cookies from the given WebView to the specified file.
bool LoadCookies(wxWebView *aWebView, const wxString &aSourceFile)
Load cookies from the specified file into the given WebView.
bool DeleteCookies(wxWebView *aWebView)
Delete all cookies from the given WebView.
std::string path
wxString result
Test unit parsing edge cases and error handling.