Skip to content

Commit ece9908

Browse files
trflynn89ADKaster
authored andcommitted
Ladybird: Add context menu items to the AppKit chrome to inspect nodes
1 parent bd1e35c commit ece9908

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed

Ladybird/AppKit/UI/Inspector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- (instancetype)init:(Tab*)tab;
1717

1818
- (void)inspect;
19+
- (void)inspectHoveredElement;
1920
- (void)reset;
2021

2122
@end

Ladybird/AppKit/UI/Inspector.mm

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <AK/DeprecatedString.h>
88
#include <AK/HashMap.h>
9+
#include <AK/Optional.h>
910
#include <AK/Traits.h>
1011
#include <LibWeb/CSS/Selector.h>
1112
#include <LibWebView/ViewImplementation.h>
@@ -43,7 +44,10 @@ static unsigned hash(NSDictionary* dictionary)
4344

4445
@interface Inspector () <NSOutlineViewDataSource, NSOutlineViewDelegate, NSTableViewDataSource>
4546
{
47+
BOOL m_dom_tree_loaded;
48+
4649
Selection m_selection;
50+
Optional<i32> m_pending_selection;
4751

4852
HashMap<NSDictionary*, NSDictionary*> m_dom_node_to_parent_map;
4953
HashMap<i32, NSDictionary*> m_node_id_to_dom_node_map;
@@ -118,6 +122,11 @@ - (instancetype)init:(Tab*)tab
118122
[strong_self.dom_tree_outline_view sizeToFit];
119123

120124
[strong_self prepareDOMNodeMaps:strong_self.dom_tree parentNode:nil];
125+
strong_self->m_dom_tree_loaded = YES;
126+
127+
if (strong_self->m_pending_selection.has_value()) {
128+
[strong_self inspectDOMNodeID:strong_self->m_pending_selection.release_value()];
129+
}
121130
} else {
122131
strong_self.dom_tree = @{};
123132
}
@@ -149,9 +158,25 @@ - (void)inspect
149158
web_view.inspect_dom_tree();
150159
}
151160

161+
- (void)inspectHoveredElement
162+
{
163+
auto& web_view = [[self.tab web_view] view];
164+
auto node_id = web_view.get_hovered_node_id();
165+
166+
if (!m_dom_tree_loaded) {
167+
m_pending_selection = node_id;
168+
return;
169+
}
170+
171+
[self inspectDOMNodeID:node_id];
172+
}
173+
152174
- (void)reset
153175
{
176+
m_dom_tree_loaded = NO;
177+
154178
m_selection = {};
179+
m_pending_selection = {};
155180

156181
m_dom_node_to_parent_map = {};
157182
m_node_id_to_dom_node_map = {};
@@ -261,6 +286,34 @@ - (void)prepareDOMNodeMaps:(NSDictionary*)dom_node
261286
}
262287
}
263288

289+
- (void)inspectDOMNodeID:(i32)node_id
290+
{
291+
auto dom_node = m_node_id_to_dom_node_map.get(node_id);
292+
if (!dom_node.has_value()) {
293+
return;
294+
}
295+
296+
[self expandDOMNode:*dom_node];
297+
298+
if (auto row = [self.dom_tree_outline_view rowForItem:*dom_node]; row != -1) {
299+
auto index = [NSIndexSet indexSetWithIndex:row];
300+
[self.dom_tree_outline_view selectRowIndexes:index byExtendingSelection:NO];
301+
}
302+
303+
[self setSelection: { node_id }];
304+
}
305+
306+
- (void)expandDOMNode:(NSDictionary*)dom_node
307+
{
308+
// We can't expand an item unless its parent is also already expanded, so walk up the tree to
309+
// expand all ancestors.
310+
if (auto parent_node = m_dom_node_to_parent_map.get(dom_node); parent_node.has_value()) {
311+
[self expandDOMNode:*parent_node];
312+
}
313+
314+
[self.dom_tree_outline_view expandItem:dom_node];
315+
}
316+
264317
- (void)setSelection:(Selection)selection
265318
{
266319
if (selection == m_selection)

Ladybird/AppKit/UI/LadybirdWebView.mm

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,9 @@ - (NSMenu*)page_context_menu
722722
[_page_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"View Source"
723723
action:@selector(viewSource:)
724724
keyEquivalent:@""]];
725+
[_page_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Inspect Element"
726+
action:@selector(inspectElement:)
727+
keyEquivalent:@""]];
725728
}
726729

727730
return _page_context_menu;
@@ -743,6 +746,11 @@ - (NSMenu*)link_context_menu
743746
[_link_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy URL"
744747
action:@selector(copyLink:)
745748
keyEquivalent:@""]];
749+
[_link_context_menu addItem:[NSMenuItem separatorItem]];
750+
751+
[_link_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Inspect Element"
752+
action:@selector(inspectElement:)
753+
keyEquivalent:@""]];
746754
}
747755

748756
return _link_context_menu;
@@ -767,6 +775,11 @@ - (NSMenu*)image_context_menu
767775
[_image_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy Image URL"
768776
action:@selector(copyLink:)
769777
keyEquivalent:@""]];
778+
[_image_context_menu addItem:[NSMenuItem separatorItem]];
779+
780+
[_image_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Inspect Element"
781+
action:@selector(inspectElement:)
782+
keyEquivalent:@""]];
770783
}
771784

772785
return _image_context_menu;
@@ -814,6 +827,11 @@ - (NSMenu*)audio_context_menu
814827
[_audio_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy Audio URL"
815828
action:@selector(copyLink:)
816829
keyEquivalent:@""]];
830+
[_audio_context_menu addItem:[NSMenuItem separatorItem]];
831+
832+
[_audio_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Inspect Element"
833+
action:@selector(inspectElement:)
834+
keyEquivalent:@""]];
817835
}
818836

819837
return _audio_context_menu;
@@ -861,6 +879,11 @@ - (NSMenu*)video_context_menu
861879
[_video_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Copy Video URL"
862880
action:@selector(copyLink:)
863881
keyEquivalent:@""]];
882+
[_video_context_menu addItem:[NSMenuItem separatorItem]];
883+
884+
[_video_context_menu addItem:[[NSMenuItem alloc] initWithTitle:@"Inspect Element"
885+
action:@selector(inspectElement:)
886+
keyEquivalent:@""]];
864887
}
865888

866889
return _video_context_menu;

Ladybird/AppKit/UI/Tab.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ - (void)onInspectorClosed
149149
self.inspector_controller = nil;
150150
}
151151

152+
- (void)inspectElement:(id)sender
153+
{
154+
[self openInspector:sender];
155+
156+
auto* inspector = (Inspector*)[self.inspector_controller window];
157+
[inspector inspectHoveredElement];
158+
}
159+
152160
#pragma mark - Private methods
153161

154162
- (TabController*)tabController

0 commit comments

Comments
 (0)