use strict; use warnings; use CSS; use XML::XPath; use Data::Dumper; sub selector_to_xpath { my $selector = shift; my $xpath = ''; # doesn't deal with much of the CSS spec ... foreach my $token ( split(/\s/, $selector) ) { if ( $token =~ /(\w+)? (?: \#(\w+) | \.(\w+) )?/x ) { $xpath .= '//'; my ( $tag, $id, $class ) = ( $1, $2, $3 ); if ( $tag ) { $xpath .= $tag; } if ( $id ) { $xpath .= "*" unless $tag; $xpath .= "[\@id='$id']"; } if ( $class ) { $xpath .= "*" unless $tag; $xpath .= "[\@class='$class']"; } } } return $xpath; } my $css = CSS->new(); # this rule matches an element in our doc $css->read_string('div#foo p.bar { background-image : url(/foo/bar.gif) }'); # this doesn't match an element in our doc $css->read_string('div#foo p.qux { background-image : url(/foo/qux.gif) }'); # nor does this $css->read_string('div#baz p.bar { background-image : url(/foo/baz.gif) }'); # but this does $css->read_string('div { background-image : url(/foo/div.gif) }'); # gather up all rules talking about backgrounds my %bg_rules; foreach my $rule ( @{ $css->{'styles'} } ) { foreach my $prop ( @{ $rule->{'properties'} } ) { if ( $prop->{'property'} =~ /^background(?:-image)?$/ ) { foreach my $selector ( @{ $rule->{'selectors'} } ) { $bg_rules{$selector->{'name'}} = $prop->{'simple_value'}; } } } } # slurp up the XML and parse for XPath-ery my $xml; { local $/; $xml = XML::XPath->new(ioref => *DATA); } # go through our list of CSS rules seeing which ones apply my @used_images; while ( my ( $sel, $propvalue ) = each %bg_rules ) { my $xpath = selector_to_xpath($sel); push(@used_images, $propvalue) if $xml->exists($xpath); } # let's see what we got ... warn Dumper \@used_images; __END__