mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-15 20:01:31 +00:00
* 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>
547 lines
22 KiB
C++
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();
|
|
}
|
|
}
|