1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-14 06:12:08 +00:00
serenity/Ladybird/SimpleWebView.cpp
Luke Wilde e04ec39984 Ladybird: Start applying the default content filter
For the first cut, the file path is not configurable and the content
filter cannot be toggled on or off. If we fail to apply the content
filters for any reason (e.g. the filter file doesn't exist), we simply
just stop loading the content filters to allow using Ladybird without
content filters.
2022-12-25 07:58:58 -07:00

678 lines
23 KiB
C++

/*
* Copyright (c) 2022, Dex♪ <dexes.ttp@gmail.com>
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#define AK_DONT_REPLACE_STD
#include "SimpleWebView.h"
#include "ConsoleClient.h"
#include "CookieJar.h"
#include "EventLoopPluginQt.h"
#include "FontPluginQt.h"
#include "ImageCodecPluginLadybird.h"
#include "ModelTranslator.h"
#include "PageClientLadybird.h"
#include "RequestManagerQt.h"
#include "Utilities.h"
#include "WebSocketClientManagerLadybird.h"
#include <AK/Assertions.h>
#include <AK/ByteBuffer.h>
#include <AK/Format.h>
#include <AK/HashTable.h>
#include <AK/LexicalPath.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/StringBuilder.h>
#include <AK/Types.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibCore/File.h>
#include <LibCore/IODevice.h>
#include <LibCore/MemoryStream.h>
#include <LibCore/Stream.h>
#include <LibCore/System.h>
#include <LibCore/Timer.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/PNGWriter.h>
#include <LibGfx/Rect.h>
#include <LibJS/Runtime/ConsoleObject.h>
#include <LibMain/Main.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/Cookie/ParsedCookie.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Dump.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/Scripting/ClassicScript.h>
#include <LibWeb/HTML/Storage.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Layout/InitialContainingBlock.h>
#include <LibWeb/Loader/ContentFilter.h>
#include <LibWeb/Loader/ResourceLoader.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Painting/StackingContext.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWebView/DOMTreeModel.h>
#include <QApplication>
#include <QCursor>
#include <QIcon>
#include <QLineEdit>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QPainter>
#include <QScrollBar>
#include <QTextEdit>
#include <QToolTip>
#include <QTreeView>
#include <QVBoxLayout>
String s_serenity_resource_root;
SimpleWebView::SimpleWebView()
{
setMouseTracking(true);
m_page_client = Ladybird::PageClientLadybird::create(*this);
m_page_client->setup_palette(Gfx::load_system_theme(String::formatted("{}/res/themes/Default.ini", s_serenity_resource_root)));
// FIXME: Allow passing these values as arguments
m_page_client->set_viewport_rect({ 0, 0, 800, 600 });
m_inverse_pixel_scaling_ratio = 1.0 / devicePixelRatio();
verticalScrollBar()->setSingleStep(24);
horizontalScrollBar()->setSingleStep(24);
QObject::connect(verticalScrollBar(), &QScrollBar::valueChanged, [this](int) {
update_viewport_rect();
});
QObject::connect(horizontalScrollBar(), &QScrollBar::valueChanged, [this](int) {
update_viewport_rect();
});
}
SimpleWebView::~SimpleWebView()
{
}
void SimpleWebView::reload()
{
auto url = m_page_client->page().top_level_browsing_context().active_document()->url();
m_page_client->load(url);
}
void SimpleWebView::load(String const& url)
{
m_page_client->load(AK::URL(url));
}
unsigned get_button_from_qt_event(QMouseEvent const& event)
{
if (event.button() == Qt::MouseButton::LeftButton)
return 1;
if (event.button() == Qt::MouseButton::RightButton)
return 2;
if (event.button() == Qt::MouseButton::MiddleButton)
return 4;
if (event.button() == Qt::MouseButton::BackButton)
return 8;
if (event.buttons() == Qt::MouseButton::ForwardButton)
return 16;
return 0;
}
unsigned get_buttons_from_qt_event(QMouseEvent const& event)
{
unsigned buttons = 0;
if (event.buttons() & Qt::MouseButton::LeftButton)
buttons |= 1;
if (event.buttons() & Qt::MouseButton::RightButton)
buttons |= 2;
if (event.buttons() & Qt::MouseButton::MiddleButton)
buttons |= 4;
if (event.buttons() & Qt::MouseButton::BackButton)
buttons |= 8;
if (event.buttons() & Qt::MouseButton::ForwardButton)
buttons |= 16;
return buttons;
}
unsigned get_modifiers_from_qt_mouse_event(QMouseEvent const& event)
{
unsigned modifiers = 0;
if (event.modifiers() & Qt::Modifier::ALT)
modifiers |= 1;
if (event.modifiers() & Qt::Modifier::CTRL)
modifiers |= 2;
if (event.modifiers() & Qt::Modifier::SHIFT)
modifiers |= 4;
return modifiers;
}
unsigned get_modifiers_from_qt_keyboard_event(QKeyEvent const& event)
{
auto modifiers = 0;
if (event.modifiers().testFlag(Qt::AltModifier))
modifiers |= KeyModifier::Mod_Alt;
if (event.modifiers().testFlag(Qt::ControlModifier))
modifiers |= KeyModifier::Mod_Ctrl;
if (event.modifiers().testFlag(Qt::MetaModifier))
modifiers |= KeyModifier::Mod_Super;
if (event.modifiers().testFlag(Qt::ShiftModifier))
modifiers |= KeyModifier::Mod_Shift;
if (event.modifiers().testFlag(Qt::AltModifier))
modifiers |= KeyModifier::Mod_AltGr;
return modifiers;
}
KeyCode get_keycode_from_qt_keyboard_event(QKeyEvent const& event)
{
struct Mapping {
constexpr Mapping(Qt::Key q, KeyCode s)
: qt_key(q)
, serenity_key(s)
{
}
Qt::Key qt_key;
KeyCode serenity_key;
};
constexpr Mapping mappings[] = {
{ Qt::Key_0, Key_0 },
{ Qt::Key_1, Key_1 },
{ Qt::Key_2, Key_2 },
{ Qt::Key_3, Key_3 },
{ Qt::Key_4, Key_4 },
{ Qt::Key_5, Key_5 },
{ Qt::Key_6, Key_6 },
{ Qt::Key_7, Key_7 },
{ Qt::Key_8, Key_8 },
{ Qt::Key_9, Key_9 },
{ Qt::Key_A, Key_A },
{ Qt::Key_Alt, Key_Alt },
{ Qt::Key_Ampersand, Key_Ampersand },
{ Qt::Key_Apostrophe, Key_Apostrophe },
{ Qt::Key_AsciiCircum, Key_Circumflex },
{ Qt::Key_AsciiTilde, Key_Tilde },
{ Qt::Key_Asterisk, Key_Asterisk },
{ Qt::Key_At, Key_AtSign },
{ Qt::Key_B, Key_B },
{ Qt::Key_Backslash, Key_Backslash },
{ Qt::Key_Backspace, Key_Backspace },
{ Qt::Key_Bar, Key_Pipe },
{ Qt::Key_BraceLeft, Key_LeftBrace },
{ Qt::Key_BraceRight, Key_RightBrace },
{ Qt::Key_BracketLeft, Key_LeftBracket },
{ Qt::Key_BracketRight, Key_RightBracket },
{ Qt::Key_C, Key_C },
{ Qt::Key_CapsLock, Key_CapsLock },
{ Qt::Key_Colon, Key_Colon },
{ Qt::Key_Comma, Key_Comma },
{ Qt::Key_Control, Key_Control },
{ Qt::Key_D, Key_D },
{ Qt::Key_Delete, Key_Delete },
{ Qt::Key_Dollar, Key_Dollar },
{ Qt::Key_Down, Key_Down },
{ Qt::Key_E, Key_E },
{ Qt::Key_End, Key_End },
{ Qt::Key_Equal, Key_Equal },
{ Qt::Key_Escape, Key_Escape },
{ Qt::Key_exclamdown, Key_ExclamationPoint },
{ Qt::Key_F, Key_F },
{ Qt::Key_F1, Key_F1 },
{ Qt::Key_F10, Key_F10 },
{ Qt::Key_F11, Key_F11 },
{ Qt::Key_F12, Key_F12 },
{ Qt::Key_F2, Key_F2 },
{ Qt::Key_F3, Key_F3 },
{ Qt::Key_F4, Key_F4 },
{ Qt::Key_F5, Key_F5 },
{ Qt::Key_F6, Key_F6 },
{ Qt::Key_F7, Key_F7 },
{ Qt::Key_F8, Key_F8 },
{ Qt::Key_F9, Key_F9 },
{ Qt::Key_G, Key_G },
{ Qt::Key_Greater, Key_GreaterThan },
{ Qt::Key_H, Key_H },
{ Qt::Key_Home, Key_Home },
{ Qt::Key_I, Key_I },
{ Qt::Key_Insert, Key_Insert },
{ Qt::Key_J, Key_J },
{ Qt::Key_K, Key_K },
{ Qt::Key_L, Key_L },
{ Qt::Key_Left, Key_Left },
{ Qt::Key_Less, Key_LessThan },
{ Qt::Key_M, Key_M },
{ Qt::Key_Menu, Key_Menu },
{ Qt::Key_Minus, Key_Minus },
{ Qt::Key_N, Key_N },
{ Qt::Key_NumLock, Key_NumLock },
{ Qt::Key_O, Key_O },
{ Qt::Key_P, Key_P },
{ Qt::Key_PageDown, Key_PageDown },
{ Qt::Key_PageUp, Key_PageUp },
{ Qt::Key_ParenLeft, Key_LeftParen },
{ Qt::Key_ParenRight, Key_RightParen },
{ Qt::Key_Percent, Key_Percent },
{ Qt::Key_Period, Key_Period },
{ Qt::Key_Plus, Key_Plus },
{ Qt::Key_Print, Key_PrintScreen },
{ Qt::Key_Q, Key_Q },
{ Qt::Key_Question, Key_QuestionMark },
{ Qt::Key_QuoteDbl, Key_DoubleQuote },
{ Qt::Key_R, Key_R },
{ Qt::Key_Return, Key_Return },
{ Qt::Key_Right, Key_Right },
{ Qt::Key_S, Key_S },
{ Qt::Key_ScrollLock, Key_ScrollLock },
{ Qt::Key_Semicolon, Key_Semicolon },
{ Qt::Key_Shift, Key_LeftShift },
{ Qt::Key_Slash, Key_Slash },
{ Qt::Key_Space, Key_Space },
{ Qt::Key_Super_L, Key_Super },
{ Qt::Key_SysReq, Key_SysRq },
{ Qt::Key_T, Key_T },
{ Qt::Key_Tab, Key_Tab },
{ Qt::Key_U, Key_U },
{ Qt::Key_Underscore, Key_Underscore },
{ Qt::Key_Up, Key_Up },
{ Qt::Key_V, Key_V },
{ Qt::Key_W, Key_W },
{ Qt::Key_X, Key_X },
{ Qt::Key_Y, Key_Y },
{ Qt::Key_Z, Key_Z },
};
for (auto const& mapping : mappings) {
if (event.key() == mapping.qt_key)
return mapping.serenity_key;
}
return Key_Invalid;
}
void SimpleWebView::mouseMoveEvent(QMouseEvent* event)
{
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
auto buttons = get_buttons_from_qt_event(*event);
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
m_page_client->page().handle_mousemove(to_content(position), buttons, modifiers);
}
void SimpleWebView::mousePressEvent(QMouseEvent* event)
{
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
auto button = get_button_from_qt_event(*event);
if (button == 0) {
// We could not convert Qt buttons to something that Lagom can
// recognize - don't even bother propagating this to the web engine
// as it will not handle it anyway, and it will (currently) assert
return;
}
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
m_page_client->page().handle_mousedown(to_content(position), button, modifiers);
}
void SimpleWebView::mouseReleaseEvent(QMouseEvent* event)
{
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
auto button = get_button_from_qt_event(*event);
if (button == 0) {
// We could not convert Qt buttons to something that Lagom can
// recognize - don't even bother propagating this to the web engine
// as it will not handle it anyway, and it will (currently) assert
return;
}
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
m_page_client->page().handle_mouseup(to_content(position), button, modifiers);
}
void SimpleWebView::keyPressEvent(QKeyEvent* event)
{
switch (event->key()) {
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_PageUp:
case Qt::Key_PageDown:
QAbstractScrollArea::keyPressEvent(event);
break;
default:
break;
}
auto text = event->text();
if (text.isEmpty()) {
return;
}
auto point = event->text()[0].unicode();
auto keycode = get_keycode_from_qt_keyboard_event(*event);
auto modifiers = get_modifiers_from_qt_keyboard_event(*event);
m_page_client->page().handle_keydown(keycode, modifiers, point);
}
void SimpleWebView::keyReleaseEvent(QKeyEvent* event)
{
auto text = event->text();
if (text.isEmpty()) {
return;
}
auto point = event->text()[0].unicode();
auto keycode = get_keycode_from_qt_keyboard_event(*event);
auto modifiers = get_modifiers_from_qt_keyboard_event(*event);
m_page_client->page().handle_keyup(keycode, modifiers, point);
}
Gfx::IntPoint SimpleWebView::to_content(Gfx::IntPoint viewport_position) const
{
return viewport_position.translated(horizontalScrollBar()->value(), verticalScrollBar()->value());
}
Gfx::IntPoint SimpleWebView::to_widget(Gfx::IntPoint content_position) const
{
return content_position.translated(-horizontalScrollBar()->value(), -verticalScrollBar()->value());
}
void SimpleWebView::paintEvent(QPaintEvent* event)
{
QPainter painter(viewport());
painter.setClipRect(event->rect());
painter.scale(m_inverse_pixel_scaling_ratio, m_inverse_pixel_scaling_ratio);
auto output_rect = m_page_client->viewport_rect();
output_rect.set_x(horizontalScrollBar()->value());
output_rect.set_y(verticalScrollBar()->value());
auto output_bitmap = MUST(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, output_rect.size()));
m_page_client->paint(output_rect, output_bitmap);
QImage q_image(output_bitmap->scanline_u8(0), output_bitmap->width(), output_bitmap->height(), QImage::Format_RGB32);
painter.drawImage(QPoint(0, 0), q_image);
}
void SimpleWebView::resizeEvent(QResizeEvent* event)
{
QAbstractScrollArea::resizeEvent(event);
update_viewport_rect();
}
void SimpleWebView::update_viewport_rect()
{
auto scaled_width = int(size().width() / m_inverse_pixel_scaling_ratio);
auto scaled_height = int(size().height() / m_inverse_pixel_scaling_ratio);
Gfx::IntRect rect(horizontalScrollBar()->value(), verticalScrollBar()->value(), scaled_width, scaled_height);
m_page_client->set_viewport_rect(rect);
}
static void platform_init()
{
#ifdef AK_OS_ANDROID
extern void android_platform_init();
android_platform_init();
#else
s_serenity_resource_root = [] {
auto const* source_dir = getenv("SERENITY_SOURCE_DIR");
if (source_dir) {
return String::formatted("{}/Base", source_dir);
}
auto* home = getenv("XDG_CONFIG_HOME") ?: getenv("HOME");
VERIFY(home);
auto home_lagom = String::formatted("{}/.lagom", home);
if (Core::File::is_directory(home_lagom))
return home_lagom;
auto app_dir = akstring_from_qstring(QApplication::applicationDirPath());
return LexicalPath(app_dir).parent().append("share"sv).string();
}();
#endif
}
static ErrorOr<void> load_content_filters()
{
auto file = TRY(Core::Stream::File::open(String::formatted("{}/home/anon/.config/BrowserContentFilters.txt", s_serenity_resource_root), Core::Stream::OpenMode::Read));
auto ad_filter_list = TRY(Core::Stream::BufferedFile::create(move(file)));
auto buffer = TRY(ByteBuffer::create_uninitialized(4096));
while (TRY(ad_filter_list->can_read_line())) {
auto line = TRY(ad_filter_list->read_line(buffer));
if (!line.is_empty())
Web::ContentFilter::the().add_pattern(line);
}
return {};
}
void initialize_web_engine()
{
platform_init();
Web::Platform::EventLoopPlugin::install(*new Ladybird::EventLoopPluginQt);
Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPluginLadybird);
Web::ResourceLoader::initialize(RequestManagerQt::create());
Web::WebSockets::WebSocketClientManager::initialize(Ladybird::WebSocketClientManagerLadybird::create());
Web::FrameLoader::set_default_favicon_path(String::formatted("{}/res/icons/16x16/app-browser.png", s_serenity_resource_root));
Web::Platform::FontPlugin::install(*new Ladybird::FontPluginQt);
Web::FrameLoader::set_error_page_url(String::formatted("file://{}/res/html/error.html", s_serenity_resource_root));
auto maybe_content_filter_error = load_content_filters();
if (maybe_content_filter_error.is_error())
dbgln("Failed to load content filters: {}", maybe_content_filter_error.error());
}
void SimpleWebView::debug_request(String const& request, String const& argument)
{
auto& page = m_page_client->page();
if (request == "dump-dom-tree") {
if (auto* doc = page.top_level_browsing_context().active_document())
Web::dump_tree(*doc);
}
if (request == "dump-layout-tree") {
if (auto* doc = page.top_level_browsing_context().active_document()) {
if (auto* icb = doc->layout_node())
Web::dump_tree(*icb);
}
}
if (request == "dump-stacking-context-tree") {
if (auto* doc = page.top_level_browsing_context().active_document()) {
if (auto* icb = doc->layout_node()) {
if (auto* stacking_context = icb->paint_box()->stacking_context())
stacking_context->dump();
}
}
}
if (request == "dump-style-sheets") {
if (auto* doc = page.top_level_browsing_context().active_document()) {
for (auto& sheet : doc->style_sheets().sheets()) {
Web::dump_sheet(sheet);
}
}
}
if (request == "collect-garbage") {
Web::Bindings::main_thread_vm().heap().collect_garbage(JS::Heap::CollectionType::CollectGarbage, true);
}
if (request == "set-line-box-borders") {
bool state = argument == "on";
m_page_client->set_should_show_line_box_borders(state);
page.top_level_browsing_context().set_needs_display(page.top_level_browsing_context().viewport_rect());
}
if (request == "clear-cache") {
Web::ResourceLoader::the().clear_cache();
}
if (request == "spoof-user-agent") {
Web::ResourceLoader::the().set_user_agent(argument);
}
if (request == "same-origin-policy") {
page.set_same_origin_policy_enabled(argument == "on");
}
if (request == "scripting") {
page.set_is_scripting_enabled(argument == "on");
}
if (request == "dump-local-storage") {
if (auto* doc = page.top_level_browsing_context().active_document())
doc->window().local_storage()->dump();
}
if (request == "dump-cookies"sv)
m_page_client->dump_cookies();
}
String SimpleWebView::source() const
{
auto* document = m_page_client->page().top_level_browsing_context().active_document();
if (!document)
return String::empty();
return document->source();
}
void SimpleWebView::run_javascript(String const& js_source) const
{
auto* active_document = const_cast<Web::DOM::Document*>(m_page_client->page().top_level_browsing_context().active_document());
if (!active_document)
return;
// This is partially based on "execute a javascript: URL request" https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
// Let settings be browsingContext's active document's relevant settings object.
auto& settings = active_document->relevant_settings_object();
// Let baseURL be settings's API base URL.
auto base_url = settings.api_base_url();
// Let script be the result of creating a classic script given scriptSource, settings, baseURL, and the default classic script fetch options.
// FIXME: This doesn't pass in "default classic script fetch options"
// FIXME: What should the filename be here?
auto script = Web::HTML::ClassicScript::create("(client connection run_javascript)", js_source, settings, base_url);
// Let evaluationStatus be the result of running the classic script script.
auto evaluation_status = script->run();
if (evaluation_status.is_error())
dbgln("Exception :(");
}
void SimpleWebView::did_output_js_console_message(i32 message_index)
{
m_page_client->m_console_client->send_messages(message_index);
}
void SimpleWebView::did_get_js_console_messages(i32, Vector<String>, Vector<String> messages)
{
ensure_js_console_widget();
for (auto& message : messages) {
m_js_console_output_edit->append(qstring_from_akstring(message).trimmed());
}
}
void SimpleWebView::ensure_js_console_widget()
{
if (!m_js_console_widget) {
m_js_console_widget = new QWidget;
m_js_console_widget->setWindowTitle("JS Console");
auto* layout = new QVBoxLayout;
m_js_console_widget->setLayout(layout);
m_js_console_output_edit = new QTextEdit;
m_js_console_output_edit->setReadOnly(true);
m_js_console_input_edit = new QLineEdit;
layout->addWidget(m_js_console_output_edit);
layout->addWidget(m_js_console_input_edit);
m_js_console_widget->resize(640, 480);
QObject::connect(m_js_console_input_edit, &QLineEdit::returnPressed, [this] {
auto code = m_js_console_input_edit->text().trimmed();
m_js_console_input_edit->clear();
m_js_console_output_edit->append(QString("> %1").arg(code));
m_page_client->initialize_js_console();
m_page_client->m_console_client->handle_input(akstring_from_qstring(code));
});
}
}
void SimpleWebView::show_js_console()
{
ensure_js_console_widget();
m_js_console_widget->show();
m_js_console_input_edit->setFocus();
}
void SimpleWebView::ensure_inspector_widget()
{
if (m_inspector_widget)
return;
m_inspector_widget = new QWidget;
m_inspector_widget->setWindowTitle("Inspector");
auto* layout = new QVBoxLayout;
m_inspector_widget->setLayout(layout);
auto* tree_view = new QTreeView;
layout->addWidget(tree_view);
auto dom_tree = m_page_client->page().top_level_browsing_context().active_document()->dump_dom_tree_as_json();
auto dom_tree_model = ::WebView::DOMTreeModel::create(dom_tree);
auto* model = new Ladybird::ModelTranslator(dom_tree_model);
tree_view->setModel(model);
tree_view->setHeaderHidden(true);
tree_view->expandToDepth(3);
m_inspector_widget->resize(640, 480);
QObject::connect(tree_view->selectionModel(), &QItemSelectionModel::currentChanged, [this](QModelIndex const& index, QModelIndex const&) {
auto const* json = (JsonObject const*)index.internalPointer();
m_page_client->page().top_level_browsing_context().active_document()->set_inspected_node(Web::DOM::Node::from_id(json->get("id"sv).to_i32()));
});
}
void SimpleWebView::show_inspector()
{
ensure_inspector_widget();
m_inspector_widget->show();
}
void SimpleWebView::set_color_scheme(ColorScheme color_scheme)
{
switch (color_scheme) {
case ColorScheme::Auto:
m_page_client->m_preferred_color_scheme = Web::CSS::PreferredColorScheme::Auto;
break;
case ColorScheme::Light:
m_page_client->m_preferred_color_scheme = Web::CSS::PreferredColorScheme::Light;
break;
case ColorScheme::Dark:
m_page_client->m_preferred_color_scheme = Web::CSS::PreferredColorScheme::Dark;
break;
}
if (auto* document = m_page_client->page().top_level_browsing_context().active_document())
document->invalidate_style();
}
void SimpleWebView::showEvent(QShowEvent* event)
{
QAbstractScrollArea::showEvent(event);
m_page_client->page().top_level_browsing_context().set_system_visibility_state(Web::HTML::VisibilityState::Visible);
}
void SimpleWebView::hideEvent(QHideEvent* event)
{
QAbstractScrollArea::hideEvent(event);
m_page_client->page().top_level_browsing_context().set_system_visibility_state(Web::HTML::VisibilityState::Hidden);
}