/[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.8 - (show annotations) (download)
Thu Jan 3 08:37:22 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.7: +16 -1 lines
++ whatpm/Whatpm/CSS/ChangeLog	3 Jan 2008 08:36:47 -0000
2008-01-03  Wakaba  <wakaba@suika.fam.cx>

	* Parser.pm: 'font-size' support.

	* Cascade.pm (new): Set default value for 'font-size'
	relative- and absolute- value computations.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24