mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 23:31:19 +00:00
A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block.
349 lines
11 KiB
C++
349 lines
11 KiB
C++
#include "PrinterWebView.hpp"
|
|
|
|
#include "PrinterWebViewHandler.hpp"
|
|
#include "slic3r/GUI/PrinterWebView.hpp"
|
|
#include "slic3r/GUI/GUI_App.hpp"
|
|
#include "slic3r/GUI/MainFrame.hpp"
|
|
#include "libslic3r_version.h"
|
|
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <utility>
|
|
#include <wx/panel.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <memory>
|
|
#include <wx/log.h>
|
|
#include "libslic3r/Utils.hpp"
|
|
#include <wx/event.h>
|
|
#include <boost/log/trivial.hpp>
|
|
#include <wx/sizer.h>
|
|
#include <wx/string.h>
|
|
#include <wx/toolbar.h>
|
|
|
|
#include <slic3r/GUI/Widgets/WebView.hpp>
|
|
#include <wx/utils.h>
|
|
#include <wx/webview.h>
|
|
|
|
class wxWindow;
|
|
|
|
#ifdef __linux__
|
|
#include <webkit2/webkit2.h>
|
|
#endif
|
|
|
|
namespace Slic3r {
|
|
namespace GUI {
|
|
|
|
#ifdef __linux__
|
|
// Workaround for #7210: WebKitGTK crashes on vue-resize's hidden <object> probe used by
|
|
// older Fluidd/Mainsail pages. Swap that <object> for a <div> shim at appendChild time
|
|
// and bridge resize events through a fake contentDocument.defaultView so vue-resize keeps
|
|
// working. Workaround proposed by @VittC.
|
|
static void inject_vue_resize_workaround(wxWebView *webView)
|
|
{
|
|
webView->AddUserScript(
|
|
"(function() {"
|
|
" 'use strict';"
|
|
" function isVueResizeObject(el) {"
|
|
" return el && el.tagName === 'OBJECT'"
|
|
" && el.type === 'text/html'"
|
|
" && el.getAttribute('aria-hidden') === 'true'"
|
|
" && el.getAttribute('tabindex') === '-1';"
|
|
" }"
|
|
" function isResizeObserverParent(p) {"
|
|
" return p && p.classList && p.classList.contains('resize-observer');"
|
|
" }"
|
|
" function makeShim(orig, parentForRO) {"
|
|
" var shim = document.createElement('div');"
|
|
" shim.setAttribute('aria-hidden', 'true');"
|
|
" shim.setAttribute('tabindex', '-1');"
|
|
" shim.style.display = 'none';"
|
|
" var fakeWin = document.createElement('div');"
|
|
" var ro = null;"
|
|
" var origRemoveEL = fakeWin.removeEventListener.bind(fakeWin);"
|
|
" fakeWin.removeEventListener = function(type, fn, opts) {"
|
|
" origRemoveEL(type, fn, opts);"
|
|
" if (type === 'resize' && ro) { ro.disconnect(); ro = null; }"
|
|
" };"
|
|
" Object.defineProperty(shim, 'contentDocument', {"
|
|
" configurable: true,"
|
|
" get: function() { return { defaultView: fakeWin }; }"
|
|
" });"
|
|
" Object.defineProperty(shim, 'contentWindow', {"
|
|
" configurable: true,"
|
|
" get: function() { return fakeWin; }"
|
|
" });"
|
|
" if (typeof orig.onload === 'function') { shim.onload = orig.onload; }"
|
|
" queueMicrotask(function() {"
|
|
" if (parentForRO && typeof ResizeObserver !== 'undefined') {"
|
|
" ro = new ResizeObserver(function() {"
|
|
" fakeWin.dispatchEvent(new Event('resize'));"
|
|
" });"
|
|
" ro.observe(parentForRO);"
|
|
" }"
|
|
" if (typeof shim.onload === 'function') {"
|
|
" try { shim.onload(new Event('load')); } catch (e) {}"
|
|
" }"
|
|
" shim.dispatchEvent(new Event('load'));"
|
|
" });"
|
|
" return shim;"
|
|
" }"
|
|
" var origAppend = Node.prototype.appendChild;"
|
|
" Node.prototype.appendChild = function(child) {"
|
|
" if (isResizeObserverParent(this) && isVueResizeObject(child)) {"
|
|
" return origAppend.call(this, makeShim(child, this));"
|
|
" }"
|
|
" return origAppend.call(this, child);"
|
|
" };"
|
|
" var origInsertBefore = Node.prototype.insertBefore;"
|
|
" Node.prototype.insertBefore = function(child, ref) {"
|
|
" if (isResizeObserverParent(this) && isVueResizeObject(child)) {"
|
|
" return origInsertBefore.call(this, makeShim(child, this), ref);"
|
|
" }"
|
|
" return origInsertBefore.call(this, child, ref);"
|
|
" };"
|
|
" console.log('[vr-fix] vue-resize WebKitGTK patch active');"
|
|
"})();",
|
|
wxWEBVIEW_INJECT_AT_DOCUMENT_START
|
|
);
|
|
}
|
|
#endif
|
|
|
|
PrinterWebView::PrinterWebView(wxWindow *parent)
|
|
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
|
, m_browser(nullptr)
|
|
, m_zoomFactor(100)
|
|
, m_apikey()
|
|
, m_apikey_sent(false)
|
|
, m_url_deferred()
|
|
, m_handler(std::make_unique<PrinterWebViewHandler>(*this))
|
|
{
|
|
|
|
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
// Create the webview
|
|
create_browser();
|
|
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
|
|
|
SetSizer(topsizer);
|
|
|
|
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
|
|
|
// Log backend information
|
|
/* m_browser->GetUserAgent() may lead crash
|
|
if (wxGetApp().get_mode() == comDevelop) {
|
|
wxLogMessage(wxWebView::GetBackendVersionInfo().ToString());
|
|
wxLogMessage("Backend: %s Version: %s", m_browser->GetClassInfo()->GetClassName(),
|
|
wxWebView::GetBackendVersionInfo().ToString());
|
|
wxLogMessage("User Agent: %s", m_browser->GetUserAgent());
|
|
}
|
|
*/
|
|
|
|
//Connect the idle events
|
|
Bind(wxEVT_CLOSE_WINDOW, &PrinterWebView::OnClose, this);
|
|
|
|
}
|
|
|
|
PrinterWebView::~PrinterWebView()
|
|
{
|
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Start";
|
|
SetEvtHandlerEnabled(false);
|
|
m_handler.reset();
|
|
|
|
// Destroy the webview
|
|
if(m_browser){
|
|
m_browser->Destroy();
|
|
m_browser = nullptr;
|
|
}
|
|
|
|
|
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " End";
|
|
}
|
|
|
|
void PrinterWebView::create_browser()
|
|
{
|
|
m_browser = WebView::CreateWebView(this, "");
|
|
|
|
#ifdef __linux__
|
|
inject_vue_resize_workaround(m_browser);
|
|
|
|
auto cookiesPath = boost::filesystem::path(data_dir() + "/cache/cookies.db");
|
|
auto wv = static_cast<WebKitWebView*>(m_browser->GetNativeBackend());
|
|
auto wv_ctx = webkit_web_view_get_context(wv);
|
|
auto cookieManager = webkit_web_context_get_cookie_manager(wv_ctx);
|
|
webkit_cookie_manager_set_persistent_storage(cookieManager, cookiesPath.c_str(), WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE);
|
|
#endif
|
|
|
|
m_browser->Bind(wxEVT_WEBVIEW_ERROR, &PrinterWebView::OnError, this);
|
|
m_browser->Bind(wxEVT_WEBVIEW_LOADED, &PrinterWebView::OnLoaded, this);
|
|
m_browser->Bind(wxEVT_WEBVIEW_NEWWINDOW, &PrinterWebView::OnNewWindow, this);
|
|
m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &PrinterWebView::OnScriptMessage, this);
|
|
update_mode();
|
|
}
|
|
|
|
void PrinterWebView::reset_browser()
|
|
{
|
|
m_browser->Destroy(); // also removes it from the sizer
|
|
create_browser();
|
|
GetSizer()->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
|
Layout();
|
|
|
|
// OnLoaded may have cleared m_url_deferred already, so requeue the last url for Show().
|
|
m_apikey_sent = false;
|
|
m_url_deferred = m_url;
|
|
}
|
|
|
|
void PrinterWebView::load_url(wxString& url, wxString apikey)
|
|
{
|
|
// this->Show();
|
|
// this->Raise();
|
|
m_url = url;
|
|
m_apikey = apikey;
|
|
m_apikey_sent = false;
|
|
m_handler = create_printer_webview_handler(*this);
|
|
|
|
if (this->IsShown()) {
|
|
//ORCA: m_url_deferred will be cleared on load success
|
|
//m_url_deferred.clear();
|
|
m_browser->LoadURL(url);
|
|
} else {
|
|
m_url_deferred = url;
|
|
}
|
|
//m_browser->SetFocus();
|
|
UpdateState();
|
|
}
|
|
|
|
bool PrinterWebView::Show(bool show)
|
|
{
|
|
if (show && std::exchange(m_reset_on_show, false))
|
|
reset_browser();
|
|
if (show && !m_url_deferred.empty()) {
|
|
m_browser->LoadURL(m_url_deferred);
|
|
//ORCA: m_url_deferred will be cleared on load success
|
|
//m_url_deferred.clear();
|
|
}
|
|
return wxPanel::Show(show);
|
|
}
|
|
|
|
void PrinterWebView::reload()
|
|
{
|
|
m_browser->Reload();
|
|
}
|
|
|
|
void PrinterWebView::update_mode()
|
|
{
|
|
m_browser->EnableAccessToDevTools(wxGetApp().app_config->get_bool("developer_mode"));
|
|
}
|
|
|
|
/**
|
|
* Method that retrieves the current state from the web control and updates the
|
|
* GUI the reflect this current state.
|
|
*/
|
|
void PrinterWebView::UpdateState() {
|
|
// SetTitle(m_browser->GetCurrentTitle());
|
|
|
|
}
|
|
|
|
void PrinterWebView::OnClose(wxCloseEvent& evt)
|
|
{
|
|
this->Hide();
|
|
}
|
|
|
|
void PrinterWebView::SendAPIKey()
|
|
{
|
|
if (m_apikey_sent || m_apikey.IsEmpty())
|
|
return;
|
|
m_apikey_sent = true;
|
|
wxString script = wxString::Format(R"(
|
|
// Check if window.fetch exists before overriding
|
|
if (window.fetch) {
|
|
const originalFetch = window.fetch;
|
|
window.fetch = function(input, init = {}) {
|
|
init.headers = init.headers || {};
|
|
init.headers['X-API-Key'] = '%s';
|
|
return originalFetch(input, init);
|
|
};
|
|
}
|
|
)",
|
|
m_apikey);
|
|
m_browser->RemoveAllUserScripts();
|
|
|
|
// RemoveAllUserScripts causes WebView to forget about our script message handler,
|
|
// so re-add it here.
|
|
m_browser->RemoveScriptMessageHandler("wx");
|
|
if (m_browser->AddScriptMessageHandler("wx"))
|
|
WebView::MarkScriptMessageHandlerAdded(m_browser);
|
|
else
|
|
wxLogError("Could not add script message handler");
|
|
|
|
#ifdef __linux__
|
|
// Re-inject the vue-resize/WebKitGTK workaround that RemoveAllUserScripts just cleared.
|
|
inject_vue_resize_workaround(m_browser);
|
|
#endif
|
|
|
|
m_browser->AddUserScript(script);
|
|
m_browser->Reload();
|
|
}
|
|
|
|
void PrinterWebView::OnError(wxWebViewEvent &evt)
|
|
{
|
|
auto e = "unknown error";
|
|
switch (evt.GetInt()) {
|
|
case wxWEBVIEW_NAV_ERR_CONNECTION:
|
|
e = "wxWEBVIEW_NAV_ERR_CONNECTION";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_CERTIFICATE:
|
|
e = "wxWEBVIEW_NAV_ERR_CERTIFICATE";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_AUTH:
|
|
e = "wxWEBVIEW_NAV_ERR_AUTH";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_SECURITY:
|
|
e = "wxWEBVIEW_NAV_ERR_SECURITY";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_NOT_FOUND:
|
|
e = "wxWEBVIEW_NAV_ERR_NOT_FOUND";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_REQUEST:
|
|
e = "wxWEBVIEW_NAV_ERR_REQUEST";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_USER_CANCELLED:
|
|
e = "wxWEBVIEW_NAV_ERR_USER_CANCELLED";
|
|
break;
|
|
case wxWEBVIEW_NAV_ERR_OTHER:
|
|
e = "wxWEBVIEW_NAV_ERR_OTHER";
|
|
break;
|
|
}
|
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": error loading page %1% %2% %3% %4%") %evt.GetURL() %evt.GetTarget() %e %evt.GetString();
|
|
}
|
|
|
|
void PrinterWebView::OnLoaded(wxWebViewEvent& evt)
|
|
{
|
|
if (evt.GetURL().IsEmpty())
|
|
return;
|
|
//ORCA: url loaded successfully, safe to clear
|
|
m_url_deferred.clear();
|
|
SendAPIKey();
|
|
|
|
if (m_handler != nullptr) {
|
|
m_handler->on_loaded(evt);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PrinterWebView::OnNewWindow(wxWebViewEvent& evt)
|
|
{
|
|
const wxString url = evt.GetURL();
|
|
if (!url.empty())
|
|
wxLaunchDefaultBrowser(url);
|
|
evt.Veto();
|
|
}
|
|
|
|
void PrinterWebView::OnScriptMessage(wxWebViewEvent& evt)
|
|
{
|
|
if (m_handler != nullptr)
|
|
m_handler->on_script_message(evt);
|
|
}
|
|
|
|
|
|
} // GUI
|
|
} // Slic3r
|