/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "iv/iv_controller.h" #include "base/platform/base_platform_info.h" #include "iv/iv_data.h" #include "ui/widgets/rp_window.h" #include "webview/webview_data_stream_memory.h" #include "webview/webview_embed.h" #include "webview/webview_interface.h" #include "styles/palette.h" #include #include #include #include #include #include namespace Iv { Controller::Controller() = default; Controller::~Controller() { _webview = nullptr; _window = nullptr; } void Controller::show(const QString &dataPath, Prepared page) { _window = std::make_unique(); const auto window = _window.get(); window->setGeometry({ 200, 200, 600, 800 }); const auto container = Ui::CreateChild( window->body().get()); window->sizeValue() | rpl::start_with_next([=](QSize size) { container->setGeometry(QRect(QPoint(), size)); }, container->lifetime()); container->show(); _webview = std::make_unique( container, Webview::WindowConfig{ .userDataPath = dataPath }); const auto raw = _webview.get(); window->lifetime().add([=] { _webview = nullptr; }); if (!raw->widget()) { _events.fire(Event::Close); return; } window->events( ) | rpl::start_with_next([=](not_null e) { if (e->type() == QEvent::Close) { close(); } else if (e->type() == QEvent::KeyPress) { const auto event = static_cast(e.get()); if (event->key() == Qt::Key_Escape) { escape(); } } }, window->lifetime()); raw->widget()->show(); container->geometryValue( ) | rpl::start_with_next([=](QRect geometry) { raw->widget()->setGeometry(geometry); }, container->lifetime()); container->paintRequest() | rpl::start_with_next([=](QRect clip) { QPainter(container).fillRect(clip, st::windowBg); }, container->lifetime()); raw->setNavigationStartHandler([=](const QString &uri, bool newWindow) { return true; }); raw->setNavigationDoneHandler([=](bool success) { }); raw->setMessageHandler([=](const QJsonDocument &message) { crl::on_main(_window.get(), [=] { const auto object = message.object(); const auto event = object.value("event").toString(); if (event == u"keydown"_q) { const auto key = object.value("key").toString(); const auto modifier = object.value("modifier").toString(); const auto ctrl = Platform::IsMac() ? u"cmd"_q : u"ctrl"_q; if (key == u"escape"_q) { escape(); } else if (key == u"w"_q && modifier == ctrl) { close(); } else if (key == u"m"_q && modifier == ctrl) { minimize(); } else if (key == u"q"_q && modifier == ctrl) { quit(); } } }); }); raw->setDataRequestHandler([=](Webview::DataRequest request) { if (!request.id.starts_with("iv/")) { _dataRequests.fire(std::move(request)); return Webview::DataResult::Pending; } const auto finishWith = [&](QByteArray data, std::string mime) { request.done({ .stream = std::make_unique( std::move(data), std::move(mime)), }); return Webview::DataResult::Done; }; const auto id = std::string_view(request.id).substr(3); if (id == "page.html") { return finishWith(page.html, "text/html"); } const auto css = id.ends_with(".css"); const auto js = !css && id.ends_with(".js"); if (!css && !js) { return Webview::DataResult::Failed; } const auto qstring = QString::fromUtf8(id.data(), id.size()); const auto pattern = u"^[a-zA-Z\\.\\-_0-9]+$"_q; if (QRegularExpression(pattern).match(qstring).hasMatch()) { auto file = QFile(u":/iv/"_q + qstring); if (file.open(QIODevice::ReadOnly)) { const auto mime = css ? "text/css" : "text/javascript"; return finishWith(file.readAll(), mime); } } return Webview::DataResult::Failed; }); raw->init(R"( )"); raw->navigateToData("iv/page.html"); window->show(); } bool Controller::active() const { return _window && _window->isActiveWindow(); } void Controller::minimize() { if (_window) { _window->setWindowState(_window->windowState() | Qt::WindowMinimized); } } void Controller::escape() { close(); } void Controller::close() { _events.fire(Event::Close); } void Controller::quit() { _events.fire(Event::Quit); } rpl::lifetime &Controller::lifetime() { return _lifetime; } } // namespace Iv