From 229460595fd3abcc86132a20f6c7246860ed5d3c Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Wed, 14 Oct 2026 15:51:00 +0800 Subject: [PATCH] Give the Legacy Web Pages Their Theme and Host Bridge Without the User-Agent The bundled pages took two facts from the User-Agent header: whether the app is in dark mode ("dark") and whether they run inside OrcaSlicer at all ("BBL-Slicer", the gate on every message they post to the host). That made the header carry application state. #15104 switched Linux to WebKitGTK's own header so that Cloudflare accepts third-party print-host pages, and with it the home page came up light on a dark app and New Project, Open Project, the Project tab, the wizard and the web dialogs stopped reaching the host. The host now tells each page its theme through a document-start user script (window.__orcaTheme), added when the view is created, again on a theme change before the reload, and after PrinterWebView clears its user scripts. The page tests for the injected window.wx bridge instead of the header, which is what the plugin pages already do. Nothing reads the header any more, so Linux keeps sending WebKitGTK's own on every view. Windows and macOS keep the headers they always sent: the Windows wrapper still derives WebView2's colour scheme from it. Four copies of the helper file lived next to the pages that used them (homepage/js, guide/js, dialog/js, include), differing only in the relative path each one passed to its dark.css toggle. The toggle now switches whichever dark.css the page already links, so the one copy in include/ serves all 17 pages and the other three are gone. That file was stored with CRLF and is normalised to LF, which is most of its diff. --- .../web/dialog/ExportPresetDialog/index.html | 2 +- resources/web/dialog/PluginsDialog/index.html | 2 +- .../web/dialog/PresetBundleDialog/index.html | 2 +- .../web/dialog/TerminalDialog/index.html | 2 +- resources/web/dialog/js/globalapi.js | 339 -------- resources/web/guide/0/index.html | 2 +- resources/web/guide/1/index.html | 2 +- resources/web/guide/11/index.html | 2 +- resources/web/guide/21/index.html | 2 +- resources/web/guide/22/index.html | 2 +- resources/web/guide/23/index.html | 2 +- resources/web/guide/24/index.html | 2 +- resources/web/guide/4orca/index.html | 2 +- resources/web/guide/5/index.html | 2 +- resources/web/guide/js/globalapi.js | 339 -------- resources/web/homepage/js/globalapi.js | 340 -------- resources/web/include/globalapi.js | 800 ++++++++---------- resources/web/orca/missing_connection.html | 2 +- src/slic3r/GUI/PrinterWebView.cpp | 1 + src/slic3r/GUI/Widgets/WebView.cpp | 43 +- src/slic3r/GUI/Widgets/WebView.hpp | 4 + 21 files changed, 426 insertions(+), 1468 deletions(-) delete mode 100644 resources/web/dialog/js/globalapi.js delete mode 100644 resources/web/guide/js/globalapi.js delete mode 100644 resources/web/homepage/js/globalapi.js diff --git a/resources/web/dialog/ExportPresetDialog/index.html b/resources/web/dialog/ExportPresetDialog/index.html index 70f7fd19b6..4ffe1684f0 100644 --- a/resources/web/dialog/ExportPresetDialog/index.html +++ b/resources/web/dialog/ExportPresetDialog/index.html @@ -11,7 +11,7 @@ - + diff --git a/resources/web/dialog/PluginsDialog/index.html b/resources/web/dialog/PluginsDialog/index.html index ae4ffc8b9e..779869d1e2 100644 --- a/resources/web/dialog/PluginsDialog/index.html +++ b/resources/web/dialog/PluginsDialog/index.html @@ -13,7 +13,7 @@ - + diff --git a/resources/web/dialog/PresetBundleDialog/index.html b/resources/web/dialog/PresetBundleDialog/index.html index 76e32d72ac..1bb9bf8938 100644 --- a/resources/web/dialog/PresetBundleDialog/index.html +++ b/resources/web/dialog/PresetBundleDialog/index.html @@ -12,7 +12,7 @@ - + diff --git a/resources/web/dialog/TerminalDialog/index.html b/resources/web/dialog/TerminalDialog/index.html index 598a36e89d..83911f36b1 100644 --- a/resources/web/dialog/TerminalDialog/index.html +++ b/resources/web/dialog/TerminalDialog/index.html @@ -12,7 +12,7 @@ - + diff --git a/resources/web/dialog/js/globalapi.js b/resources/web/dialog/js/globalapi.js deleted file mode 100644 index f78f1e928b..0000000000 --- a/resources/web/dialog/js/globalapi.js +++ /dev/null @@ -1,339 +0,0 @@ - -/*------------------ Date Function ------------------------*/ -function GetFullToday( ) -{ - var d=new Date(); - - var nday=d.getDate(); - var nmonth=d.getMonth()+1; - var nyear=d.getFullYear(); - - var strM=nmonth+''; - if( nmonth<10 ) - strM='0'+nmonth; - - var strD=nday+''; - if( nday<10 ) - strD='0'+nday; - - return nyear+'-'+strM+'-'+strD; -} - -function GetFullDate() -{ - var d=new Date(); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - var strH=tDate.nhour+''; - if( tDate.nhour<10 ) - strH='0'+tDate.nhour; - - var strMin=tDate.nminute+''; - if( tDate.nminute<10 ) - strMin='0'+tDate.nminute; - - var strS=tDate.nsecond+''; - if( tDate.nsecond<10 ) - strS='0'+tDate.nsecond; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; - - return tDate; -} - - -function Unixtimestamp2Date( nSecond ) -{ - var d=new Date(nSecond*1000); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - - return tDate.strdate; -} - - -//------------Array Function------------- -Array.prototype.in_array = function (e) { - let sArray= ',' + this.join(this.S) + ','; - let skey=','+e+','; - - if(sArray.indexOf(skey)>=0) - return true; - else - return false; - } - - - -//------------String Function------------------ -/** -* Delete Left/Right Side Blank -*/ -String.prototype.trim=function() -{ - return this.replace(/(^\s*)|(\s*$)/g, ''); -} -/** -* Delete Left Side Blank -*/ -String.prototype.ltrim=function() -{ - return this.replace(/(^\s*)/g,''); -} -/** -* Delete Right Side Blank -*/ -String.prototype.rtrim=function() -{ - return this.replace(/(\s*$)/g,''); -} - - -//----------------Get Param------------- -function GetQueryString(name) -{ - var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); - var r = window.location.search.substr(1).match(reg); - if (r!=null) - { - return unescape(r[2]); - } - else - { - return null; - } -} - -function GetGetStr() -{ - let strGet=""; - - //获取当前URL - let url = document.location.href; - - //获取?的位置 - let index = url.indexOf("?") - if(index != -1) { - //截取出?后面的字符串 - strGet = url.substr(index + 1); - } - - return strGet; -} - - -/*--------------------JSON Function------------*/ - -/* -功能:检查一个字符串是不是标准的JSON格式 -参数: strJson 被检查的字符串 -返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 - 如果字符串不是标准JSON格式,则返回null -*/ -function IsJson( strJson ) -{ - var tJson=null; - try - { - tJson=JSON.parse(strJson); - } - catch(exception) - { - return null; - } - - return tJson; -} - -/*-----------------------Ajax Function--------------------*/ -/*对JQuery的Ajax函数的封装,只支持异步 -参数说明: - url 目标地址 - action post/get - data 字符串格式的发送内容 - asyn true---异步模式;false-----同步模式; -*/ -function HttpReq( url,action, data,callbackfunc) -{ - var strAction=action.toLowerCase(); - - if( strAction=="post") - { - $.post(url,data,callbackfunc); - } - else if( strAction=="get") - { - $.get(url,callbackfunc); - } -} - -/*---------------Cookie Function-------------------*/ -function setCookie(name, value, time='',path='') { - if(time && path){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; - }else if(time){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); - }else if(path){ - document.cookie = name + "=" + escape(value) + ";path="+path; - }else{ - document.cookie = name + "=" + escape(value); - } -} - -function getCookie(c_name) -{ - if(document.cookie.length > 0) { - c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 - if(c_start != -1) { - c_start = c_start + c_name.length + 1;//获取值的起点 - c_end = document.cookie.indexOf(";", c_start);//获取结尾处 - if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 - return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 - } - } - - return ""; -} - -function checkCookie(c_name) { - username = getCookie(c_name); - console.log(username); - if (username != null && username != "") - { return true; } - else - { return false; } -} - -function clearCookie(name) { - setCookie(name, "", -1); -} - - -/*--------Studio WX Message-------*/ -function IsInSlicer() -{ - let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); - - return bMatch; -} - - - -function SendWXMessage( strMsg ) -{ - let bCheck=IsInSlicer(); - - if(bCheck!=null) - { - window.wx.postMessage(strMsg); - } -} - -/*------CSS Link Control----*/ -function RemoveCssLink( LinkPath ) -{ - let pNow=$("head link[href='"+LinkPath+"']"); - - let nTotal=pNow.length; - for( let n=0;n0) - RemoveCssLink(DarkCssPath); - } - else - { - if(nNow==0) - AddCssLink(DarkCssPath); - } -} - -// Dialog pages are themed by the injected --orca-* contract + theme.css; the legacy dark.css poll is fully retired. \ No newline at end of file diff --git a/resources/web/guide/0/index.html b/resources/web/guide/0/index.html index c379078db5..e4e4614e87 100644 --- a/resources/web/guide/0/index.html +++ b/resources/web/guide/0/index.html @@ -10,7 +10,7 @@ - + diff --git a/resources/web/guide/1/index.html b/resources/web/guide/1/index.html index 521193706c..0a22b416d9 100644 --- a/resources/web/guide/1/index.html +++ b/resources/web/guide/1/index.html @@ -10,7 +10,7 @@ - + diff --git a/resources/web/guide/11/index.html b/resources/web/guide/11/index.html index db39174fe3..4e7574e6ff 100644 --- a/resources/web/guide/11/index.html +++ b/resources/web/guide/11/index.html @@ -11,7 +11,7 @@ - + diff --git a/resources/web/guide/21/index.html b/resources/web/guide/21/index.html index 7a92c15ab4..082e42c01a 100644 --- a/resources/web/guide/21/index.html +++ b/resources/web/guide/21/index.html @@ -13,7 +13,7 @@ - + diff --git a/resources/web/guide/22/index.html b/resources/web/guide/22/index.html index d9191a1ce9..2ec9dd841a 100644 --- a/resources/web/guide/22/index.html +++ b/resources/web/guide/22/index.html @@ -13,7 +13,7 @@ - + diff --git a/resources/web/guide/23/index.html b/resources/web/guide/23/index.html index c7f9d9f1ff..0aabc285ed 100644 --- a/resources/web/guide/23/index.html +++ b/resources/web/guide/23/index.html @@ -12,7 +12,7 @@ - + diff --git a/resources/web/guide/24/index.html b/resources/web/guide/24/index.html index 902736bf5a..7382e7cc22 100644 --- a/resources/web/guide/24/index.html +++ b/resources/web/guide/24/index.html @@ -13,7 +13,7 @@ - + diff --git a/resources/web/guide/4orca/index.html b/resources/web/guide/4orca/index.html index e5845d64d3..c3ce0e1fce 100644 --- a/resources/web/guide/4orca/index.html +++ b/resources/web/guide/4orca/index.html @@ -11,7 +11,7 @@ - + diff --git a/resources/web/guide/5/index.html b/resources/web/guide/5/index.html index 607a381d51..eecb6f08f0 100644 --- a/resources/web/guide/5/index.html +++ b/resources/web/guide/5/index.html @@ -11,7 +11,7 @@ - + diff --git a/resources/web/guide/js/globalapi.js b/resources/web/guide/js/globalapi.js deleted file mode 100644 index 2463dbe537..0000000000 --- a/resources/web/guide/js/globalapi.js +++ /dev/null @@ -1,339 +0,0 @@ - -/*------------------ Date Function ------------------------*/ -function GetFullToday( ) -{ - var d=new Date(); - - var nday=d.getDate(); - var nmonth=d.getMonth()+1; - var nyear=d.getFullYear(); - - var strM=nmonth+''; - if( nmonth<10 ) - strM='0'+nmonth; - - var strD=nday+''; - if( nday<10 ) - strD='0'+nday; - - return nyear+'-'+strM+'-'+strD; -} - -function GetFullDate() -{ - var d=new Date(); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - var strH=tDate.nhour+''; - if( tDate.nhour<10 ) - strH='0'+tDate.nhour; - - var strMin=tDate.nminute+''; - if( tDate.nminute<10 ) - strMin='0'+tDate.nminute; - - var strS=tDate.nsecond+''; - if( tDate.nsecond<10 ) - strS='0'+tDate.nsecond; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; - - return tDate; -} - - -function Unixtimestamp2Date( nSecond ) -{ - var d=new Date(nSecond*1000); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - - return tDate.strdate; -} - - -//------------Array Function------------- -Array.prototype.in_array = function (e) { - let sArray= ',' + this.join(this.S) + ','; - let skey=','+e+','; - - if(sArray.indexOf(skey)>=0) - return true; - else - return false; - } - - - -//------------String Function------------------ -/** -* Delete Left/Right Side Blank -*/ -String.prototype.trim=function() -{ - return this.replace(/(^\s*)|(\s*$)/g, ''); -} -/** -* Delete Left Side Blank -*/ -String.prototype.ltrim=function() -{ - return this.replace(/(^\s*)/g,''); -} -/** -* Delete Right Side Blank -*/ -String.prototype.rtrim=function() -{ - return this.replace(/(\s*$)/g,''); -} - - -//----------------Get Param------------- -function GetQueryString(name) -{ - var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); - var r = window.location.search.substr(1).match(reg); - if (r!=null) - { - return unescape(r[2]); - } - else - { - return null; - } -} - -function GetGetStr() -{ - let strGet=""; - - //获取当前URL - let url = document.location.href; - - //获取?的位置 - let index = url.indexOf("?") - if(index != -1) { - //截取出?后面的字符串 - strGet = url.substr(index + 1); - } - - return strGet; -} - - -/*--------------------JSON Function------------*/ - -/* -功能:检查一个字符串是不是标准的JSON格式 -参数: strJson 被检查的字符串 -返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 - 如果字符串不是标准JSON格式,则返回null -*/ -function IsJson( strJson ) -{ - var tJson=null; - try - { - tJson=JSON.parse(strJson); - } - catch(exception) - { - return null; - } - - return tJson; -} - -/*-----------------------Ajax Function--------------------*/ -/*对JQuery的Ajax函数的封装,只支持异步 -参数说明: - url 目标地址 - action post/get - data 字符串格式的发送内容 - asyn true---异步模式;false-----同步模式; -*/ -function HttpReq( url,action, data,callbackfunc) -{ - var strAction=action.toLowerCase(); - - if( strAction=="post") - { - $.post(url,data,callbackfunc); - } - else if( strAction=="get") - { - $.get(url,callbackfunc); - } -} - -/*---------------Cookie Function-------------------*/ -function setCookie(name, value, time='',path='') { - if(time && path){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; - }else if(time){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); - }else if(path){ - document.cookie = name + "=" + escape(value) + ";path="+path; - }else{ - document.cookie = name + "=" + escape(value); - } -} - -function getCookie(c_name) -{ - if(document.cookie.length > 0) { - c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 - if(c_start != -1) { - c_start = c_start + c_name.length + 1;//获取值的起点 - c_end = document.cookie.indexOf(";", c_start);//获取结尾处 - if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 - return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 - } - } - - return ""; -} - -function checkCookie(c_name) { - username = getCookie(c_name); - console.log(username); - if (username != null && username != "") - { return true; } - else - { return false; } -} - -function clearCookie(name) { - setCookie(name, "", -1); -} - - -/*--------Studio WX Message-------*/ -function IsInSlicer() -{ - let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); - - return bMatch; -} - - - -function SendWXMessage( strMsg ) -{ - let bCheck=IsInSlicer(); - - if(bCheck!=null) - { - window.wx.postMessage(strMsg); - } -} - -/*------CSS Link Control----*/ -function RemoveCssLink( LinkPath ) -{ - let pNow=$("head link[href='"+LinkPath+"']"); - - let nTotal=pNow.length; - for( let n=0;n0) - RemoveCssLink(DarkCssPath); - } - else - { - if(nNow==0) - AddCssLink(DarkCssPath); - } -} - -SwitchDarkMode( "../css/dark.css" ); \ No newline at end of file diff --git a/resources/web/homepage/js/globalapi.js b/resources/web/homepage/js/globalapi.js deleted file mode 100644 index 80bbaa01d5..0000000000 --- a/resources/web/homepage/js/globalapi.js +++ /dev/null @@ -1,340 +0,0 @@ - -/*------------------ Date Function ------------------------*/ -function GetFullToday( ) -{ - var d=new Date(); - - var nday=d.getDate(); - var nmonth=d.getMonth()+1; - var nyear=d.getFullYear(); - - var strM=nmonth+''; - if( nmonth<10 ) - strM='0'+nmonth; - - var strD=nday+''; - if( nday<10 ) - strD='0'+nday; - - return nyear+'-'+strM+'-'+strD; -} - -function GetFullDate() -{ - var d=new Date(); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - var strH=tDate.nhour+''; - if( tDate.nhour<10 ) - strH='0'+tDate.nhour; - - var strMin=tDate.nminute+''; - if( tDate.nminute<10 ) - strMin='0'+tDate.nminute; - - var strS=tDate.nsecond+''; - if( tDate.nsecond<10 ) - strS='0'+tDate.nsecond; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; - - return tDate; -} - - -function Unixtimestamp2Date( nSecond ) -{ - var d=new Date(nSecond*1000); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - - return tDate.strdate; -} - - -//------------Array Function------------- -Array.prototype.in_array = function (e) { - let sArray= ',' + this.join(this.S) + ','; - let skey=','+e+','; - - if(sArray.indexOf(skey)>=0) - return true; - else - return false; - } - - - -//------------String Function------------------ -/** -* Delete Left/Right Side Blank -*/ -String.prototype.trim=function() -{ - return this.replace(/(^\s*)|(\s*$)/g, ''); -} -/** -* Delete Left Side Blank -*/ -String.prototype.ltrim=function() -{ - return this.replace(/(^\s*)/g,''); -} -/** -* Delete Right Side Blank -*/ -String.prototype.rtrim=function() -{ - return this.replace(/(\s*$)/g,''); -} - - -//----------------Get Param------------- -function GetQueryString(name) -{ - var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); - var r = window.location.search.substr(1).match(reg); - if (r!=null) - { - return unescape(r[2]); - } - else - { - return null; - } -} - -function GetGetStr() -{ - let strGet=""; - - //获取当前URL - let url = document.location.href; - - //获取?的位置 - let index = url.indexOf("?") - if(index != -1) { - //截取出?后面的字符串 - strGet = url.substr(index + 1); - } - - return strGet; -} - - -/*--------------------JSON Function------------*/ - -/* -功能:检查一个字符串是不是标准的JSON格式 -参数: strJson 被检查的字符串 -返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 - 如果字符串不是标准JSON格式,则返回null -*/ -function IsJson( strJson ) -{ - var tJson=null; - try - { - tJson=JSON.parse(strJson); - } - catch(exception) - { - return null; - } - - return tJson; -} - -/*-----------------------Ajax Function--------------------*/ -/*对JQuery的Ajax函数的封装,只支持异步 -参数说明: - url 目标地址 - action post/get - data 字符串格式的发送内容 - asyn true---异步模式;false-----同步模式; -*/ -function HttpReq( url,action, data,callbackfunc) -{ - var strAction=action.toLowerCase(); - - if( strAction=="post") - { - $.post(url,data,callbackfunc); - } - else if( strAction=="get") - { - $.get(url,callbackfunc); - } -} - -/*---------------Cookie Function-------------------*/ -function setCookie(name, value, time='',path='') { - if(time && path){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; - }else if(time){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); - }else if(path){ - document.cookie = name + "=" + escape(value) + ";path="+path; - }else{ - document.cookie = name + "=" + escape(value); - } -} - -function getCookie(c_name) -{ - if(document.cookie.length > 0) { - c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 - if(c_start != -1) { - c_start = c_start + c_name.length + 1;//获取值的起点 - c_end = document.cookie.indexOf(";", c_start);//获取结尾处 - if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 - return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 - } - } - - return ""; -} - -function checkCookie(c_name) { - username = getCookie(c_name); - console.log(username); - if (username != null && username != "") - { return true; } - else - { return false; } -} - -function clearCookie(name) { - setCookie(name, "", -1); -} - - -/*--------Studio WX Message-------*/ -function IsInSlicer() -{ - let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); - - return bMatch; -} - - - -function SendWXMessage( strMsg ) -{ - let bCheck=IsInSlicer(); - - if(bCheck!=null) - { - window.wx.postMessage(strMsg); - } -} - - -/*------CSS Link Control----*/ -function RemoveCssLink( LinkPath ) -{ - let pNow=$("head link[href='"+LinkPath+"']"); - - let nTotal=pNow.length; - for( let n=0;n0) - RemoveCssLink(DarkCssPath); - } - else - { - if(nNow==0) - AddCssLink(DarkCssPath); - } -} - -SwitchDarkMode("css/dark.css"); \ No newline at end of file diff --git a/resources/web/include/globalapi.js b/resources/web/include/globalapi.js index 0553483e1a..f22310ca6f 100644 --- a/resources/web/include/globalapi.js +++ b/resources/web/include/globalapi.js @@ -1,427 +1,373 @@ - -/*------------------ Date Function ------------------------*/ -function GetFullToday( ) -{ - var d=new Date(); - - var nday=d.getDate(); - var nmonth=d.getMonth()+1; - var nyear=d.getFullYear(); - - var strM=nmonth+''; - if( nmonth<10 ) - strM='0'+nmonth; - - var strD=nday+''; - if( nday<10 ) - strD='0'+nday; - - return nyear+'-'+strM+'-'+strD; -} - -function GetFullDate() -{ - var d=new Date(); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - var strH=tDate.nhour+''; - if( tDate.nhour<10 ) - strH='0'+tDate.nhour; - - var strMin=tDate.nminute+''; - if( tDate.nminute<10 ) - strMin='0'+tDate.nminute; - - var strS=tDate.nsecond+''; - if( tDate.nsecond<10 ) - strS='0'+tDate.nsecond; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; - - return tDate; -} - - -function Unixtimestamp2Date( nSecond ) -{ - var d=new Date(nSecond*1000); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - - return tDate.strdate; -} - - -//------------Array Function------------- -Array.prototype.in_array = function (e) { - let sArray= ',' + this.join(this.S) + ','; - let skey=','+e+','; - - if(sArray.indexOf(skey)>=0) - return true; - else - return false; - } - - - -//------------String Function------------------ -/** -* Delete Left/Right Side Blank -*/ -String.prototype.trim=function() -{ - return this.replace(/(^\s*)|(\s*$)/g, ''); -} -/** -* Delete Left Side Blank -*/ -String.prototype.ltrim=function() -{ - return this.replace(/(^\s*)/g,''); -} -/** -* Delete Right Side Blank -*/ -String.prototype.rtrim=function() -{ - return this.replace(/(\s*$)/g,''); -} - - -//----------------Get Param------------- -function GetQueryString(name) -{ - var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); - var r = window.location.search.substr(1).match(reg); - if (r!=null) - { - return unescape(r[2]); - } - else - { - return null; - } -} - -function GetGetStr() -{ - let strGet=""; - - //获取当前URL - let url = document.location.href; - - //获取?的位置 - let index = url.indexOf("?") - if(index != -1) { - //截取出?后面的字符串 - strGet = url.substr(index + 1); - } - - return strGet; -} - -/*--------------File Function--------------*/ -function getFileName(path) -{ - var pos1 = path.lastIndexOf('/'); - var pos2 = path.lastIndexOf('\\'); - var pos = Math.max(pos1, pos2); - if (pos < 0) { - return null; - } - else - { - return path.substring(pos + 1); - } -} - -function getFileTail(path) -{ - var pos = path.lastIndexOf('.'); - if (pos < 0) { - return null; - } - else - { - return path.substring(pos + 1); - } -} - -/*--------------String Function-----------*/ -function html_encode(str) -{ - var s = ""; - if (str.length == 0) return ""; - s = str.replace(/&/g, "&"); - s = s.replace(//g, ">"); - s = s.replace(/ /g, " "); - s = s.replace(/\'/g, "'"); - s = s.replace(/\"/g, """); - s = s.replace(/\n/g, "
"); - - return s; -} - -function html_decode(str) -{ - var s = ""; - if (str.length == 0) return ""; - s = str.replace(/&/g, "&"); - s = s.replace(/</g, "<"); - s = s.replace(/>/g, ">"); - s = s.replace(/ /g, " "); - s = s.replace(/'/g, "\'"); - s = s.replace(/"/g, "\""); - s = s.replace(//g, "\n"); - - return s; -} - -/*--------------------JSON Function------------*/ - -/* -功能:检查一个字符串是不是标准的JSON格式 -参数: strJson 被检查的字符串 -返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 - 如果字符串不是标准JSON格式,则返回null -*/ -function IsJson( strJson ) -{ - var tJson=null; - try - { - tJson=JSON.parse(strJson); - } - catch(exception) - { - return null; - } - - return tJson; -} - -function DecodeJsonObject( pJson ) -{ - let tmpJson=JSON.stringify(pJson); - tmpJson=decodeURIComponent(tmpJson); - - pJson=JSON.parse(tmpJson); - - return pJson; -} - -/*-----------------------Ajax Function--------------------*/ -/*对JQuery的Ajax函数的封装,只支持异步 -参数说明: - url 目标地址 - action post/get - data 字符串格式的发送内容 - asyn true---异步模式;false-----同步模式; -*/ -function HttpReq( url,action, data,callbackfunc) -{ - var strAction=action.toLowerCase(); - - if( strAction=="post") - { - $.post(url,data,callbackfunc); - } - else if( strAction=="get") - { - $.get(url,callbackfunc); - } -} - -/*---------------Cookie Function-------------------*/ -function setCookie(name, value, time='',path='') { - if(time && path){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; - }else if(time){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); - }else if(path){ - document.cookie = name + "=" + escape(value) + ";path="+path; - }else{ - document.cookie = name + "=" + escape(value); - } -} - -function getCookie(c_name) -{ - if(document.cookie.length > 0) { - c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 - if(c_start != -1) { - c_start = c_start + c_name.length + 1;//获取值的起点 - c_end = document.cookie.indexOf(";", c_start);//获取结尾处 - if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 - return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 - } - } - - return ""; -} - -function checkCookie(c_name) { - username = getCookie(c_name); - console.log(username); - if (username != null && username != "") - { return true; } - else - { return false; } -} - -function clearCookie(name) { - setCookie(name, "", -1); -} - - -/*--------Studio WX Message-------*/ -function IsInSlicer() -{ - let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); - - return bMatch; -} - - - -function SendWXMessage( strMsg ) -{ - let bCheck=IsInSlicer(); - - if(bCheck!=null) - { - window.wx.postMessage(strMsg); - } -} - -function SendWXDebugInfo( strMsg ) -{ - var tSend={}; - tSend['sequence_id']=Math.round(new Date() / 1000); - tSend['command']="debug_info"; - tSend['msg']=strMsg; - - SendWXMessage( JSON.stringify(tSend) ); -} - -function OpenUrlInLocalBrowser( strUrl ) -{ - var tSend={}; - tSend['sequence_id']=Math.round(new Date() / 1000); - tSend['command']="common_openurl"; - tSend['url']=strUrl; - - SendWXMessage( JSON.stringify(tSend) ); -} - - -/*------CSS Link Control----*/ -function RemoveCssLink( LinkPath ) -{ - let pNow=$("head link[href='"+LinkPath+"']"); - - let nTotal=pNow.length; - for( let n=0;n0) - RemoveCssLink(DarkCssPath); - } - else - { - if(nNow==0) - AddCssLink(DarkCssPath); - } -} - -SwitchDarkMode( "./css/dark.css" ); \ No newline at end of file + +/*------------------ Date Function ------------------------*/ +function GetFullToday( ) +{ + var d=new Date(); + + var nday=d.getDate(); + var nmonth=d.getMonth()+1; + var nyear=d.getFullYear(); + + var strM=nmonth+''; + if( nmonth<10 ) + strM='0'+nmonth; + + var strD=nday+''; + if( nday<10 ) + strD='0'+nday; + + return nyear+'-'+strM+'-'+strD; +} + +function GetFullDate() +{ + var d=new Date(); + + var tDate={}; + + tDate.nyear=d.getFullYear(); + tDate.nmonth=d.getMonth()+1; + tDate.nday=d.getDate(); + + tDate.nhour=d.getHours(); + tDate.nminute=d.getMinutes(); + tDate.nsecond=d.getSeconds(); + + tDate.nweek=d.getDay(); + tDate.ndate=d.getDate(); + + var strM=tDate.nmonth+''; + if( tDate.nmonth<10 ) + strM='0'+tDate.nmonth; + + var strD=tDate.nday+''; + if( tDate.nday<10 ) + strD='0'+tDate.nday; + + var strH=tDate.nhour+''; + if( tDate.nhour<10 ) + strH='0'+tDate.nhour; + + var strMin=tDate.nminute+''; + if( tDate.nminute<10 ) + strMin='0'+tDate.nminute; + + var strS=tDate.nsecond+''; + if( tDate.nsecond<10 ) + strS='0'+tDate.nsecond; + + tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; + tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; + + return tDate; +} + + +function Unixtimestamp2Date( nSecond ) +{ + var d=new Date(nSecond*1000); + + var tDate={}; + + tDate.nyear=d.getFullYear(); + tDate.nmonth=d.getMonth()+1; + tDate.nday=d.getDate(); + + tDate.nhour=d.getHours(); + tDate.nminute=d.getMinutes(); + tDate.nsecond=d.getSeconds(); + + tDate.nweek=d.getDay(); + tDate.ndate=d.getDate(); + + var strM=tDate.nmonth+''; + if( tDate.nmonth<10 ) + strM='0'+tDate.nmonth; + + var strD=tDate.nday+''; + if( tDate.nday<10 ) + strD='0'+tDate.nday; + + tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; + + return tDate.strdate; +} + + +//------------Array Function------------- +Array.prototype.in_array = function (e) { + let sArray= ',' + this.join(this.S) + ','; + let skey=','+e+','; + + if(sArray.indexOf(skey)>=0) + return true; + else + return false; + } + + + +//------------String Function------------------ +/** +* Delete Left/Right Side Blank +*/ +String.prototype.trim=function() +{ + return this.replace(/(^\s*)|(\s*$)/g, ''); +} +/** +* Delete Left Side Blank +*/ +String.prototype.ltrim=function() +{ + return this.replace(/(^\s*)/g,''); +} +/** +* Delete Right Side Blank +*/ +String.prototype.rtrim=function() +{ + return this.replace(/(\s*$)/g,''); +} + + +//----------------Get Param------------- +function GetQueryString(name) +{ + var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); + var r = window.location.search.substr(1).match(reg); + if (r!=null) + { + return unescape(r[2]); + } + else + { + return null; + } +} + +function GetGetStr() +{ + let strGet=""; + + //获取当前URL + let url = document.location.href; + + //获取?的位置 + let index = url.indexOf("?") + if(index != -1) { + //截取出?后面的字符串 + strGet = url.substr(index + 1); + } + + return strGet; +} + +/*--------------File Function--------------*/ +function getFileName(path) +{ + var pos1 = path.lastIndexOf('/'); + var pos2 = path.lastIndexOf('\\'); + var pos = Math.max(pos1, pos2); + if (pos < 0) { + return null; + } + else + { + return path.substring(pos + 1); + } +} + +function getFileTail(path) +{ + var pos = path.lastIndexOf('.'); + if (pos < 0) { + return null; + } + else + { + return path.substring(pos + 1); + } +} + +/*--------------String Function-----------*/ +function html_encode(str) +{ + var s = ""; + if (str.length == 0) return ""; + s = str.replace(/&/g, "&"); + s = s.replace(//g, ">"); + s = s.replace(/ /g, " "); + s = s.replace(/\'/g, "'"); + s = s.replace(/\"/g, """); + s = s.replace(/\n/g, "
"); + + return s; +} + +function html_decode(str) +{ + var s = ""; + if (str.length == 0) return ""; + s = str.replace(/&/g, "&"); + s = s.replace(/</g, "<"); + s = s.replace(/>/g, ">"); + s = s.replace(/ /g, " "); + s = s.replace(/'/g, "\'"); + s = s.replace(/"/g, "\""); + s = s.replace(//g, "\n"); + + return s; +} + +/*--------------------JSON Function------------*/ + +/* +功能:检查一个字符串是不是标准的JSON格式 +参数: strJson 被检查的字符串 +返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 + 如果字符串不是标准JSON格式,则返回null +*/ +function IsJson( strJson ) +{ + var tJson=null; + try + { + tJson=JSON.parse(strJson); + } + catch(exception) + { + return null; + } + + return tJson; +} + +function DecodeJsonObject( pJson ) +{ + let tmpJson=JSON.stringify(pJson); + tmpJson=decodeURIComponent(tmpJson); + + pJson=JSON.parse(tmpJson); + + return pJson; +} + +/*-----------------------Ajax Function--------------------*/ +/*对JQuery的Ajax函数的封装,只支持异步 +参数说明: + url 目标地址 + action post/get + data 字符串格式的发送内容 + asyn true---异步模式;false-----同步模式; +*/ +function HttpReq( url,action, data,callbackfunc) +{ + var strAction=action.toLowerCase(); + + if( strAction=="post") + { + $.post(url,data,callbackfunc); + } + else if( strAction=="get") + { + $.get(url,callbackfunc); + } +} + +/*---------------Cookie Function-------------------*/ +function setCookie(name, value, time='',path='') { + if(time && path){ + var strsec = time * 1000; + var exp = new Date(); + exp.setTime(exp.getTime() + strsec * 1); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; + }else if(time){ + var strsec = time * 1000; + var exp = new Date(); + exp.setTime(exp.getTime() + strsec * 1); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); + }else if(path){ + document.cookie = name + "=" + escape(value) + ";path="+path; + }else{ + document.cookie = name + "=" + escape(value); + } +} + +function getCookie(c_name) +{ + if(document.cookie.length > 0) { + c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 + if(c_start != -1) { + c_start = c_start + c_name.length + 1;//获取值的起点 + c_end = document.cookie.indexOf(";", c_start);//获取结尾处 + if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 + return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 + } + } + + return ""; +} + +function checkCookie(c_name) { + username = getCookie(c_name); + console.log(username); + if (username != null && username != "") + { return true; } + else + { return false; } +} + +function clearCookie(name) { + setCookie(name, "", -1); +} + + +/*--------Studio WX Message-------*/ +function IsInSlicer() +{ + // window.wx is the host's message handler, registered on every webview OrcaSlicer creates. + return !!(window.wx && window.wx.postMessage); +} + + + +function SendWXMessage( strMsg ) +{ + if( IsInSlicer() ) + window.wx.postMessage(strMsg); +} + +function SendWXDebugInfo( strMsg ) +{ + var tSend={}; + tSend['sequence_id']=Math.round(new Date() / 1000); + tSend['command']="debug_info"; + tSend['msg']=strMsg; + + SendWXMessage( JSON.stringify(tSend) ); +} + +function OpenUrlInLocalBrowser( strUrl ) +{ + var tSend={}; + tSend['sequence_id']=Math.round(new Date() / 1000); + tSend['command']="common_openurl"; + tSend['url']=strUrl; + + SendWXMessage( JSON.stringify(tSend) ); +} + + +/*------Dark Mode------*/ +// The host sets window.__orcaTheme at document start (WebView::AddHostThemeScript) and reloads the +// page when the theme changes. Every legacy page links its dark.css up front; it is switched on and +// off here instead of being added and removed, so the page's own path to it does not matter. +function ExecuteDarkMode() +{ + let bDarkMode = window.__orcaTheme === "dark"; + document.querySelectorAll('head link[href$="dark.css"]').forEach( function(link) { link.disabled = !bDarkMode; } ); +} + +ExecuteDarkMode(); diff --git a/resources/web/orca/missing_connection.html b/resources/web/orca/missing_connection.html index 543eb2c787..00fcc061ba 100644 --- a/resources/web/orca/missing_connection.html +++ b/resources/web/orca/missing_connection.html @@ -9,7 +9,7 @@ - + diff --git a/src/slic3r/GUI/PrinterWebView.cpp b/src/slic3r/GUI/PrinterWebView.cpp index e3c1202b5a..f8893f7591 100644 --- a/src/slic3r/GUI/PrinterWebView.cpp +++ b/src/slic3r/GUI/PrinterWebView.cpp @@ -288,6 +288,7 @@ void PrinterWebView::SendAPIKey() WebView::MarkScriptMessageHandlerAdded(m_browser); else wxLogError("Could not add script message handler"); + WebView::AddHostThemeScript(m_browser); #ifdef __linux__ // Re-inject the vue-resize/WebKitGTK workaround that RemoveAllUserScripts just cleared. diff --git a/src/slic3r/GUI/Widgets/WebView.cpp b/src/slic3r/GUI/Widgets/WebView.cpp index d102aff2ef..8b3cf98ba0 100644 --- a/src/slic3r/GUI/Widgets/WebView.cpp +++ b/src/slic3r/GUI/Widgets/WebView.cpp @@ -284,6 +284,25 @@ static WebViewRef *webview_ref(wxWebView *webView) return webView ? static_cast(webView->GetRefData()) : nullptr; } +#ifndef __linux__ +// The User-Agent Windows and macOS have always sent, with the app's version, dark mode and language +// appended. No bundled page reads it any more (they get the theme from the script below), so Linux +// sends WebKitGTK's own, which Cloudflare accepts on third-party print-host pages. +static wxString tagged_user_agent(bool dark) +{ + const wxString language_code = Slic3r::GUI::wxGetApp().current_language_code().BeforeFirst('_'); + return 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); +} +#endif + +// Document-start script the legacy pages read their theme from (globalapi.js). Plugin pages have +// their own, richer contract in WebViewHostDialog. +static wxString host_theme_script() +{ + return wxString::Format("window.__orcaTheme=\"%s\";", Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light"); +} + wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url) { #if wxUSE_WEBVIEW_EDGE @@ -314,9 +333,8 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url) if (webView) { webView->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); - wxString language_code = Slic3r::GUI::wxGetApp().current_language_code().BeforeFirst('_'); - language_code = language_code.ToStdString(); #ifdef __WIN32__ + const wxString language_code = Slic3r::GUI::wxGetApp().current_language_code().BeforeFirst('_'); 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())); @@ -337,10 +355,10 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url) } webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); #ifndef __linux__ - 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())); + webView->SetUserAgent(tagged_user_agent(Slic3r::GUI::wxGetApp().dark_mode())); #endif #endif + WebView::AddHostThemeScript(webView); #ifdef __WXMAC__ WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend(); Slic3r::GUI::WKWebView_setTransparentBackground(wkWebView); @@ -393,6 +411,11 @@ void WebView::MarkScriptMessageHandlerAdded(wxWebView * webView) ref->m_script_handler_added = true; } +void WebView::AddHostThemeScript(wxWebView *webView) +{ + webView->AddUserScript(host_theme_script(), wxWEBVIEW_INJECT_AT_DOCUMENT_START); +} + bool WebView::NeedsRecreateOnShow() { const bool recreating = Slic3r::GUI::wxGetApp().is_recreating_gui(); @@ -468,14 +491,16 @@ bool WebView::RunScript(wxWebView *webView, wxString const &javascript) 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(); +#ifndef __linux__ + const wxString user_agent = tagged_user_agent(Slic3r::GUI::wxGetApp().dark_mode()); +#endif for (auto webView : g_webviews) { #ifndef __linux__ - 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())); + webView->SetUserAgent(user_agent); #endif + // User scripts run in the order they were added, so the newest theme assignment wins on the + // reload below; the earlier ones are a few bytes each. + AddHostThemeScript(webView); // 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). diff --git a/src/slic3r/GUI/Widgets/WebView.hpp b/src/slic3r/GUI/Widgets/WebView.hpp index cdd885f9bd..9154f7a8ca 100644 --- a/src/slic3r/GUI/Widgets/WebView.hpp +++ b/src/slic3r/GUI/Widgets/WebView.hpp @@ -26,6 +26,10 @@ public: // Marks "wx" as registered so CreateWebView's deferred add skips the duplicate. static void MarkScriptMessageHandlerAdded(wxWebView * webView); + // Tells the page the app's theme at document start (window.__orcaTheme). Added by CreateWebView and + // again on a theme change; a view that cleared its user scripts has to add it back. + static void AddHostThemeScript(wxWebView *webView); + // On Windows, a WebView2 backend created during a GUI rebuild (language switch) can come up // ignoring every navigation. A panel that gets true here recreates its view on first Show(). static bool NeedsRecreateOnShow();