/[suikacvs]/markup/html/whatpm/Whatpm/CSS/Cascade.pm
Suika

Contents of /markup/html/whatpm/Whatpm/CSS/Cascade.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (show annotations) (download)
Wed Jan 2 07:39:21 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.6: +3 -2 lines
++ whatpm/Whatpm/CSS/ChangeLog	2 Jan 2008 07:39:15 -0000
	* Cascade.pm (get_cascaded_value): "*"-only declaration blocks
	were ignored.

	* Parser.pm (cursor): Implemented.

2008-01-02  Wakaba  <wakaba@suika.fam.cx>

1 package Whatpm::CSS::Cascade;
2 use strict;
3
4 require Whatpm::CSS::Parser; ## NOTE: For property definitions.
5 require Whatpm::CSS::SelectorsSerializer;
6 use Scalar::Util qw/refaddr/;
7
8 ## Cascading and value computations
9
10 sub new ($$) {
11 my $self = bless {style_sheets => []}, shift;
12 $self->{document} = shift;
13 return $self;
14 } # new
15
16 ## NOTE: This version does not support dynamic addition --- style
17 ## sheets must be added before any other operation.
18 ## NOTE: The $ss argument must be a value that can be interpreted as
19 ## an array reference of CSSStyleSheet objects.
20 ## NOTE: |type| and |media| attributes are not accounted by this
21 ## method (and any others in the class); CSSStyleSheet objects must
22 ## be filtered before they are passed to this method.
23 sub add_style_sheets ($$) {
24 my ($self, $ss) = @_;
25
26 push @{$self->{style_sheets}}, @$ss;
27 } # add_style_sheets
28
29 ## TODO: non-CSS presentation hints
30 ## TODO: style=""
31
32 sub ___associate_rules ($) {
33 my $self = shift;
34
35 my $selectors_to_elements;
36
37 for my $sheet (@{$self->{style_sheets}}) {
38 ## TODO: @media
39 ## TODO: @import
40 ## TODO: style sheet sources
41
42 for my $rule (@{$sheet->css_rules}) {
43 next if $rule->type != 1; # STYLE_RULE
44
45 my $elements_to_specificity = {};
46
47 for my $selector (@{$$rule->{_selectors}}) {
48 my $selector_str = Whatpm::CSS::SelectorsSerializer->serialize_test
49 ([$selector]);
50 unless ($selectors_to_elements->{$selector_str}) {
51 $selectors_to_elements->{$selector_str}
52 = $self->{document}->___query_selector_all ([$selector]);
53 }
54 next unless @{$selectors_to_elements->{$selector_str}};
55
56 my $selector_specificity
57 = Whatpm::CSS::SelectorsParser->get_selector_specificity
58 ($selector);
59 for (@{$selectors_to_elements->{$selector_str}}) {
60 my $current_specificity = $elements_to_specificity->{refaddr $_};
61 if (not defined $current_specificity or ## "*"-only case.
62 $selector_specificity->[0] > $current_specificity->[0] or
63 $selector_specificity->[1] > $current_specificity->[1] or
64 $selector_specificity->[2] > $current_specificity->[2] or
65 $selector_specificity->[3] > $current_specificity->[3]) {
66 $elements_to_specificity->{refaddr $_} = $selector_specificity;
67 }
68 }
69 }
70
71 my $sd = $rule->style;
72 for (keys %$elements_to_specificity) {
73 push @{$self->{element_to_sds}->{$_} ||= []},
74 [$sd, $elements_to_specificity->{$_}];
75 }
76 }
77 }
78
79 for my $eid (keys %{$self->{element_to_sds} or {}}) {
80 $self->{element_to_sds}->{$eid} = [sort {
81 $a->[1]->[0] <=> $b->[1]->[0] or
82 $a->[1]->[1] <=> $b->[1]->[1] or
83 $a->[1]->[2] <=> $b->[1]->[2] or
84 $a->[1]->[3] <=> $b->[1]->[3]
85 ## NOTE: Perl |sort| is stable.
86 } @{$self->{element_to_sds}->{$eid} or []}];
87 }
88 } # associate_rules
89
90 sub get_cascaded_value ($$$) {
91 my ($self, $element, $prop_name) = @_;
92 return undef unless $Whatpm::CSS::Parser::Prop->{$prop_name};
93
94 my $key = $Whatpm::CSS::Parser::Prop->{$prop_name}->{key};
95 return undef unless defined $key; ## Shorthand property or some.
96
97 my $value;
98 for my $sds (reverse @{$self->{element_to_sds}->{refaddr $element} or []}) {
99 my $vp = ${$sds->[0]}->{$key};
100 if (defined $vp->[1] and $vp->[1] eq 'important') {
101 return $vp->[0];
102 } else {
103 $value = $vp->[0] unless defined $value;
104 }
105 }
106
107 return $value; # might be |undef|.
108 } # get_cascaded_value
109
110 sub get_specified_value ($$$) {
111 my ($self, $element, $prop_name) = @_;
112
113 ## TODO: Remove {specified_value} caching, once we implement most
114 ## of CSS 2.1 properties and confirm that it makes almost non sence
115 ## because of its duplication with {computed_value} caching.
116
117 my $eid = refaddr $element;
118 unless (exists $self->{specified_value}->{$eid}->{$prop_name}) {
119 my $cascaded = $self->get_cascaded_value ($element, $prop_name);
120 $self->{specified_value}->{$eid}->{$prop_name} = $cascaded;
121
122 unless (defined $cascaded) {
123 my $prop_def = $Whatpm::CSS::Parser::Prop->{$prop_name};
124 if (defined $prop_def) {
125 if ($prop_def->{inherited}) {
126 my $parent_element = $element->manakai_parent_element;
127 if (defined $parent_element) {
128 $self->{specified_value}->{$eid}->{$prop_name}
129 = $self->get_computed_value ($parent_element, $prop_name);
130 } else {
131 $self->{specified_value}->{$eid}->{$prop_name}
132 = $prop_def->{initial};
133 }
134 } else {
135 $self->{specified_value}->{$eid}->{$prop_name}
136 = $prop_def->{initial};
137 }
138 } else {
139 $self->{specified_value}->{$eid}->{$prop_name} = undef;
140 }
141 }
142 }
143
144 ## NOTE: Always |undef| for shorthand properties.
145
146 return $self->{specified_value}->{$eid}->{$prop_name};
147 } # get_specified_value
148
149 sub get_computed_value ($$$) {
150 my ($self, $element, $prop_name) = @_;
151
152 my $eid = refaddr $element;
153 unless (exists $self->{computed_value}->{$eid}->{$prop_name}) {
154 my $prop_def = $Whatpm::CSS::Parser::Prop->{$prop_name};
155 if (defined $prop_def) {
156 my $specified = $self->get_specified_value ($element, $prop_name);
157 if (defined $specified and $specified->[0] eq 'INHERIT') {
158 ## ISSUE: CSS 2.1 does not say to resolve computed value of the parent.
159 ## However, it is necessary for some cases (see
160 ## <http://suika.fam.cx/gate/2005/sw/inherit>). In addition,
161 ## the initial value is not a computed value for some properties.
162 my $parent_element = $element->manakai_parent_element;
163 if (defined $parent_element) {
164 $specified = $self->get_computed_value ($parent_element, $prop_name);
165 } else {
166 $specified = $prop_def->{compute}->($self, $element, $prop_name,
167 $prop_def->{initial});
168 }
169 ## NOTE: Because of this handling, {compute} codes must be
170 ## idempotent.
171 }
172
173 $self->{computed_value}->{$eid}->{$prop_name}
174 = $prop_def->{compute}->($self, $element, $prop_name, $specified);
175 } else {
176 $self->{computed_value}->{$eid}->{$prop_name} = undef;
177 }
178 }
179
180 ## NOTE: Always |undef| for shorthand properties.
181
182 return $self->{computed_value}->{$eid}->{$prop_name};
183 } # get_computed_value
184
185 1;
186 ## $Date: 2008/01/01 15:56:24 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24