File Coverage

blib/lib/CSS/DOM.pm
Criterion Covered Total %
statement 97 123 78.8
branch 33 50 66.0
condition 8 11 72.7
subroutine 32 32 100.0
pod 19 19 100.0
total 189 235 80.4


line stmt bran cond sub pod time code
1             package CSS::DOM;
2              
3 23     23   171011 use 5.008002;
  23         92  
  23         1505  
4              
5             $VERSION = '0.15';
6              
7             use # to keep CPANTS happy :-)
8 23     23   140 strict;
  23         49  
  23         802  
9             use # same here
10 23     23   149 warnings;
  23         49  
  23         848  
11              
12             use CSS::DOM::Exception
13 23     23   5678 'SYNTAX_ERR' ,'HIERARCHY_REQUEST_ERR', 'INDEX_SIZE_ERR';
  23         54  
  23         3185  
14 23     23   203 use CSS::DOM::Constants 'STYLE_RULE';
  23         46  
  23         1094  
15 23     23   146 use Scalar::Util 'weaken';
  23         44  
  23         2326  
16              
17             require CSS::DOM::RuleList;
18              
19 23         3115 use constant 1.03 our $_constants = {
20             ruls => 0,
21             ownr => 1, # owner rule
22             node => 2, # owner node
23             dsbl => 3,
24             hrfe => 4,
25             medi => 5,
26             fetc => 6, # url fetcher
27             prsh => 7, # parent sheet
28             prpp => 8, # property parser
29 23     23   134 };
  23         435  
30 23     23   146 { no strict; delete @CSS::DOM::{_constants => keys %{our $_constants}} }
  23         62  
  23         16610  
