#include "webview_utils.h" #include "defines/globals.h" #include "fmt/xchar.h" #include "global/globals.h" #include "log/candidate_diag_log.h" #include "spdlog/spdlog.h" #include "utils/common_utils.h" #include #undef DIAG_LOGF #define DIAG_LOGF(...) ((void)0) namespace json = boost::json; void MeasureDomUpdateTime(ComPtr webview) { std::wstring script = LR"(document.body.innerHTML = '
1. 原来
2. 如此
3. 竟然
4. 这样
5. 可恶
6. 棋盘
7. 磨合
8. 樱花
';)"; auto start = std::chrono::high_resolution_clock::now(); webview->ExecuteScript(script.c_str(), nullptr); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); std::wstring message = L"DOM update time: " + std::to_wstring(duration.count()) + L" μs"; (void)0; } std::pair ParseDivSize(const std::wstring &jsonResult) { std::string jsonStr = wstring_to_string(jsonResult); auto size = std::make_pair(static_cast(::CANDIDATE_WINDOW_WIDTH), static_cast(::CANDIDATE_WINDOW_HEIGHT)); try { json::value parsed = json::parse(jsonStr); if (parsed.is_string()) { parsed = json::parse(parsed.as_string()); } double width = parsed.at("width").is_int64() // ? parsed.at("width").as_int64() // : parsed.at("width").as_double(); // double height = parsed.at("height").is_int64() // ? parsed.at("height").as_int64() // : parsed.at("height").as_double(); // size = std::make_pair(width, height); } catch (const std::exception &e) { (void)0; } return size; } namespace { void GetCandidateCardSize( ComPtr webview, const wchar_t *boxId, const wchar_t *parentId, double maxWidthDip, double maxHeightDip, std::function)> callback) { if (!webview) { DIAG_LOGF(L"ui-measure box={} parent={} skipped: no webview", boxId, parentId); callback({0.0, 0.0}); return; } if (maxWidthDip < 1.0) { maxWidthDip = static_cast(::CANDIDATE_WINDOW_MAX_WIDTH_DIP); } if (maxHeightDip < 1.0) { // Same order of magnitude as the stable quarter-screen host height. maxHeightDip = static_cast(::CANDIDATE_WINDOW_MAX_WIDTH_DIP); } std::wstring script = fmt::format( LR"( (function() {{ var box = document.getElementById("{0}"); var el = document.getElementById("{1}"); var target = box || el; if (!target) {{ return JSON.stringify({{width: 0, height: 0}}); }} var maxW = {2}; var maxH = {3}; if (box) {{ box.style.maxWidth = maxW + "px"; box.style.maxHeight = maxH + "px"; box.style.width = "fit-content"; box.style.boxSizing = "border-box"; box.style.whiteSpace = "nowrap"; box.style.overflowX = "hidden"; box.style.overflowY = "auto"; }} if (el) {{ el.style.maxWidth = maxW + "px"; el.style.width = "fit-content"; }} void target.offsetWidth; void target.offsetHeight; var rect = target.getBoundingClientRect(); // scrollWidth catches glyph overflow that rect/offsetWidth miss at // high DPI; +1 DIP avoids ceil→floor undersize that clips the last // candidate against SetWindowRgn / screen edges. var width = Math.min( maxW, Math.max(rect.width, target.offsetWidth || 0, target.scrollWidth || 0) + 1); // Visible (capped) height for host region — NOT full scrollHeight. var height = Math.min( maxH, Math.max(rect.height, target.offsetHeight || 0) + 1); return JSON.stringify({{width: width, height: height}}); }})(); )", boxId, parentId, maxWidthDip, maxHeightDip); const HRESULT submitHr = webview->ExecuteScript( script.c_str(), Callback([callback, boxId = std::wstring(boxId), parentId = std::wstring(parentId), maxWidthDip, maxHeightDip](HRESULT errorCode, LPCWSTR result) -> HRESULT { std::pair size{}; if (SUCCEEDED(errorCode) && result) { size = ParseDivSize(result); } DIAG_LOGF(L"ui-measure box={} parent={} max_dip=({:.1f},{:.1f}) callback_hr={:#x} " L"result_chars={} measured_dip=({:.2f},{:.2f})", boxId, parentId, maxWidthDip, maxHeightDip, static_cast(errorCode), result ? wcslen(result) : 0, size.first, size.second); callback(size); return S_OK; }).Get()); DIAG_LOGF(L"ui-measure submit box={} parent={} max_dip=({:.1f},{:.1f}) hr={:#x}", boxId, parentId, maxWidthDip, maxHeightDip, static_cast(submitHr)); if (FAILED(submitHr)) { callback({0.0, 0.0}); } } } // namespace void GetContainerSizeCand(ComPtr webview, std::function)> callback, double maxWidthDip, double maxHeightDip) { GetCandidateCardSize(webview, L"measureContainer", L"measureContainerParent", maxWidthDip, maxHeightDip, std::move(callback)); } void GetRealCandidateCardSize(ComPtr webview, std::function)> callback, double maxWidthDip, double maxHeightDip) { GetCandidateCardSize(webview, L"realContainer", L"realContainerParent", maxWidthDip, maxHeightDip, std::move(callback)); } void GetContainerSizeFtb(ComPtr webview, std::function)> callback) { if (!webview) { callback({0.0, 0.0}); return; } const wchar_t *script = LR"((function() { var el = document.querySelector('.status-bar'); if (!el) { return JSON.stringify({width: 0, height: 0}); } void el.offsetWidth; var rect = el.getBoundingClientRect(); var width = Math.max(rect.width, el.offsetWidth || 0, el.scrollWidth || 0); var height = Math.max(rect.height, el.offsetHeight || 0, el.scrollHeight || 0); return JSON.stringify({width: width, height: height}); })();)"; const HRESULT submitHr = webview->ExecuteScript( script, Callback([callback](HRESULT errorCode, LPCWSTR result) -> HRESULT { std::pair size{}; if (SUCCEEDED(errorCode) && result) { size = ParseDivSize(result); } callback(size); return S_OK; }).Get()); (void)submitHr; } void GetContainerSizeMenu(ComPtr webview, std::function)> callback) { std::wstring script = LR"( (function() { document.documentElement.style.overflow = "hidden"; document.body.style.overflow = "hidden"; var menu = document.getElementById("menuContainer"); var rect = menu.getBoundingClientRect(); return JSON.stringify({ width: Math.max(rect.width, menu.offsetWidth || 0, menu.scrollWidth || 0), height: Math.max(rect.height, menu.offsetHeight || 0, menu.scrollHeight || 0) }); })(); )"; webview->ExecuteScript( // script.c_str(), // Callback([callback](HRESULT errorCode, LPCWSTR result) -> HRESULT { std::pair size; if (SUCCEEDED(errorCode) && result) { size = ParseDivSize(result); // (void)0; } else { } callback(size); return S_OK; }).Get()); } void MoveContainerBottom(ComPtr webview, int marginTop) { MoveContainerBottom(webview, marginTop, nullptr); } void MoveContainerBottom(ComPtr webview, int marginTop, std::function onComplete) { if (!webview) { if (onComplete) { onComplete(); } return; } std::wstring script; script.reserve(256); script.append(L"var el = document.getElementById('realContainerParent');"); script.append(L"if (el) {"); script.append(L"el.style.marginTop = '"); script.append(std::to_wstring(marginTop)); script.append(L"px';"); script.append(L"el.style.marginLeft = '"); script.append(std::to_wstring(Global::MarginLeft)); script.append(L"px';"); script.append(L"}"); if (!onComplete) { webview->ExecuteScript(script.c_str(), nullptr); return; } webview->ExecuteScript( script.c_str(), Callback([onComplete](HRESULT, LPCWSTR) -> HRESULT { onComplete(); return S_OK; }).Get()); } void MakeBodyVisible(ComPtr webview) { if (!webview) { return; } std::wstring script = L"document.body.style.visibility = \"visible\";"; webview->ExecuteScript(script.c_str(), nullptr); }