mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:52:44 +00:00 
			
		
		
		
	Ladybird: Implement the JavaScript console using a WebContentView
This aligns the Ladybird console implementation with the Browser console a bit more, which uses OutOfProcessWebView for rendering console output. This allows us to style the console output to try and match the system theme. Using a WebContentView is simpler than trying to style the old QTextEdit widget, as the console output is HTML with built-in "-libweb-palette-*" colors. These will override any color we set on the QTextEdit widget.
This commit is contained in:
		
							parent
							
								
									5af715e394
								
							
						
					
					
						commit
						4aca24481e
					
				
					 5 changed files with 80 additions and 22 deletions
				
			
		|  | @ -8,6 +8,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include "BrowserWindow.h" | ||||
| #include "ConsoleWidget.h" | ||||
| #include "Settings.h" | ||||
| #include "SettingsDialog.h" | ||||
| #include "Utilities.h" | ||||
|  | @ -546,8 +547,13 @@ void BrowserWindow::reset_zoom() | |||
| 
 | ||||
| void BrowserWindow::select_all() | ||||
| { | ||||
|     if (auto* tab = m_current_tab) | ||||
|         tab->view().select_all(); | ||||
|     if (!m_current_tab) | ||||
|         return; | ||||
| 
 | ||||
|     if (auto* console = m_current_tab->view().console(); console && console->isActiveWindow()) | ||||
|         console->view().select_all(); | ||||
|     else | ||||
|         m_current_tab->view().select_all(); | ||||
| } | ||||
| 
 | ||||
| void BrowserWindow::update_displayed_zoom_level() | ||||
|  | @ -560,11 +566,18 @@ void BrowserWindow::update_displayed_zoom_level() | |||
| 
 | ||||
| void BrowserWindow::copy_selected_text() | ||||
| { | ||||
|     if (auto* tab = m_current_tab) { | ||||
|         auto text = tab->view().selected_text(); | ||||
|         auto* clipboard = QGuiApplication::clipboard(); | ||||
|         clipboard->setText(qstring_from_ak_deprecated_string(text)); | ||||
|     } | ||||
|     if (!m_current_tab) | ||||
|         return; | ||||
| 
 | ||||
|     DeprecatedString text; | ||||
| 
 | ||||
|     if (auto* console = m_current_tab->view().console(); console && console->isActiveWindow()) | ||||
|         text = console->view().selected_text(); | ||||
|     else | ||||
|         text = m_current_tab->view().selected_text(); | ||||
| 
 | ||||
|     auto* clipboard = QGuiApplication::clipboard(); | ||||
|     clipboard->setText(qstring_from_ak_deprecated_string(text)); | ||||
| } | ||||
| 
 | ||||
| void BrowserWindow::resizeEvent(QResizeEvent* event) | ||||
|  |  | |||
|  | @ -11,25 +11,46 @@ | |||
| 
 | ||||
| #include "ConsoleWidget.h" | ||||
| #include "Utilities.h" | ||||
| #include "WebContentView.h" | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <LibJS/MarkupGenerator.h> | ||||
| #include <QLineEdit> | ||||
| #include <QPalette> | ||||
| #include <QPushButton> | ||||
| #include <QTextEdit> | ||||
| #include <QVBoxLayout> | ||||
| 
 | ||||