31              
32              
33             # NON-DOM METHODS
34              
35             # classy method
36             sub new {
37 101     101 1 17133 my $self = bless[],shift;
38 101         300 my %args = @_;
39 101 100       640 if(defined(my $arg = delete $args{url_fetcher})) {
40 9         43 $self->[fetc] = $arg;
41             }
42 101         369 $self->[prpp] = delete $args{property_parser};
43 101         511 $self;
44             }
45              
46             # objectionable methods
47             sub url_fetcher {
48 30     30 1 64 my $old = (my$ self = shift)->[fetc];
49 30 100       89 $ self -> [ fetc ] = shift if @ _ ;
50 30         92 $old
51             }
52 87     87 1 424 sub property_parser { shift->[prpp] }
53              
54              
55             # FUNCTIONS
56              
57             sub parse {
58 65     65 1 40131 require CSS::DOM::Parser;
59 65         486 goto &CSS::DOM::Parser::parse;
60             }
61              
62             sub compute_style {
63 1     1 1 8 my %args = @_;
64             # ~~~ for now we just ignore medium/height/width/ppi. We need to
65             # support those, too.
66              
67 1         14 require CSS::DOM::Style;
68 1         4 my $style = new CSS::DOM::Style;
69              
70 1         4 my $elem = delete $args{element};
71 1         4 my $pseudo = delete $args{pseudo};
72 1 50       5 $pseudo && $pseudo =~ s/^::?//;
73            
74             # The specificity returned by the style rule is a three-character
75             # string representing the number of id, attr, and elem selector
76             # components (e.g., li.red.level gives "\0\2\1"). We prefix that
77             # with two more chars, to make:
78             # XXXXX
79             # ||||`-- element
80             # |||`-- attribute
81             # ||`-- id
82             # |`-- style attribute
83             # `-- style sheet
84              
85             # ‘Style attribute’ is \1 or \0, indicating whether the CSS proper-
86             # ties originate from a style attribute. ‘Style sheet’ is
87             # as follows:
88             # "\0") user agent normal declarations
89             # "\1") user normal declarations
90             # "\2") author normal "
91             # "\3") user agent !important declarations
92             # "\4") author !important "
93             # "\5") user " "
94              
95             # The individual properties are sorted according to this scheme.
96              
97              
98             # ~~~ This isn’t the most efficient algorithm. Perhaps we can cache
99             # some of this.
100              
101 1         2 my %specificity; # per property
102              
103             my @normal_spec;
104 0         0 my @important_spec;
105 0         0 my @sheets;
106 1 50       6 if(defined $args{ua_sheet}) {
107 0         0 push @normal_spec, chr 0;
108 0         0 push @important_spec, chr 3;
109 0         0 push @sheets, delete $args{ua_sheet};
110             }
111 1 50       4 if(defined $args{user_sheet}) {
112 0         0 push @normal_spec, chr 1;
113 0         0 push @important_spec, chr 5;
114 0         0 push @sheets, delete $args{user_sheet};
115             }
116 1 50       4 if(defined $args{author_sheets}) {
117 0         0 my $s = delete $args{author_sheets};
118 0         0 push @normal_spec, (chr 2) x @$s;
119 0         0 push @important_spec, (chr 4) x @$s;
120 0         0 push @sheets, @$s;
121             }
122 1         4 while(@sheets) {
123 0         0 my $n = shift @normal_spec;
124 0         0 my $i = shift @important_spec;
125 0         0 my $s = shift @sheets;
126 0         0 my @rules = $s->cssRules;
127 0         0 while(@rules) {
128 0         0 my $r = shift @rules;
129 0         0 my $type = $r->type;
130 0 0       0 if($type == STYLE_RULE) {
131             next unless
132 0 0       0 my $specificity = $r->_selector_matches(
133             $elem, $pseudo
134             );
135 0         0 my $sty = $r->style;
136 0         0 for(0..$sty->length-1) {
137 0         0 my $p = $sty->item($_);
138 0 0       0 my $spec = (
139             $sty->getPropertyPriority($p)
140             =~
141             /^important\z/i
142             ? $i : $n
143             ) . "\0$specificity";
144 23     23   5282 no warnings 'uninitialized';
  23         50  
  23         4945  
145 0 0       0 $spec ge $specificity{$p} and
146             $style->setProperty(
147             $p, $sty->getPropertyValue($p)
148             ),
149             $specificity{$p} = $spec;
150             }
151             }
152             }
153             }
154            
155 1         16 my $sty = $elem->style;
156 1         18 for(0..$sty->length-1) {
157 1         7 my $p = $sty->item($_);
158 1 50       3832 my $spec = (
159             $sty->getPropertyPriority($p)
160             =~
161             /^important\z/i
162             ? "\4" : "\3"
163             ) . "\1\0\0\0";
164 23     23   189 no warnings 'uninitialized';
  23         60  
  23         10601  
165 1 50       187 $spec ge $specificity{$p} and
166             $style->setProperty(
167             $p, $sty->getPropertyValue($p)
168             ),
169             $specificity{$p} = $spec;
170             }
171              
172 1         9 return $style;
173             }
174              
175              
176             # DOM STUFF:
177              
178             # StyleSheet interface:
179              
180 1     1 1 6 sub type { 'text/css' }
181             sub disabled {
182 4     4 1 10 my $old = (my $self = shift) ->[dsbl];
183 4 100       12 @_ and $self->[dsbl] = shift;
184 4         16 $old
185             };
186 5 100   5 1 45 sub ownerNode { defined $_[0][node]?$_[0][node]:() }
187 3     3 1 556 sub set_ownerNode { weaken($_[0]->[node] = $_[1]) }
188 2 100   2 1 1555 sub parentStyleSheet { shift->[prsh]||() }
189 9     9   56 sub _set_parentStyleSheet { weaken($_[0]->[prsh] = $_[1]) }
190 1     1 1 7 sub href { shift->[hrfe] }
191 1     1 1 534 sub set_href { $_[0]->[hrfe] = $_[1] }
192 23     23   182 sub title { no warnings 'uninitialized';
  23         61  
  23         14486  
193 1   50 1 1 8 ''.(shift->ownerNode || return)->attr('title') }
194              
195             # If you find a bug in here, Media.pm’s method probably also needs fixing.
196             sub media {
197 3 50 66 3 1 1681 wantarray ? @{$_[0]->[medi]||return} :
  1 100       14  
198             ($_[0]->[medi] ||= (
199             require CSS::DOM::MediaList,
200             CSS::DOM::MediaList->new
201             ))
202             }
203              
204              
205             # CSSStyleSheet interface:
206              
207             sub ownerRule {
208 3 100   3 1 26 shift->[ownr] || ()
209             }
210             sub _set_ownerRule {
211 10     10   64 weaken($_[0]->[ownr] = $_[1]);
212             }
213              
214             # If you find a bug in the following three methods, Media.pm’s methods
215             # probably also need fixing.
216             sub cssRules {
217             wantarray
218 405 50 66 405 1 3994 ? @{shift->[ruls]||return}
  10 100       103  
219             : (shift->[ruls]||=new CSS::DOM::RuleList);
220             }
221              
222             sub insertRule { # This is supposed to raise an HIERARCHY_REQUEST_ERR if
223             # the rule cannot be inserted at the specified index;
224             # e.g., if an @import rule is inserted after a stan-
225             # dard rule. But we don’t do that, in order to maintain
226             # future compatibility.
227 136     136 1 2137 my ($self, $rule_string, $index) = @_;
228            
229 136         1810 require CSS::DOM::Parser;
230 136         227 my ($at,$rule);
231             {
232 136         233 local *@;
  136         378  
233 136         543 $rule = CSS::DOM::Parser::parse_statement(
234             $rule_string,$self
235             );
236 136         406 $at = $@
237             }
238 136 100       426 $at and die new CSS::DOM::Exception SYNTAX_ERR, $at;
239              
240             # $rule->_set_parentStyleSheet($self);
241              
242 130         423 my $list = $self->cssRules; # cssRules takes care of ||=
243 130         382 splice @$list, $index, 0, $rule;
244              
245 130 100       756 $index < 0 ? $#$list + $index :
    100          
246             $index <= $#$list ? $index :
247             $#$list
248             }
249              
250             sub deleteRule {
251 2     2 1 34 my ($self,$index) = @_;
252 2         5 my $list = $self->[ruls];
253 2 100       18 $index > $#$list and die CSS::DOM::Exception->new(
254             INDEX_SIZE_ERR,
255             "The index passed to deleteRule ($index) is too large"
256             );
257 1         4 splice @$list, $index, 1;
258             return # nothing;
259 1         12 }
260              
261              
262              
263             my %features = (
264             stylesheets => { '2.0' => 1 },
265             # css => { '2.0' => 1 },
266             css2 => { '2.0' => 1 },
267             );
268              
269             sub hasFeature {
270 24     24 1 1030 my($feature,$v) = (lc $_[1], $_[2]);
271 24 50 100     248 exists $features{$feature} and
272             !defined $v || exists $features{$feature}{$v};
273             }
274              
275             !()__END__()!
276              
277             =head1 NAME
278              
279             CSS::DOM - Document Object Model for Cascading Style Sheets
280              
281             =head1 VERSION
282              
283             Version 0.14
284              
285             This is an alpha version. The API is still subject to change. Many features
286             have not been implemented yet (but patches would be welcome :-).
287              
288             The interface for feeding CSS code to CSS::DOM changed incompatibly in
289             version 0.03.
290              
291             =for comment
292             This is an alpha version. If you could please test it and report any bugs
293             (via e-mail), I would be grateful.
294              
295             =head1 SYNOPSIS
296              
297             use CSS::DOM;
298              
299             my $sheet = CSS::DOM::parse( $css_source );
300              
301             use CSS::DOM::Style;
302             my $style = CSS::DOM::Style::parse(
303             'background: red; font-size: large'
304             );
305              
306             my $other_sheet = new CSS::DOM; # empty
307             $other_sheet->insertRule(
308             'a{ text-decoration: none }',
309             $other_sheet->cssRules->length,
310             );
311             # etc.
312            
313             # access DOM properties
314             $other_sheet->cssRules->[0]->selectorText('p'); # change it
315             $style->fontSize; # returns 'large'
316             $style->fontSize('small'); # change it
317              
318             =head1 DESCRIPTION
319              
320             This set of modules provides the CSS-specific interfaces described in the
321             W3C DOM
322             recommendation.
323              
324             The CSS::DOM class itself implements the StyleSheet and CSSStyleSheet DOM
325             interfaces.
326              
327             This set of modules has two modes:
328              
329             =over
330              
331             =item 1
332              
333             It can validate property values,
334             ignoring those that are invalid (just like a real web browser), and support shorthand
335             properties. This means you can set font to '13px/15px My Font' and have the
336             font-size, line-height, and font-family properties (among others) set automatically. Also, C<color: green; color: kakariki> will assign 'green'
337             to the color
338             property, 'kakariki' not being a recognised color value.
339              
340             =item 2
341              
342             It can
343             blithely accept all property assignments as being valid. In the case of
344             C<color: green; color kakariki>, 'kakariki' will be assigned, since it overrides the previous
345             assignment.
346              
347             =back
348              
349             These two modes are controlled by the C<property_parser> option to the
350             constructors.
351              
352             =head1 CONSTRUCTORS
353              
354             =over 4
355              
356             =item CSS::DOM::parse( $string )
357              
358             This method parses the C<$string> and returns a style sheet object. If you
359             just have a CSS style declaration, e.g., from an HTML C<style> attribute,
360             see L<CSS::DOM::Style/parse>.
361              
362             =item new CSS::DOM
363              
364             Creates a new, empty style sheet object. Use this only if you plan to build
365             the style sheet piece by piece, instead of parsing a block of CSS code.
366              
367             =back
368              
369             You can pass named arguments to both of those. C<parse> accepts all of
370             them; C<new> understands only the first two, C<property_parser> and
371             C<url_fetcher>.
372              
373             =over
374              
375             =item property_parser
376              
377             Set this to a L<PropertyParser|CSS::DOM::PropertyParser> object to specify
378             which properties are supported and how they are parsed.
379              
380             If this option is not specified or is set to C<undef>, all property
381             values are treated as valid.
382              
383             See L<CSS::DOM::PropertyParser> for more details.
384              
385             =item url_fetcher
386              
387             This has to be a code ref that returns the contents
388             of the style sheet at the URL passed as the sole argument. E.g.,
389              
390             # Disclaimer: This does not work with relative URLs.
391             use LWP::Simple;
392             use CSS::DOM;
393             $css = '@import "file.css"; /* other stuff ... ';
394             $ss = CSS::DOM::parse $css, url_fetcher => sub { get shift };
395             $ss->cssRules->[0]->styleSheet; # returns a style sheet object
396             # corresponding to file.css
397              
398             The subroutine can choose to return C<undef> or an empty list, in which
399             case the @import
400             rule's C<styleSheet> method will return null (empty list or C<undef>), as
401             it would if no C<url_fetcher> were specified.
402              
403             It can also return named items after the CSS code, like this:
404              
405             return $css_code, decode => 1, encoding_hint => 'iso-8859-1';
406              
407             These correspond to the next two items:
408              
409             =item decode
410              
411             If this is specified and set to a true value, then CSS::DOM will treat the
412             CSS code as a string of bytes, and try to decode it based on @charset rules
413             and byte order marks.
414              
415             By default it assumes that it is already in Unicode (i.e., decoded).
416              
417             =item encoding_hint
418              
419             Use this to provide a hint as to what the encoding might be.
420              
421             If this is specified, and C<decode> is not, then C<< decode => 1 >> is
422             assumed.
423              
424             =back
425              
426             =head1 STYLE SHEET ENCODING
427              
428             See the options above. This section explains how and when you I<should> use
429             those options.
430              
431             According to the CSS spec, any encoding specified in the 'charset' field on
432             an HTTP Content-Type header, or the equivalent in other protocols, takes
433             precedence. In such a case, since CSS::DOM doesn't deal with HTTP, you have
434             to decode it yourself.
435              
436             Otherwise, you should use C<< decode => 1 >> to instruct CSS::DOM to use
437             byte order marks or @charset rules.
438              
439             If neither of those is present, then encoding data in the referencing
440             document (e.g., <link charset="..."> or an HTML document's own encoding),
441             if available/applicable, should be used. In this case, you should use the
442             C<< encoding_hint >> option, so that CSS::DOM has something to fall back
443             to.
444              
445             If you use C<< decode => 1 >> with no encoding hint, and no BOM or @charset
446             is to be found, UTF-8 is assumed.
447              
448             =head1 SYNTAX ERRORS
449              
450             The two constructors above, and also
451             L<C<CSS::DOM::Style::parse>|CSS::DOM::Style/parse>, set C<$@> to the empty
452             string upon success. If
453             they
454             encounter a syntax error, they set C<$@> to the error and return an object
455             that represents whatever was parsed up to that point.
456              
457             Other methods that parse CSS code might die on encountering
458             syntax errors, and should usually be wrapped in an C<eval>.
459              
460             The parser follows the 'future-compatible' syntax described in the CSS 2.1
461             specification, and also the spec's rules for handling parsing errors.
462             Anything not handled by those two is a syntax error.
463              
464             In other words, a syntax error is one of the following:
465              
466             =over 4
467              
468             =item *
469              
470             An unexpected closing bracket, as
471             in these examples
472              
473             a { text-decoration: none )
474             *[name=~'foo'} {}
475             #thing { clip: rect( ]
476              
477             =item *
478              
479             An HTML comment delimiter within a rule; e.g.,
480              
481             a { text-decoration : none <!-- /* Oops! */ }
482             <!-- /*ok*/ @media --> /* bad! */ print { }
483              
484             =item *
485              
486             An extra C<@> keyword or semicolon where it doesn't belong; e.g.,
487              
488             @media @print { .... }
489             @import "file.css" @print;
490             td, @page { ... }
491             #tabbar td; #tab1 { }
492              
493             =back
494              
495             =head1 OBJECT METHODS
496              
497             =head2 Attributes
498              
499             =over 4
500              
501             =item type
502              
503             Returns the string 'text/css'.
504              
505             =item disabled
506              
507             Allows one to specify whether the style sheet is used. (This attribute is
508             not actually used yet by CSS::DOM.) You can set it by passing an argument.
509              
510             =item ownerNode
511              
512             Returns the node that 'owns' this style sheet.
513              
514             =item parentStyleSheet
515              
516             If the style sheet belongs to an '@import' rule, this returns the style
517             sheet containing that rule. Otherwise it returns an empty list.
518              
519             =item href
520              
521             Returns the style sheet's URI, if applicable.
522              
523             =item title
524              
525             Returns the value of the owner node's title attribute.
526              
527             =item media
528              
529             Returns the MediaList associated with the style sheet (or a plain list in
530             list context). This defaults to an
531             empty list. You can pass a comma-delimited string to the MediaList's
532             C<mediaText> method to initialise it.
533              
534             (The medium information is not actually used [yet] by CSS::DOM, but you
535             can put it there.)
536              
537             =item ownerRule
538              
539             If this style sheet was created by an @import rule, this returns the rule;
540             otherwise it returns an empty list (or undef in scalar context).
541              
542             =item cssRules
543              
544             In scalar context, this returns a L<CSS::DOM::RuleList> object (simply a
545             blessed
546             array reference) of L<CSS::DOM::Rule> objects. In list context it returns a
547             list.
548              
549             =back
550              
551             =head2 Methods
552              
553             =over 4
554              
555             =item insertRule ( $css_code, $index )
556              
557             Parses the rule contained in the C<$css_code>, inserting it in the style
558             sheet's list of rules at the given C<$index>.
559              
560             =item deleteRule ( $index )
561              
562             Deletes the rule at the given C<$index>.
563              
564             =item hasFeature ( $feature, $version )
565              
566             You can call this either as an object or class method.
567              
568             This is actually supposed to be a method of the 'DOMImplementation' object.
569             (See, for instance, L<HTML::DOM::Interface>'s method of the same name,
570             which delegates to this one.) This returns a boolean indicating whether a
571             particular DOM module is implemented. Right now it returns true only for
572             the 'CSS2' and 'StyleSheets' features (version '2.0').
573              
574             =back
575              
576             =head2 Non-DOM Methods
577              
578             =over 4
579              
580             =item set_ownerNode
581              
582             This allows you to set the value of C<ownerNode>. Passing an argument to
583             C<ownerNode> does nothing, because it is supposed to be read-only. But you
584             have to be able to set it somehow, so that's why this method is here.
585              
586             The style sheet will hold a weak reference to the object passed to this
587             method.
588              
589             =item set_href
590              
591             Like C<set_ownerNode>, but for C<href>.
592              
593             =item property_parser
594              
595             =item url_fetcher
596              
597             These two both return what was passed to the constructor. The second one,
598             C<url_fetcher> also allows an assignment, but this is not propagated to
599             sub-rules and is intended mainly for internal use.
600              
601             =back
602              
603             =head1 FUNCTIONS
604              
605             =over
606              
607             =item CSS::DOM::parse
608              
609             See L</CONSTRUCTORS>, above.
610              
611             =item CSS::DOM::compute_style( %options )
612              
613             B<Warning:> This is still highly experimental and crawling with bugs.
614              
615             This computes the style for a given HTML element. It does not yet calculate
616             actual measurements (e.g., converting percentages to pixels), but simply
617             applies the cascading rules and selectors. Pseudo-classes are
618             not yet supported (but pseudo-elements are).
619              
620             The precedence rules for normal vs important declarations in the CSS 2
621             specification are used. (CSS 2.1 is unclear.) The precedence is as follows,
622             from lowest to highest:
623              
624             user agent normal declarations
625             user normal declarations
626             author normal "
627             user agent !important declarations
628             author !important "
629             user " "
630              
631             The C<%options> are as follows. They are all optional except for
632             C<element>.
633              
634             =over
635              
636             =item ua_sheet
637              
638             The user agent style sheet
639              
640             =item user_sheet
641              
642             The user style sheet
643              
644             =item author_sheets
645              
646             Array ref of style sheets that the HTML document defines or links to.
647              
648             =item element
649              
650             The element, as an L<HTML::DOM::Element> object.
651              
652             =item pseudo
653              
654             The pseudo-element (e.g., 'first-line'). This can be specified with no
655             colons (the way Opera
656             requires it) or
657             with one or two colons (the way Firefox requires it).
658              
659             =item medium
660              
661             =item height
662              
663             =item width
664              
665             =item ppi
666              
667             (To be implemented)
668              
669             =back
670              
671             The
672              
673             =back
674              
675             =head1 CLASSES AND DOM INTERFACES
676              
677             Here are the inheritance hierarchy of CSS::DOM's various classes and the
678             DOM interfaces those classes implement. For brevity's sake, a simple '::'
679             at the beginning of a class name in the left column is used for
680             'CSS::DOM::'. Items in brackets do not exist yet. (See also
681             L<CSS::DOM::Interface> for a
682             machine-readable list of standard methods.)
683              
684             Class Inheritance Hierarchy Interfaces
685             --------------------------- ----------
686            
687             CSS::DOM StyleSheet, CSSStyleSheet
688             ::Array
689             ::MediaList MediaList
690             ::StyleSheetList StyleSheetList
691             ::RuleList CSSRuleList
692             ::Rule CSSRule, CSSUnknownRule
693             ::Rule::Style CSSStyleRule
694             ::Rule::Media CSSMediaRule
695             ::Rule::FontFace CSSFontFaceRule
696             ::Rule::Page CSSPageRule
697             ::Rule::Import CSSImportRule
698             ::Rule::Charset CSSCharsetRule
699             ::Style CSSStyleDeclaration, CSS2Properties
700             ::Value CSSValue
701             ::Value::Primitive CSSPrimitiveValue, RGBColor, Rect
702             ::Value::List CSSValueList
703             [::Counter Counter]
704              
705             CSS::DOM does not implement the following interfaces (see L<HTML::DOM> for
706             these):
707              
708             LinkStyle
709             DocumentStyle
710             ViewCSS
711             DocumentCSS
712             DOMImplementationCSS
713             ElementCSSInlineStyle
714              
715             =head1 IMPLEMENTATION NOTES
716              
717             =over 4
718              
719             =item *
720              
721             Attributes of objects are accessed via methods of the same name. When the
722             method
723             is invoked, the current value is returned. If an argument is supplied, the
724             attribute is set (unless it is read-only) and its old value returned.
725              
726             =item *
727              
728             Where the DOM spec. says to use null, undef or an empty list is used.
729              
730             =item *
731              
732             Instead of UTF-16 strings, CSS::DOM uses Perl's Unicode strings.
733              
734             =item *
735              
736             Each method that the specification says returns an array-like object (e.g.,
737             a RuleList) will return such an object in scalar context, or a simple list
738             in list context. You can use
739             the object as an array ref in addition to calling its C<item> and
740             C<length> methods.
741              
742             =begin for-me
743              
744             If I implement any methods that make use of the DOMTimeStamp interface, I
745             need to document that simple Perl scalars containing the time as returned
746             by Perl’s built-in ‘time’ function are used.
747              
748             =end for-me
749              
750             =back
751              
752             =head1 PREREQUISITES
753              
754             perl 5.8.2 or higher
755              
756             L<Exporter> 5.57 or later
757              
758             L<Encode> 2.10 or higher
759              
760             L<Clone> 0.09 or higher
761              
762             =head1 BUGS
763              
764             The parser has not been updated to conform to the April 2009 revision of
765             the CSS 2.1 candidate recommendation. Specifically, unexpected closing
766             brackets are not ignored, but cause syntax errors; and @media rules
767             containing unrecognised statements are themselves currently treated as
768             unrecognised (the unrecognised inner statements should be ignored,
769             rendering the outer @media rule itself valid).
770              
771             If you create a custom property parser that defines
772             'list-style-type' to include multiple tokens, then counters will become
773             C<CSS_CUSTOM> CSSValue objects instead of C<CSS_COUNTER> CSSPrimitiveValue
774             objects.
775              
776             If you change a property parser's property definitions such that a
777             primitive value becomes a list, or vice versa, and then try to modify the
778             C<cssText> property of an existing value object belonging to that property,
779             things will go awry.
780              
781             =for comment
782             This is because we can’t change a list into a prim and vice versa, because
783             one is a hash and the other is an array.
784              
785             Whitespace and comments are sometimes preserved in serialised CSS and
786             sometimes not.
787             Expect inconsistency.
788              
789             To report bugs, please e-mail the author.
790              
791             =head1 ACKNOWLEDGEMENTS
792              
793             Thanks to Ville Skyttä and Nicholas Bamber for their contributions.
794              
795             =head1 AUTHOR & COPYRIGHT
796              
797             Copyright (C) 2007-12 Father Chrysostomos <sprout [at] cpan
798             [dot] org>
799              
800             This program is free software; you may redistribute it and/or modify
801             it under the same terms as perl. The full text of the license can be found
802             in the LICENSE file included with this module.
803              
804             =head1 SEE ALSO
805              
806             All the classes listed above under L</CLASSES AND DOM INTERFACES>.
807              
808             L<CSS::SAC>, L<CSS.pm|CSS> and L<HTML::DOM>
809              
810             The DOM Level 2 Style specification at
811             S<L<http://www.w3.org/TR/DOM-Level-2-Style>>
812              
813             The CSS 2.1 specification at S<L<http://www.w3.org/TR/CSS21/>>