KiCad PCB EDA Suite
Loading...
Searching...
No Matches
webview_panel.cpp
Go to the documentation of this file.
1#include <build_version.h>
3#include <wx/sizer.h>
4#include <wx/webviewarchivehandler.h>
5#include <wx/webviewfshandler.h>
6#include <wx/utils.h>
7#include <wx/log.h>
8
9WEBVIEW_PANEL::WEBVIEW_PANEL( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
10 const wxSize& aSize, const int aStyle )
11 : wxPanel( aParent, aId, aPos, aSize, aStyle ),
12 m_initialized( false ),
13 m_browser( wxWebView::New() )
14{
15 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
16
17 if( !wxGetEnv( wxT( "WEBKIT_DISABLE_COMPOSITING_MODE" ), nullptr ) )
18 {
19 wxSetEnv( wxT( "WEBKIT_DISABLE_COMPOSITING_MODE" ), wxT( "1" ) );
20 }
21
22#ifdef __WXMAC__
23 m_browser->RegisterHandler( wxSharedPtr<wxWebViewHandler>( new wxWebViewArchiveHandler( "wxfs" ) ) );
24 m_browser->RegisterHandler( wxSharedPtr<wxWebViewHandler>( new wxWebViewFSHandler( "memory" ) ) );
25#endif
26 m_browser->SetUserAgent( wxString::Format( "KiCad/%s WebView/%s", GetMajorMinorPatchVersion(), wxGetOsDescription() ) );
27 m_browser->Create( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize );
28 sizer->Add( m_browser, 1, wxEXPAND );
29 SetSizer( sizer );
30
31#ifndef __WXMAC__
32 m_browser->RegisterHandler( wxSharedPtr<wxWebViewHandler>( new wxWebViewArchiveHandler( "wxfs" ) ) );
33 m_browser->RegisterHandler( wxSharedPtr<wxWebViewHandler>( new wxWebViewFSHandler( "memory" ) ) );
34#endif
35
36 Bind( wxEVT_WEBVIEW_NAVIGATING, &WEBVIEW_PANEL::OnNavigationRequest, this, m_browser->GetId() );
37 Bind( wxEVT_WEBVIEW_NEWWINDOW, &WEBVIEW_PANEL::OnNewWindow, this, m_browser->GetId() );
38 Bind( wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &WEBVIEW_PANEL::OnScriptMessage, this, m_browser->GetId() );
39 Bind( wxEVT_WEBVIEW_SCRIPT_RESULT, &WEBVIEW_PANEL::OnScriptResult, this, m_browser->GetId() );
40 Bind( wxEVT_WEBVIEW_ERROR, &WEBVIEW_PANEL::OnError, this, m_browser->GetId() );
41 Bind( wxEVT_WEBVIEW_LOADED, &WEBVIEW_PANEL::OnWebViewLoaded, this, m_browser->GetId() );
42}
43
47
48void WEBVIEW_PANEL::LoadURL( const wxString& aURL )
49{
50 if( aURL.starts_with( "file:/" ) && !aURL.starts_with( "file:///" ) )
51 {
52 wxString new_url = wxString( "file:///" ) + aURL.AfterFirst( '/' );
53 m_browser->LoadURL( new_url );
54 return;
55 }
56
57 if( !aURL.StartsWith( "http://" ) && !aURL.StartsWith( "https://" ) && !aURL.StartsWith( "file://" ) )
58 {
59 wxLogError( "Invalid URL: %s", aURL );
60 return;
61 }
62
63 m_browser->LoadURL( aURL );
64}
65
66void WEBVIEW_PANEL::SetPage( const wxString& aHtmlContent )
67{
68 m_browser->SetPage( aHtmlContent, "file://" );
69}
70
71bool WEBVIEW_PANEL::AddMessageHandler( const wxString& aName, MESSAGE_HANDLER aHandler )
72{
73 m_msgHandlers.emplace( aName, std::move(aHandler) );
74 return true;
75}
76
81
82void WEBVIEW_PANEL::OnNavigationRequest( wxWebViewEvent& aEvt )
83{
84 // Default behavior: open external links in the system browser
85 bool isExternal = aEvt.GetURL().StartsWith( "http://" ) || aEvt.GetURL().StartsWith( "https://" );
86 if( isExternal )
87 {
88 wxLaunchDefaultBrowser( aEvt.GetURL() );
89 aEvt.Veto();
90 }
91}
92
93void WEBVIEW_PANEL::OnWebViewLoaded( wxWebViewEvent& aEvt )
94{
95 if( !m_initialized )
96 {
97 // Defer handler registration to avoid running during modal dialog/yield
98 CallAfter([this]() {
99 static bool handler_added_inner = false;
100 if (!handler_added_inner) {
101
102 for( const auto& handler : m_msgHandlers )
103 {
104 if( !m_browser->AddScriptMessageHandler( handler.first ) )
105 {
106 wxLogDebug( "Could not add script message handler %s", handler.first );
107 }
108 }
109
110 handler_added_inner = true;
111 }
112
113 // Inject navigation hook for SPA/JS navigation to prevent webkit crashing without new window
114 m_browser->AddUserScript(R"(
115 (function() {
116 // Change window.open to navigate in the same window
117 window.open = function(url) { if (url) window.location.href = url; return null; };
118 window.showModalDialog = function() { return null; };
119
120 if (window.external && window.external.invoke) {
121 function notifyHost() {
122 window.external.invoke('navigation:' + window.location.href);
123 }
124 window.addEventListener('popstate', notifyHost);
125 window.addEventListener('pushstate', notifyHost);
126 window.addEventListener('replacestate', notifyHost);
127 ['pushState', 'replaceState'].forEach(function(type) {
128 var orig = history[type];
129 history[type] = function() {
130 var rv = orig.apply(this, arguments);
131 window.dispatchEvent(new Event(type.toLowerCase()));
132 return rv;
133 };
134 });
135 }
136 })();
137 )");
138 });
139
140 m_initialized = true;
141 }
142}
143
144void WEBVIEW_PANEL::OnNewWindow( wxWebViewEvent& aEvt )
145{
146 m_browser->LoadURL( aEvt.GetURL() );
147 aEvt.Veto(); // Prevent default behavior of opening a new window
148 wxLogTrace( "webview", "New window requested for URL: %s", aEvt.GetURL() );
149 wxLogTrace( "webview", "Target: %s", aEvt.GetTarget() );
150 wxLogTrace( "webview", "Action flags: %d", static_cast<int>(aEvt.GetNavigationAction()) );
151 wxLogTrace( "webview", "Message handler: %s", aEvt.GetMessageHandler() );
152}
153
154void WEBVIEW_PANEL::OnScriptMessage( wxWebViewEvent& aEvt )
155{
156 wxLogTrace( "webview", "Script message received: %s for handler %s", aEvt.GetString(), aEvt.GetMessageHandler() );
157
158 if( aEvt.GetMessageHandler().IsEmpty() )
159 {
160 wxLogDebug( "No message handler specified for script message: %s", aEvt.GetString() );
161 return;
162 }
163
164 auto it = m_msgHandlers.find( aEvt.GetMessageHandler() );
165 if( it == m_msgHandlers.end() )
166 {
167 wxLogDebug( "No handler registered for message: %s", aEvt.GetMessageHandler() );
168 return;
169 }
170
171 // Call the registered handler with the message
172 wxLogTrace( "webview", "Calling handler for message: %s", aEvt.GetMessageHandler() );
173 it->second( aEvt.GetString() );
174}
175
176void WEBVIEW_PANEL::OnScriptResult( wxWebViewEvent& aEvt )
177{
178 if( aEvt.IsError() )
179 wxLogDebug( "Async script execution failed: %s", aEvt.GetString() );
180}
181
182void WEBVIEW_PANEL::OnError( wxWebViewEvent& aEvt )
183{
184 wxLogDebug( "WebView error: %s", aEvt.GetString() );
185}
wxString GetMajorMinorPatchVersion()
Get the major, minor and patch version in a string major.minor.patch This is extracted by CMake from ...
WEBVIEW_PANEL(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, const int style=0)
void OnScriptMessage(wxWebViewEvent &evt)
void OnNewWindow(wxWebViewEvent &evt)
std::map< wxString, MESSAGE_HANDLER > m_msgHandlers
void SetPage(const wxString &htmlContent)
void OnWebViewLoaded(wxWebViewEvent &evt)
void OnNavigationRequest(wxWebViewEvent &evt)
void ClearMessageHandlers()
bool AddMessageHandler(const wxString &name, MESSAGE_HANDLER handler)
std::function< void(const wxString &)> MESSAGE_HANDLER
void OnScriptResult(wxWebViewEvent &evt)
void OnError(wxWebViewEvent &evt)
~WEBVIEW_PANEL() override
void LoadURL(const wxString &url)
wxWebView * m_browser