| namespace Ladybird { | ||||
| 
 | ||||
| static bool is_using_dark_system_theme(QWidget& widget) | ||||
| { | ||||
|     // FIXME: Qt does not provide any method to query if the system is using a dark theme. We will have to implement
 | ||||
|     //        platform-specific methods if we wish to have better detection. For now, this inspects if Qt is using a
 | ||||
|     //        dark color for widget backgrounds using Rec. 709 luma coefficients.
 | ||||
|     //        https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients
 | ||||
| 
 | ||||
|     auto color = widget.palette().color(widget.backgroundRole()); | ||||
|     auto luma = 0.2126f * color.redF() + 0.7152f * color.greenF() + 0.0722f * color.blueF(); | ||||
| 
 | ||||
|     return luma <= 0.5f; | ||||
| } | ||||
| 
 | ||||
| ConsoleWidget::ConsoleWidget() | ||||
| { | ||||
|     setLayout(new QVBoxLayout); | ||||
| 
 | ||||
|     m_output_view = new QTextEdit(this); | ||||
|     m_output_view->setReadOnly(true); | ||||
|     layout()->addWidget(m_output_view); | ||||
|     m_output_view = new WebContentView({}, WebView::EnableCallgrindProfiling::No); | ||||
|     if (is_using_dark_system_theme(*this)) | ||||
|         m_output_view->update_palette(WebContentView::PaletteMode::Dark); | ||||
| 
 | ||||
|     if (on_request_messages) | ||||
|         on_request_messages(0); | ||||
|     m_output_view->load("data:text/html,<html></html>"sv); | ||||
|     // Wait until our output WebView is loaded, and then request any messages that occurred before we existed
 | ||||
|     m_output_view->on_load_finish = [this](auto&) { | ||||
|         if (on_request_messages) | ||||
|             on_request_messages(0); | ||||
|     }; | ||||
| 
 | ||||
|     layout()->addWidget(m_output_view); | ||||
| 
 | ||||
|     auto* bottom_container = new QWidget(this); | ||||
|     bottom_container->setLayout(new QHBoxLayout); | ||||
|  | @ -139,12 +160,27 @@ void ConsoleWidget::print_source_line(StringView source) | |||
| 
 | ||||
| void ConsoleWidget::print_html(StringView line) | ||||
| { | ||||
|     m_output_view->append(QString::fromUtf8(line.characters_without_null_termination(), line.length())); | ||||
|     StringBuilder builder; | ||||
| 
 | ||||
|     builder.append(R"~~~( | ||||
|         var p = document.createElement("p"); | ||||
|         p.innerHTML = ")~~~"sv); | ||||
|     builder.append_escaped_for_json(line); | ||||
|     builder.append(R"~~~(" | ||||
|         document.body.appendChild(p); | ||||
| )~~~"sv); | ||||
| 
 | ||||
|     // FIXME: Make it scroll to the bottom, using `window.scrollTo()` in the JS above.
 | ||||
|     //        We used to call `m_output_view->scroll_to_bottom();` here, but that does not work because
 | ||||
|     //        it runs synchronously, meaning it happens before the HTML is output via IPC above.
 | ||||
|     m_output_view->run_javascript(builder.string_view()); | ||||
| } | ||||
| 
 | ||||
| void ConsoleWidget::clear_output() | ||||
| { | ||||
|     m_output_view->clear(); | ||||
|     m_output_view->run_javascript(R"~~~( | ||||
|         document.body.innerHTML = ""; | ||||
|     )~~~"sv); | ||||
| } | ||||
| 
 | ||||
| void ConsoleWidget::reset() | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| #include <QWidget> | ||||
| 
 | ||||
| class QLineEdit; | ||||
| class QTextEdit; | ||||
| class WebContentView; | ||||
| 
 | ||||
| namespace Ladybird { | ||||
| 
 | ||||
|  | @ -31,6 +31,8 @@ public: | |||
|     void print_html(StringView); | ||||
|     void reset(); | ||||
| 
 | ||||
|     WebContentView& view() { return *m_output_view; } | ||||
| 
 | ||||
|     Function<void(DeprecatedString const&)> on_js_input; | ||||
|     Function<void(i32)> on_request_messages; | ||||
| 
 | ||||
|  | @ -38,7 +40,7 @@ private: | |||
|     void request_console_messages(); | ||||
|     void clear_output(); | ||||
| 
 | ||||
|     QTextEdit* m_output_view { nullptr }; | ||||
|     WebContentView* m_output_view { nullptr }; | ||||
|     QLineEdit* m_input { nullptr }; | ||||
| 
 | ||||
|     i32 m_highest_notified_message_index { -1 }; | ||||
|  |  | |||
|  | @ -567,11 +567,12 @@ void WebContentView::hideEvent(QHideEvent* event) | |||
|     client().async_set_system_visibility_state(false); | ||||
| } | ||||
| 
 | ||||
| static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget) | ||||
| static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget, WebContentView::PaletteMode mode) | ||||
| { | ||||
|     auto qt_palette = widget.palette(); | ||||
| 
 | ||||
|     auto theme = Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/Default.ini", s_serenity_resource_root)).release_value_but_fixme_should_propagate_errors(); | ||||
|     auto theme_file = mode == WebContentView::PaletteMode::Default ? "Default"sv : "Dark"sv; | ||||
|     auto theme = Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/{}.ini", s_serenity_resource_root, theme_file)).release_value_but_fixme_should_propagate_errors(); | ||||
|     auto palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme); | ||||
|     auto palette = Gfx::Palette(move(palette_impl)); | ||||
| 
 | ||||
|  | @ -594,9 +595,9 @@ static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget) | |||
|     return theme; | ||||
| } | ||||
| 
 | ||||
| void WebContentView::update_palette() | ||||
| void WebContentView::update_palette(PaletteMode mode) | ||||
| { | ||||
|     client().async_update_system_theme(make_system_theme_from_qt_palette(*this)); | ||||
|     client().async_update_system_theme(make_system_theme_from_qt_palette(*this, mode)); | ||||
| } | ||||
| 
 | ||||
| void WebContentView::create_client(WebView::EnableCallgrindProfiling enable_callgrind_profiling) | ||||
|  |  | |||
|  | @ -98,6 +98,8 @@ public: | |||
|     void show_js_console(); | ||||
|     void show_inspector(); | ||||
| 
 | ||||
|     Ladybird::ConsoleWidget* console() { return m_console_widget; }; | ||||
| 
 | ||||
|     ErrorOr<String> dump_layout_tree(); | ||||
| 
 | ||||
|     void set_viewport_rect(Gfx::IntRect); | ||||
|  | @ -107,6 +109,12 @@ public: | |||
|     Gfx::IntPoint to_content(Gfx::IntPoint) const; | ||||
|     Gfx::IntPoint to_widget(Gfx::IntPoint) const; | ||||
| 
 | ||||
|     enum class PaletteMode { | ||||
|         Default, | ||||
|         Dark, | ||||
|     }; | ||||
|     void update_palette(PaletteMode = PaletteMode::Default); | ||||
| 
 | ||||
|     virtual void notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) override; | ||||
|     virtual void notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id) override; | ||||
|     virtual void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, Gfx::IntRect const&) override; | ||||
|  | @ -198,8 +206,6 @@ private: | |||
|     bool is_inspector_open() const; | ||||
|     void close_sub_widgets(); | ||||
| 
 | ||||
|     void update_palette(); | ||||
| 
 | ||||
|     qreal m_inverse_pixel_scaling_ratio { 1.0 }; | ||||
|     bool m_should_show_line_box_borders { false }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Flynn
						Timothy Flynn