Files
OrcaSlicer/src/slic3r/GUI/Widgets/WebView.cpp
T
Lam Wei LunandSoftFever 0caefcf4aa Linux: only disable WebKit compositing for WebKitGTK older than 2.42 (#16385)
* Linux: only disable WebKit compositing for WebKitGTK older than 2.42

The default Wayland path set WEBKIT_DISABLE_COMPOSITING_MODE=1 whenever the
session had both DISPLAY and WAYLAND_DISPLAY, i.e. any Wayland session with
XWayland. That forces WebKitGTK accelerated compositing off and also disables
threaded scrolling, so the Device tab web view (Mainsail/Fluidd) lagged unless
the app was launched with GDK_BACKEND=x11, the only path that skipped the
variable. Disabling compositing also turns off threaded scrolling.

The XWayland compositing bug the fallback worked around is fixed in WebKitGTK
>= 2.42, so apply it only for older WebKit. WebView::NeedsCompositingWorkaround()
reads the runtime WebKitGTK version; the X11 opt-in path is unchanged.

Behavior is unchanged on WebKit < 2.42, on pure X11, and on native Wayland
without XWayland. The wxWidgets skill reference is updated to match.

* Linux: use WebKit's shared-memory transport for the NVIDIA DMABUF renderer

The Device tab web view is slow on NVIDIA proprietary drivers even with
accelerated compositing left on: since the DMABUF renderer became the
default in WebKitGTK 2.42, the driver cannot back it, and web views fall
back to a slow path. Gating the old WEBKIT_DISABLE_COMPOSITING_MODE
fallback on WebKit < 2.42 is the wrong axis, because the failure is
version-independent from 2.42 on.

Force the shared-memory transport with WEBKIT_DMABUF_RENDERER_FORCE_SHM=1.
That keeps compositing and threaded scrolling and skips only the hardware
path. Also set WEBKIT_FORCE_DMABUF_RENDERER=1, which clears the isNVIDIA()
early return that Debian/Ubuntu/openSUSE patch into
rendererBufferTransportMode() and which otherwise returns before the
FORCE_SHM check is reached.

Both are set before the X11-vs-Wayland split so the workaround also covers
XWayland, and only when WebKitGTK is new enough to honour FORCE_SHM
(>= 2.44, WebView::SupportsForceSharedMemory()).

The AppImage launcher exported the superseded
WEBKIT_DISABLE_DMABUF_RENDERER=1, which is read first, leaves the transport
set empty and so disables compositing outright; replace it with the same
pair. The wxWidgets skill reference is updated to match.

* Linux: drop the AppImage WebKit DMABUF exports; correct the skill notes

The launcher cannot gate on the runtime WebKitGTK version, so exporting
WEBKIT_DMABUF_RENDERER_FORCE_SHM/WEBKIT_FORCE_DMABUF_RENDERER there would
clear a distro isNVIDIA() guard on hosts older than 2.44, where FORCE_SHM
is ignored, re-exposing the broken hardware path. The binary already sets
both with the correct gate, so remove them and say who owns the workaround.

Also correct two claims in the skill reference. An empty transport set only
means "no accelerated compositing" from 2.44 on; 2.42/2.43 still fall back
to the Wayland/X11 renderers, removed in the same release that added
FORCE_SHM. And threaded scrolling is off by default in 2.44/2.45 (on from
2.46), so FORCE_SHM keeps compositing rather than scrolling. Document the
module-level detection cost and the WEBKIT_DMABUF_RENDERER_FORCE_SHM=0
opt-out.

* Flatpak: force WebKit's shared-memory transport instead of disabling DMABUF

The entrypoint exported WEBKIT_DISABLE_DMABUF_RENDERER=1 for NVIDIA. WebKit
reads that variable first and leaves the transport set empty, which turns
accelerated compositing off, so the Device tab still lagged for NVIDIA
Flatpak users, and the binary could not override it because its setenv is
non-replacing.

The runtime's WebKitGTK is vanilla, so there is no distro isNVIDIA() guard
to clear and WEBKIT_DMABUF_RENDERER_FORCE_SHM=1 alone is the right switch:
compositing stays on and only the hardware DMABUF path is skipped.

* Flatpak: drop the WebKit NVIDIA export; the binary owns the workaround

/proc/driver/nvidia/version is readable inside the Flatpak sandbox, so the
binary's NVIDIA branch already sets WEBKIT_DMABUF_RENDERER_FORCE_SHM with
the correct runtime gate and the entrypoint's own export is redundant.

Verified with:
  flatpak run --command=cat com.orcaslicer.OrcaSlicer /proc/driver/nvidia/version
which prints the NVRM version (driver 615.71.09) from inside the sandbox.

* Linux: say what the XWayland compositing gate actually tests

DISPLAY and WAYLAND_DISPLAY are both set in a native Wayland session as
well, because GNOME and KDE run Xwayland as part of the session and export
DISPLAY for it whether or not this process is an X client. The pair means
XWayland is available in the session, not that GTK picked X11 for this
process, so "(an XWayland session)" reads as more than the condition
establishes.

Say what the pair means, and name the case the fallback actually covers:
the X11 backend inside a Wayland session, which is what the forced
GDK_BACKEND=x11 above produces in a build without EGL.

Comment only; behaviour is unchanged.

* Linux: apply the XWayland compositing fallback only where GTK picks X11

The default path set WEBKIT_DISABLE_COMPOSITING_MODE whenever DISPLAY and
WAYLAND_DISPLAY were both set, but a native Wayland app inherits both, so a
build with EGL disabled accelerated compositing — and with it threaded
scrolling — while running on Wayland, where the XWayland bug does not apply.

The only default-path case that really runs through XWayland is the forced
GDK_BACKEND=x11 used when wx lacks EGL, so set the variable there. The
backend cannot be re-checked later: that branch exports GDK_BACKEND itself,
so the force and a user's GDK_BACKEND=x11 opt-in, which deliberately keeps
compositing, would be indistinguishable.

Behaviour is unchanged for WebKitGTK >= 2.42, where the gate already skipped
the variable, and for builds without EGL. A build with EGL on WebKitGTK <
2.42 in a Wayland session no longer loses compositing and threaded scrolling.

The wxWidgets skill reference is updated to match.

---------

Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
2026-10-15 22:35:56 +08:00

547 lines
22 KiB
C++

#include "WebView.hpp"
#include "slic3r/GUI/Widgets/StateColor.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/Utils/MacDarkMode.hpp"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <exception>
#include <thread>
#include <wx/setup.h>
#include <wx/webview.h>
#include <wx/string.h>
#include <wx/gdicmn.h>
#include <wx/sharedptr.h>
#include <wx/vector.h>
#include <wx/event.h>
#include <vector>
#include <wx/object.h>
#include <wx/log.h>
#include <utility>
#include <wx/webviewarchivehandler.h>
#include <wx/webviewfshandler.h>
#include <wx/weakref.h>
#if wxUSE_WEBVIEW_EDGE
#include <wx/msw/webview_edge.h>
#elif defined(__WXMAC__)
#include <wx/osx/webview_webkit.h>
#endif
#include <wx/uri.h>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <ios>
#include <string>
#include <wx/versioninfo.h>
namespace fs = boost::filesystem;
#if defined(__WIN32__) || defined(__WXMAC__)
#include "wx/private/jsscriptwrapper.h"
#endif
#ifdef __WIN32__
#include <WebView2.h>
#include <Shellapi.h>
#include <slic3r/Utils/Http.hpp>
#elif defined __linux__
#include <gtk/gtk.h>
#define WEBKIT_API
struct WebKitWebView;
struct WebKitJavascriptResult;
struct WebKitSettings;
extern "C" {
WEBKIT_API WebKitSettings *
webkit_settings_new (void);
WEBKIT_API const gchar *
webkit_settings_get_user_agent (WebKitSettings *settings);
WEBKIT_API void
webkit_web_view_run_javascript (WebKitWebView *web_view,
const gchar *script,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
WEBKIT_API WebKitJavascriptResult *
webkit_web_view_run_javascript_finish (WebKitWebView *web_view,
GAsyncResult *result,
GError **error);
WEBKIT_API void
webkit_javascript_result_unref (WebKitJavascriptResult *js_result);
WEBKIT_API guint
webkit_get_major_version (void);
WEBKIT_API guint
webkit_get_minor_version (void);
}
#endif
#ifdef __WIN32__
// Run Download and Install in another thread so we don't block the UI thread
DWORD DownloadAndInstallWV2RT() {
int returnCode = 2; // Download failed
// Use fwlink to download WebView2 Bootstrapper at runtime and invoke installation
// Broken/Invalid Https Certificate will fail to download
// Use of the download link below is governed by the below terms. You may acquire the link
// for your use at https://developer.microsoft.com/microsoft-edge/webview2/. Microsoft owns
// all legal right, title, and interest in and to the WebView2 Runtime Bootstrapper
// ("Software") and related documentation, including any intellectual property in the
// Software. You must acquire all code, including any code obtained from a Microsoft URL,
// under a separate license directly from Microsoft, including a Microsoft download site
// (e.g., https://developer.microsoft.com/microsoft-edge/webview2/).
// HRESULT hr = URLDownloadToFileW(NULL, L"https://go.microsoft.com/fwlink/p/?LinkId=2124703",
// L".\\plugin\\MicrosoftEdgeWebview2Setup.exe", 0, 0);
fs::path target_file_path = (fs::temp_directory_path() / "MicrosoftEdgeWebview2Setup.exe");
bool downloaded = false;
Slic3r::Http::get("https://go.microsoft.com/fwlink/p/?LinkId=2124703")
.on_error([](std::string body, std::string error, unsigned http_status) {
})
.on_complete([&downloaded, target_file_path](std::string body, unsigned http_status) {
fs::fstream file(target_file_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(body.c_str(), body.size());
file.flush();
file.close();
downloaded = true;
})
.perform_sync();
// Sleep for 1 second to wait for the buffer writen into disk
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if (downloaded) {
// Either Package the WebView2 Bootstrapper with your app or download it using fwlink
// Then invoke install at Runtime.
// Keep the path string alive for the duration of the ShellExecuteExW call;
// assigning .c_str() of a temporary directly would leave lpFile dangling.
const std::wstring installer_path = target_file_path.generic_wstring();
SHELLEXECUTEINFOW shExInfo = {0};
shExInfo.cbSize = sizeof(shExInfo);
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shExInfo.hwnd = 0;
shExInfo.lpVerb = L"runas";
shExInfo.lpFile = installer_path.c_str();
shExInfo.lpParameters = L" /silent /install";
shExInfo.lpDirectory = 0;
shExInfo.nShow = 0;
shExInfo.hInstApp = 0;
if (ShellExecuteExW(&shExInfo)) {
WaitForSingleObject(shExInfo.hProcess, INFINITE);
returnCode = 0; // Install successfull
} else {
returnCode = 1; // Install failed
}
}
return returnCode;
}
class WebViewEdge : public wxWebViewEdge
{
public:
bool SetUserAgent(const wxString &userAgent) override
{
bool dark = userAgent.Contains("dark");
SetColorScheme(dark ? COREWEBVIEW2_PREFERRED_COLOR_SCHEME_DARK : COREWEBVIEW2_PREFERRED_COLOR_SCHEME_LIGHT);
ICoreWebView2 *webView2 = (ICoreWebView2 *) GetNativeBackend();
if (webView2) {
ICoreWebView2Settings *settings;
HRESULT hr = webView2->get_Settings(&settings);
if (hr == S_OK) {
ICoreWebView2Settings2 *settings2;
hr = settings->QueryInterface(&settings2);
if (hr == S_OK) {
settings2->put_UserAgent(userAgent.wc_str());
settings2->Release();
return true;
}
}
settings->Release();
return false;
}
pendingUserAgent = userAgent;
return true;
}
bool SetColorScheme(COREWEBVIEW2_PREFERRED_COLOR_SCHEME colorScheme)
{
ICoreWebView2 *webView2 = (ICoreWebView2 *) GetNativeBackend();
if (webView2) {
ICoreWebView2_13 * webView2_13;
HRESULT hr = webView2->QueryInterface(&webView2_13);
if (hr == S_OK) {
ICoreWebView2Profile *profile;
hr = webView2_13->get_Profile(&profile);
if (hr == S_OK) {
profile->put_PreferredColorScheme(colorScheme);
profile->Release();
return true;
}
webView2_13->Release();
}
return false;
}
pendingColorScheme = colorScheme;
return true;
}
void DoGetClientSize(int *x, int *y) const override
{
if (!pendingUserAgent.empty()) {
auto thiz = const_cast<WebViewEdge *>(this);
auto userAgent = std::move(thiz->pendingUserAgent);
thiz->pendingUserAgent.clear();
thiz->SetUserAgent(userAgent);
}
if (pendingColorScheme) {
auto thiz = const_cast<WebViewEdge *>(this);
auto colorScheme = pendingColorScheme;
thiz->pendingColorScheme = COREWEBVIEW2_PREFERRED_COLOR_SCHEME_AUTO;
thiz->SetColorScheme(colorScheme);
}
wxWebViewEdge::DoGetClientSize(x, y);
};
private:
wxString pendingUserAgent;
COREWEBVIEW2_PREFERRED_COLOR_SCHEME pendingColorScheme = COREWEBVIEW2_PREFERRED_COLOR_SCHEME_AUTO;
};
#elif defined __WXOSX__
class WebViewWebKit : public wxWebViewWebKit
{
public:
WebViewWebKit()
: wxWebViewWebKit(wxWebView::NewConfiguration(wxWebViewBackendWebKit))
{
}
~WebViewWebKit() override
{
RemoveScriptMessageHandler("wx");
}
};
#endif
class FakeWebView : public wxWebView
{
virtual bool Create(wxWindow* parent, wxWindowID id, const wxString& url, const wxPoint& pos, const wxSize& size, long style, const wxString& name) override { return false; }
virtual wxString GetCurrentTitle() const override { return wxString(); }
virtual wxString GetCurrentURL() const override { return wxString(); }
virtual bool IsBusy() const override { return false; }
virtual bool IsEditable() const override { return false; }
virtual void LoadURL(const wxString& url) override { }
virtual void Print() override { }
virtual void RegisterHandler(wxSharedPtr<wxWebViewHandler> handler) override { }
virtual void Reload(wxWebViewReloadFlags flags = wxWEBVIEW_RELOAD_DEFAULT) override { }
virtual bool RunScript(const wxString& javascript, wxString* output = NULL) const override { return false; }
virtual void SetEditable(bool enable = true) override { }
virtual void Stop() override { }
virtual bool CanGoBack() const override { return false; }
virtual bool CanGoForward() const override { return false; }
virtual void GoBack() override { }
virtual void GoForward() override { }
virtual void ClearHistory() override { }
virtual void EnableHistory(bool enable = true) override { }
virtual wxVector<wxSharedPtr<wxWebViewHistoryItem>> GetBackwardHistory() override { return {}; }
virtual wxVector<wxSharedPtr<wxWebViewHistoryItem>> GetForwardHistory() override { return {}; }
virtual void LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item) override { }
virtual bool CanSetZoomType(wxWebViewZoomType type) const override { return false; }
virtual float GetZoomFactor() const override { return 0.0f; }
virtual wxWebViewZoomType GetZoomType() const override { return wxWebViewZoomType(); }
virtual void SetZoomFactor(float zoom) override { }
virtual void SetZoomType(wxWebViewZoomType zoomType) override { }
virtual bool CanUndo() const override { return false; }
virtual bool CanRedo() const override { return false; }
virtual void Undo() override { }
virtual void Redo() override { }
virtual void* GetNativeBackend() const override { return nullptr; }
virtual void DoSetPage(const wxString& html, const wxString& baseUrl) override { }
};
wxDEFINE_EVENT(EVT_WEBVIEW_RECREATED, wxCommandEvent);
static std::vector<wxWebView*> g_webviews;
// Webviews waiting for their script handler while another one is added; adding it yields, so a
// view can be destroyed while it waits.
static std::vector<wxWeakRef<wxWebView>> g_delay_webviews;
class WebViewRef : public wxObjectRefData
{
public:
WebViewRef(wxWebView *webView) : m_webView(webView) {}
~WebViewRef() {
auto iter = std::find(g_webviews.begin(), g_webviews.end(), m_webView);
assert(iter != g_webviews.end());
if (iter != g_webviews.end())
g_webviews.erase(iter);
}
wxWebView *m_webView;
// Guards against registering the "wx" handler twice (a duplicate throws on WKWebView).
bool m_script_handler_added = false;
};
static WebViewRef *webview_ref(wxWebView *webView)
{
return webView ? static_cast<WebViewRef *>(webView->GetRefData()) : nullptr;
}
#ifdef __linux__
// WebKitGTK's own User-Agent with the tags the bundled pages (globalapi.js) and Bambu's cloud read
// appended. wx's GTK backend cannot report the stock string, so it comes from a throwaway settings object.
static wxString linux_user_agent()
{
static const wxString stock = [] {
WebKitSettings *settings = webkit_settings_new();
wxString ua = wxString::FromUTF8(webkit_settings_get_user_agent(settings));
g_object_unref(settings);
return ua;
}();
return wxString::Format("%s BBL-Slicer/v%s (%s) BBL-Language/%s", stock, Slic3r::GUI::wxGetApp().get_bbl_client_version(),
Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light",
Slic3r::GUI::wxGetApp().current_language_code().BeforeFirst('_'));
}
#endif
wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
{
#if wxUSE_WEBVIEW_EDGE
// Check if a fixed version of edge is present in
// $executable_path/edge_fixed and use it
wxFileName edgeFixedDir(wxStandardPaths::Get().GetExecutablePath());
edgeFixedDir.SetFullName("");
edgeFixedDir.AppendDir("edge_fixed");
if (edgeFixedDir.DirExists()) {
wxWebViewEdge::MSWSetBrowserExecutableDir(edgeFixedDir.GetFullPath());
wxLogMessage("Using fixed edge version");
}
#endif
auto url2 = url;
#ifdef __WIN32__
url2.Replace("\\", "/");
#endif
if (!url2.empty()) { url2 = wxURI(url2).BuildURI(); }
BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": " << url2.ToUTF8();
#ifdef __WIN32__
wxWebView* webView = new WebViewEdge;
#elif defined(__WXOSX__)
wxWebView* webView = new WebViewWebKit;
#else
auto webView = wxWebView::New();
#endif
if (webView) {
webView->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
wxString language_code = Slic3r::GUI::wxGetApp().current_language_code().BeforeFirst('_');
language_code = language_code.ToStdString();
#ifdef __WIN32__
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52 BBL-Slicer/v%s (%s) BBL-Language/%s",
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
// We register the wxfs:// protocol for testing purposes
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("bbl")));
// And the memory: file system
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
#else
// With WKWebView handlers need to be registered before creation.
// On Linux (WebKit2GTK), URI schemes are registered globally and can only
// be registered once, so guard against multiple registrations.
static bool s_schemes_registered = false;
if (!s_schemes_registered) {
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("wxfs")));
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
s_schemes_registered = true;
}
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
#ifdef __linux__
webView->SetUserAgent(linux_user_agent());
#else
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
#endif
#endif
#ifdef __WXMAC__
WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend();
Slic3r::GUI::WKWebView_setTransparentBackground(wkWebView);
#endif
auto addScriptMessageHandler = [] (wxWebView *webView) {
// Skip if SendAPIKey() already registered "wx"; a duplicate add throws an
// uncatchable NSException on WKWebView, killing the app at startup.
WebViewRef *ref = webview_ref(webView);
if (ref && ref->m_script_handler_added)
return;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": begin to add script message handler for wx.";
Slic3r::GUI::wxGetApp().set_adding_script_handler(true);
if (!webView->AddScriptMessageHandler("wx"))
wxLogError("Could not add script message handler");
else if (ref)
ref->m_script_handler_added = true;
Slic3r::GUI::wxGetApp().set_adding_script_handler(false);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished add script message handler for wx.";
};
#ifndef __WIN32__
webView->CallAfter([webView, addScriptMessageHandler] {
#endif
if (Slic3r::GUI::wxGetApp().is_adding_script_handler()) {
g_delay_webviews.push_back(webView);
} else {
addScriptMessageHandler(webView);
while (!g_delay_webviews.empty()) {
auto views = std::move(g_delay_webviews);
for (const wxWeakRef<wxWebView>& wv : views)
if (wv)
addScriptMessageHandler(wv.get());
}
}
#ifndef __WIN32__
});
#endif
webView->EnableContextMenu(true);
} else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": failed. Use fake web view.";
webView = new FakeWebView;
}
webView->SetRefData(new WebViewRef(webView));
g_webviews.push_back(webView);
return webView;
}
void WebView::MarkScriptMessageHandlerAdded(wxWebView * webView)
{
if (WebViewRef *ref = webview_ref(webView))
ref->m_script_handler_added = true;
}
bool WebView::NeedsRecreateOnShow()
{
const bool recreating = Slic3r::GUI::wxGetApp().is_recreating_gui();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": is_recreating_gui = " << recreating;
#ifdef __WIN32__
return recreating;
#else
return false;
#endif
}
bool WebView::NeedsCompositingWorkaround()
{
#ifdef __linux__
const guint major = webkit_get_major_version();
const guint minor = webkit_get_minor_version();
return major < 2 || (major == 2 && minor < 42);
#else
return false;
#endif
}
bool WebView::SupportsForceSharedMemory()
{
#ifdef __linux__
const guint major = webkit_get_major_version();
const guint minor = webkit_get_minor_version();
return major > 2 || (major == 2 && minor >= 44);
#else
return false;
#endif
}
#if wxUSE_WEBVIEW_EDGE
bool WebView::CheckWebViewRuntime()
{
wxWebViewFactoryEdge factory;
auto wxVersion = factory.GetVersionInfo(wxVersionContext::RunTime);
bool present = wxVersion.GetMajor() != 0;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": WebView2 runtime "
<< (present ? "found, version " : "not found (")
<< wxVersion.ToString().ToUTF8().data() << (present ? "" : ")");
return present;
}
bool WebView::DownloadAndInstallWebViewRuntime()
{
return DownloadAndInstallWV2RT() == 0;
}
#endif
wxString WebView::RedactUrl(const wxString& url)
{
const size_t cut = url.find_first_of(wxString("?#"));
return cut == wxString::npos ? url : url.Left(cut);
}
void WebView::LoadUrl(wxWebView * webView, wxString const &url)
{
auto url2 = url;
#ifdef __WIN32__
url2.Replace("\\", "/");
#endif
if (!url2.empty()) { url2 = wxURI(url2).BuildURI(); }
BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << RedactUrl(url2).ToUTF8();
webView->LoadURL(url2);
}
bool WebView::RunScript(wxWebView *webView, wxString const &javascript)
{
if (Slic3r::GUI::wxGetApp().app_config->get("internal_developer_mode") == "true"
&& javascript.find("studio_userlogin") == wxString::npos)
wxLogMessage("Running JavaScript:\n%s\n", javascript);
try {
#ifdef __WIN32__
ICoreWebView2 * webView2 = (ICoreWebView2 *) webView->GetNativeBackend();
if (webView2 == nullptr)
return false;
return webView2->ExecuteScript(javascript, NULL) == 0;
#elif defined __WXMAC__
WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend();
Slic3r::GUI::WKWebView_evaluateJavaScript(wkWebView, javascript, nullptr);
return true;
#else
WebKitWebView *wkWebView = (WebKitWebView *) webView->GetNativeBackend();
webkit_web_view_run_javascript(
wkWebView, javascript.utf8_str(), NULL,
[](GObject *wkWebView, GAsyncResult *res, void *) {
GError * error = NULL;
auto result = webkit_web_view_run_javascript_finish((WebKitWebView*)wkWebView, res, &error);
if (!result)
g_error_free (error);
else
webkit_javascript_result_unref (result);
}, NULL);
return true;
#endif
} catch (std::exception &) {
return false;
}
}
void WebView::RecreateAll()
{
auto dark = Slic3r::GUI::wxGetApp().dark_mode();
wxString language_code = Slic3r::GUI::wxGetApp().current_language_code().BeforeFirst('_');
language_code = language_code.ToStdString();
for (auto webView : g_webviews) {
#ifdef __linux__
webView->SetUserAgent(linux_user_agent());
#else
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
Slic3r::GUI::wxGetApp().get_bbl_client_version(), dark ? "dark" : "light", language_code.mb_str()));
#endif
// A host-themed WebViewHostDialog re-themes in place (no reload). If it handles
// the event, skip the reload; legacy pages fall through and reload as before
// (their own dark.css swap re-themes them on reload).
wxCommandEvent evt(EVT_WEBVIEW_RECREATED);
evt.SetEventObject(webView);
if (!webView->GetEventHandler()->ProcessEvent(evt))
webView->Reload();
}
}