mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:57:34 +00:00
LibWebView: Support generating property tables in the Inspector
Since all chromes currently show their own tables, this is opt-out until they are updated to use the WebView tables.
This commit is contained in:
parent
6fc189ea4a
commit
ad9dfffda7
1 changed files with 153 additions and 11 deletions
|
@ -48,6 +48,10 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
|
||||||
m_inspector_web_view.use_native_user_style_sheet();
|
m_inspector_web_view.use_native_user_style_sheet();
|
||||||
|
|
||||||
m_inspector_web_view.on_inspector_loaded = [this]() {
|
m_inspector_web_view.on_inspector_loaded = [this]() {
|
||||||
|
if (on_dom_node_properties_received) {
|
||||||
|
m_inspector_web_view.run_javascript("document.getElementById(\"inspector-bottom\").style.display = \"none\";"sv);
|
||||||
|
}
|
||||||
|
|
||||||
m_inspector_loaded = true;
|
m_inspector_loaded = true;
|
||||||
|
|
||||||
if (m_pending_selection.has_value())
|
if (m_pending_selection.has_value())
|
||||||
|
@ -59,8 +63,34 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
|
||||||
m_inspector_web_view.on_inspector_selected_dom_node = [this](auto node_id, auto const& pseudo_element) {
|
m_inspector_web_view.on_inspector_selected_dom_node = [this](auto node_id, auto const& pseudo_element) {
|
||||||
auto inspected_node_properties = m_content_web_view.inspect_dom_node(node_id, pseudo_element);
|
auto inspected_node_properties = m_content_web_view.inspect_dom_node(node_id, pseudo_element);
|
||||||
|
|
||||||
if (on_dom_node_properties_received)
|
if (on_dom_node_properties_received) {
|
||||||
on_dom_node_properties_received(move(inspected_node_properties));
|
on_dom_node_properties_received(move(inspected_node_properties));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder;
|
||||||
|
|
||||||
|
// FIXME: Support box model metrics and ARIA properties.
|
||||||
|
auto generate_property_script = [&](auto const& computed_style, auto const& resolved_style, auto const& custom_properties) {
|
||||||
|
builder.append("inspector.createPropertyTables(\""sv);
|
||||||
|
builder.append_escaped_for_json(computed_style);
|
||||||
|
builder.append("\", \""sv);
|
||||||
|
builder.append_escaped_for_json(resolved_style);
|
||||||
|
builder.append("\", \""sv);
|
||||||
|
builder.append_escaped_for_json(custom_properties);
|
||||||
|
builder.append("\");"sv);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (inspected_node_properties.is_error()) {
|
||||||
|
generate_property_script("{}"sv, "{}"sv, "{}"sv);
|
||||||
|
} else {
|
||||||
|
generate_property_script(
|
||||||
|
inspected_node_properties.value().computed_style_json,
|
||||||
|
inspected_node_properties.value().resolved_style_json,
|
||||||
|
inspected_node_properties.value().custom_properties_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_inspector_web_view.run_javascript(builder.string_view());
|
||||||
};
|
};
|
||||||
|
|
||||||
inspect();
|
inspect();
|
||||||
|
@ -145,10 +175,18 @@ void InspectorClient::maybe_load_inspector()
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.split-view {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-container {
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
.tab-controls-container {
|
.tab-controls-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
top: 0;
|
|
||||||
|
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
||||||
|
@ -269,34 +307,106 @@ void InspectorClient::maybe_load_inspector()
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.property-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.property-table th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.property-table th,
|
||||||
|
.property-table td {
|
||||||
|
padding: 4px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.property-table th {
|
||||||
|
background-color: rgb(57, 57, 57);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.property-table th {
|
||||||
|
background-color: rgb(229, 229, 229);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="tab-controls-container">
|
<div class="split-view">
|
||||||
<div class="tab-controls">
|
<div id="inspector-top" class="split-view-container" style="height: 60%">
|
||||||
<button id="dom-tree-button" onclick="selectTopTab(this, 'dom-tree')">DOM Tree</button>
|
<div class="tab-controls-container">
|
||||||
<button id="accessibility-tree-button" onclick="selectTopTab(this, 'accessibility-tree')">Accessibility Tree</button>
|
<div class="tab-controls">
|
||||||
</div>
|
<button id="dom-tree-button" onclick="selectTopTab(this, 'dom-tree')">DOM Tree</button>
|
||||||
</div>
|
<button id="accessibility-tree-button" onclick="selectTopTab(this, 'accessibility-tree')">Accessibility Tree</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="dom-tree" class="tab-content html">
|
<div id="dom-tree" class="tab-content html">
|
||||||
)~~~"sv);
|
)~~~"sv);
|
||||||
|
|
||||||
generate_dom_tree(builder);
|
generate_dom_tree(builder);
|
||||||
|
|
||||||
builder.append(R"~~~(
|
builder.append(R"~~~(
|
||||||
</div>
|
</div>
|
||||||
<div id="accessibility-tree" class="tab-content">
|
<div id="accessibility-tree" class="tab-content">
|
||||||
)~~~"sv);
|
)~~~"sv);
|
||||||
|
|
||||||
generate_accessibility_tree(builder);
|
generate_accessibility_tree(builder);
|
||||||
|
|
||||||
builder.append(R"~~~(
|
builder.append(R"~~~(
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="inspector-bottom" class="split-view-container" style="height: 40%">
|
||||||
|
<div class="tab-controls-container">
|
||||||
|
<div class="tab-controls">
|
||||||
|
<button id="computed-style-button" onclick="selectBottomTab(this, 'computed-style')">Computed Style</button>
|
||||||
|
<button id="resolved-style-button" onclick="selectBottomTab(this, 'resolved-style')">Resolved Style</button>
|
||||||
|
<button id="custom-properties-button" onclick="selectBottomTab(this, 'custom-properties')">Custom Properties</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)~~~"sv);
|
||||||
|
|
||||||
|
auto generate_property_table = [&](auto name) {
|
||||||
|
builder.appendff(R"~~~(
|
||||||
|
<div id="{0}" class="tab-content">
|
||||||
|
<table class="property-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="{0}-table">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)~~~",
|
||||||
|
name);
|
||||||
|
};
|
||||||
|
|
||||||
|
generate_property_table("computed-style"sv);
|
||||||
|
generate_property_table("resolved-style"sv);
|
||||||
|
generate_property_table("custom-properties"sv);
|
||||||
|
|
||||||
|
builder.append(R"~~~(
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
let selectedTopTab = null;
|
let selectedTopTab = null;
|
||||||
let selectedTopTabButton = null;
|
let selectedTopTabButton = null;
|
||||||
|
|
||||||
|
let selectedBottomTab = null;
|
||||||
|
let selectedBottomTabButton = null;
|
||||||
|
|
||||||
let selectedDOMNode = null;
|
let selectedDOMNode = null;
|
||||||
|
|
||||||
const selectTab = (tabButton, tabID, selectedTab, selectedTabButton) => {
|
const selectTab = (tabButton, tabID, selectedTab, selectedTabButton) => {
|
||||||
|
@ -321,9 +431,17 @@ void InspectorClient::maybe_load_inspector()
|
||||||
selectedTopTabButton = tabButton;
|
selectedTopTabButton = tabButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectBottomTab = (tabButton, tabID) => {
|
||||||
|
selectedBottomTab = selectTab(tabButton, tabID, selectedBottomTab, selectedBottomTabButton);
|
||||||
|
selectedBottomTabButton = tabButton;
|
||||||
|
};
|
||||||
|
|
||||||
let initialTopTabButton = document.getElementById("dom-tree-button");
|
let initialTopTabButton = document.getElementById("dom-tree-button");
|
||||||
selectTopTab(initialTopTabButton, "dom-tree");
|
selectTopTab(initialTopTabButton, "dom-tree");
|
||||||
|
|
||||||
|
let initialBottomTabButton = document.getElementById("computed-style-button");
|
||||||
|
selectBottomTab(initialBottomTabButton, "computed-style");
|
||||||
|
|
||||||
const scrollToElement = (element) => {
|
const scrollToElement = (element) => {
|
||||||
// Include an offset to prevent the element being placed behind the fixed `tab-controls` header.
|
// Include an offset to prevent the element being placed behind the fixed `tab-controls` header.
|
||||||
const offset = 45;
|
const offset = 45;
|
||||||
|
@ -357,6 +475,30 @@ void InspectorClient::maybe_load_inspector()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inspector.createPropertyTables = (computedStyle, resolvedStyle, customProperties) => {
|
||||||
|
const createPropertyTable = (tableID, properties) => {
|
||||||
|
let oldTable = document.getElementById(tableID);
|
||||||
|
|
||||||
|
let newTable = document.createElement("tbody");
|
||||||
|
newTable.setAttribute("id", tableID);
|
||||||
|
|
||||||
|
Object.keys(properties).sort().forEach(name => {
|
||||||
|
let row = newTable.insertRow();
|
||||||
|
|
||||||
|
let nameColumn = row.insertCell();
|
||||||
|
nameColumn.innerText = name;
|
||||||
|
|
||||||
|
let valueColumn = row.insertCell();
|
||||||
|
valueColumn.innerText = properties[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
oldTable.parentNode.replaceChild(newTable, oldTable)
|
||||||
|
};
|
||||||
|
|
||||||
|
createPropertyTable("computed-style-table", JSON.parse(computedStyle));
|
||||||
|
createPropertyTable("resolved-style-table", JSON.parse(resolvedStyle));
|
||||||
|
createPropertyTable("custom-properties-table", JSON.parse(customProperties));
|
||||||
|
};
|
||||||
|
|
||||||
const inspectDOMNode = domNode => {
|
const inspectDOMNode = domNode => {
|
||||||
if (selectedDOMNode === domNode) {
|
if (selectedDOMNode === domNode) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue