mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:22:43 +00:00 
			
		
		
		
	Browser+LibWeb+WebContent: Show style for pseudo-elements :^)
This expands the InspectorWidget::Selection to include an optional PseudoElement, which is then passed over IPC to request style information for it. As noted, this has some pretty big limitations because pseudo-elements don't have DOM nodes: - Declared style has to be recalculated when it's requested. - We don't display the computed style. - We don't display custom properties.
This commit is contained in:
		
							parent
							
								
									2c970b9516
								
							
						
					
					
						commit
						0326ad34df
					
				
					 9 changed files with 69 additions and 20 deletions
				
			
		|  | @ -46,12 +46,19 @@ void InspectorWidget::set_selection(GUI::ModelIndex const index) | ||||||
|     auto* json = static_cast<JsonObject const*>(index.internal_data()); |     auto* json = static_cast<JsonObject const*>(index.internal_data()); | ||||||
|     VERIFY(json); |     VERIFY(json); | ||||||
| 
 | 
 | ||||||
|     Selection selection { json->get("id").to_i32() }; |     Selection selection {}; | ||||||
|  |     if (json->has_u32("pseudo-element")) { | ||||||
|  |         selection.dom_node_id = json->get("parent-id").to_i32(); | ||||||
|  |         selection.pseudo_element = static_cast<Web::CSS::Selector::PseudoElement>(json->get("pseudo-element").to_u32()); | ||||||
|  |     } else { | ||||||
|  |         selection.dom_node_id = json->get("id").to_i32(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (selection == m_selection) |     if (selection == m_selection) | ||||||
|         return; |         return; | ||||||
|     m_selection = move(selection); |     m_selection = move(selection); | ||||||
| 
 | 
 | ||||||
|     auto maybe_inspected_node_properties = m_web_view->inspect_dom_node(m_inspected_node_id); |     auto maybe_inspected_node_properties = m_web_view->inspect_dom_node(m_selection.dom_node_id, m_selection.pseudo_element); | ||||||
|     if (maybe_inspected_node_properties.has_value()) { |     if (maybe_inspected_node_properties.has_value()) { | ||||||
|         auto inspected_node_properties = maybe_inspected_node_properties.value(); |         auto inspected_node_properties = maybe_inspected_node_properties.value(); | ||||||
|         load_style_json(inspected_node_properties.specified_values_json, inspected_node_properties.computed_values_json, inspected_node_properties.custom_properties_json); |         load_style_json(inspected_node_properties.specified_values_json, inspected_node_properties.computed_values_json, inspected_node_properties.custom_properties_json); | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "ElementSizePreviewWidget.h" | #include "ElementSizePreviewWidget.h" | ||||||
| #include <LibGUI/Widget.h> | #include <LibGUI/Widget.h> | ||||||
|  | #include <LibWeb/CSS/Selector.h> | ||||||
| #include <LibWeb/Forward.h> | #include <LibWeb/Forward.h> | ||||||
| #include <LibWeb/Layout/BoxModelMetrics.h> | #include <LibWeb/Layout/BoxModelMetrics.h> | ||||||
| 
 | 
 | ||||||
|  | @ -20,14 +21,17 @@ class InspectorWidget final : public GUI::Widget { | ||||||
| public: | public: | ||||||
|     struct Selection { |     struct Selection { | ||||||
|         i32 dom_node_id { 0 }; |         i32 dom_node_id { 0 }; | ||||||
|  |         Optional<Web::CSS::Selector::PseudoElement> pseudo_element {}; | ||||||
| 
 | 
 | ||||||
|         bool operator==(Selection const& other) const |         bool operator==(Selection const& other) const | ||||||
|         { |         { | ||||||
|             return dom_node_id == other.dom_node_id; |             return dom_node_id == other.dom_node_id && pseudo_element == other.pseudo_element; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         String to_string() const |         String to_string() const | ||||||
|         { |         { | ||||||
|  |             if (pseudo_element.has_value()) | ||||||
|  |                 return String::formatted("id: {}, pseudo: {}", dom_node_id, Web::CSS::pseudo_element_name(pseudo_element.value())); | ||||||
|             return String::formatted("id: {}", dom_node_id); |             return String::formatted("id: {}", dom_node_id); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -182,10 +182,26 @@ void DOMTreeModel::map_dom_nodes_to_parent(JsonObject const* parent, JsonObject | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GUI::ModelIndex DOMTreeModel::index_for_node(i32 node_id) const | GUI::ModelIndex DOMTreeModel::index_for_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element) const | ||||||
| { | { | ||||||
|     auto node = m_node_id_to_dom_node_map.get(node_id).value_or(nullptr); |     auto node = m_node_id_to_dom_node_map.get(node_id).value_or(nullptr); | ||||||
|     if (node) { |     if (node) { | ||||||
|  |         if (pseudo_element.has_value()) { | ||||||
|  |             // Find pseudo-element child of the node.
 | ||||||
|  |             auto node_children = get_children(*node); | ||||||
|  |             for (size_t i = 0; i < node_children->size(); i++) { | ||||||
|  |                 auto& child = node_children->at(i).as_object(); | ||||||
|  |                 if (!child.has("pseudo-element")) | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 auto child_pseudo_element = child.get("pseudo-element"); | ||||||
|  |                 if (!child_pseudo_element.is_i32()) | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 if (child_pseudo_element.as_i32() == to_underlying(pseudo_element.value())) | ||||||
|  |                     return create_index(i, 0, &child); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|             auto* parent = get_parent(*node); |             auto* parent = get_parent(*node); | ||||||
|             if (!parent) |             if (!parent) | ||||||
|                 return {}; |                 return {}; | ||||||
|  | @ -196,8 +212,9 @@ GUI::ModelIndex DOMTreeModel::index_for_node(i32 node_id) const | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     dbgln("Didn't find index for node {}!", node_id); |     dbgln("Didn't find index for node {}, pseudo-element {}!", node_id, pseudo_element.has_value() ? CSS::pseudo_element_name(pseudo_element.value()) : "NONE"); | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <AK/HashMap.h> | #include <AK/HashMap.h> | ||||||
| #include <AK/JsonObject.h> | #include <AK/JsonObject.h> | ||||||
| #include <LibGUI/Model.h> | #include <LibGUI/Model.h> | ||||||
|  | #include <LibWeb/CSS/Selector.h> | ||||||
| #include <LibWeb/Forward.h> | #include <LibWeb/Forward.h> | ||||||
| 
 | 
 | ||||||
| namespace Web { | namespace Web { | ||||||
|  | @ -30,7 +31,7 @@ public: | ||||||
|     virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; |     virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; | ||||||
|     virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; |     virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; | ||||||
| 
 | 
 | ||||||
|     GUI::ModelIndex index_for_node(i32 node_id) const; |     GUI::ModelIndex index_for_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element) const; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     DOMTreeModel(JsonObject, GUI::TreeView&); |     DOMTreeModel(JsonObject, GUI::TreeView&); | ||||||
|  |  | ||||||
|  | @ -439,9 +439,9 @@ void OutOfProcessWebView::inspect_dom_tree() | ||||||
|     client().async_inspect_dom_tree(); |     client().async_inspect_dom_tree(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Optional<OutOfProcessWebView::DOMNodeProperties> OutOfProcessWebView::inspect_dom_node(i32 node_id) | Optional<OutOfProcessWebView::DOMNodeProperties> OutOfProcessWebView::inspect_dom_node(i32 node_id, Optional<CSS::Selector::PseudoElement> pseudo_element) | ||||||
| { | { | ||||||
|     auto response = client().inspect_dom_node(node_id); |     auto response = client().inspect_dom_node(node_id, pseudo_element); | ||||||
|     if (!response.has_style()) |     if (!response.has_style()) | ||||||
|         return {}; |         return {}; | ||||||
|     return DOMNodeProperties { |     return DOMNodeProperties { | ||||||
|  | @ -454,7 +454,7 @@ Optional<OutOfProcessWebView::DOMNodeProperties> OutOfProcessWebView::inspect_do | ||||||
| 
 | 
 | ||||||
| void OutOfProcessWebView::clear_inspected_dom_node() | void OutOfProcessWebView::clear_inspected_dom_node() | ||||||
| { | { | ||||||
|     client().inspect_dom_node(0); |     client().inspect_dom_node(0, {}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| i32 OutOfProcessWebView::get_hovered_node_id() | i32 OutOfProcessWebView::get_hovered_node_id() | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <AK/URL.h> | #include <AK/URL.h> | ||||||
| #include <LibGUI/AbstractScrollableWidget.h> | #include <LibGUI/AbstractScrollableWidget.h> | ||||||
| #include <LibGUI/Widget.h> | #include <LibGUI/Widget.h> | ||||||
|  | #include <LibWeb/CSS/Selector.h> | ||||||
| #include <LibWeb/Page/Page.h> | #include <LibWeb/Page/Page.h> | ||||||
| #include <LibWeb/WebViewHooks.h> | #include <LibWeb/WebViewHooks.h> | ||||||
| 
 | 
 | ||||||
|  | @ -40,7 +41,7 @@ public: | ||||||
|         String custom_properties_json; |         String custom_properties_json; | ||||||
|         String node_box_sizing_json; |         String node_box_sizing_json; | ||||||
|     }; |     }; | ||||||
|     Optional<DOMNodeProperties> inspect_dom_node(i32 node_id); |     Optional<DOMNodeProperties> inspect_dom_node(i32 node_id, Optional<CSS::Selector::PseudoElement>); | ||||||
|     void clear_inspected_dom_node(); |     void clear_inspected_dom_node(); | ||||||
|     i32 get_hovered_node_id(); |     i32 get_hovered_node_id(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -245,7 +245,7 @@ void ConnectionFromClient::inspect_dom_tree() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect_dom_node(i32 node_id) | Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> const& pseudo_element) | ||||||
| { | { | ||||||
|     auto& top_context = page().top_level_browsing_context(); |     auto& top_context = page().top_level_browsing_context(); | ||||||
| 
 | 
 | ||||||
|  | @ -261,6 +261,7 @@ Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect | ||||||
|         return { false, "", "", "", "" }; |         return { false, "", "", "", "" }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // FIXME: Pass the pseudo-element here.
 | ||||||
|     node->document().set_inspected_node(node); |     node->document().set_inspected_node(node); | ||||||
| 
 | 
 | ||||||
|     if (node->is_element()) { |     if (node->is_element()) { | ||||||
|  | @ -326,6 +327,23 @@ Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect | ||||||
|             MUST(serializer.finish()); |             MUST(serializer.finish()); | ||||||
|             return builder.to_string(); |             return builder.to_string(); | ||||||
|         }; |         }; | ||||||
|  | 
 | ||||||
|  |         if (pseudo_element.has_value()) { | ||||||
|  |             auto pseudo_element_node = element.get_pseudo_element_node(pseudo_element.value()); | ||||||
|  |             if (pseudo_element_node.is_null()) | ||||||
|  |                 return { false, "", "", "", "" }; | ||||||
|  | 
 | ||||||
|  |             // FIXME: Pseudo-elements only exist as Layout::Nodes, which don't have style information
 | ||||||
|  |             //        in a format we can use. So, we run the StyleComputer again to get the specified
 | ||||||
|  |             //        values, and have to ignore the computed values and custom properties.
 | ||||||
|  |             auto pseudo_element_style = page().focused_context().active_document()->style_computer().compute_style(element, pseudo_element); | ||||||
|  |             String specified_values_json = serialize_json(pseudo_element_style); | ||||||
|  |             String computed_values_json = "{}"; | ||||||
|  |             String custom_properties_json = "{}"; | ||||||
|  |             String node_box_sizing_json = "{}"; | ||||||
|  |             return { true, specified_values_json, computed_values_json, custom_properties_json, node_box_sizing_json }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         String specified_values_json = serialize_json(*element.specified_css_values()); |         String specified_values_json = serialize_json(*element.specified_css_values()); | ||||||
|         String computed_values_json = serialize_json(element.computed_style()); |         String computed_values_json = serialize_json(element.computed_style()); | ||||||
|         String custom_properties_json = serialize_custom_properties_json(element); |         String custom_properties_json = serialize_custom_properties_json(element); | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ private: | ||||||
|     virtual void debug_request(String const&, String const&) override; |     virtual void debug_request(String const&, String const&) override; | ||||||
|     virtual void get_source() override; |     virtual void get_source() override; | ||||||
|     virtual void inspect_dom_tree() override; |     virtual void inspect_dom_tree() override; | ||||||
|     virtual Messages::WebContentServer::InspectDomNodeResponse inspect_dom_node(i32) override; |     virtual Messages::WebContentServer::InspectDomNodeResponse inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> const& pseudo_element) override; | ||||||
|     virtual Messages::WebContentServer::GetHoveredNodeIdResponse get_hovered_node_id() override; |     virtual Messages::WebContentServer::GetHoveredNodeIdResponse get_hovered_node_id() override; | ||||||
|     virtual Messages::WebContentServer::DumpLayoutTreeResponse dump_layout_tree() override; |     virtual Messages::WebContentServer::DumpLayoutTreeResponse dump_layout_tree() override; | ||||||
|     virtual void set_content_filters(Vector<String> const&) override; |     virtual void set_content_filters(Vector<String> const&) override; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| #include <LibCore/AnonymousBuffer.h> | #include <LibCore/AnonymousBuffer.h> | ||||||
| #include <LibGfx/ShareableBitmap.h> | #include <LibGfx/ShareableBitmap.h> | ||||||
| #include <LibWeb/CSS/PreferredColorScheme.h> | #include <LibWeb/CSS/PreferredColorScheme.h> | ||||||
|  | #include <LibWeb/CSS/Selector.h> | ||||||
| 
 | 
 | ||||||
| endpoint WebContentServer | endpoint WebContentServer | ||||||
| { | { | ||||||
|  | @ -29,7 +30,7 @@ endpoint WebContentServer | ||||||
|     debug_request(String request, String argument) =| |     debug_request(String request, String argument) =| | ||||||
|     get_source() =| |     get_source() =| | ||||||
|     inspect_dom_tree() =| |     inspect_dom_tree() =| | ||||||
|     inspect_dom_node(i32 node_id) => (bool has_style, String specified_style, String computed_style, String custom_properties, String node_box_sizing) |     inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element) => (bool has_style, String specified_style, String computed_style, String custom_properties, String node_box_sizing) | ||||||
|     get_hovered_node_id() => (i32 node_id) |     get_hovered_node_id() => (i32 node_id) | ||||||
|     js_console_input(String js_source) =| |     js_console_input(String js_source) =| | ||||||
|     js_console_request_messages(i32 start_index) =| |     js_console_request_messages(i32 start_index) =| | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Atkins
						Sam Atkins