From 6304abf94c4974f29f73991708798d25ea262e0d Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 13 Sep 2023 16:05:36 -0400 Subject: [PATCH] Ladybird: Add a table to display CSS properties to the AppKit Inspector --- Ladybird/AppKit/UI/Inspector.mm | 186 +++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) diff --git a/Ladybird/AppKit/UI/Inspector.mm b/Ladybird/AppKit/UI/Inspector.mm index caae1119c9..6b3ca0a201 100644 --- a/Ladybird/AppKit/UI/Inspector.mm +++ b/Ladybird/AppKit/UI/Inspector.mm @@ -5,6 +5,7 @@ */ #include +#include #include #import @@ -20,13 +21,38 @@ static constexpr CGFloat const WINDOW_WIDTH = 600; static constexpr CGFloat const WINDOW_HEIGHT = 800; -@interface Inspector () +static NSString* const CSS_PROPERTY_COLUMN = @"Property"; +static NSString* const CSS_VALUE_COLUMN = @"Value"; + +struct Selection { + bool operator==(Selection const& other) const = default; + + i32 dom_node_id { 0 }; + Optional pseudo_element {}; +}; + +@interface Inspector () +{ + Selection m_selection; +} @property (nonatomic, strong) Tab* tab; @property (nonatomic, strong) NSOutlineView* dom_tree_outline_view; @property (nonatomic, strong) NSDictionary* dom_tree; +@property (nonatomic, strong) NSTableView* computed_style_table_view; +@property (nonatomic, strong) NSDictionary* computed_style; +@property (nonatomic, strong) NSArray* computed_style_keys; + +@property (nonatomic, strong) NSTableView* resolved_style_table_view; +@property (nonatomic, strong) NSDictionary* resolved_style; +@property (nonatomic, strong) NSArray* resolved_style_keys; + +@property (nonatomic, strong) NSTableView* variables_table_view; +@property (nonatomic, strong) NSDictionary* variables; +@property (nonatomic, strong) NSArray* variables_keys; + @end @implementation Inspector @@ -56,7 +82,11 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; auto* top_tab_view = [[NSTabView alloc] init]; [split_view addSubview:top_tab_view]; + auto* bottom_tab_view = [[NSTabView alloc] init]; + [split_view addSubview:bottom_tab_view]; + [self initializeDOMTreeTab:top_tab_view]; + [self initializeCSSTables:bottom_tab_view]; [self reset]; auto& web_view = [[self.tab web_view] view]; @@ -93,6 +123,7 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; { auto& web_view = [[self.tab web_view] view]; web_view.on_received_dom_tree = nullptr; + web_view.clear_inspected_dom_node(); } #pragma mark - Public methods @@ -105,9 +136,23 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; - (void)reset { + m_selection = {}; + self.dom_tree = @{}; [self.dom_tree_outline_view reloadItem:nil reloadChildren:YES]; [self.dom_tree_outline_view sizeToFit]; + + self.computed_style = @{}; + self.computed_style_keys = @[]; + [self.computed_style_table_view reloadData]; + + self.resolved_style = @{}; + self.resolved_style_keys = @[]; + [self.resolved_style_table_view reloadData]; + + self.variables = @{}; + self.variables_keys = @[]; + [self.variables_table_view reloadData]; } #pragma mark - Private methods @@ -136,6 +181,42 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; [tab_view addTabViewItem:tab]; } +- (NSTableView*)createCSSTable:(NSTabView*)tab_view identifier:(NSString*)identifier +{ + auto* tab = [[NSTabViewItem alloc] initWithIdentifier:identifier]; + [tab setLabel:identifier]; + + auto* scroll_view = [[NSScrollView alloc] init]; + [scroll_view setHasVerticalScroller:YES]; + [scroll_view setHasHorizontalScroller:YES]; + [scroll_view setLineScroll:24]; + [tab setView:scroll_view]; + + auto* table_view = [[NSTableView alloc] initWithFrame:[tab_view frame]]; + [table_view setDataSource:self]; + [table_view setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle]; + [scroll_view setDocumentView:table_view]; + + auto* property_column = [[NSTableColumn alloc] initWithIdentifier:CSS_PROPERTY_COLUMN]; + [property_column setTitle:CSS_PROPERTY_COLUMN]; + [table_view addTableColumn:property_column]; + + auto* value_column = [[NSTableColumn alloc] initWithIdentifier:CSS_VALUE_COLUMN]; + [value_column setTitle:CSS_VALUE_COLUMN]; + [table_view addTableColumn:value_column]; + + [tab_view addTabViewItem:tab]; + + return table_view; +} + +- (void)initializeCSSTables:(NSTabView*)tab_view +{ + self.computed_style_table_view = [self createCSSTable:tab_view identifier:@"Computed Style"]; + self.resolved_style_table_view = [self createCSSTable:tab_view identifier:@"Resolved Style"]; + self.variables_table_view = [self createCSSTable:tab_view identifier:@"Variables"]; +} + - (void)onTreeDoubleClick:(id)sender { NSOutlineView* outline_view = sender; @@ -148,6 +229,45 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; } } +- (void)setSelection:(Selection)selection +{ + if (selection == m_selection) + return; + + m_selection = move(selection); + + auto deserialize_json = [](auto const& json) { + auto* dictionary = Ladybird::deserialize_json_to_dictionary(json); + if (!dictionary) { + return @{}; + } + + return dictionary; + }; + + auto& web_view = [[self.tab web_view] view]; + auto properties = web_view.inspect_dom_node(m_selection.dom_node_id, m_selection.pseudo_element); + + if (!properties.is_error()) { + self.computed_style = deserialize_json(properties.value().computed_style_json); + self.resolved_style = deserialize_json(properties.value().resolved_style_json); + self.variables = deserialize_json(properties.value().custom_properties_json); + } else { + self.computed_style = @{}; + self.resolved_style = @{}; + self.variables = @{}; + } + + self.computed_style_keys = [[self.computed_style allKeys] sortedArrayUsingSelector:@selector(compare:)]; + [self.computed_style_table_view reloadData]; + + self.resolved_style_keys = [[self.resolved_style allKeys] sortedArrayUsingSelector:@selector(compare:)]; + [self.resolved_style_table_view reloadData]; + + self.variables_keys = [[self.variables allKeys] sortedArrayUsingSelector:@selector(compare:)]; + [self.variables_table_view reloadData]; +} + #pragma mark - NSOutlineViewDataSource - (id)outlineView:(NSOutlineView*)view @@ -254,4 +374,68 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; return NO; } +- (BOOL)outlineView:(NSOutlineView*)outline_view + shouldSelectItem:(id)item +{ + i32 dom_node_id { 0 }; + Optional pseudo_element; + + if (id element = [item objectForKey:@"pseudo-element"]) { + dom_node_id = [[item objectForKey:@"parent-id"] intValue]; + pseudo_element = static_cast([element intValue]); + } else { + dom_node_id = [[item objectForKey:@"id"] intValue]; + } + + [self setSelection: { dom_node_id, move(pseudo_element) }]; + return YES; +} + +#pragma mark - NSTableViewDataSource + +- (NSInteger)numberOfRowsInTableView:(NSTableView*)table_view +{ + if (table_view == self.computed_style_table_view) { + return static_cast(self.computed_style.count); + } + if (table_view == self.resolved_style_table_view) { + return static_cast(self.resolved_style.count); + } + if (table_view == self.variables_table_view) { + return static_cast(self.variables.count); + } + + return 0; +} + +- (id)tableView:(NSTableView*)table_view + objectValueForTableColumn:(NSTableColumn*)table_column + row:(NSInteger)row +{ + NSDictionary* values = nil; + NSArray* keys = nil; + + if (table_view == self.computed_style_table_view) { + values = self.computed_style; + keys = self.computed_style_keys; + } else if (table_view == self.resolved_style_table_view) { + values = self.resolved_style; + keys = self.resolved_style_keys; + } else if (table_view == self.variables_table_view) { + values = self.variables; + keys = self.variables_keys; + } else { + return nil; + } + + if ([[table_column identifier] isEqualToString:CSS_PROPERTY_COLUMN]) { + return keys[row]; + } + if ([[table_column identifier] isEqualToString:CSS_VALUE_COLUMN]) { + return values[keys[row]]; + } + + return nil; +} + @end