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
17 * along with this program. If not, see <https://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 return false;
236
237 // Read JSON file (binary mode)
238 wxFFile file( aSourceFile, "rb" );
239
240 if( !file.IsOpened() )
241 {
242 wxLogTrace( "webview", "Failed to open cookie file for reading: %s", aSourceFile );
243 return false;
244 }
245
246 wxFileOffset len = file.Length();
247
248 if( len == 0 )
249 return false;
250
251 std::string jsonStr;
252 jsonStr.resize( len );
253
254 if( file.Read( &jsonStr[0], len ) != len )
255 {
256 wxLogTrace( "webview", "Failed to read cookie file" );
257 return false;
258 }
259
260 nlohmann::json cookieArray;
261
262 try
263 {
264 cookieArray = nlohmann::json::parse( jsonStr );
265 }
266 catch( const nlohmann::json::exception& e )
267 {
268 wxLogTrace( "webview", "Failed to parse cookie JSON: %s", e.what() );
269 return false;
270 }
271
272 for( const auto& cookieObj : cookieArray )
273 {
274 std::string name = cookieObj.value( "name", "" );
275 std::string value = cookieObj.value( "value", "" );
276 std::string domain = cookieObj.value( "domain", "" );
277 std::string path = cookieObj.value( "path", "/" );
278 bool secure = cookieObj.value( "secure", false );
279 bool httpOnly = cookieObj.value( "httpOnly", false );
280 int64_t expires = cookieObj.value( "expires", 0 );
281
282 ComPtr<ICoreWebView2Cookie> cookie;
283 if( FAILED( cookieManager->CreateCookie(
284 wxString::FromUTF8( name.c_str() ).wc_str(),
285 wxString::FromUTF8( value.c_str() ).wc_str(),
286 wxString::FromUTF8( domain.c_str() ).wc_str(),
287 wxString::FromUTF8( path.c_str() ).wc_str(),
288 &cookie ) ) )
289 {
290 continue;
291 }
292
293 cookie->put_Expires( static_cast<double>( expires ) );
294 cookie->put_IsSecure( secure ? TRUE : FALSE );
295 cookie->put_IsHttpOnly( httpOnly ? TRUE : FALSE );
296
297 cookieManager->AddOrUpdateCookie( cookie.Get() );
298 }
299
300 return true;
301#endif
302}
303
304bool DeleteCookies( wxWebView* aWebView )
305{
306 if( !aWebView )
307 return false;
308
309 void* nativeBackend = aWebView->GetNativeBackend();
310
311 if( !nativeBackend )
312 return false;
313#if defined(__MINGW32__)
314 return false;
315#else
316
317 ICoreWebView2* coreWebView = static_cast<ICoreWebView2*>( nativeBackend );
318
319 ComPtr<ICoreWebView2_2> webView2;
320
321 if( FAILED( coreWebView->QueryInterface( IID_PPV_ARGS( &webView2 ) ) ) )
322 {
323 wxLogTrace( "webview", "Failed to get ICoreWebView2_2 interface. WebView2 runtime might be too old." );
324 return false;
325 }
326
327 ComPtr<ICoreWebView2CookieManager> cookieManager;
328
329 if( FAILED( webView2->get_CookieManager( &cookieManager ) ) )
330 {
331 wxLogTrace( "webview", "Failed to get cookie manager" );
332 return false;
333 }
334
335 if( FAILED( cookieManager->DeleteAllCookies() ) )
336 {
337 wxLogTrace( "webview", "Failed to delete all cookies" );
338 return false;
339 }
340
341 return true;
342#endif
343}
344
345} // 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.