mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 03:25:13 +00:00
LibWeb+LibWebView+WebContent: Remove the built-in Inspector
This commit is contained in:
parent
1c696e7893
commit
810d04b3f4
Notes:
github-actions[bot]
2025-03-15 18:10:57 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/810d04b3f4b Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3956
34 changed files with 24 additions and 3082 deletions
|
@ -1,383 +0,0 @@
|
|||
:root {
|
||||
--code-font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: rgb(23, 23, 23);
|
||||
--separator: dimgray;
|
||||
--separator-accent: rgb(57, 57, 57);
|
||||
--tab-controls: rgb(57, 57, 57);
|
||||
--tab-button-background: rgb(43, 42, 50);
|
||||
--text-color: white;
|
||||
--tab-button-active-background: rgb(22 100 219);
|
||||
--tab-button-active-color: var(--text-color);
|
||||
--tab-button-border: rgb(96, 96, 96);
|
||||
--hoverable-background: #31383e;
|
||||
--selected-border: cyan;
|
||||
--console-prompt-color: cyan;
|
||||
--console-message-color: lightskyblue;
|
||||
--console-warning-color: orange;
|
||||
--console-input-color: rgb(57, 57, 57);
|
||||
--console-input-focus-color: cyan;
|
||||
--console-table-row-odd: rgb(57, 57, 57);
|
||||
--console-table-row-hover: rgb(80, 79, 79);
|
||||
--console-table-border: gray;
|
||||
--property-table-head: rgb(57, 57, 57);
|
||||
--property-table-row: rgb(45, 45, 45);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--background: white;
|
||||
--separator: lightgray;
|
||||
--separator-accent: white;
|
||||
--tab-controls: rgb(229, 229, 229);
|
||||
--tab-button-background: white;
|
||||
--text-color: black;
|
||||
--tab-button-active: rgb(22 100 219);
|
||||
--tab-button-border: rgb(242, 242, 242);
|
||||
--hoverable-background: rgb(236, 236, 236);
|
||||
--selected-border: blue;
|
||||
--console-prompt-color: blue;
|
||||
--console-message-color: blue;
|
||||
--console-warning-color: darkorange;
|
||||
--console-input-color: rgb(229, 229, 229);
|
||||
--console-input-focus-color: blue;
|
||||
--console-table-row-odd: rgb(229, 229, 229);
|
||||
--console-table-row-hover: rgb(199, 198, 198);
|
||||
--console-table-border: gray;
|
||||
--property-table-head: rgb(229, 229, 229);
|
||||
--property-table-row: rgb(240, 240, 240);
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: var(--background);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
font-size: 10pt;
|
||||
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.split-view {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.split-view-container {
|
||||
max-height: calc(100% - 40px);
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.split-view-separator {
|
||||
background-color: var(--separator);
|
||||
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
cursor: row-resize;
|
||||
user-select: none;
|
||||
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.split-view-separator circle {
|
||||
fill: var(--separator-accent);
|
||||
}
|
||||
|
||||
.tab-controls-container {
|
||||
background-color: var(--tab-controls);
|
||||
|
||||
width: 100%;
|
||||
|
||||
padding: 4px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.tab-controls {
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab-controls button {
|
||||
color: var(--text-color);
|
||||
background-color: var(--tab-button-background);
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
|
||||
float: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.tab-controls :first-child {
|
||||
border-radius: 0.5rem 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.tab-controls :last-child {
|
||||
border-radius: 0 0.5rem 0.5rem 0;
|
||||
}
|
||||
|
||||
.tab-controls button.active {
|
||||
background-color: var(--tab-button-active-background);
|
||||
color: var(--tab-button-active-color);
|
||||
}
|
||||
|
||||
.tab-controls button + button {
|
||||
border-left: 1px solid var(--tab-button-border);
|
||||
}
|
||||
|
||||
.global-controls {
|
||||
margin: 0 8px 0 8px;
|
||||
}
|
||||
|
||||
.global-controls button {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#export-inspector-button {
|
||||
background-image: url("resource://icons/16x16/download.png");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--tab-controls);
|
||||
}
|
||||
|
||||
#export-inspector-button:hover {
|
||||
background-color: var(--tab-button-background);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
height: calc(100% - 40px);
|
||||
|
||||
display: none;
|
||||
|
||||
padding: 8px 0px 0px 4px;
|
||||
|
||||
overflow: auto scroll;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
position: sticky;
|
||||
top: 2px; /* FIXME: Remove this when https://github.com/LadybirdBrowser/ladybird/issues/1245 is resolved. */
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--tab-controls);
|
||||
border-top: 2px solid var(--background);
|
||||
display: flex;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
details > :not(:first-child) {
|
||||
display: list-item;
|
||||
list-style: none inside;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.hoverable {
|
||||
display: block;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.dom-editor {
|
||||
width: fit-content;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.hoverable:hover {
|
||||
background-color: var(--hoverable-background);
|
||||
}
|
||||
|
||||
.selected {
|
||||
border: 1px dashed var(--selected-border);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#console {
|
||||
overflow: unset;
|
||||
}
|
||||
|
||||
.console {
|
||||
font-family: var(--code-font-family);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.console-output {
|
||||
height: calc(100% - 32px);
|
||||
overflow: auto scroll;
|
||||
}
|
||||
|
||||
.console-input {
|
||||
background-color: var(--console-input-color);
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.console-input input {
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.console-input input:focus {
|
||||
outline: 1px dashed var(--console-input-focus-color);
|
||||
}
|
||||
|
||||
.console-prompt {
|
||||
color: var(--console-prompt-color);
|
||||
}
|
||||
|
||||
.console-message {
|
||||
color: var(--console-message-color);
|
||||
}
|
||||
|
||||
.console-warning {
|
||||
color: var(--console-warning-color);
|
||||
}
|
||||
|
||||
.property-filter {
|
||||
display: block;
|
||||
width: calc(100% - 10px);
|
||||
height: 20px;
|
||||
padding: 4px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
border: 1px solid var(--console-table-border);
|
||||
background-color: var(--console-input-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.property-table {
|
||||
width: 100%;
|
||||
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.property-table th {
|
||||
background-color: var(--property-table-head);
|
||||
position: sticky;
|
||||
top: 30px;
|
||||
}
|
||||
|
||||
.property-table th,
|
||||
.property-table td {
|
||||
padding: 4px;
|
||||
text-align: left;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.hidden-row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.property-table tr:nth-child(even of :not(.hidden-row)) {
|
||||
background-color: var(--property-table-row);
|
||||
}
|
||||
|
||||
#fonts {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#fonts-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#fonts-list .font {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#fonts-list .font div {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#fonts-list .font div.name {
|
||||
background-color: var(--property-table-head);
|
||||
font-weight: bold;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.console-log-table {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.console-log-table table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid var(--console-table-border);
|
||||
}
|
||||
|
||||
.console-log-table thead {
|
||||
border-bottom: 1px solid var(--console-table-border);
|
||||
}
|
||||
|
||||
.console-log-table th {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
border: 1px solid var(--console-table-border);
|
||||
}
|
||||
|
||||
.console-log-table td {
|
||||
border-left: 1px solid var(--console-table-border);
|
||||
border-right: 1px solid var(--console-table-border);
|
||||
}
|
||||
|
||||
.console-log-table tbody tr:nth-of-type(2n + 1) {
|
||||
background-color: var(--console-table-row-odd);
|
||||
}
|
||||
|
||||
.console-log-table tbody tr:hover {
|
||||
background-color: var(--console-table-row-hover);
|
||||
}
|
||||
|
||||
.console-log-table th,
|
||||
.console-log-table td {
|
||||
padding: 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#style-sheet-picker {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#style-sheet-source {
|
||||
font-size: 10pt;
|
||||
font-family: var(--code-font-family);
|
||||
white-space: pre;
|
||||
padding: 0.5em;
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="color-scheme" content="dark light">
|
||||
<title>Inspector</title>
|
||||
|
||||
<style type="text/css">@INSPECTOR_STYLE@</style>
|
||||
<link href="@INSPECTOR_CSS@" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="split-view">
|
||||
<div id="inspector-top" class="split-view-container" style="height: 60%">
|
||||
<div class="tab-controls-container">
|
||||
<div class="global-controls"></div>
|
||||
|
||||
<div class="tab-controls">
|
||||
<button id="dom-tree-button" onclick="selectTopTab(this, 'dom-tree')">DOM Tree</button>
|
||||
<button id="accessibility-tree-button" onclick="selectTopTab(this, 'accessibility-tree')">Accessibility Tree</button>
|
||||
<button id="storage-button" onclick="selectTopTab(this, 'storage')">Storage</button>
|
||||
<button id="style-sheets-button" onclick="selectTopTab(this, 'style-sheets')">Style Sheets</button>
|
||||
</div>
|
||||
|
||||
<div class="global-controls">
|
||||
<button id="export-inspector-button" title="Export the Inspector to an HTML file" onclick="inspector.exportInspector()"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dom-tree" class="tab-content html"></div>
|
||||
<div id="accessibility-tree" class="tab-content"></div>
|
||||
|
||||
<div id="storage" class="tab-content" style="padding: 0">
|
||||
<div class="tab-header">
|
||||
<select id="storage-picker">
|
||||
<option value="cookies" selected>Cookies</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="cookie-storage">
|
||||
<table class="property-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">Name</th>
|
||||
<th style="width: 15%">Value</th>
|
||||
<th style="width: 10%">Domain</th>
|
||||
<th style="width: 5%">Path</th>
|
||||
<th style="width: 20%">Created</th>
|
||||
<th style="width: 20%">Last Accessed</th>
|
||||
<th style="width: 20%">Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cookie-table">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="style-sheets" class="tab-content" style="padding: 0">
|
||||
<div class="tab-header">
|
||||
<select id="style-sheet-picker" disabled onchange="loadStyleSheet()">
|
||||
<option value="." selected>No style sheets found</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="style-sheet-source"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="inspector-separator" class="split-view-separator">
|
||||
<svg viewBox="0 0 16 5" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="2" cy="2.5" r="2" />
|
||||
<circle cx="8" cy="2.5" r="2" />
|
||||
<circle cx="14" cy="2.5" r="2" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="inspector-bottom" class="split-view-container" style="height: calc(40% - 5px)">
|
||||
<div class="tab-controls-container">
|
||||
<div class="global-controls"></div>
|
||||
|
||||
<div class="tab-controls">
|
||||
<button id="console-button" onclick="selectBottomTab(this, 'console')">Console</button>
|
||||
<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>
|
||||
<button id="font-button" onclick="selectBottomTab(this, 'fonts')">Fonts</button>
|
||||
</div>
|
||||
|
||||
<div class="global-controls"></div>
|
||||
</div>
|
||||
|
||||
<div id="console" class="tab-content">
|
||||
<div class="console">
|
||||
<div id="console-output" class="console-output"></div>
|
||||
<div class="console-input">
|
||||
<label for="console-input" class="console-prompt">>></label>
|
||||
<input id="console-input" type="text" placeholder="Enter statement to execute">
|
||||
<button id="console-clear" title="Clear the console output" onclick="inspector.clearConsoleOutput()">X</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@COMPUTED_STYLE@
|
||||
@RESOVLED_STYLE@
|
||||
@CUSTOM_PROPERTIES@
|
||||
|
||||
<div id="fonts" class="tab-content">
|
||||
<div id="fonts-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="@INSPECTOR_JS@"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,866 +0,0 @@
|
|||
let selectedTopTab = null;
|
||||
let selectedTopTabButton = null;
|
||||
|
||||
let selectedBottomTab = null;
|
||||
let selectedBottomTabButton = null;
|
||||
|
||||
let selectedDOMNode = null;
|
||||
let pendingEditDOMNode = null;
|
||||
|
||||
let visibleDOMNodes = [];
|
||||
|
||||
let consoleGroupStack = [];
|
||||
let consoleGroupNextID = 0;
|
||||
|
||||
let consoleHistory = [];
|
||||
let consoleHistoryIndex = 0;
|
||||
|
||||
const decodeBase64 = encoded => {
|
||||
return new TextDecoder().decode(Uint8Array.from(atob(encoded), c => c.charCodeAt(0)));
|
||||
};
|
||||
|
||||
const beginSplitViewDrag = () => {
|
||||
let inspectorTop = document.getElementById("inspector-top");
|
||||
let inspectorBottom = document.getElementById("inspector-bottom");
|
||||
let inspectorSeparator = document.getElementById("inspector-separator");
|
||||
|
||||
const windowHeight = window.innerHeight;
|
||||
const separatorHeight = inspectorSeparator.clientHeight;
|
||||
|
||||
const updateSplitView = event => {
|
||||
let position = Math.min(event.clientY, windowHeight - separatorHeight);
|
||||
position = Math.max(position, 0);
|
||||
|
||||
inspectorTop.style.height = `${position}px`;
|
||||
inspectorBottom.style.height = `${windowHeight - position - separatorHeight}px`;
|
||||
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const endSplitViewDrag = () => {
|
||||
document.removeEventListener("mousemove", updateSplitView);
|
||||
document.removeEventListener("mouseup", endSplitViewDrag);
|
||||
document.body.style.cursor = "";
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", updateSplitView);
|
||||
document.addEventListener("mouseup", endSplitViewDrag);
|
||||
document.body.style.cursor = "row-resize";
|
||||
};
|
||||
|
||||
const selectTab = (tabButton, tabID, selectedTab, selectedTabButton) => {
|
||||
let tab = document.getElementById(tabID);
|
||||
|
||||
if (selectedTab === tab) {
|
||||
return selectedTab;
|
||||
}
|
||||
if (selectedTab !== null) {
|
||||
selectedTab.style.display = "none";
|
||||
selectedTabButton.classList.remove("active");
|
||||
}
|
||||
|
||||
tab.style.display = "block";
|
||||
tabButton.classList.add("active");
|
||||
|
||||
// Apply any filtering if we have it
|
||||
let filterInput = tab.querySelector(".property-filter");
|
||||
let propertyTable = tab.querySelector(".property-table");
|
||||
if (filterInput && propertyTable) {
|
||||
filterInput.value = inspector.propertyFilterText || "";
|
||||
filterInput.dispatchEvent(new InputEvent("input"));
|
||||
}
|
||||
|
||||
return tab;
|
||||
};
|
||||
|
||||
const selectTopTab = (tabButton, tabID) => {
|
||||
selectedTopTab = selectTab(tabButton, tabID, selectedTopTab, selectedTopTabButton);
|
||||
selectedTopTabButton = tabButton;
|
||||
};
|
||||
|
||||
const selectBottomTab = (tabButton, tabID) => {
|
||||
selectedBottomTab = selectTab(tabButton, tabID, selectedBottomTab, selectedBottomTabButton);
|
||||
selectedBottomTabButton = tabButton;
|
||||
};
|
||||
|
||||
let initialTopTabButton = document.getElementById("dom-tree-button");
|
||||
selectTopTab(initialTopTabButton, "dom-tree");
|
||||
|
||||
let initialBottomTabButton = document.getElementById("console-button");
|
||||
selectBottomTab(initialBottomTabButton, "console");
|
||||
|
||||
const scrollToElement = element => {
|
||||
// Include an offset to prevent the element being placed behind the fixed `tab-controls` header.
|
||||
const offset = 45;
|
||||
|
||||
let position = element.getBoundingClientRect().top;
|
||||
position += window.pageYOffset - offset;
|
||||
|
||||
window.scrollTo(0, position);
|
||||
};
|
||||
|
||||
inspector.exportInspector = () => {
|
||||
const html = `<!DOCTYPE ${document.doctype.name}>\n${document.documentElement.outerHTML}`;
|
||||
inspector.exportInspectorHTML(html);
|
||||
};
|
||||
|
||||
inspector.reset = () => {
|
||||
let domTree = document.getElementById("dom-tree");
|
||||
domTree.innerHTML = "";
|
||||
|
||||
let accessibilityTree = document.getElementById("accessibility-tree");
|
||||
accessibilityTree.innerHTML = "";
|
||||
|
||||
let cookieTable = document.getElementById("cookie-table");
|
||||
cookieTable.innerHTML = "";
|
||||
|
||||
let styleSheetPicker = document.getElementById("style-sheet-picker");
|
||||
styleSheetPicker.replaceChildren();
|
||||
|
||||
let styleSheetSource = document.getElementById("style-sheet-source");
|
||||
styleSheetSource.innerHTML = "";
|
||||
|
||||
let fontsList = document.getElementById("fonts-list");
|
||||
fontsList.innerHTML = "";
|
||||
|
||||
selectedDOMNode = null;
|
||||
pendingEditDOMNode = null;
|
||||
|
||||
inspector.clearConsoleOutput();
|
||||
};
|
||||
|
||||
inspector.loadDOMTree = tree => {
|
||||
let domTree = document.getElementById("dom-tree");
|
||||
domTree.innerHTML = decodeBase64(tree);
|
||||
|
||||
let domNodes = domTree.querySelectorAll(".hoverable");
|
||||
|
||||
for (let domNode of domNodes) {
|
||||
domNode.addEventListener("click", event => {
|
||||
inspectDOMNode(domNode);
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
domNodes = domTree.querySelectorAll(".editable");
|
||||
|
||||
for (let domNode of domNodes) {
|
||||
domNode.addEventListener("dblclick", event => {
|
||||
const type = domNode.dataset.nodeType;
|
||||
const text = event.target.innerText;
|
||||
|
||||
if (type === "attribute" && event.target.classList.contains("attribute-value")) {
|
||||
text = text.substring(1, text.length - 1);
|
||||
}
|
||||
|
||||
editDOMNode(domNode, text);
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
domNodes = domTree.querySelectorAll("details");
|
||||
|
||||
for (let domNode of domNodes) {
|
||||
domNode.addEventListener("toggle", event => {
|
||||
updateVisibleDOMNodes();
|
||||
});
|
||||
}
|
||||
|
||||
updateVisibleDOMNodes();
|
||||
};
|
||||
|
||||
inspector.loadAccessibilityTree = tree => {
|
||||
let accessibilityTree = document.getElementById("accessibility-tree");
|
||||
accessibilityTree.innerHTML = decodeBase64(tree);
|
||||
};
|
||||
|
||||
inspector.inspectDOMNodeID = nodeID => {
|
||||
let domNodes = document.querySelectorAll(`[data-id="${nodeID}"]`);
|
||||
if (domNodes.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let domNode = domNodes[0]; domNode; domNode = domNode.parentNode) {
|
||||
if (domNode.tagName === "DETAILS") {
|
||||
domNode.setAttribute("open", "");
|
||||
}
|
||||
}
|
||||
|
||||
inspectDOMNode(domNodes[0]);
|
||||
scrollToElement(selectedDOMNode);
|
||||
};
|
||||
|
||||
inspector.clearInspectedDOMNode = () => {
|
||||
if (selectedDOMNode !== null) {
|
||||
selectedDOMNode.classList.remove("selected");
|
||||
selectedDOMNode = null;
|
||||
}
|
||||
};
|
||||
|
||||
inspector.editDOMNodeID = nodeID => {
|
||||
if (pendingEditDOMNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
inspector.inspectDOMNodeID(nodeID);
|
||||
editDOMNode(pendingEditDOMNode);
|
||||
|
||||
pendingEditDOMNode = null;
|
||||
};
|
||||
|
||||
inspector.addAttributeToDOMNodeID = nodeID => {
|
||||
if (pendingEditDOMNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
inspector.inspectDOMNodeID(nodeID);
|
||||
addAttributeToDOMNode(pendingEditDOMNode);
|
||||
|
||||
pendingEditDOMNode = null;
|
||||
};
|
||||
|
||||
inspector.setCookies = cookies => {
|
||||
let oldTable = document.getElementById("cookie-table");
|
||||
|
||||
let newTable = document.createElement("tbody");
|
||||
newTable.setAttribute("id", oldTable.id);
|
||||
|
||||
const addColumn = (row, value) => {
|
||||
let column = row.insertCell();
|
||||
column.innerText = value;
|
||||
column.title = value;
|
||||
};
|
||||
|
||||
cookies
|
||||
.sort((lhs, rhs) => lhs.name.localeCompare(rhs.name))
|
||||
.forEach(cookie => {
|
||||
let row = newTable.insertRow();
|
||||
|
||||
addColumn(row, cookie.name);
|
||||
addColumn(row, cookie.value);
|
||||
addColumn(row, cookie.domain);
|
||||
addColumn(row, cookie.path);
|
||||
addColumn(row, new Date(cookie.creationTime).toLocaleString());
|
||||
addColumn(row, new Date(cookie.lastAccessTime).toLocaleString());
|
||||
addColumn(row, new Date(cookie.expiryTime).toLocaleString());
|
||||
|
||||
row.addEventListener("contextmenu", event => {
|
||||
inspector.requestCookieContextMenu(cookie.index, event.clientX, event.clientY);
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
oldTable.parentNode.replaceChild(newTable, oldTable);
|
||||
};
|
||||
|
||||
inspector.setStyleSheets = styleSheets => {
|
||||
const styleSheetPicker = document.getElementById("style-sheet-picker");
|
||||
const styleSheetSource = document.getElementById("style-sheet-source");
|
||||
styleSheetPicker.replaceChildren();
|
||||
styleSheetSource.innerHTML = "";
|
||||
|
||||
function addOption(styleSheet, text) {
|
||||
const option = document.createElement("option");
|
||||
option.innerText = text;
|
||||
if (styleSheet.type) {
|
||||
option.dataset["type"] = styleSheet.type;
|
||||
}
|
||||
if (styleSheet.domNodeId) {
|
||||
option.dataset["domNodeId"] = styleSheet.domNodeId;
|
||||
}
|
||||
if (styleSheet.url) {
|
||||
option.dataset["url"] = styleSheet.url;
|
||||
}
|
||||
styleSheetPicker.add(option);
|
||||
}
|
||||
|
||||
if (styleSheets.length > 0) {
|
||||
let styleElementIndex = 1;
|
||||
for (const styleSheet of styleSheets) {
|
||||
switch (styleSheet.type) {
|
||||
case "StyleElement":
|
||||
addOption(styleSheet, `Style element #${styleElementIndex++}`);
|
||||
break;
|
||||
case "LinkElement":
|
||||
addOption(styleSheet, styleSheet.url);
|
||||
break;
|
||||
case "ImportRule":
|
||||
addOption(styleSheet, styleSheet.url);
|
||||
break;
|
||||
case "UserAgent":
|
||||
addOption(styleSheet, `User agent: ${styleSheet.url}`);
|
||||
break;
|
||||
case "UserStyle":
|
||||
addOption(styleSheet, "User style");
|
||||
break;
|
||||
}
|
||||
}
|
||||
styleSheetPicker.disabled = false;
|
||||
} else {
|
||||
addOption({}, "No style sheets found");
|
||||
styleSheetPicker.disabled = true;
|
||||
}
|
||||
|
||||
styleSheetPicker.selectedIndex = 0;
|
||||
|
||||
if (!styleSheetPicker.disabled) {
|
||||
loadStyleSheet();
|
||||
}
|
||||
};
|
||||
|
||||
const loadStyleSheet = () => {
|
||||
const styleSheetPicker = document.getElementById("style-sheet-picker");
|
||||
const styleSheetSource = document.getElementById("style-sheet-source");
|
||||
const selectedOption = styleSheetPicker.selectedOptions[0];
|
||||
|
||||
styleSheetSource.innerHTML = "Loading...";
|
||||
inspector.requestStyleSheetSource(
|
||||
selectedOption.dataset["type"],
|
||||
selectedOption.dataset["domNodeId"],
|
||||
selectedOption.dataset["url"]
|
||||
);
|
||||
};
|
||||
|
||||
inspector.setStyleSheetSource = (identifier, sourceBase64) => {
|
||||
const styleSheetPicker = document.getElementById("style-sheet-picker");
|
||||
const styleSheetSource = document.getElementById("style-sheet-source");
|
||||
const selectedOption = styleSheetPicker.selectedOptions[0];
|
||||
|
||||
// Make sure this is the source for the currently-selected style sheet.
|
||||
// NOTE: These are != not !== intentionally.
|
||||
if (
|
||||
identifier.type != selectedOption.dataset["type"] ||
|
||||
identifier.domNodeId != selectedOption.dataset["domNodeId"] ||
|
||||
identifier.url != selectedOption.dataset["url"]
|
||||
) {
|
||||
console.log(
|
||||
JSON.stringify(identifier),
|
||||
"doesn't match",
|
||||
JSON.stringify(selectedOption.dataset)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
styleSheetSource.innerHTML = decodeBase64(sourceBase64);
|
||||
};
|
||||
|
||||
const applyPropertyFilter = (row, searchText) => {
|
||||
let matches = false;
|
||||
if (searchText) {
|
||||
for (let cell of row.cells) {
|
||||
if (cell.textContent.toLowerCase().includes(searchText)) {
|
||||
matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Empty searchText matches everything, so skip the checks.
|
||||
matches = true;
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
row.classList.remove("hidden-row");
|
||||
} else {
|
||||
row.classList.add("hidden-row");
|
||||
}
|
||||
};
|
||||
|
||||
const setupPropertyFilter = inputId => {
|
||||
const filterInput = document.getElementById(`${inputId}-filter`);
|
||||
|
||||
filterInput.addEventListener("input", event => {
|
||||
inspector.propertyFilterText = event.target.value.toLowerCase();
|
||||
const tbody = document.getElementById(`${inputId}-table`);
|
||||
const rows = tbody.getElementsByTagName("tr");
|
||||
|
||||
for (let row of rows) {
|
||||
applyPropertyFilter(row, inspector.propertyFilterText);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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((a, b) => {
|
||||
let baseResult = a.localeCompare(b);
|
||||
// Manually move vendor-prefixed items after non-prefixed ones.
|
||||
if (a[0] === "-") {
|
||||
if (b[0] === "-") {
|
||||
return baseResult;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (b[0] === "-") {
|
||||
return -1;
|
||||
}
|
||||
return baseResult;
|
||||
})
|
||||
.forEach(name => {
|
||||
let row = newTable.insertRow();
|
||||
|
||||
let nameColumn = row.insertCell();
|
||||
nameColumn.innerText = name;
|
||||
|
||||
let valueColumn = row.insertCell();
|
||||
valueColumn.innerText = properties[name];
|
||||
|
||||
if (inspector.propertyFilterText) {
|
||||
applyPropertyFilter(row, inspector.propertyFilterText);
|
||||
}
|
||||
});
|
||||
|
||||
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));
|
||||
};
|
||||
|
||||
inspector.createFontList = fonts => {
|
||||
let fontsJSON = JSON.parse(fonts);
|
||||
if (!Array.isArray(fontsJSON)) return;
|
||||
|
||||
const listId = "fonts-list";
|
||||
let oldList = document.getElementById(listId);
|
||||
|
||||
let newList = document.createElement("div");
|
||||
newList.setAttribute("id", listId);
|
||||
const createFontEntry = (listContainer, font) => {
|
||||
let fontEntry = document.createElement("div");
|
||||
fontEntry.classList.add("font");
|
||||
|
||||
let fontName = document.createElement("div");
|
||||
fontName.classList.add("name");
|
||||
fontName.innerText = font.name;
|
||||
fontEntry.appendChild(fontName);
|
||||
|
||||
let fontSize = document.createElement("div");
|
||||
fontSize.classList.add("size");
|
||||
fontSize.innerText = font.size;
|
||||
fontEntry.appendChild(fontSize);
|
||||
|
||||
let fontWeight = document.createElement("div");
|
||||
fontWeight.classList.add("Weight");
|
||||
fontWeight.innerText = font.weight;
|
||||
fontEntry.appendChild(fontWeight);
|
||||
|
||||
let fontVariant = document.createElement("div");
|
||||
fontVariant.classList.add("Variant");
|
||||
fontVariant.innerText = font.variant;
|
||||
fontEntry.appendChild(fontVariant);
|
||||
|
||||
listContainer.appendChild(fontEntry);
|
||||
};
|
||||
|
||||
for (let font of fontsJSON) createFontEntry(newList, font);
|
||||
|
||||
oldList.parentNode.replaceChild(newList, oldList);
|
||||
};
|
||||
|
||||
const inspectDOMNode = domNode => {
|
||||
if (selectedDOMNode === domNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
inspector.clearInspectedDOMNode();
|
||||
|
||||
domNode.classList.add("selected");
|
||||
selectedDOMNode = domNode;
|
||||
|
||||
inspector.inspectDOMNode(domNode.dataset.id, domNode.dataset.pseudoElement);
|
||||
};
|
||||
|
||||
const createDOMEditor = (onHandleChange, onCancelChange) => {
|
||||
selectedDOMNode.classList.remove("selected");
|
||||
|
||||
let input = document.createElement("input");
|
||||
input.classList.add("dom-editor");
|
||||
input.classList.add("selected");
|
||||
|
||||
const handleChange = () => {
|
||||
input.removeEventListener("change", handleChange);
|
||||
input.removeEventListener("blur", cancelChange);
|
||||
input.removeEventListener("keydown", handleInput);
|
||||
|
||||
try {
|
||||
onHandleChange(input.value);
|
||||
} catch {
|
||||
cancelChange();
|
||||
}
|
||||
};
|
||||
|
||||
const cancelChange = () => {
|
||||
input.removeEventListener("change", handleChange);
|
||||
input.removeEventListener("blur", cancelChange);
|
||||
input.removeEventListener("keydown", handleInput);
|
||||
|
||||
selectedDOMNode.classList.add("selected");
|
||||
onCancelChange(input);
|
||||
};
|
||||
|
||||
const handleInput = event => {
|
||||
const ESCAPE_KEYCODE = 27;
|
||||
|
||||
if (event.keyCode === ESCAPE_KEYCODE) {
|
||||
cancelChange();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
input.addEventListener("change", handleChange);
|
||||
input.addEventListener("blur", cancelChange);
|
||||
input.addEventListener("keydown", handleInput);
|
||||
|
||||
setTimeout(() => {
|
||||
input.focus();
|
||||
});
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
const parseDOMAttributes = value => {
|
||||
let element = document.createElement("div");
|
||||
element.innerHTML = `<div ${value}></div>`;
|
||||
|
||||
return element.children[0].attributes;
|
||||
};
|
||||
|
||||
const editDOMNode = (domNode, textToSelect) => {
|
||||
if (selectedDOMNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const domNodeID = selectedDOMNode.dataset.id;
|
||||
const type = domNode.dataset.nodeType;
|
||||
|
||||
const handleChange = value => {
|
||||
if (type === "text" || type === "comment") {
|
||||
inspector.setDOMNodeText(domNodeID, value);
|
||||
} else if (type === "tag") {
|
||||
const element = document.createElement(value);
|
||||
inspector.setDOMNodeTag(domNodeID, value);
|
||||
} else if (type === "attribute") {
|
||||
const attributeIndex = domNode.dataset.attributeIndex;
|
||||
const attributes = parseDOMAttributes(value);
|
||||
|
||||
inspector.replaceDOMNodeAttribute(domNodeID, attributeIndex, attributes);
|
||||
}
|
||||
};
|
||||
|
||||
const cancelChange = editor => {
|
||||
editor.parentNode.replaceChild(domNode, editor);
|
||||
};
|
||||
|
||||
let editor = createDOMEditor(handleChange, cancelChange);
|
||||
|
||||
if (type === "text") {
|
||||
let emptyTextSpan = domNode.querySelector(".internal");
|
||||
|
||||
if (emptyTextSpan === null) {
|
||||
editor.value = domNode.innerText;
|
||||
}
|
||||
} else {
|
||||
editor.value = domNode.innerText;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (typeof textToSelect !== "undefined") {
|
||||
const index = editor.value.indexOf(textToSelect);
|
||||
if (index !== -1) {
|
||||
editor.setSelectionRange(index, index + textToSelect.length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
editor.select();
|
||||
});
|
||||
|
||||
domNode.parentNode.replaceChild(editor, domNode);
|
||||
};
|
||||
|
||||
const addAttributeToDOMNode = domNode => {
|
||||
if (selectedDOMNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const domNodeID = selectedDOMNode.dataset.id;
|
||||
|
||||
const handleChange = value => {
|
||||
const attributes = parseDOMAttributes(value);
|
||||
inspector.addDOMNodeAttributes(domNodeID, attributes);
|
||||
};
|
||||
|
||||
const cancelChange = () => {
|
||||
container.remove();
|
||||
};
|
||||
|
||||
let editor = createDOMEditor(handleChange, cancelChange);
|
||||
editor.placeholder = 'name="value"';
|
||||
|
||||
let nbsp = document.createElement("span");
|
||||
nbsp.innerHTML = " ";
|
||||
|
||||
let container = document.createElement("span");
|
||||
container.appendChild(nbsp);
|
||||
container.appendChild(editor);
|
||||
|
||||
domNode.parentNode.insertBefore(container, domNode.parentNode.lastChild);
|
||||
};
|
||||
|
||||
const updateVisibleDOMNodes = () => {
|
||||
let domTree = document.getElementById("dom-tree");
|
||||
|
||||
visibleDOMNodes = [];
|
||||
|
||||
function recurseDOMNodes(node) {
|
||||
for (let child of node.children) {
|
||||
if (child.classList.contains("hoverable")) {
|
||||
visibleDOMNodes.push(child);
|
||||
}
|
||||
|
||||
if (child.tagName === "DIV") {
|
||||
if (node.open) {
|
||||
recurseDOMNodes(child);
|
||||
}
|
||||
} else {
|
||||
recurseDOMNodes(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recurseDOMNodes(domTree);
|
||||
};
|
||||
|
||||
const requestContextMenu = (clientX, clientY, domNode) => {
|
||||
pendingEditDOMNode = null;
|
||||
|
||||
if (typeof domNode.dataset.nodeType === "undefined") {
|
||||
if (domNode.parentNode !== null) {
|
||||
domNode = domNode.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
const domNodeID = domNode.closest(".hoverable")?.dataset.id;
|
||||
const type = domNode.dataset.nodeType;
|
||||
|
||||
if (typeof domNodeID === "undefined" || typeof type === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
let tag = null;
|
||||
let attributeIndex = null;
|
||||
|
||||
if (type === "tag") {
|
||||
tag = domNode.dataset.tag;
|
||||
} else if (type === "attribute") {
|
||||
tag = domNode.dataset.tag;
|
||||
attributeIndex = domNode.dataset.attributeIndex;
|
||||
}
|
||||
|
||||
pendingEditDOMNode = domNode;
|
||||
inspector.requestDOMTreeContextMenu(domNodeID, clientX, clientY, type, tag, attributeIndex);
|
||||
};
|
||||
|
||||
const executeConsoleScript = consoleInput => {
|
||||
const script = consoleInput.value;
|
||||
|
||||
if (!/\S/.test(script)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (consoleHistory.length === 0 || consoleHistory[consoleHistory.length - 1] !== script) {
|
||||
consoleHistory.push(script);
|
||||
}
|
||||
|
||||
consoleHistoryIndex = consoleHistory.length;
|
||||
|
||||
inspector.executeConsoleScript(script);
|
||||
consoleInput.value = "";
|
||||
};
|
||||
|
||||
const setConsoleInputToPreviousHistoryItem = consoleInput => {
|
||||
if (consoleHistoryIndex === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
--consoleHistoryIndex;
|
||||
|
||||
const script = consoleHistory[consoleHistoryIndex];
|
||||
consoleInput.value = script;
|
||||
};
|
||||
|
||||
const setConsoleInputToNextHistoryItem = consoleInput => {
|
||||
if (consoleHistory.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastIndex = consoleHistory.length - 1;
|
||||
|
||||
if (consoleHistoryIndex < lastIndex) {
|
||||
++consoleHistoryIndex;
|
||||
|
||||
consoleInput.value = consoleHistory[consoleHistoryIndex];
|
||||
return;
|
||||
}
|
||||
|
||||
if (consoleHistoryIndex === lastIndex) {
|
||||
++consoleHistoryIndex;
|
||||
|
||||
consoleInput.value = "";
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const consoleParentGroup = () => {
|
||||
if (consoleGroupStack.length === 0) {
|
||||
return document.getElementById("console-output");
|
||||
}
|
||||
|
||||
const lastConsoleGroup = consoleGroupStack[consoleGroupStack.length - 1];
|
||||
return document.getElementById(`console-group-${lastConsoleGroup.id}`);
|
||||
};
|
||||
|
||||
const scrollConsoleToBottom = () => {
|
||||
let consoleOutput = document.getElementById("console-output");
|
||||
|
||||
// FIXME: It should be sufficient to scrollTo a y value of document.documentElement.offsetHeight,
|
||||
// but due to an unknown bug offsetHeight seems to not be properly updated after spamming
|
||||
// a lot of document changes.
|
||||
//
|
||||
// The setTimeout makes the scrollTo async and allows the DOM to be updated.
|
||||
setTimeout(function () {
|
||||
consoleOutput.scrollTo(0, 1_000_000_000);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
inspector.appendConsoleOutput = output => {
|
||||
let parent = consoleParentGroup();
|
||||
|
||||
let element = document.createElement("p");
|
||||
element.innerHTML = decodeBase64(output);
|
||||
|
||||
parent.appendChild(element);
|
||||
scrollConsoleToBottom();
|
||||
};
|
||||
|
||||
inspector.clearConsoleOutput = () => {
|
||||
let consoleOutput = document.getElementById("console-output");
|
||||
consoleOutput.innerHTML = "";
|
||||
|
||||
consoleGroupStack = [];
|
||||
};
|
||||
|
||||
inspector.beginConsoleGroup = (label, startExpanded) => {
|
||||
let parent = consoleParentGroup();
|
||||
|
||||
const group = {
|
||||
id: ++consoleGroupNextID,
|
||||
label: label,
|
||||
};
|
||||
consoleGroupStack.push(group);
|
||||
|
||||
let details = document.createElement("details");
|
||||
details.id = `console-group-${group.id}`;
|
||||
details.open = startExpanded;
|
||||
|
||||
let summary = document.createElement("summary");
|
||||
summary.innerHTML = decodeBase64(label);
|
||||
|
||||
details.appendChild(summary);
|
||||
parent.appendChild(details);
|
||||
scrollConsoleToBottom();
|
||||
};
|
||||
|
||||
inspector.endConsoleGroup = () => {
|
||||
consoleGroupStack.pop();
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let inspectorSeparator = document.getElementById("inspector-separator");
|
||||
inspectorSeparator.addEventListener("mousedown", beginSplitViewDrag);
|
||||
|
||||
let consoleInput = document.getElementById("console-input");
|
||||
consoleInput.focus();
|
||||
|
||||
consoleInput.addEventListener("keydown", event => {
|
||||
const UP_ARROW_KEYCODE = 38;
|
||||
const DOWN_ARROW_KEYCODE = 40;
|
||||
const RETURN_KEYCODE = 13;
|
||||
|
||||
if (event.keyCode === UP_ARROW_KEYCODE) {
|
||||
setConsoleInputToPreviousHistoryItem(consoleInput);
|
||||
event.preventDefault();
|
||||
} else if (event.keyCode === DOWN_ARROW_KEYCODE) {
|
||||
setConsoleInputToNextHistoryItem(consoleInput);
|
||||
event.preventDefault();
|
||||
} else if (event.keyCode === RETURN_KEYCODE) {
|
||||
executeConsoleScript(consoleInput);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("contextmenu", event => {
|
||||
requestContextMenu(event.clientX, event.clientY, event.target);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", event => {
|
||||
const UP_ARROW_KEYCODE = 38;
|
||||
const DOWN_ARROW_KEYCODE = 40;
|
||||
const RIGHT_ARROW_KEYCODE = 39;
|
||||
const LEFT_ARROW_KEYCODE = 37;
|
||||
const RETURN_KEYCODE = 13;
|
||||
const SPACE_KEYCODE = 32;
|
||||
|
||||
const move = delta => {
|
||||
let selectedIndex = visibleDOMNodes.indexOf(selectedDOMNode);
|
||||
if (selectedIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newIndex = selectedIndex + delta;
|
||||
|
||||
if (visibleDOMNodes[newIndex]) {
|
||||
inspectDOMNode(visibleDOMNodes[newIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
if (document.activeElement.tagName !== "INPUT") {
|
||||
const isSummary = selectedDOMNode.parentNode.tagName === "SUMMARY";
|
||||
const isDiv = selectedDOMNode.parentNode.tagName === "DIV";
|
||||
|
||||
if (event.keyCode == UP_ARROW_KEYCODE) {
|
||||
move(-1);
|
||||
} else if (event.keyCode == DOWN_ARROW_KEYCODE) {
|
||||
move(1);
|
||||
} else if (event.keyCode == RETURN_KEYCODE || event.keyCode == SPACE_KEYCODE) {
|
||||
if (isSummary) {
|
||||
selectedDOMNode.parentNode.click();
|
||||
}
|
||||
} else if (event.keyCode == RIGHT_ARROW_KEYCODE) {
|
||||
if (isSummary && selectedDOMNode.parentNode.parentNode.open === false) {
|
||||
selectedDOMNode.parentNode.click();
|
||||
} else if (selectedDOMNode.parentNode.parentNode.open === true && !isDiv) {
|
||||
move(1);
|
||||
}
|
||||
} else if (event.keyCode == LEFT_ARROW_KEYCODE) {
|
||||
if (isSummary && selectedDOMNode.parentNode.parentNode.open === true) {
|
||||
selectedDOMNode.parentNode.click();
|
||||
} else if (selectedDOMNode.parentNode.parentNode.open === false || isDiv) {
|
||||
move(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Setup filters for property tables
|
||||
["computed-style", "resolved-style", "custom-properties"].forEach(setupPropertyFilter);
|
||||
|
||||
inspector.inspectorLoaded();
|
||||
});
|
|
@ -555,7 +555,6 @@ set(SOURCES
|
|||
IndexedDB/Internal/Database.cpp
|
||||
IndexedDB/Internal/Key.cpp
|
||||
IndexedDB/Internal/RequestList.cpp
|
||||
Internals/Inspector.cpp
|
||||
Internals/InternalAnimationTimeline.cpp
|
||||
Internals/Internals.cpp
|
||||
IntersectionObserver/IntersectionObserver.cpp
|
||||
|
|
|
@ -621,7 +621,6 @@ class RequestList;
|
|||
}
|
||||
|
||||
namespace Web::Internals {
|
||||
class Inspector;
|
||||
class Internals;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@
|
|||
#include <LibWeb/HTML/WindowProxy.h>
|
||||
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
#include <LibWeb/Internals/Inspector.h>
|
||||
#include <LibWeb/Internals/Internals.h>
|
||||
#include <LibWeb/Layout/Viewport.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
@ -716,14 +715,8 @@ Vector<GC::Ref<MimeType>> Window::pdf_viewer_mime_type_objects()
|
|||
return m_pdf_viewer_mime_type_objects;
|
||||
}
|
||||
|
||||
static bool s_inspector_object_exposed = false;
|
||||
static bool s_internals_object_exposed = false;
|
||||
|
||||
void Window::set_inspector_object_exposed(bool exposed)
|
||||
{
|
||||
s_inspector_object_exposed = exposed;
|
||||
}
|
||||
|
||||
void Window::set_internals_object_exposed(bool exposed)
|
||||
{
|
||||
s_internals_object_exposed = exposed;
|
||||
|
@ -739,8 +732,6 @@ WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironm
|
|||
Bindings::WindowGlobalMixin::initialize(realm, *this);
|
||||
WindowOrWorkerGlobalScopeMixin::initialize(realm);
|
||||
|
||||
if (s_inspector_object_exposed)
|
||||
define_direct_property("inspector", realm.create<Internals::Inspector>(realm), JS::default_attributes);
|
||||
if (s_internals_object_exposed)
|
||||
define_direct_property("internals", realm.create<Internals::Internals>(realm), JS::default_attributes);
|
||||
|
||||
|
|
|
@ -247,7 +247,6 @@ public:
|
|||
|
||||
void consume_history_action_user_activation();
|
||||
|
||||
static void set_inspector_object_exposed(bool);
|
||||
static void set_internals_object_exposed(bool);
|
||||
|
||||
[[nodiscard]] OrderedHashMap<FlyString, GC::Ref<Navigable>> document_tree_child_navigable_target_name_property_set();
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/Bindings/InspectorPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/CSS/Selector.h>
|
||||
#include <LibWeb/CSS/StyleSheetIdentifier.h>
|
||||
#include <LibWeb/DOM/NamedNodeMap.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Internals/Inspector.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
||||
namespace Web::Internals {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(Inspector);
|
||||
|
||||
Inspector::Inspector(JS::Realm& realm)
|
||||
: Bindings::PlatformObject(realm)
|
||||
{
|
||||
}
|
||||
|
||||
Inspector::~Inspector() = default;
|
||||
|
||||
void Inspector::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(Inspector);
|
||||
}
|
||||
|
||||
PageClient& Inspector::inspector_page_client() const
|
||||
{
|
||||
return as<HTML::Window>(HTML::relevant_global_object(*this)).page().client();
|
||||
}
|
||||
|
||||
void Inspector::inspector_loaded()
|
||||
{
|
||||
inspector_page_client().inspector_did_load();
|
||||
}
|
||||
|
||||
void Inspector::inspect_dom_node(i64 node_id, Optional<i32> const& pseudo_element)
|
||||
{
|
||||
inspector_page_client().inspector_did_select_dom_node(node_id, pseudo_element.map([](auto value) {
|
||||
VERIFY(value < to_underlying(Web::CSS::Selector::PseudoElement::Type::KnownPseudoElementCount));
|
||||
return static_cast<Web::CSS::Selector::PseudoElement::Type>(value);
|
||||
}));
|
||||
}
|
||||
|
||||
void Inspector::set_dom_node_text(i64 node_id, String const& text)
|
||||
{
|
||||
inspector_page_client().inspector_did_set_dom_node_text(node_id, text);
|
||||
}
|
||||
|
||||
void Inspector::set_dom_node_tag(i64 node_id, String const& tag)
|
||||
{
|
||||
inspector_page_client().inspector_did_set_dom_node_tag(node_id, tag);
|
||||
}
|
||||
|
||||
void Inspector::add_dom_node_attributes(i64 node_id, GC::Ref<DOM::NamedNodeMap> attributes)
|
||||
{
|
||||
inspector_page_client().inspector_did_add_dom_node_attributes(node_id, attributes);
|
||||
}
|
||||
|
||||
void Inspector::replace_dom_node_attribute(i64 node_id, WebIDL::UnsignedLongLong attribute_index, GC::Ref<DOM::NamedNodeMap> replacement_attributes)
|
||||
{
|
||||
inspector_page_client().inspector_did_replace_dom_node_attribute(node_id, static_cast<size_t>(attribute_index), replacement_attributes);
|
||||
}
|
||||
|
||||
void Inspector::request_dom_tree_context_menu(i64 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index)
|
||||
{
|
||||
inspector_page_client().inspector_did_request_dom_tree_context_menu(node_id, { client_x, client_y }, type, tag, attribute_index.map([](auto index) { return static_cast<size_t>(index); }));
|
||||
}
|
||||
|
||||
void Inspector::request_cookie_context_menu(WebIDL::UnsignedLongLong cookie_index, i32 client_x, i32 client_y)
|
||||
{
|
||||
inspector_page_client().inspector_did_request_cookie_context_menu(cookie_index, { client_x, client_y });
|
||||
}
|
||||
|
||||
void Inspector::request_style_sheet_source(String const& type_string, Optional<i64> const& dom_node_unique_id, Optional<String> const& url)
|
||||
{
|
||||
auto type = CSS::style_sheet_identifier_type_from_string(type_string);
|
||||
VERIFY(type.has_value());
|
||||
|
||||
Optional<UniqueNodeID> dom_node_unique_id_opt;
|
||||
if (dom_node_unique_id.has_value())
|
||||
dom_node_unique_id_opt = dom_node_unique_id.value();
|
||||
|
||||
inspector_page_client().inspector_did_request_style_sheet_source({
|
||||
.type = type.value(),
|
||||
.dom_element_unique_id = dom_node_unique_id_opt,
|
||||
.url = url,
|
||||
});
|
||||
}
|
||||
|
||||
void Inspector::execute_console_script(String const& script)
|
||||
{
|
||||
inspector_page_client().inspector_did_execute_console_script(script);
|
||||
}
|
||||
|
||||
void Inspector::export_inspector_html(String const& html)
|
||||
{
|
||||
inspector_page_client().inspector_did_export_inspector_html(html);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
namespace Web::Internals {
|
||||
|
||||
class Inspector final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(Inspector, Bindings::PlatformObject);
|
||||
GC_DECLARE_ALLOCATOR(Inspector);
|
||||
|
||||
public:
|
||||
virtual ~Inspector() override;
|
||||
|
||||
void inspector_loaded();
|
||||
void inspect_dom_node(i64 node_id, Optional<i32> const& pseudo_element);
|
||||
|
||||
void set_dom_node_text(i64 node_id, String const& text);
|
||||
void set_dom_node_tag(i64 node_id, String const& tag);
|
||||
void add_dom_node_attributes(i64 node_id, GC::Ref<DOM::NamedNodeMap> attributes);
|
||||
void replace_dom_node_attribute(i64 node_id, WebIDL::UnsignedLongLong attribute_index, GC::Ref<DOM::NamedNodeMap> replacement_attributes);
|
||||
|
||||
void request_dom_tree_context_menu(i64 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index);
|
||||
|
||||
void request_cookie_context_menu(WebIDL::UnsignedLongLong cookie_index, i32 client_x, i32 client_y);
|
||||
|
||||
void request_style_sheet_source(String const& type, Optional<i64> const& dom_node_unique_id, Optional<String> const& url);
|
||||
|
||||
void execute_console_script(String const& script);
|
||||
|
||||
void export_inspector_html(String const& html);
|
||||
|
||||
private:
|
||||
explicit Inspector(JS::Realm&);
|
||||
|
||||
PageClient& inspector_page_client() const;
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#import <DOM/NamedNodeMap.idl>
|
||||
|
||||
[Exposed=Nobody] interface Inspector {
|
||||
|
||||
undefined inspectorLoaded();
|
||||
undefined inspectDOMNode(long long nodeID, optional long pseudoElement);
|
||||
|
||||
undefined setDOMNodeText(long long nodeID, DOMString text);
|
||||
undefined setDOMNodeTag(long long nodeID, DOMString tag);
|
||||
undefined addDOMNodeAttributes(long long nodeID, NamedNodeMap attributes);
|
||||
undefined replaceDOMNodeAttribute(long long nodeID, unsigned long long attributeIndex, NamedNodeMap replacementAttributes);
|
||||
|
||||
undefined requestDOMTreeContextMenu(long long nodeID, long clientX, long clientY, DOMString type, DOMString? tag, unsigned long long? attributeIndex);
|
||||
|
||||
undefined requestCookieContextMenu(unsigned long long cookieIndex, long clientX, long clientY);
|
||||
|
||||
undefined requestStyleSheetSource(DOMString type, long long? domNodeID, DOMString? url);
|
||||
|
||||
undefined executeConsoleScript(DOMString script);
|
||||
|
||||
undefined exportInspectorHTML(DOMString html);
|
||||
|
||||
};
|
|
@ -9,12 +9,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibGC/Heap.h>
|
||||
#include <LibGC/Root.h>
|
||||
#include <LibGfx/Cursor.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
@ -28,8 +23,6 @@
|
|||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
#include <LibWeb/CSS/PreferredContrast.h>
|
||||
#include <LibWeb/CSS/PreferredMotion.h>
|
||||
#include <LibWeb/CSS/Selector.h>
|
||||
#include <LibWeb/CSS/StyleSheetIdentifier.h>
|
||||
#include <LibWeb/Cookie/Cookie.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/ActivateTab.h>
|
||||
|
@ -407,18 +400,6 @@ public:
|
|||
|
||||
virtual void page_did_mutate_dom([[maybe_unused]] FlyString const& type, [[maybe_unused]] DOM::Node const& target, [[maybe_unused]] DOM::NodeList& added_nodes, [[maybe_unused]] DOM::NodeList& removed_nodes, [[maybe_unused]] GC::Ptr<DOM::Node> previous_sibling, [[maybe_unused]] GC::Ptr<DOM::Node> next_sibling, [[maybe_unused]] Optional<String> const& attribute_name) { }
|
||||
|
||||
virtual void inspector_did_load() { }
|
||||
virtual void inspector_did_select_dom_node([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] Optional<CSS::Selector::PseudoElement::Type> const& pseudo_element) { }
|
||||
virtual void inspector_did_set_dom_node_text([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] String const& text) { }
|
||||
virtual void inspector_did_set_dom_node_tag([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] String const& tag) { }
|
||||
virtual void inspector_did_add_dom_node_attributes([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] GC::Ref<DOM::NamedNodeMap> attributes) { }
|
||||
virtual void inspector_did_replace_dom_node_attribute([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] size_t attribute_index, [[maybe_unused]] GC::Ref<DOM::NamedNodeMap> replacement_attributes) { }
|
||||
virtual void inspector_did_request_dom_tree_context_menu([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] CSSPixelPoint position, [[maybe_unused]] String const& type, [[maybe_unused]] Optional<String> const& tag, [[maybe_unused]] Optional<size_t> const& attribute_index) { }
|
||||
virtual void inspector_did_request_cookie_context_menu([[maybe_unused]] size_t cookie_index, [[maybe_unused]] CSSPixelPoint position) { }
|
||||
virtual void inspector_did_request_style_sheet_source([[maybe_unused]] CSS::StyleSheetIdentifier const& identifier) { }
|
||||
virtual void inspector_did_execute_console_script([[maybe_unused]] String const& script) { }
|
||||
virtual void inspector_did_export_inspector_html([[maybe_unused]] String const& html) { }
|
||||
|
||||
virtual bool is_ready_to_paint() const = 0;
|
||||
|
||||
virtual DisplayListPlayerType display_list_player_type() const = 0;
|
||||
|
|
|
@ -262,7 +262,6 @@ libweb_js_bindings(IndexedDB/IDBOpenDBRequest)
|
|||
libweb_js_bindings(IndexedDB/IDBRequest)
|
||||
libweb_js_bindings(IndexedDB/IDBTransaction)
|
||||
libweb_js_bindings(IndexedDB/IDBVersionChangeEvent)
|
||||
libweb_js_bindings(Internals/Inspector)
|
||||
libweb_js_bindings(Internals/InternalAnimationTimeline)
|
||||
libweb_js_bindings(Internals/Internals)
|
||||
libweb_js_bindings(IntersectionObserver/IntersectionObserver)
|
||||
|
|
|
@ -8,7 +8,6 @@ set(SOURCES
|
|||
CookieJar.cpp
|
||||
Database.cpp
|
||||
HelperProcess.cpp
|
||||
InspectorClient.cpp
|
||||
Mutation.cpp
|
||||
Plugins/FontPlugin.cpp
|
||||
Plugins/ImageCodecPlugin.cpp
|
||||
|
|
|
@ -13,7 +13,6 @@ namespace WebView {
|
|||
class Application;
|
||||
class CookieJar;
|
||||
class Database;
|
||||
class InspectorClient;
|
||||
class OutOfProcessWebView;
|
||||
class ProcessManager;
|
||||
class ViewImplementation;
|
||||
|
|
|
@ -85,7 +85,6 @@ static ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_proc
|
|||
Optional<IPC::File> request_server_socket,
|
||||
ClientArguments&&... client_arguments)
|
||||
{
|
||||
auto const& chrome_options = WebView::Application::chrome_options();
|
||||
auto const& web_content_options = WebView::Application::web_content_options();
|
||||
|
||||
Vector<ByteString> arguments {
|
||||
|
@ -95,8 +94,6 @@ static ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_proc
|
|||
web_content_options.executable_path.to_byte_string(),
|
||||
};
|
||||
|
||||
if (chrome_options.devtools_port.has_value())
|
||||
arguments.append("--devtools"sv);
|
||||
if (web_content_options.config_path.has_value()) {
|
||||
arguments.append("--config-path"sv);
|
||||
arguments.append(web_content_options.config_path.value());
|
||||
|
|
|
@ -1,809 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Base64.h>
|
||||
#include <AK/Enumerate.h>
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/SourceGenerator.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibJS/MarkupGenerator.h>
|
||||
#include <LibURL/Parser.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
#include <LibWeb/Namespace.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/CookieJar.h>
|
||||
#include <LibWebView/InspectorClient.h>
|
||||
#include <LibWebView/SourceHighlighter.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
static constexpr auto INSPECTOR_HTML = "resource://ladybird/inspector.html"sv;
|
||||
static constexpr auto INSPECTOR_CSS = "resource://ladybird/inspector.css"sv;
|
||||
static constexpr auto INSPECTOR_JS = "resource://ladybird/inspector.js"sv;
|
||||
|
||||
static String style_sheet_identifier_to_json(Web::CSS::StyleSheetIdentifier const& identifier)
|
||||
{
|
||||
return MUST(String::formatted("{{ type: '{}', domNodeId: {}, url: '{}' }}"sv,
|
||||
Web::CSS::style_sheet_identifier_type_to_string(identifier.type),
|
||||
identifier.dom_element_unique_id.map([](auto& it) { return String::number(it.value()); }).value_or("undefined"_string),
|
||||
identifier.url.value_or("undefined"_string)));
|
||||
}
|
||||
|
||||
InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImplementation& inspector_web_view)
|
||||
: m_content_web_view(content_web_view)
|
||||
, m_inspector_web_view(inspector_web_view)
|
||||
{
|
||||
m_content_web_view.on_received_dom_tree = [this](auto const& dom_tree) {
|
||||
auto dom_tree_html = generate_dom_tree(dom_tree);
|
||||
auto dom_tree_base64 = MUST(encode_base64(dom_tree_html.bytes()));
|
||||
|
||||
auto script = MUST(String::formatted("inspector.loadDOMTree(\"{}\");", dom_tree_base64));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
|
||||
m_dom_tree_loaded = true;
|
||||
|
||||
if (m_pending_selection.has_value())
|
||||
select_node(m_pending_selection.release_value());
|
||||
else
|
||||
select_default_node();
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_dom_node_properties = [this](auto const& properties) {
|
||||
StringBuilder builder;
|
||||
|
||||
// FIXME: Support box model metrics and ARIA properties.
|
||||
builder.append("inspector.createPropertyTables(\""sv);
|
||||
builder.append_escaped_for_json(properties.computed_style.serialized());
|
||||
builder.append("\", \""sv);
|
||||
builder.append_escaped_for_json(properties.resolved_style.serialized());
|
||||
builder.append("\", \""sv);
|
||||
builder.append_escaped_for_json(properties.custom_properties.serialized());
|
||||
builder.append("\");"sv);
|
||||
|
||||
builder.append("inspector.createFontList(\""sv);
|
||||
builder.append_escaped_for_json(properties.fonts.serialized());
|
||||
builder.append("\");"sv);
|
||||
|
||||
m_inspector_web_view.run_javascript(MUST(builder.to_string()));
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_accessibility_tree = [this](auto const& accessibility_tree) {
|
||||
auto accessibility_tree_html = generate_accessibility_tree(accessibility_tree);
|
||||
auto accessibility_tree_base64 = MUST(encode_base64(accessibility_tree_html.bytes()));
|
||||
|
||||
auto script = MUST(String::formatted("inspector.loadAccessibilityTree(\"{}\");", accessibility_tree_base64));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_hovered_node_id = [this](auto node_id) {
|
||||
select_node(node_id);
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_style_sheet_list = [this](auto const& style_sheets) {
|
||||
StringBuilder builder;
|
||||
builder.append("inspector.setStyleSheets(["sv);
|
||||
for (auto& style_sheet : style_sheets) {
|
||||
builder.appendff("{}, "sv, style_sheet_identifier_to_json(style_sheet));
|
||||
}
|
||||
builder.append("]);"sv);
|
||||
|
||||
m_inspector_web_view.run_javascript(MUST(builder.to_string()));
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_style_sheet_source = [this](Web::CSS::StyleSheetIdentifier const& identifier, URL::URL const& base_url, String const& source) {
|
||||
auto html = highlight_source(URL::Parser::basic_parse(identifier.url.value_or({})), base_url, source, Syntax::Language::CSS, HighlightOutputMode::SourceOnly);
|
||||
auto script = MUST(String::formatted("inspector.setStyleSheetSource({}, \"{}\");",
|
||||
style_sheet_identifier_to_json(identifier),
|
||||
MUST(encode_base64(html.bytes()))));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
};
|
||||
|
||||
m_content_web_view.on_finshed_editing_dom_node = [this](auto const& node_id) {
|
||||
m_pending_selection = node_id;
|
||||
m_dom_tree_loaded = false;
|
||||
m_dom_node_attributes.clear();
|
||||
|
||||
inspect();
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_dom_node_html = [this](auto const& html) {
|
||||
if (m_content_web_view.on_insert_clipboard_entry)
|
||||
m_content_web_view.on_insert_clipboard_entry(html, "unspecified"_string, "text/plain"_string);
|
||||
};
|
||||
|
||||
m_content_web_view.on_console_message_available = [this](auto message_index) {
|
||||
console_message_available(message_index);
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_styled_console_messages = [this](auto start_index, auto const& message_types, auto const& messages) {
|
||||
console_messages_received(start_index, message_types, messages);
|
||||
};
|
||||
|
||||
m_inspector_web_view.enable_inspector_prototype();
|
||||
m_inspector_web_view.use_native_user_style_sheet();
|
||||
|
||||
m_inspector_web_view.on_inspector_loaded = [this]() {
|
||||
m_inspector_loaded = true;
|
||||
inspect();
|
||||
|
||||
m_content_web_view.js_console_request_messages(0);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_requested_dom_tree_context_menu = [this](auto node_id, auto position, auto const& type, auto const& tag, auto const& attribute_index) {
|
||||
Optional<Attribute> attribute;
|
||||
if (attribute_index.has_value())
|
||||
attribute = m_dom_node_attributes.get(node_id)->at(*attribute_index);
|
||||
|
||||
m_context_menu_data = ContextMenuData { node_id, tag, attribute };
|
||||
|
||||
if (type.is_one_of("text"sv, "comment"sv)) {
|
||||
if (on_requested_dom_node_text_context_menu)
|
||||
on_requested_dom_node_text_context_menu(position);
|
||||
} else if (type == "tag"sv) {
|
||||
VERIFY(tag.has_value());
|
||||
|
||||
if (on_requested_dom_node_tag_context_menu)
|
||||
on_requested_dom_node_tag_context_menu(position, *tag);
|
||||
} else if (type == "attribute"sv) {
|
||||
VERIFY(tag.has_value());
|
||||
VERIFY(attribute.has_value());
|
||||
|
||||
if (on_requested_dom_node_attribute_context_menu)
|
||||
on_requested_dom_node_attribute_context_menu(position, *tag, *attribute);
|
||||
}
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_selected_dom_node = [this](auto node_id, auto const& pseudo_element) {
|
||||
m_content_web_view.highlight_dom_node(node_id, pseudo_element);
|
||||
m_content_web_view.inspect_dom_node(node_id, pseudo_element);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_set_dom_node_text = [this](auto node_id, auto const& text) {
|
||||
m_content_web_view.set_dom_node_text(node_id, text);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_set_dom_node_tag = [this](auto node_id, auto const& tag) {
|
||||
m_content_web_view.set_dom_node_tag(node_id, tag);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_added_dom_node_attributes = [this](auto node_id, auto const& attributes) {
|
||||
m_content_web_view.add_dom_node_attributes(node_id, attributes);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_replaced_dom_node_attribute = [this](auto node_id, u32 attribute_index, auto const& replacement_attributes) {
|
||||
auto const& attribute = m_dom_node_attributes.get(node_id)->at(attribute_index);
|
||||
m_content_web_view.replace_dom_node_attribute(node_id, attribute.name, replacement_attributes);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_requested_cookie_context_menu = [this](auto cookie_index, auto position) {
|
||||
if (cookie_index >= m_cookies.size())
|
||||
return;
|
||||
|
||||
m_cookie_context_menu_index = cookie_index;
|
||||
|
||||
if (on_requested_cookie_context_menu)
|
||||
on_requested_cookie_context_menu(position, m_cookies[cookie_index]);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_requested_style_sheet_source = [this](auto const& identifier) {
|
||||
m_content_web_view.request_style_sheet_source(identifier);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_executed_console_script = [this](auto const& script) {
|
||||
append_console_source(script);
|
||||
|
||||
m_content_web_view.js_console_input(script);
|
||||
};
|
||||
|
||||
m_inspector_web_view.on_inspector_exported_inspector_html = [this](String const& html) {
|
||||
auto maybe_inspector_path = Application::the().path_for_downloaded_file("inspector"sv);
|
||||
|
||||
if (maybe_inspector_path.is_error()) {
|
||||
append_console_warning(MUST(String::formatted("Unable to select a download location: {}", maybe_inspector_path.error())));
|
||||
return;
|
||||
}
|
||||
|
||||
auto inspector_path = maybe_inspector_path.release_value();
|
||||
|
||||
if (auto result = Core::Directory::create(inspector_path.string(), Core::Directory::CreateDirectories::Yes); result.is_error()) {
|
||||
append_console_warning(MUST(String::formatted("Unable to create {}: {}", inspector_path, result.error())));
|
||||
return;
|
||||
}
|
||||
|
||||
auto export_file = [&](auto name, auto const& contents) {
|
||||
auto path = inspector_path.append(name);
|
||||
|
||||
auto file = Core::File::open(path.string(), Core::File::OpenMode::Write);
|
||||
if (file.is_error()) {
|
||||
append_console_warning(MUST(String::formatted("Unable to open {}: {}", path, file.error())));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto result = file.value()->write_until_depleted(contents); result.is_error()) {
|
||||
append_console_warning(MUST(String::formatted("Unable to save {}: {}", path, result.error())));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto inspector_css = MUST(Core::Resource::load_from_uri(INSPECTOR_CSS));
|
||||
auto inspector_js = MUST(Core::Resource::load_from_uri(INSPECTOR_JS));
|
||||
|
||||
auto inspector_html = MUST(html.replace(INSPECTOR_CSS, "inspector.css"sv, ReplaceMode::All));
|
||||
inspector_html = MUST(inspector_html.replace(INSPECTOR_JS, "inspector.js"sv, ReplaceMode::All));
|
||||
|
||||
if (!export_file("inspector.html"sv, inspector_html))
|
||||
return;
|
||||
if (!export_file("inspector.css"sv, inspector_css->data()))
|
||||
return;
|
||||
if (!export_file("inspector.js"sv, inspector_js->data()))
|
||||
return;
|
||||
|
||||
append_console_message(MUST(String::formatted("Exported Inspector files to {}", inspector_path)));
|
||||
};
|
||||
|
||||
load_inspector();
|
||||
}
|
||||
|
||||
InspectorClient::~InspectorClient()
|
||||
{
|
||||
m_content_web_view.on_finshed_editing_dom_node = nullptr;
|
||||
m_content_web_view.on_received_accessibility_tree = nullptr;
|
||||
m_content_web_view.on_console_message_available = nullptr;
|
||||
m_content_web_view.on_received_styled_console_messages = nullptr;
|
||||
m_content_web_view.on_received_dom_node_html = nullptr;
|
||||
m_content_web_view.on_received_dom_node_properties = nullptr;
|
||||
m_content_web_view.on_received_dom_tree = nullptr;
|
||||
m_content_web_view.on_received_hovered_node_id = nullptr;
|
||||
m_content_web_view.on_received_style_sheet_list = nullptr;
|
||||
m_content_web_view.on_inspector_requested_style_sheet_source = nullptr;
|
||||
}
|
||||
|
||||
void InspectorClient::inspect()
|
||||
{
|
||||
if (!m_inspector_loaded)
|
||||
return;
|
||||
|
||||
m_content_web_view.inspect_dom_tree();
|
||||
m_content_web_view.inspect_accessibility_tree();
|
||||
m_content_web_view.list_style_sheets();
|
||||
load_cookies();
|
||||
}
|
||||
|
||||
void InspectorClient::reset()
|
||||
{
|
||||
static auto script = "inspector.reset();"_string;
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
|
||||
m_body_or_frameset_node_id.clear();
|
||||
m_pending_selection.clear();
|
||||
m_dom_tree_loaded = false;
|
||||
|
||||
m_dom_node_attributes.clear();
|
||||
|
||||
m_highest_notified_message_index = -1;
|
||||
m_highest_received_message_index = -1;
|
||||
m_waiting_for_messages = false;
|
||||
}
|
||||
|
||||
void InspectorClient::select_hovered_node()
|
||||
{
|
||||
m_content_web_view.get_hovered_node_id();
|
||||
}
|
||||
|
||||
void InspectorClient::select_default_node()
|
||||
{
|
||||
if (m_body_or_frameset_node_id.has_value())
|
||||
select_node(*m_body_or_frameset_node_id);
|
||||
}
|
||||
|
||||
void InspectorClient::clear_selection()
|
||||
{
|
||||
m_content_web_view.clear_highlighted_dom_node();
|
||||
m_content_web_view.clear_inspected_dom_node();
|
||||
|
||||
static auto script = "inspector.clearInspectedDOMNode();"_string;
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
}
|
||||
|
||||
void InspectorClient::select_node(Web::UniqueNodeID node_id)
|
||||
{
|
||||
if (!m_dom_tree_loaded) {
|
||||
m_pending_selection = node_id;
|
||||
return;
|
||||
}
|
||||
|
||||
auto script = MUST(String::formatted("inspector.inspectDOMNodeID({});", node_id.value()));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
}
|
||||
|
||||
void InspectorClient::load_cookies()
|
||||
{
|
||||
m_cookies = Application::cookie_jar().get_all_cookies(m_content_web_view.url());
|
||||
JsonArray json_cookies;
|
||||
|
||||
for (auto const& [index, cookie] : enumerate(m_cookies)) {
|
||||
JsonObject json_cookie;
|
||||
|
||||
json_cookie.set("index"sv, JsonValue { index });
|
||||
json_cookie.set("name"sv, JsonValue { cookie.name });
|
||||
json_cookie.set("value"sv, JsonValue { cookie.value });
|
||||
json_cookie.set("domain"sv, JsonValue { cookie.domain });
|
||||
json_cookie.set("path"sv, JsonValue { cookie.path });
|
||||
json_cookie.set("creationTime"sv, JsonValue { cookie.creation_time.milliseconds_since_epoch() });
|
||||
json_cookie.set("lastAccessTime"sv, JsonValue { cookie.last_access_time.milliseconds_since_epoch() });
|
||||
json_cookie.set("expiryTime"sv, JsonValue { cookie.expiry_time.milliseconds_since_epoch() });
|
||||
|
||||
MUST(json_cookies.append(move(json_cookie)));
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append("inspector.setCookies("sv);
|
||||
json_cookies.serialize(builder);
|
||||
builder.append(");"sv);
|
||||
|
||||
m_inspector_web_view.run_javascript(MUST(builder.to_string()));
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_edit_dom_node()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
auto script = MUST(String::formatted("inspector.editDOMNodeID({});", m_context_menu_data->dom_node_id));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_copy_dom_node()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
m_content_web_view.get_dom_node_outer_html(m_context_menu_data->dom_node_id);
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_screenshot_dom_node()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
m_content_web_view.take_dom_node_screenshot(m_context_menu_data->dom_node_id)
|
||||
->when_resolved([this](auto const& path) {
|
||||
append_console_message(MUST(String::formatted("Screenshot saved to: {}", path)));
|
||||
})
|
||||
.when_rejected([this](auto const& error) {
|
||||
append_console_warning(MUST(String::formatted("Warning: {}", error)));
|
||||
});
|
||||
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_create_child_element()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
m_content_web_view.create_child_element(m_context_menu_data->dom_node_id);
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_create_child_text_node()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
m_content_web_view.create_child_text_node(m_context_menu_data->dom_node_id);
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_clone_dom_node()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
m_content_web_view.clone_dom_node(m_context_menu_data->dom_node_id);
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_remove_dom_node()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
m_content_web_view.remove_dom_node(m_context_menu_data->dom_node_id);
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_add_dom_node_attribute()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
|
||||
auto script = MUST(String::formatted("inspector.addAttributeToDOMNodeID({});", m_context_menu_data->dom_node_id));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_remove_dom_node_attribute()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
VERIFY(m_context_menu_data->attribute.has_value());
|
||||
|
||||
m_content_web_view.replace_dom_node_attribute(m_context_menu_data->dom_node_id, m_context_menu_data->attribute->name, {});
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_copy_dom_node_attribute_value()
|
||||
{
|
||||
VERIFY(m_context_menu_data.has_value());
|
||||
VERIFY(m_context_menu_data->attribute.has_value());
|
||||
|
||||
if (m_content_web_view.on_insert_clipboard_entry)
|
||||
m_content_web_view.on_insert_clipboard_entry(m_context_menu_data->attribute->value, "unspecified"_string, "text/plain"_string);
|
||||
|
||||
m_context_menu_data.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_delete_cookie()
|
||||
{
|
||||
VERIFY(m_cookie_context_menu_index.has_value());
|
||||
VERIFY(*m_cookie_context_menu_index < m_cookies.size());
|
||||
|
||||
auto& cookie = m_cookies[*m_cookie_context_menu_index];
|
||||
cookie.expiry_time = UnixDateTime::earliest();
|
||||
|
||||
Application::cookie_jar().update_cookie(move(cookie));
|
||||
load_cookies();
|
||||
|
||||
m_cookie_context_menu_index.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::context_menu_delete_all_cookies()
|
||||
{
|
||||
for (auto& cookie : m_cookies) {
|
||||
cookie.expiry_time = UnixDateTime::earliest();
|
||||
|
||||
Application::cookie_jar().update_cookie(move(cookie));
|
||||
}
|
||||
|
||||
load_cookies();
|
||||
|
||||
m_cookie_context_menu_index.clear();
|
||||
}
|
||||
|
||||
void InspectorClient::load_inspector()
|
||||
{
|
||||
auto inspector_html = MUST(Core::Resource::load_from_uri(INSPECTOR_HTML));
|
||||
|
||||
auto generate_property_table = [&](auto name) {
|
||||
return MUST(String::formatted(R"~~~(
|
||||
<div id="{0}" class="tab-content">
|
||||
<input class="property-filter" id="{0}-filter" placeholder="Filter properties" />
|
||||
<table class="property-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="{0}-table">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)~~~",
|
||||
name));
|
||||
};
|
||||
|
||||
StringBuilder builder;
|
||||
|
||||
SourceGenerator generator { builder };
|
||||
generator.set("INSPECTOR_CSS"sv, INSPECTOR_CSS);
|
||||
generator.set("INSPECTOR_JS"sv, INSPECTOR_JS);
|
||||
generator.set("INSPECTOR_STYLE"sv, HTML_HIGHLIGHTER_STYLE);
|
||||
generator.set("COMPUTED_STYLE"sv, generate_property_table("computed-style"sv));
|
||||
generator.set("RESOVLED_STYLE"sv, generate_property_table("resolved-style"sv));
|
||||
generator.set("CUSTOM_PROPERTIES"sv, generate_property_table("custom-properties"sv));
|
||||
generator.append(inspector_html->data());
|
||||
|
||||
m_inspector_web_view.load_html(generator.as_string_view());
|
||||
}
|
||||
|
||||
template<typename Generator>
|
||||
static void generate_tree(StringBuilder& builder, JsonObject const& node, Generator&& generator)
|
||||
{
|
||||
if (auto children = node.get_array("children"sv); children.has_value() && !children->is_empty()) {
|
||||
auto name = node.get_string("name"sv).value_or({});
|
||||
builder.append("<details>"sv);
|
||||
|
||||
builder.append("<summary>"sv);
|
||||
generator(node);
|
||||
builder.append("</summary>"sv);
|
||||
|
||||
children->for_each([&](auto const& child) {
|
||||
builder.append("<div>"sv);
|
||||
generate_tree(builder, child.as_object(), generator);
|
||||
builder.append("</div>"sv);
|
||||
});
|
||||
|
||||
builder.append("</details>"sv);
|
||||
} else {
|
||||
generator(node);
|
||||
}
|
||||
}
|
||||
|
||||
String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
|
||||
{
|
||||
StringBuilder builder;
|
||||
|
||||
generate_tree(builder, dom_tree, [&](JsonObject const& node) {
|
||||
auto type = node.get_string("type"sv).value_or("unknown"_string);
|
||||
auto name = node.get_string("name"sv).value_or({});
|
||||
|
||||
StringBuilder data_attributes;
|
||||
auto append_data_attribute = [&](auto name, auto value) {
|
||||
if (!data_attributes.is_empty())
|
||||
data_attributes.append(' ');
|
||||
data_attributes.appendff("data-{}=\"{}\"", name, value);
|
||||
};
|
||||
|
||||
i32 node_id = 0;
|
||||
|
||||
if (auto pseudo_element = node.get_integer<i32>("pseudo-element"sv); pseudo_element.has_value()) {
|
||||
node_id = node.get_integer<i32>("parent-id"sv).value();
|
||||
append_data_attribute("pseudo-element"sv, *pseudo_element);
|
||||
} else {
|
||||
node_id = node.get_integer<i32>("id"sv).value();
|
||||
}
|
||||
|
||||
append_data_attribute("id"sv, node_id);
|
||||
|
||||
if (type == "text"sv) {
|
||||
auto deprecated_text = escape_html_entities(*node.get_string("text"sv));
|
||||
auto text = MUST(Web::Infra::strip_and_collapse_whitespace(deprecated_text));
|
||||
|
||||
builder.appendff("<span data-node-type=\"text\" class=\"hoverable editable\" {}>", data_attributes.string_view());
|
||||
|
||||
if (text.is_empty())
|
||||
builder.appendff("<span class=\"internal\">{}</span>", name);
|
||||
else
|
||||
builder.append(text);
|
||||
|
||||
builder.append("</span>"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "comment"sv) {
|
||||
auto comment = escape_html_entities(*node.get_string("data"sv));
|
||||
|
||||
builder.appendff("<span class=\"hoverable comment\" {}>", data_attributes.string_view());
|
||||
builder.append("<span><!--</span>"sv);
|
||||
builder.appendff("<span data-node-type=\"comment\" class=\"editable\">{}</span>", comment);
|
||||
builder.append("<span>--></span>"sv);
|
||||
builder.append("</span>"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "shadow-root"sv) {
|
||||
auto mode = node.get_string("mode"sv).release_value();
|
||||
|
||||
builder.appendff("<span class=\"hoverable internal\" {}>", data_attributes.string_view());
|
||||
builder.appendff("{} ({})", name, mode);
|
||||
builder.append("</span>"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != "element"sv) {
|
||||
builder.appendff("<span class=\"hoverable internal\" {}>", data_attributes.string_view());
|
||||
builder.appendff(name);
|
||||
} else {
|
||||
if (name.equals_ignoring_ascii_case("BODY"sv) || name.equals_ignoring_ascii_case("FRAMESET"sv))
|
||||
m_body_or_frameset_node_id = node_id;
|
||||
|
||||
auto tag = name;
|
||||
if (node.get_string("namespace"sv) == Web::Namespace::HTML.bytes_as_string_view())
|
||||
tag = MUST(tag.to_lowercase());
|
||||
|
||||
builder.appendff("<span class=\"hoverable\" {}>", data_attributes.string_view());
|
||||
builder.append("<span><</span>"sv);
|
||||
builder.appendff("<span data-node-type=\"tag\" data-tag=\"{0}\" class=\"editable tag\">{0}</span>", tag);
|
||||
|
||||
if (auto attributes = node.get_object("attributes"sv); attributes.has_value()) {
|
||||
attributes->for_each_member([&](auto const& name, auto const& value) {
|
||||
auto& dom_node_attributes = m_dom_node_attributes.ensure(node_id);
|
||||
auto value_string = value.as_string();
|
||||
|
||||
builder.append(" "sv);
|
||||
builder.appendff("<span data-node-type=\"attribute\" data-tag=\"{}\" data-attribute-index={} class=\"editable\">", tag, dom_node_attributes.size());
|
||||
builder.appendff("<span class=\"attribute-name\">{}</span>", escape_html_entities(name));
|
||||
builder.append('=');
|
||||
builder.appendff("<span class=\"attribute-value\">\"{}\"</span>", escape_html_entities(value_string));
|
||||
builder.append("</span>"sv);
|
||||
|
||||
dom_node_attributes.empend(name, value_string);
|
||||
});
|
||||
}
|
||||
|
||||
builder.append("<span>></span>"sv);
|
||||
}
|
||||
|
||||
// display miscellaneous extra bits of info about the element
|
||||
Vector<String> extra;
|
||||
if (node.get_bool("scrollable"sv).value_or(false)) {
|
||||
extra.append("scrollable"_string);
|
||||
}
|
||||
if (node.get_bool("invisible"sv).value_or(false)) {
|
||||
extra.append("invisible"_string);
|
||||
}
|
||||
if (node.get_bool("stackingContext"sv).value_or(false)) {
|
||||
extra.append("isolated"_string);
|
||||
}
|
||||
if (!extra.is_empty()) {
|
||||
builder.append(" <span>("sv);
|
||||
builder.append(extra[0]);
|
||||
for (size_t i = 1; i < extra.size(); i++) {
|
||||
builder.appendff(", {}", extra[i]);
|
||||
}
|
||||
builder.append(")</span>"sv);
|
||||
}
|
||||
|
||||
builder.append("</span>"sv);
|
||||
});
|
||||
|
||||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
String InspectorClient::generate_accessibility_tree(JsonObject const& accessibility_tree)
|
||||
{
|
||||
StringBuilder builder;
|
||||
|
||||
generate_tree(builder, accessibility_tree, [&](JsonObject const& node) {
|
||||
auto type = node.get_string("type"sv).value_or("unknown"_string);
|
||||
auto role = node.get_string("role"sv).value_or({});
|
||||
|
||||
if (type == "text"sv) {
|
||||
auto text = escape_html_entities(*node.get_string("text"sv));
|
||||
|
||||
builder.appendff("<span class=\"hoverable\">");
|
||||
builder.append(MUST(Web::Infra::strip_and_collapse_whitespace(text)));
|
||||
builder.append("</span>"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != "element"sv) {
|
||||
builder.appendff("<span class=\"hoverable internal\">");
|
||||
builder.appendff(MUST(role.to_lowercase()));
|
||||
builder.append("</span>"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
auto name = node.get_string("name"sv).value_or({});
|
||||
auto description = node.get_string("description"sv).value_or({});
|
||||
|
||||
builder.appendff("<span class=\"hoverable\">");
|
||||
builder.append(MUST(role.to_lowercase()));
|
||||
builder.appendff(" name: \"{}\", description: \"{}\"", name, description);
|
||||
builder.append("</span>"sv);
|
||||
});
|
||||
|
||||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
void InspectorClient::request_console_messages()
|
||||
{
|
||||
VERIFY(!m_waiting_for_messages);
|
||||
|
||||
m_content_web_view.js_console_request_messages(m_highest_received_message_index + 1);
|
||||
m_waiting_for_messages = true;
|
||||
}
|
||||
|
||||
void InspectorClient::console_message_available(i32 message_index)
|
||||
{
|
||||
if (message_index <= m_highest_received_message_index) {
|
||||
dbgln("Notified about console message we already have");
|
||||
return;
|
||||
}
|
||||
if (message_index <= m_highest_notified_message_index) {
|
||||
dbgln("Notified about console message we're already aware of");
|
||||
return;
|
||||
}
|
||||
|
||||
m_highest_notified_message_index = message_index;
|
||||
|
||||
if (!m_waiting_for_messages)
|
||||
request_console_messages();
|
||||
}
|
||||
|
||||
void InspectorClient::console_messages_received(i32 start_index, ReadonlySpan<String> message_types, ReadonlySpan<String> messages)
|
||||
{
|
||||
auto end_index = start_index + static_cast<i32>(message_types.size()) - 1;
|
||||
if (end_index <= m_highest_received_message_index) {
|
||||
dbgln("Received old console messages");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < message_types.size(); ++i) {
|
||||
auto const& type = message_types[i];
|
||||
auto const& message = messages[i];
|
||||
|
||||
if (type == "html"sv)
|
||||
append_console_output(message);
|
||||
else if (type == "clear"sv)
|
||||
clear_console_output();
|
||||
else if (type == "group"sv)
|
||||
begin_console_group(message, true);
|
||||
else if (type == "groupCollapsed"sv)
|
||||
begin_console_group(message, false);
|
||||
else if (type == "groupEnd"sv)
|
||||
end_console_group();
|
||||
else
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_highest_received_message_index = end_index;
|
||||
m_waiting_for_messages = false;
|
||||
|
||||
if (m_highest_received_message_index < m_highest_notified_message_index)
|
||||
request_console_messages();
|
||||
}
|
||||
|
||||
void InspectorClient::append_console_source(StringView source)
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append("<span class=\"console-prompt\">> </span>"sv);
|
||||
builder.append(MUST(JS::MarkupGenerator::html_from_source(source)));
|
||||
|
||||
append_console_output(builder.string_view());
|
||||
}
|
||||
|
||||
void InspectorClient::append_console_message(StringView message)
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append("<span class=\"console-prompt\"># </span>"sv);
|
||||
builder.appendff("<span class=\"console-message\">{}</span>", message);
|
||||
|
||||
append_console_output(builder.string_view());
|
||||
}
|
||||
|
||||
void InspectorClient::append_console_warning(StringView warning)
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append("<span class=\"console-prompt\"># </span>"sv);
|
||||
builder.appendff("<span class=\"console-warning\">{}</span>", warning);
|
||||
|
||||
append_console_output(builder.string_view());
|
||||
}
|
||||
|
||||
void InspectorClient::append_console_output(StringView html)
|
||||
{
|
||||
auto html_base64 = MUST(encode_base64(html.bytes()));
|
||||
|
||||
auto script = MUST(String::formatted("inspector.appendConsoleOutput(\"{}\");", html_base64));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
}
|
||||
|
||||
void InspectorClient::clear_console_output()
|
||||
{
|
||||
static auto script = "inspector.clearConsoleOutput();"_string;
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
}
|
||||
|
||||
void InspectorClient::begin_console_group(StringView label, bool start_expanded)
|
||||
{
|
||||
auto label_base64 = MUST(encode_base64(label.bytes()));
|
||||
|
||||
auto script = MUST(String::formatted("inspector.beginConsoleGroup(\"{}\", {});", label_base64, start_expanded));
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
}
|
||||
|
||||
void InspectorClient::end_console_group()
|
||||
{
|
||||
static auto script = "inspector.endConsoleGroup();"_string;
|
||||
m_inspector_web_view.run_javascript(script);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibWebView/Attribute.h>
|
||||
#include <LibWebView/ViewImplementation.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace WebView {
|
||||
|
||||
class InspectorClient {
|
||||
public:
|
||||
InspectorClient(ViewImplementation& content_web_view, ViewImplementation& inspector_web_view);
|
||||
~InspectorClient();
|
||||
|
||||
void inspect();
|
||||
void reset();
|
||||
|
||||
void select_hovered_node();
|
||||
void select_default_node();
|
||||
void clear_selection();
|
||||
|
||||
void context_menu_edit_dom_node();
|
||||
void context_menu_copy_dom_node();
|
||||
void context_menu_screenshot_dom_node();
|
||||
void context_menu_create_child_element();
|
||||
void context_menu_create_child_text_node();
|
||||
void context_menu_clone_dom_node();
|
||||
void context_menu_remove_dom_node();
|
||||
void context_menu_add_dom_node_attribute();
|
||||
void context_menu_remove_dom_node_attribute();
|
||||
void context_menu_copy_dom_node_attribute_value();
|
||||
void context_menu_delete_cookie();
|
||||
void context_menu_delete_all_cookies();
|
||||
|
||||
Function<void(Gfx::IntPoint)> on_requested_dom_node_text_context_menu;
|
||||
Function<void(Gfx::IntPoint, String const&)> on_requested_dom_node_tag_context_menu;
|
||||
Function<void(Gfx::IntPoint, String const&, Attribute const&)> on_requested_dom_node_attribute_context_menu;
|
||||
Function<void(Gfx::IntPoint, Web::Cookie::Cookie const&)> on_requested_cookie_context_menu;
|
||||
|
||||
private:
|
||||
void load_inspector();
|
||||
|
||||
String generate_dom_tree(JsonObject const&);
|
||||
String generate_accessibility_tree(JsonObject const&);
|
||||
void select_node(Web::UniqueNodeID);
|
||||
|
||||
void load_cookies();
|
||||
|
||||
void request_console_messages();
|
||||
void console_message_available(i32 message_index);
|
||||
void console_messages_received(i32 start_index, ReadonlySpan<String> message_types, ReadonlySpan<String> messages);
|
||||
|
||||
void append_console_source(StringView);
|
||||
void append_console_message(StringView);
|
||||
void append_console_warning(StringView);
|
||||
void append_console_output(StringView);
|
||||
void clear_console_output();
|
||||
|
||||
void begin_console_group(StringView label, bool start_expanded);
|
||||
void end_console_group();
|
||||
|
||||
ViewImplementation& m_content_web_view;
|
||||
ViewImplementation& m_inspector_web_view;
|
||||
|
||||
Optional<Web::UniqueNodeID> m_body_or_frameset_node_id;
|
||||
Optional<Web::UniqueNodeID> m_pending_selection;
|
||||
|
||||
bool m_inspector_loaded { false };
|
||||
bool m_dom_tree_loaded { false };
|
||||
|
||||
struct ContextMenuData {
|
||||
Web::UniqueNodeID dom_node_id;
|
||||
Optional<String> tag;
|
||||
Optional<Attribute> attribute;
|
||||
};
|
||||
Optional<ContextMenuData> m_context_menu_data;
|
||||
|
||||
HashMap<Web::UniqueNodeID, Vector<Attribute>> m_dom_node_attributes;
|
||||
|
||||
Vector<Web::Cookie::Cookie> m_cookies;
|
||||
Optional<size_t> m_cookie_context_menu_index;
|
||||
|
||||
i32 m_highest_notified_message_index { -1 };
|
||||
i32 m_highest_received_message_index { -1 };
|
||||
bool m_waiting_for_messages { false };
|
||||
};
|
||||
|
||||
}
|
|
@ -779,9 +779,4 @@ void ViewImplementation::use_native_user_style_sheet()
|
|||
set_user_style_sheet(native_stylesheet_source);
|
||||
}
|
||||
|
||||
void ViewImplementation::enable_inspector_prototype()
|
||||
{
|
||||
client().async_enable_inspector_prototype(page_id());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -180,8 +180,6 @@ public:
|
|||
// native GUI widgets as possible.
|
||||
void use_native_user_style_sheet();
|
||||
|
||||
void enable_inspector_prototype();
|
||||
|
||||
Function<void()> on_ready_to_paint;
|
||||
Function<String(Web::HTML::ActivateTab, Web::HTML::WebViewHints, Optional<u64>)> on_new_web_view;
|
||||
Function<void()> on_activate_tab;
|
||||
|
@ -215,13 +213,12 @@ public:
|
|||
Function<void(JsonObject)> on_received_dom_tree;
|
||||
Function<void(DOMNodeProperties)> on_received_dom_node_properties;
|
||||
Function<void(JsonObject)> on_received_accessibility_tree;
|
||||
Function<void(Vector<Web::CSS::StyleSheetIdentifier>)> on_received_style_sheet_list;
|
||||
Function<void(Web::CSS::StyleSheetIdentifier const&)> on_inspector_requested_style_sheet_source;
|
||||
Function<void(Web::CSS::StyleSheetIdentifier const&, URL::URL const&, String const&)> on_received_style_sheet_source;
|
||||
Function<void(Web::UniqueNodeID)> on_received_hovered_node_id;
|
||||
Function<void(Mutation)> on_dom_mutation_received;
|
||||
Function<void(Optional<Web::UniqueNodeID> const& node_id)> on_finshed_editing_dom_node;
|
||||
Function<void(String)> on_received_dom_node_html;
|
||||
Function<void(Vector<Web::CSS::StyleSheetIdentifier>)> on_received_style_sheet_list;
|
||||
Function<void(Web::CSS::StyleSheetIdentifier const&, URL::URL const&, String const&)> on_received_style_sheet_source;
|
||||
Function<void(JsonValue)> on_received_js_console_result;
|
||||
Function<void(i32 message_id)> on_console_message_available;
|
||||
Function<void(i32 start_index, Vector<String> const& message_types, Vector<String> const& messages)> on_received_styled_console_messages;
|
||||
|
@ -246,16 +243,6 @@ public:
|
|||
Function<void(String const&, String const&, String const&)> on_insert_clipboard_entry;
|
||||
Function<void(Web::HTML::AudioPlayState)> on_audio_play_state_changed;
|
||||
Function<void(bool, bool)> on_navigation_buttons_state_changed;
|
||||
Function<void()> on_inspector_loaded;
|
||||
Function<void(Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type> const&)> on_inspector_selected_dom_node;
|
||||
Function<void(Web::UniqueNodeID, String const&)> on_inspector_set_dom_node_text;
|
||||
Function<void(Web::UniqueNodeID, String const&)> on_inspector_set_dom_node_tag;
|
||||
Function<void(Web::UniqueNodeID, Vector<Attribute> const&)> on_inspector_added_dom_node_attributes;
|
||||
Function<void(Web::UniqueNodeID, size_t, Vector<Attribute> const&)> on_inspector_replaced_dom_node_attribute;
|
||||
Function<void(Web::UniqueNodeID, Gfx::IntPoint, String const&, Optional<String> const&, Optional<size_t> const&)> on_inspector_requested_dom_tree_context_menu;
|
||||
Function<void(size_t, Gfx::IntPoint)> on_inspector_requested_cookie_context_menu;
|
||||
Function<void(String const&)> on_inspector_executed_console_script;
|
||||
Function<void(String const&)> on_inspector_exported_inspector_html;
|
||||
Function<void()> on_web_content_crashed;
|
||||
|
||||
virtual Web::DevicePixelSize viewport_size() const = 0;
|
||||
|
|
|
@ -375,6 +375,22 @@ void WebContentClient::did_get_dom_node_html(u64 page_id, String html)
|
|||
}
|
||||
}
|
||||
|
||||
void WebContentClient::did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> stylesheets)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_received_style_sheet_list)
|
||||
view->on_received_style_sheet_list(stylesheets);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL base_url, String source)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_received_style_sheet_source)
|
||||
view->on_received_style_sheet_source(identifier, base_url, source);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::did_take_screenshot(u64 page_id, Gfx::ShareableBitmap screenshot)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value())
|
||||
|
@ -664,86 +680,6 @@ void WebContentClient::did_allocate_backing_stores(u64 page_id, i32 front_bitmap
|
|||
view->did_allocate_backing_stores({}, front_bitmap_id, front_bitmap, back_bitmap_id, back_bitmap);
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_load(u64 page_id)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_loaded)
|
||||
view->on_inspector_loaded();
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_select_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_selected_dom_node)
|
||||
view->on_inspector_selected_dom_node(node_id, pseudo_element);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_set_dom_node_text)
|
||||
view->on_inspector_set_dom_node_text(node_id, text);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_set_dom_node_tag(u64 page_id, Web::UniqueNodeID node_id, String tag)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_set_dom_node_tag)
|
||||
view->on_inspector_set_dom_node_tag(node_id, tag);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_add_dom_node_attributes(u64 page_id, Web::UniqueNodeID node_id, Vector<Attribute> attributes)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_added_dom_node_attributes)
|
||||
view->on_inspector_added_dom_node_attributes(node_id, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID node_id, size_t attribute_index, Vector<Attribute> replacement_attributes)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_replaced_dom_node_attribute)
|
||||
view->on_inspector_replaced_dom_node_attribute(node_id, attribute_index, replacement_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_request_dom_tree_context_menu(u64 page_id, Web::UniqueNodeID node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<size_t> attribute_index)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_requested_dom_tree_context_menu)
|
||||
view->on_inspector_requested_dom_tree_context_menu(node_id, view->to_widget_position(position), type, tag, attribute_index);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_requested_cookie_context_menu)
|
||||
view->on_inspector_requested_cookie_context_menu(cookie_index, view->to_widget_position(position));
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_execute_console_script(u64 page_id, String script)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_executed_console_script)
|
||||
view->on_inspector_executed_console_script(script);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_export_inspector_html(u64 page_id, String html)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_exported_inspector_html)
|
||||
view->on_inspector_exported_inspector_html(html);
|
||||
}
|
||||
}
|
||||
|
||||
Messages::WebContentClient::RequestWorkerAgentResponse WebContentClient::request_worker_agent(u64 page_id)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
|
@ -754,30 +690,6 @@ Messages::WebContentClient::RequestWorkerAgentResponse WebContentClient::request
|
|||
return IPC::File {};
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> stylesheets)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_received_style_sheet_list)
|
||||
view->on_received_style_sheet_list(stylesheets);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_inspector_requested_style_sheet_source)
|
||||
view->on_inspector_requested_style_sheet_source(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContentClient::did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL base_url, String source)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_received_style_sheet_source)
|
||||
view->on_received_style_sheet_source(identifier, base_url, source);
|
||||
}
|
||||
}
|
||||
|
||||
Optional<ViewImplementation&> WebContentClient::view_for_page_id(u64 page_id, SourceLocation location)
|
||||
{
|
||||
// Don't bother logging anything for the spare WebContent process. It will only receive a load notification for about:blank.
|
||||
|
|
|
@ -83,6 +83,8 @@ private:
|
|||
virtual void did_finish_editing_dom_node(u64 page_id, Optional<Web::UniqueNodeID> node_id) override;
|
||||
virtual void did_mutate_dom(u64 page_id, Mutation) override;
|
||||
virtual void did_get_dom_node_html(u64 page_id, String html) override;
|
||||
virtual void did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> stylesheets) override;
|
||||
virtual void did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL, String source) override;
|
||||
virtual void did_take_screenshot(u64 page_id, Gfx::ShareableBitmap screenshot) override;
|
||||
virtual void did_get_internal_page_info(u64 page_id, PageInfoType, String) override;
|
||||
virtual void did_execute_js_console_input(u64 page_id, JsonValue) override;
|
||||
|
@ -126,20 +128,7 @@ private:
|
|||
virtual void did_change_audio_play_state(u64 page_id, Web::HTML::AudioPlayState) override;
|
||||
virtual void did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) override;
|
||||
virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap, i32 back_bitmap_id, Gfx::ShareableBitmap) override;
|
||||
virtual void inspector_did_load(u64 page_id) override;
|
||||
virtual void inspector_did_select_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) override;
|
||||
virtual void inspector_did_set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text) override;
|
||||
virtual void inspector_did_set_dom_node_tag(u64 page_id, Web::UniqueNodeID node_id, String tag) override;
|
||||
virtual void inspector_did_add_dom_node_attributes(u64 page_id, Web::UniqueNodeID node_id, Vector<Attribute> attributes) override;
|
||||
virtual void inspector_did_replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID node_id, size_t attribute_index, Vector<Attribute> replacement_attributes) override;
|
||||
virtual void inspector_did_request_dom_tree_context_menu(u64 page_id, Web::UniqueNodeID node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<size_t> attribute_index) override;
|
||||
virtual void inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position) override;
|
||||
virtual void inspector_did_execute_console_script(u64 page_id, String script) override;
|
||||
virtual void inspector_did_export_inspector_html(u64 page_id, String html) override;
|
||||
virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override;
|
||||
virtual void inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> stylesheets) override;
|
||||
virtual void inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) override;
|
||||
virtual void did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL, String source) override;
|
||||
|
||||
Optional<ViewImplementation&> view_for_page_id(u64, SourceLocation = SourceLocation::current());
|
||||
|
||||
|
|
|
@ -261,7 +261,6 @@ standard_idl_files = [
|
|||
"//Userland/Libraries/LibWeb/IndexedDB/IDBFactory.idl",
|
||||
"//Userland/Libraries/LibWeb/IndexedDB/IDBOpenDBRequest.idl",
|
||||
"//Userland/Libraries/LibWeb/IndexedDB/IDBRequest.idl",
|
||||
"//Userland/Libraries/LibWeb/Internals/Inspector.idl",
|
||||
"//Userland/Libraries/LibWeb/Internals/InternalAnimationTimeline.idl",
|
||||
"//Userland/Libraries/LibWeb/Internals/Internals.idl",
|
||||
"//Userland/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.idl",
|
||||
|
|
|
@ -5,7 +5,6 @@ set(SOURCES
|
|||
ConnectionFromClient.cpp
|
||||
ConsoleGlobalEnvironmentExtensions.cpp
|
||||
DevToolsConsoleClient.cpp
|
||||
InspectorConsoleClient.cpp
|
||||
PageClient.cpp
|
||||
PageHost.cpp
|
||||
WebContentConsoleClient.cpp
|
||||
|
|
|
@ -652,7 +652,7 @@ void ConnectionFromClient::list_style_sheets(u64 page_id)
|
|||
if (!page.has_value())
|
||||
return;
|
||||
|
||||
async_inspector_did_list_style_sheets(page_id, page->list_style_sheets());
|
||||
async_did_list_style_sheets(page_id, page->list_style_sheets());
|
||||
}
|
||||
|
||||
void ConnectionFromClient::request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier)
|
||||
|
@ -1323,11 +1323,6 @@ void ConnectionFromClient::set_user_style(u64 page_id, String source)
|
|||
page->page().set_user_style(move(source));
|
||||
}
|
||||
|
||||
void ConnectionFromClient::enable_inspector_prototype(u64)
|
||||
{
|
||||
Web::HTML::Window::set_inspector_object_exposed(true);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::system_time_zone_changed()
|
||||
{
|
||||
JS::clear_system_time_zone_cache();
|
||||
|
|
|
@ -135,8 +135,6 @@ private:
|
|||
|
||||
virtual void set_user_style(u64 page_id, String) override;
|
||||
|
||||
virtual void enable_inspector_prototype(u64 page_id) override;
|
||||
|
||||
virtual void take_document_screenshot(u64 page_id) override;
|
||||
virtual void take_dom_node_screenshot(u64 page_id, Web::UniqueNodeID node_id) override;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ namespace WebContent {
|
|||
class ConnectionFromClient;
|
||||
class ConsoleGlobalEnvironmentExtensions;
|
||||
class DevToolsConsoleClient;
|
||||
class InspectorConsoleClient;
|
||||
class PageHost;
|
||||
class PageClient;
|
||||
class WebContentConsoleClient;
|
||||
|
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brandon Scott <xeon.productions@gmail.com>
|
||||
* Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com>
|
||||
* Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2024, Gasim Gasimzada <gasim@gasimzada.net>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibJS/MarkupGenerator.h>
|
||||
#include <LibJS/Print.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <WebContent/ConsoleGlobalEnvironmentExtensions.h>
|
||||
#include <WebContent/InspectorConsoleClient.h>
|
||||
#include <WebContent/PageClient.h>
|
||||
|
||||
namespace WebContent {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(InspectorConsoleClient);
|
||||
|
||||
GC::Ref<InspectorConsoleClient> InspectorConsoleClient::create(JS::Realm& realm, JS::Console& console, PageClient& client)
|
||||
{
|
||||
auto& window = as<Web::HTML::Window>(realm.global_object());
|
||||
auto console_global_environment_extensions = realm.create<ConsoleGlobalEnvironmentExtensions>(realm, window);
|
||||
|
||||
return realm.heap().allocate<InspectorConsoleClient>(realm, console, client, console_global_environment_extensions);
|
||||
}
|
||||
|
||||
InspectorConsoleClient::InspectorConsoleClient(JS::Realm& realm, JS::Console& console, PageClient& client, ConsoleGlobalEnvironmentExtensions& console_global_environment_extensions)
|
||||
: WebContentConsoleClient(realm, console, client, console_global_environment_extensions)
|
||||
{
|
||||
}
|
||||
|
||||
InspectorConsoleClient::~InspectorConsoleClient() = default;
|
||||
|
||||
void InspectorConsoleClient::handle_result(JS::Value result)
|
||||
{
|
||||
print_html(JS::MarkupGenerator::html_from_value(result).release_value_but_fixme_should_propagate_errors());
|
||||
}
|
||||
|
||||
void InspectorConsoleClient::report_exception(JS::Error const& exception, bool in_promise)
|
||||
{
|
||||
print_html(JS::MarkupGenerator::html_from_error(exception, in_promise).release_value_but_fixme_should_propagate_errors());
|
||||
}
|
||||
|
||||
void InspectorConsoleClient::begin_group(String const& label, bool start_expanded)
|
||||
{
|
||||
m_message_log.append({ .type = start_expanded ? ConsoleOutput::Type::BeginGroup : ConsoleOutput::Type::BeginGroupCollapsed, .data = label });
|
||||
m_client->did_output_js_console_message(m_message_log.size() - 1);
|
||||
}
|
||||
|
||||
void InspectorConsoleClient::end_group()
|
||||
{
|
||||
m_message_log.append({ .type = ConsoleOutput::Type::EndGroup, .data = String {} });
|
||||
m_client->did_output_js_console_message(m_message_log.size() - 1);
|
||||
}
|
||||
|
||||
void InspectorConsoleClient::clear()
|
||||
{
|
||||
m_message_log.append({ .type = ConsoleOutput::Type::Clear, .data = String {} });
|
||||
m_client->did_output_js_console_message(m_message_log.size() - 1);
|
||||
}
|
||||
|
||||
void InspectorConsoleClient::print_html(String const& line)
|
||||
{
|
||||
m_message_log.append({ .type = ConsoleOutput::Type::HTML, .data = line });
|
||||
m_client->did_output_js_console_message(m_message_log.size() - 1);
|
||||
}
|
||||
|
||||
void InspectorConsoleClient::send_messages(i32 start_index)
|
||||
{
|
||||
auto messages_to_send = m_message_log.size() - start_index;
|
||||
if (messages_to_send < 1) {
|
||||
// When the console is first created, it requests any messages that happened before then, by requesting with
|
||||
// start_index=0. If we don't have any messages at all, that is still a valid request, and we can just ignore it.
|
||||
if (start_index != 0)
|
||||
m_client->console_peer_did_misbehave("Requested non-existent console message index");
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Replace with a single Vector of message structs
|
||||
Vector<String> message_types;
|
||||
Vector<String> messages;
|
||||
message_types.ensure_capacity(messages_to_send);
|
||||
messages.ensure_capacity(messages_to_send);
|
||||
|
||||
for (size_t i = start_index; i < m_message_log.size(); i++) {
|
||||
auto& message = m_message_log[i];
|
||||
switch (message.type) {
|
||||
case ConsoleOutput::Type::HTML:
|
||||
message_types.append("html"_string);
|
||||
break;
|
||||
case ConsoleOutput::Type::Clear:
|
||||
message_types.append("clear"_string);
|
||||
break;
|
||||
case ConsoleOutput::Type::BeginGroup:
|
||||
message_types.append("group"_string);
|
||||
break;
|
||||
case ConsoleOutput::Type::BeginGroupCollapsed:
|
||||
message_types.append("groupCollapsed"_string);
|
||||
break;
|
||||
case ConsoleOutput::Type::EndGroup:
|
||||
message_types.append("groupEnd"_string);
|
||||
break;
|
||||
}
|
||||
|
||||
messages.append(message.data);
|
||||
}
|
||||
|
||||
m_client->did_get_styled_js_console_messages(start_index, message_types, messages);
|
||||
}
|
||||
|
||||
// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
|
||||
JS::ThrowCompletionOr<JS::Value> InspectorConsoleClient::printer(JS::Console::LogLevel log_level, PrinterArguments arguments)
|
||||
{
|
||||
auto styling = escape_html_entities(m_current_message_style.string_view());
|
||||
m_current_message_style.clear();
|
||||
|
||||
if (log_level == JS::Console::LogLevel::Table) {
|
||||
auto& vm = m_console->realm().vm();
|
||||
|
||||
auto table_args = arguments.get<GC::RootVector<JS::Value>>();
|
||||
auto& table = table_args.at(0).as_object();
|
||||
auto& columns = TRY(table.get(vm.names.columns)).as_array().indexed_properties();
|
||||
auto& rows = TRY(table.get(vm.names.rows)).as_array().indexed_properties();
|
||||
|
||||
StringBuilder html;
|
||||
|
||||
html.appendff("<div class=\"console-log-table\">");
|
||||
html.appendff("<table>");
|
||||
html.appendff("<thead>");
|
||||
html.appendff("<tr>");
|
||||
for (auto const& col : columns) {
|
||||
auto index = col.index();
|
||||
auto value = columns.storage()->get(index).value().value;
|
||||
html.appendff("<td>{}</td>", value);
|
||||
}
|
||||
|
||||
html.appendff("</tr>");
|
||||
html.appendff("</thead>");
|
||||
html.appendff("<tbody>");
|
||||
|
||||
for (auto const& row : rows) {
|
||||
auto row_index = row.index();
|
||||
auto& row_obj = rows.storage()->get(row_index).value().value.as_object();
|
||||
html.appendff("<tr>");
|
||||
|
||||
for (auto const& col : columns) {
|
||||
auto col_index = col.index();
|
||||
auto col_name = columns.storage()->get(col_index).value().value;
|
||||
|
||||
auto property_key = TRY(JS::PropertyKey::from_value(vm, col_name));
|
||||
auto cell = TRY(row_obj.get(property_key));
|
||||
html.appendff("<td>");
|
||||
if (TRY(cell.is_array(vm))) {
|
||||
AllocatingMemoryStream stream;
|
||||
JS::PrintContext ctx { vm, stream, true };
|
||||
TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes()));
|
||||
TRY_OR_THROW_OOM(vm, JS::print(cell, ctx));
|
||||
auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size()));
|
||||
|
||||
auto size = cell.as_array().indexed_properties().array_like_size();
|
||||
html.appendff("<details><summary>Array({})</summary>{}</details>", size, output);
|
||||
|
||||
} else if (cell.is_object()) {
|
||||
AllocatingMemoryStream stream;
|
||||
JS::PrintContext ctx { vm, stream, true };
|
||||
TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes()));
|
||||
TRY_OR_THROW_OOM(vm, JS::print(cell, ctx));
|
||||
auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size()));
|
||||
|
||||
html.appendff("<details><summary>Object({{...}})</summary>{}</details>", output);
|
||||
} else if (cell.is_function() || cell.is_constructor()) {
|
||||
html.appendff("ƒ");
|
||||
} else if (!cell.is_undefined()) {
|
||||
html.appendff("{}", cell);
|
||||
}
|
||||
html.appendff("</td>");
|
||||
}
|
||||
|
||||
html.appendff("</tr>");
|
||||
}
|
||||
|
||||
html.appendff("</tbody>");
|
||||
html.appendff("</table>");
|
||||
html.appendff("</div>");
|
||||
print_html(MUST(html.to_string()));
|
||||
|
||||
auto output = TRY(generically_format_values(table_args));
|
||||
m_console->output_debug_message(log_level, output);
|
||||
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
if (log_level == JS::Console::LogLevel::Trace) {
|
||||
auto trace = arguments.get<JS::Console::Trace>();
|
||||
StringBuilder html;
|
||||
if (!trace.label.is_empty())
|
||||
html.appendff("<span class='title' style='{}'>{}</span><br>", styling, escape_html_entities(trace.label));
|
||||
|
||||
html.append("<span class='trace'>"sv);
|
||||
for (auto& function_name : trace.stack)
|
||||
html.appendff("-> {}<br>", escape_html_entities(function_name));
|
||||
html.append("</span>"sv);
|
||||
|
||||
print_html(MUST(html.to_string()));
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
|
||||
auto group = arguments.get<JS::Console::Group>();
|
||||
begin_group(MUST(String::formatted("<span style='{}'>{}</span>", styling, escape_html_entities(group.label))), log_level == JS::Console::LogLevel::Group);
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
auto output = TRY(generically_format_values(arguments.get<GC::RootVector<JS::Value>>()));
|
||||
m_console->output_debug_message(log_level, output);
|
||||
|
||||
StringBuilder html;
|
||||
switch (log_level) {
|
||||
case JS::Console::LogLevel::Debug:
|
||||
html.appendff("<span class=\"debug\" style=\"{}\">(d) "sv, styling);
|
||||
break;
|
||||
case JS::Console::LogLevel::Error:
|
||||
html.appendff("<span class=\"error\" style=\"{}\">(e) "sv, styling);
|
||||
break;
|
||||
case JS::Console::LogLevel::Info:
|
||||
html.appendff("<span class=\"info\" style=\"{}\">(i) "sv, styling);
|
||||
break;
|
||||
case JS::Console::LogLevel::Log:
|
||||
html.appendff("<span class=\"log\" style=\"{}\"> "sv, styling);
|
||||
break;
|
||||
case JS::Console::LogLevel::Warn:
|
||||
case JS::Console::LogLevel::CountReset:
|
||||
html.appendff("<span class=\"warn\" style=\"{}\">(w) "sv, styling);
|
||||
break;
|
||||
default:
|
||||
html.appendff("<span style=\"{}\">"sv, styling);
|
||||
break;
|
||||
}
|
||||
|
||||
html.append(escape_html_entities(output));
|
||||
html.append("</span>"sv);
|
||||
print_html(MUST(html.to_string()));
|
||||
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brandon Scott <xeon.productions@gmail.com>
|
||||
* Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com>
|
||||
* Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <WebContent/Forward.h>
|
||||
#include <WebContent/WebContentConsoleClient.h>
|
||||
|
||||
namespace WebContent {
|
||||
|
||||
class InspectorConsoleClient final : public WebContentConsoleClient {
|
||||
GC_CELL(InspectorConsoleClient, WebContentConsoleClient);
|
||||
GC_DECLARE_ALLOCATOR(InspectorConsoleClient);
|
||||
|
||||
public:
|
||||
static GC::Ref<InspectorConsoleClient> create(JS::Realm&, JS::Console&, PageClient&);
|
||||
virtual ~InspectorConsoleClient() override;
|
||||
|
||||
private:
|
||||
InspectorConsoleClient(JS::Realm&, JS::Console&, PageClient&, ConsoleGlobalEnvironmentExtensions&);
|
||||
|
||||
virtual void handle_result(JS::Value) override;
|
||||
virtual void report_exception(JS::Error const&, bool) override;
|
||||
|
||||
void begin_group(String const& label, bool start_expanded);
|
||||
virtual void end_group() override;
|
||||
virtual void clear() override;
|
||||
|
||||
void print_html(String const& line);
|
||||
|
||||
virtual void send_messages(i32 start_index) override;
|
||||
virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, PrinterArguments) override;
|
||||
|
||||
virtual void add_css_style_to_current_message(StringView style) override
|
||||
{
|
||||
m_current_message_style.append(style);
|
||||
m_current_message_style.append(';');
|
||||
}
|
||||
|
||||
struct ConsoleOutput {
|
||||
enum class Type {
|
||||
HTML,
|
||||
Clear,
|
||||
BeginGroup,
|
||||
BeginGroupCollapsed,
|
||||
EndGroup,
|
||||
};
|
||||
Type type;
|
||||
String data;
|
||||
};
|
||||
|
||||
Vector<ConsoleOutput> m_message_log;
|
||||
StringBuilder m_current_message_style;
|
||||
};
|
||||
|
||||
}
|
|
@ -9,30 +9,25 @@
|
|||
|
||||
#include <AK/JsonObjectSerializer.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibGfx/ShareableBitmap.h>
|
||||
#include <LibJS/Console.h>
|
||||
#include <LibJS/Runtime/ConsoleObject.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/CSS/CSSImportRule.h>
|
||||
#include <LibWeb/Cookie/ParsedCookie.h>
|
||||
#include <LibWeb/DOM/Attr.h>
|
||||
#include <LibWeb/DOM/CharacterData.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/MutationType.h>
|
||||
#include <LibWeb/DOM/NamedNodeMap.h>
|
||||
#include <LibWeb/DOM/NodeList.h>
|
||||
#include <LibWeb/HTML/HTMLLinkElement.h>
|
||||
#include <LibWeb/HTML/HTMLStyleElement.h>
|
||||
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||
#include <LibWeb/Layout/Viewport.h>
|
||||
#include <LibWeb/Painting/PaintableBox.h>
|
||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||
#include <LibWebView/Attribute.h>
|
||||
#include <LibWebView/SiteIsolation.h>
|
||||
#include <WebContent/ConnectionFromClient.h>
|
||||
#include <WebContent/DevToolsConsoleClient.h>
|
||||
#include <WebContent/InspectorConsoleClient.h>
|
||||
#include <WebContent/PageClient.h>
|
||||
#include <WebContent/PageHost.h>
|
||||
#include <WebContent/WebContentClientEndpoint.h>
|
||||
|
@ -42,7 +37,6 @@ namespace WebContent {
|
|||
|
||||
static PageClient::UseSkiaPainter s_use_skia_painter = PageClient::UseSkiaPainter::GPUBackendIfAvailable;
|
||||
static bool s_is_headless { false };
|
||||
static bool s_devtools_enabled { false };
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PageClient);
|
||||
|
||||
|
@ -61,11 +55,6 @@ void PageClient::set_is_headless(bool is_headless)
|
|||
s_is_headless = is_headless;
|
||||
}
|
||||
|
||||
void PageClient::set_devtools_enabled(bool devtools_enabled)
|
||||
{
|
||||
s_devtools_enabled = devtools_enabled;
|
||||
}
|
||||
|
||||
GC::Ref<PageClient> PageClient::create(JS::VM& vm, PageHost& page_host, u64 id)
|
||||
{
|
||||
return vm.heap().allocate<PageClient>(page_host, id);
|
||||
|
@ -710,76 +699,6 @@ void PageClient::page_did_mutate_dom(FlyString const& type, Web::DOM::Node const
|
|||
client().async_did_mutate_dom(m_id, { type.to_string(), target.unique_id(), move(serialized_target), mutation.release_value() });
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_load()
|
||||
{
|
||||
client().async_inspector_did_load(m_id);
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_select_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
|
||||
{
|
||||
client().async_inspector_did_select_dom_node(m_id, node_id, pseudo_element);
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_set_dom_node_text(Web::UniqueNodeID node_id, String const& text)
|
||||
{
|
||||
client().async_inspector_did_set_dom_node_text(m_id, node_id, text);
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_set_dom_node_tag(Web::UniqueNodeID node_id, String const& tag)
|
||||
{
|
||||
client().async_inspector_did_set_dom_node_tag(m_id, node_id, tag);
|
||||
}
|
||||
|
||||
static Vector<WebView::Attribute> named_node_map_to_vector(GC::Ref<Web::DOM::NamedNodeMap> map)
|
||||
{
|
||||
Vector<WebView::Attribute> attributes;
|
||||
attributes.ensure_capacity(map->length());
|
||||
|
||||
for (size_t i = 0; i < map->length(); ++i) {
|
||||
auto const* attribute = map->item(i);
|
||||
VERIFY(attribute);
|
||||
|
||||
attributes.empend(attribute->name().to_string(), attribute->value());
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_add_dom_node_attributes(Web::UniqueNodeID node_id, GC::Ref<Web::DOM::NamedNodeMap> attributes)
|
||||
{
|
||||
client().async_inspector_did_add_dom_node_attributes(m_id, node_id, named_node_map_to_vector(attributes));
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_replace_dom_node_attribute(Web::UniqueNodeID node_id, size_t attribute_index, GC::Ref<Web::DOM::NamedNodeMap> replacement_attributes)
|
||||
{
|
||||
client().async_inspector_did_replace_dom_node_attribute(m_id, node_id, attribute_index, named_node_map_to_vector(replacement_attributes));
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_request_dom_tree_context_menu(Web::UniqueNodeID node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
|
||||
{
|
||||
client().async_inspector_did_request_dom_tree_context_menu(m_id, node_id, page().css_to_device_point(position).to_type<int>(), type, tag, attribute_index);
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_request_cookie_context_menu(size_t cookie_index, Web::CSSPixelPoint position)
|
||||
{
|
||||
client().async_inspector_did_request_cookie_context_menu(m_id, cookie_index, page().css_to_device_point(position).to_type<int>());
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_request_style_sheet_source(Web::CSS::StyleSheetIdentifier const& identifier)
|
||||
{
|
||||
client().async_inspector_did_request_style_sheet_source(m_id, identifier);
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_execute_console_script(String const& script)
|
||||
{
|
||||
client().async_inspector_did_execute_console_script(m_id, script);
|
||||
}
|
||||
|
||||
void PageClient::inspector_did_export_inspector_html(String const& html)
|
||||
{
|
||||
client().async_inspector_did_export_inspector_html(m_id, html);
|
||||
}
|
||||
|
||||
ErrorOr<void> PageClient::connect_to_webdriver(ByteString const& webdriver_ipc_path)
|
||||
{
|
||||
VERIFY(!m_webdriver);
|
||||
|
@ -796,12 +715,7 @@ void PageClient::initialize_js_console(Web::DOM::Document& document)
|
|||
auto& realm = document.realm();
|
||||
auto console_object = realm.intrinsics().console_object();
|
||||
|
||||
GC::Ptr<JS::ConsoleClient> console_client;
|
||||
if (s_devtools_enabled)
|
||||
console_client = DevToolsConsoleClient::create(document.realm(), console_object->console(), *this);
|
||||
else
|
||||
console_client = InspectorConsoleClient::create(document.realm(), console_object->console(), *this);
|
||||
|
||||
auto console_client = DevToolsConsoleClient::create(document.realm(), console_object->console(), *this);
|
||||
document.set_console_client(console_client);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@ public:
|
|||
virtual bool is_headless() const override;
|
||||
static void set_is_headless(bool);
|
||||
|
||||
static void set_devtools_enabled(bool);
|
||||
|
||||
virtual bool is_ready_to_paint() const override;
|
||||
|
||||
virtual Web::Page& page() override { return *m_page; }
|
||||
|
@ -176,17 +174,6 @@ private:
|
|||
virtual void page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) override;
|
||||
virtual IPC::File request_worker_agent() override;
|
||||
virtual void page_did_mutate_dom(FlyString const& type, Web::DOM::Node const& target, Web::DOM::NodeList& added_nodes, Web::DOM::NodeList& removed_nodes, GC::Ptr<Web::DOM::Node> previous_sibling, GC::Ptr<Web::DOM::Node> next_sibling, Optional<String> const& attribute_name) override;
|
||||
virtual void inspector_did_load() override;
|
||||
virtual void inspector_did_select_dom_node(Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
|
||||
virtual void inspector_did_set_dom_node_text(Web::UniqueNodeID, String const& text) override;
|
||||
virtual void inspector_did_set_dom_node_tag(Web::UniqueNodeID, String const& tag) override;
|
||||
virtual void inspector_did_add_dom_node_attributes(Web::UniqueNodeID, GC::Ref<Web::DOM::NamedNodeMap> attributes) override;
|
||||
virtual void inspector_did_replace_dom_node_attribute(Web::UniqueNodeID, size_t attribute_index, GC::Ref<Web::DOM::NamedNodeMap> replacement_attributes) override;
|
||||
virtual void inspector_did_request_dom_tree_context_menu(Web::UniqueNodeID, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
|
||||
virtual void inspector_did_request_cookie_context_menu(size_t cookie_index, Web::CSSPixelPoint position) override;
|
||||
virtual void inspector_did_request_style_sheet_source(Web::CSS::StyleSheetIdentifier const& stylesheet_source) override;
|
||||
virtual void inspector_did_execute_console_script(String const& script) override;
|
||||
virtual void inspector_did_export_inspector_html(String const& script) override;
|
||||
|
||||
Web::Layout::Viewport* layout_root();
|
||||
void setup_palette();
|
||||
|
|
|
@ -59,8 +59,7 @@ endpoint WebContentClient
|
|||
did_mutate_dom(u64 page_id, WebView::Mutation mutation) =|
|
||||
did_get_dom_node_html(u64 page_id, String html) =|
|
||||
|
||||
inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> style_sheets) =|
|
||||
inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =|
|
||||
did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> style_sheets) =|
|
||||
did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL base_url, String source) =|
|
||||
|
||||
did_take_screenshot(u64 page_id, Gfx::ShareableBitmap screenshot) =|
|
||||
|
@ -109,16 +108,4 @@ endpoint WebContentClient
|
|||
did_find_in_page(u64 page_id, size_t current_match_index, Optional<size_t> total_match_count) =|
|
||||
|
||||
request_worker_agent(u64 page_id) => (IPC::File socket) // FIXME: Add required attributes to select a SharedWorker Agent
|
||||
|
||||
inspector_did_load(u64 page_id) =|
|
||||
inspector_did_select_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
|
||||
inspector_did_set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text) =|
|
||||
inspector_did_set_dom_node_tag(u64 page_id, Web::UniqueNodeID node_id, String tag) =|
|
||||
inspector_did_add_dom_node_attributes(u64 page_id, Web::UniqueNodeID node_id, Vector<WebView::Attribute> attributes) =|
|
||||
inspector_did_replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID node_id, size_t attribute_index, Vector<WebView::Attribute> replacement_attributes) =|
|
||||
inspector_did_request_dom_tree_context_menu(u64 page_id, Web::UniqueNodeID node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<size_t> attribute_index) =|
|
||||
inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position) =|
|
||||
inspector_did_execute_console_script(u64 page_id, String script) =|
|
||||
inspector_did_export_inspector_html(u64 page_id, String html) =|
|
||||
|
||||
}
|
||||
|
|
|
@ -123,7 +123,5 @@ endpoint WebContentServer
|
|||
|
||||
set_user_style(u64 page_id, String source) =|
|
||||
|
||||
enable_inspector_prototype(u64 page_id) =|
|
||||
|
||||
system_time_zone_changed() =|
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
bool collect_garbage_on_every_allocation = false;
|
||||
bool is_headless = false;
|
||||
bool disable_scrollbar_painting = false;
|
||||
bool devtools = false;
|
||||
StringView echo_server_port_string_view {};
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
|
@ -133,7 +132,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
args_parser.add_option(disable_scrollbar_painting, "Don't paint horizontal or vertical viewport scrollbars", "disable-scrollbar-painting");
|
||||
args_parser.add_option(echo_server_port_string_view, "Echo server port used in test internals", "echo-server-port", 0, "echo_server_port");
|
||||
args_parser.add_option(is_headless, "Report that the browser is running in headless mode", "headless");
|
||||
args_parser.add_option(devtools, "Report that the browser is running with Firefox DevTools support", "devtools");
|
||||
|
||||
args_parser.parse(arguments);
|
||||
|
||||
|
@ -160,7 +158,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
WebContent::PageClient::set_use_skia_painter(force_cpu_painting ? WebContent::PageClient::UseSkiaPainter::CPUBackend : WebContent::PageClient::UseSkiaPainter::GPUBackendIfAvailable);
|
||||
|
||||
WebContent::PageClient::set_is_headless(is_headless);
|
||||
WebContent::PageClient::set_devtools_enabled(devtools);
|
||||
|
||||
if (disable_site_isolation)
|
||||
WebView::disable_site_isolation();
|
||||
|
|
|
@ -61,11 +61,6 @@ list(TRANSFORM 48x48_ICONS PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/icons/48x48/
|
|||
list(TRANSFORM 128x128_ICONS PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/icons/128x128/")
|
||||
list(TRANSFORM BROWSER_ICONS PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/icons/browser/")
|
||||
|
||||
set(WEB_RESOURCES
|
||||
inspector.css
|
||||
inspector.html
|
||||
inspector.js
|
||||
)
|
||||
set(ABOUT_PAGES
|
||||
about.html
|
||||
newtab.html
|
||||
|
@ -75,7 +70,6 @@ set(WEB_TEMPLATES
|
|||
error.html
|
||||
version.html
|
||||
)
|
||||
list(TRANSFORM WEB_RESOURCES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/")
|
||||
list(TRANSFORM ABOUT_PAGES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/about-pages/")
|
||||
list(TRANSFORM WEB_TEMPLATES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/templates/")
|
||||
|
||||
|
@ -161,10 +155,6 @@ function(copy_resources_to_build base_directory bundle_target)
|
|||
DESTINATION ${base_directory} TARGET ${bundle_target}
|
||||
)
|
||||
|
||||
copy_resource_set(ladybird RESOURCES ${WEB_RESOURCES}
|
||||
DESTINATION ${base_directory} TARGET ${bundle_target}
|
||||
)
|
||||
|
||||
copy_resource_set(ladybird/about-pages RESOURCES ${ABOUT_PAGES}
|
||||
DESTINATION ${base_directory} TARGET ${bundle_target}
|
||||
)
|
||||
|
@ -191,7 +181,6 @@ function(install_ladybird_resources destination component)
|
|||
install(FILES ${128x128_ICONS} DESTINATION "${destination}/icons/128x128" COMPONENT ${component})
|
||||
install(FILES ${BROWSER_ICONS} DESTINATION "${destination}/icons/browser" COMPONENT ${component})
|
||||
install(FILES ${THEMES} DESTINATION "${destination}/themes" COMPONENT ${component})
|
||||
install(FILES ${WEB_RESOURCES} DESTINATION "${destination}/ladybird" COMPONENT ${component})
|
||||
install(FILES ${ABOUT_PAGES} DESTINATION "${destination}/ladybird/about-pages" COMPONENT ${component})
|
||||
install(FILES ${WEB_TEMPLATES} DESTINATION "${destination}/ladybird/templates" COMPONENT ${component})
|
||||
install(FILES ${CONFIG_RESOURCES} DESTINATION "${destination}/ladybird/default-config" COMPONENT ${component})
|
||||
|
|
Loading…
Add table
Reference in a new issue