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