/[suikacvs]/markup/html/whatpm/Whatpm/ContentChecker.pm
Suika

Contents of /markup/html/whatpm/Whatpm/ContentChecker.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.72 - (hide annotations) (download)
Thu Mar 20 10:30:20 2008 UTC (18 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.71: +6 -2 lines
++ whatpm/t/ChangeLog	20 Mar 2008 10:00:26 -0000
	* content-model-atom-threading-1.dat: New test data
	on |thr:total| element are added

2008-03-20  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	20 Mar 2008 09:59:59 -0000
	* Atom.pm: Support for |thr:total| element.
	Add notes on additional Atom namespaces.

2008-03-20  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3 wakaba 1.72 our $VERSION=do{my @r=(q$Revision: 1.71 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.1
5 wakaba 1.18 require Whatpm::URIChecker;
6    
7 wakaba 1.13 ## ISSUE: How XML and XML Namespaces conformance can (or cannot)
8     ## be applied to an in-memory representation (i.e. DOM)?
9    
10 wakaba 1.50 ## TODO: Conformance of an HTML document with non-html root element.
11    
12 wakaba 1.70 ## Stability
13 wakaba 1.67 sub FEATURE_STATUS_REC () { 0b1 } ## Interoperable standard
14     sub FEATURE_STATUS_CR () { 0b10 } ## Call for implementation
15     sub FEATURE_STATUS_LC () { 0b100 } ## Last call for comments
16     sub FEATURE_STATUS_WD () { 0b1000 } ## Working or editor's draft
17    
18 wakaba 1.70 ## Deprecated
19     sub FEATURE_DEPRECATED_SHOULD () { 0b100000 } ## SHOULD-level
20     sub FEATURE_DEPRECATED_INFO () { 0b1000000 } ## Does not affect conformance
21    
22     ## Conformance
23     sub FEATURE_ALLOWED () { 0b10000 }
24    
25 wakaba 1.42 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
26 wakaba 1.9 my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
27     my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
28    
29 wakaba 1.42 my $Namespace = {
30 wakaba 1.43 q<http://www.w3.org/2005/Atom> => {module => 'Whatpm::ContentChecker::Atom'},
31 wakaba 1.72 q<http://purl.org/syndication/history/1.0>
32     => {module => 'Whatpm::ContentChecker::Atom'},
33     q<http://purl.org/syndication/threading/1.0>
34     => {module => 'Whatpm::ContentChecker::Atom'},
35 wakaba 1.42 $HTML_NS => {module => 'Whatpm::ContentChecker::HTML'},
36     $XML_NS => {loaded => 1},
37     $XMLNS_NS => {loaded => 1},
38     };
39    
40     our $AttrChecker = {
41 wakaba 1.9 $XML_NS => {
42 wakaba 1.13 space => sub {
43     my ($self, $attr) = @_;
44     my $value = $attr->value;
45     if ($value eq 'default' or $value eq 'preserve') {
46     #
47     } else {
48     ## NOTE: An XML "error"
49 wakaba 1.33 $self->{onerror}->(node => $attr, level => 'error',
50     type => 'invalid attribute value');
51 wakaba 1.13 }
52     },
53     lang => sub {
54 wakaba 1.35 my ($self, $attr) = @_;
55 wakaba 1.47 my $value = $attr->value;
56     if ($value eq '') {
57     #
58     } else {
59     require Whatpm::LangTag;
60     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
61     my %opt = @_;
62     my $type = 'LangTag:'.$opt{type};
63     $type .= ':' . $opt{subtag} if defined $opt{subtag};
64     $self->{onerror}->(node => $attr, type => $type,
65     value => $opt{value}, level => $opt{level});
66     });
67     }
68    
69 wakaba 1.13 ## NOTE: "The values of the attribute are language identifiers
70     ## as defined by [IETF RFC 3066], Tags for the Identification
71     ## of Languages, or its successor; in addition, the empty string
72     ## may be specified." ("may" in lower case)
73 wakaba 1.47 ## NOTE: Is an RFC 3066-valid (but RFC 4647-invalid) language tag
74     ## allowed today?
75    
76     ## TODO: test data
77    
78 wakaba 1.35 if ($attr->owner_document->manakai_is_html) { # MUST NOT
79 wakaba 1.36 $self->{onerror}->(node => $attr, type => 'in HTML:xml:lang');
80 wakaba 1.35 ## TODO: Test data...
81     }
82 wakaba 1.13 },
83     base => sub {
84     my ($self, $attr) = @_;
85     my $value = $attr->value;
86     if ($value =~ /[^\x{0000}-\x{10FFFF}]/) { ## ISSUE: Should we disallow noncharacters?
87     $self->{onerror}->(node => $attr,
88 wakaba 1.33 type => 'invalid attribute value');
89 wakaba 1.13 }
90 wakaba 1.18 ## NOTE: Conformance to URI standard is not checked since there is
91     ## no author requirement on conformance in the XML Base specification.
92 wakaba 1.13 },
93     id => sub {
94     my ($self, $attr) = @_;
95     my $value = $attr->value;
96     $value =~ s/[\x09\x0A\x0D\x20]+/ /g;
97     $value =~ s/^\x20//;
98     $value =~ s/\x20$//;
99     ## TODO: NCName in XML 1.0 or 1.1
100     ## TODO: declared type is ID?
101 wakaba 1.33 if ($self->{id}->{$value}) { ## NOTE: An xml:id error
102     $self->{onerror}->(node => $attr, level => 'error',
103     type => 'duplicate ID');
104 wakaba 1.37 push @{$self->{id}->{$value}}, $attr;
105 wakaba 1.13 } else {
106 wakaba 1.37 $self->{id}->{$value} = [$attr];
107 wakaba 1.13 }
108     },
109 wakaba 1.9 },
110     $XMLNS_NS => {
111 wakaba 1.13 '' => sub {
112     my ($self, $attr) = @_;
113     my $ln = $attr->manakai_local_name;
114     my $value = $attr->value;
115     if ($value eq $XML_NS and $ln ne 'xml') {
116     $self->{onerror}
117 wakaba 1.33 ->(node => $attr, level => 'NC',
118     type => 'Reserved Prefixes and Namespace Names:=xml');
119 wakaba 1.13 } elsif ($value eq $XMLNS_NS) {
120     $self->{onerror}
121 wakaba 1.33 ->(node => $attr, level => 'NC',
122     type => 'Reserved Prefixes and Namespace Names:=xmlns');
123 wakaba 1.13 }
124     if ($ln eq 'xml' and $value ne $XML_NS) {
125     $self->{onerror}
126 wakaba 1.33 ->(node => $attr, level => 'NC',
127     type => 'Reserved Prefixes and Namespace Names:xmlns:xml=');
128 wakaba 1.13 } elsif ($ln eq 'xmlns') {
129     $self->{onerror}
130 wakaba 1.33 ->(node => $attr, level => 'NC',
131     type => 'Reserved Prefixes and Namespace Names:xmlns:xmlns=');
132 wakaba 1.13 }
133     ## TODO: If XML 1.0 and empty
134     },
135     xmlns => sub {
136     my ($self, $attr) = @_;
137     ## TODO: In XML 1.0, URI reference [RFC 3986] or an empty string
138     ## TODO: In XML 1.1, IRI reference [RFC 3987] or an empty string
139 wakaba 1.18 ## TODO: relative references are deprecated
140 wakaba 1.13 my $value = $attr->value;
141     if ($value eq $XML_NS) {
142     $self->{onerror}
143 wakaba 1.33 ->(node => $attr, level => 'NC',
144     type => 'Reserved Prefixes and Namespace Names:=xml');
145 wakaba 1.13 } elsif ($value eq $XMLNS_NS) {
146     $self->{onerror}
147 wakaba 1.33 ->(node => $attr, level => 'NC',
148     type => 'Reserved Prefixes and Namespace Names:=xmlns');
149 wakaba 1.13 }
150     },
151 wakaba 1.9 },
152     };
153    
154 wakaba 1.14 ## ISSUE: Should we really allow these attributes?
155 wakaba 1.13 $AttrChecker->{''}->{'xml:space'} = $AttrChecker->{$XML_NS}->{space};
156     $AttrChecker->{''}->{'xml:lang'} = $AttrChecker->{$XML_NS}->{lang};
157     $AttrChecker->{''}->{'xml:base'} = $AttrChecker->{$XML_NS}->{base};
158     $AttrChecker->{''}->{'xml:id'} = $AttrChecker->{$XML_NS}->{id};
159    
160 wakaba 1.60 our %AnyChecker = (
161     check_start => sub { },
162     check_attrs => sub {
163     my ($self, $item, $element_state) = @_;
164     for my $attr (@{$item->{node}->attributes}) {
165 wakaba 1.9 my $attr_ns = $attr->namespace_uri;
166     $attr_ns = '' unless defined $attr_ns;
167     my $attr_ln = $attr->manakai_local_name;
168     my $checker = $AttrChecker->{$attr_ns}->{$attr_ln}
169 wakaba 1.60 || $AttrChecker->{$attr_ns}->{''};
170 wakaba 1.9 if ($checker) {
171     $checker->($self, $attr);
172 wakaba 1.17 } else {
173 wakaba 1.33 $self->{onerror}->(node => $attr, level => 'unsupported',
174     type => 'attribute');
175 wakaba 1.9 }
176     }
177     },
178 wakaba 1.60 check_child_element => sub {
179     my ($self, $item, $child_el, $child_nsuri, $child_ln,
180     $child_is_transparent, $element_state) = @_;
181     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
182     $self->{onerror}->(node => $child_el,
183     type => 'element not allowed:minus',
184     level => $self->{must_level});
185     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
186     #
187     } else {
188     #
189     }
190     },
191     check_child_text => sub { },
192     check_end => sub {
193     my ($self, $item, $element_state) = @_;
194     if ($element_state->{has_significant}) {
195 wakaba 1.66 $item->{real_parent_state}->{has_significant} = 1;
196 wakaba 1.60 }
197     },
198     );
199    
200     our $ElementDefault = {
201     %AnyChecker,
202 wakaba 1.70 status => FEATURE_ALLOWED,
203     ## NOTE: No "element not defined" error - it is not supported anyway.
204 wakaba 1.60 check_start => sub {
205     my ($self, $item, $element_state) = @_;
206     $self->{onerror}->(node => $item->{node}, level => 'unsupported',
207     type => 'element');
208     },
209 wakaba 1.1 };
210    
211 wakaba 1.60 our $HTMLEmbeddedContent = {
212     ## NOTE: All embedded content is also phrasing content.
213     $HTML_NS => {
214     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
215     canvas => 1,
216     },
217     ## NOTE: MathML is mentioned in the HTML5 spec.
218     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
219     ## NOTE: SVG is mentioned in the HTML5 spec.
220     q<http://www.w3.org/2000/svg> => {svg => 1},
221     ## NOTE: Foreign elements with content (but no metadata) are
222     ## embedded content.
223     };
224    
225 wakaba 1.7 my $HTMLTransparentElements = {
226 wakaba 1.57 $HTML_NS => {qw/ins 1 del 1 font 1 noscript 1 canvas 1/},
227 wakaba 1.29 ## NOTE: |html:noscript| is transparent if scripting is disabled
228     ## and not in |head|.
229 wakaba 1.7 };
230    
231 wakaba 1.61 my $HTMLSemiTransparentElements = {
232     $HTML_NS => {object => 1, video => 1, audio => 1},
233     };
234 wakaba 1.57
235 wakaba 1.42 our $Element = {};
236 wakaba 1.7
237 wakaba 1.56 sub check_document ($$$;$) {
238     my ($self, $doc, $onerror, $onsubdoc) = @_;
239 wakaba 1.42 $self = bless {}, $self unless ref $self;
240     $self->{onerror} = $onerror;
241 wakaba 1.56 $self->{onsubdoc} = $onsubdoc || sub {
242     warn "A subdocument is not conformance-checked";
243     };
244 wakaba 1.1
245 wakaba 1.48 $self->{must_level} = 'm';
246     $self->{fact_level} = 'f';
247     $self->{should_level} = 's';
248 wakaba 1.51 $self->{good_level} = 'w';
249 wakaba 1.67 $self->{info_level} = 'i';
250 wakaba 1.71 $self->{unsupported_level} = 'u';
251 wakaba 1.48
252 wakaba 1.42 my $docel = $doc->document_element;
253     unless (defined $docel) {
254     ## ISSUE: Should we check content of Document node?
255     $onerror->(node => $doc, type => 'no document element');
256     ## ISSUE: Is this non-conforming (to what spec)? Or just a warning?
257     return {
258     class => {},
259     id => {}, table => [], term => {},
260     };
261 wakaba 1.1 }
262    
263 wakaba 1.42 ## ISSUE: Unexpanded entity references and HTML5 conformance
264 wakaba 1.1
265 wakaba 1.42 my $docel_nsuri = $docel->namespace_uri;
266     $docel_nsuri = '' unless defined $docel_nsuri;
267 wakaba 1.43 unless ($Namespace->{$docel_nsuri}->{loaded}) {
268     if ($Namespace->{$docel_nsuri}->{module}) {
269     eval qq{ require $Namespace->{$docel_nsuri}->{module} } or die $@;
270     } else {
271     $Namespace->{$docel_nsuri}->{loaded} = 1;
272     }
273     }
274 wakaba 1.42 my $docel_def = $Element->{$docel_nsuri}->{$docel->manakai_local_name} ||
275     $Element->{$docel_nsuri}->{''} ||
276     $ElementDefault;
277     if ($docel_def->{is_root}) {
278     #
279 wakaba 1.50 } elsif ($docel_def->{is_xml_root}) {
280     unless ($doc->manakai_is_html) {
281     #
282     } else {
283     $onerror->(node => $docel, type => 'element not allowed:root:xml');
284     }
285 wakaba 1.42 } else {
286 wakaba 1.49 $onerror->(node => $docel, type => 'element not allowed:root');
287 wakaba 1.1 }
288    
289 wakaba 1.42 ## TODO: Check for other items other than document element
290     ## (second (errorous) element, text nodes, PI nodes, doctype nodes)
291 wakaba 1.2
292 wakaba 1.56 my $return = $self->check_element ($docel, $onerror, $onsubdoc);
293 wakaba 1.51
294 wakaba 1.52 ## TODO: Test for these checks are necessary.
295 wakaba 1.51 my $charset_name = $doc->input_encoding;
296     if (defined $charset_name) {
297     require Message::Charset::Info;
298     my $charset = $Message::Charset::Info::IANACharset->{$charset_name};
299    
300 wakaba 1.71 if ($doc->manakai_is_html) {
301     if (not $doc->manakai_has_bom and
302     not defined $doc->manakai_charset) {
303     unless ($charset->{is_html_ascii_superset}) {
304     $onerror->(node => $doc, level => $self->{must_level},
305     type => 'non ascii superset:'.$charset_name);
306     }
307    
308     if (not $self->{has_charset} and ## TODO: This does not work now.
309     not $charset->{iana_names}->{'us-ascii'}) {
310     $onerror->(node => $doc, level => $self->{must_level},
311     type => 'no character encoding declaration:'.$charset_name);
312     }
313 wakaba 1.51 }
314 wakaba 1.71
315     if ($charset->{iana_names}->{'utf-8'}) {
316     #
317     } elsif ($charset->{iana_names}->{'jis_x0212-1990'} or
318     $charset->{iana_names}->{'x-jis0208'} or
319     $charset->{iana_names}->{'utf-32'} or ## ISSUE: UTF-32BE? UTF-32LE?
320     $charset->{is_ebcdic_based}) {
321     $onerror->(node => $doc,
322     type => 'character encoding:'.$charset_name,
323     level => $self->{should_level});
324     } elsif ($charset->{iana_names}->{'cesu-8'} or
325     $charset->{iana_names}->{'utf-8'} or ## ISSUE: UNICODE-1-1-UTF-7?
326     $charset->{iana_names}->{'bocu-1'} or
327     $charset->{iana_names}->{'scsu'}) {
328     $onerror->(node => $doc,
329     type => 'character encoding:'.$charset_name,
330     level => $self->{must_level});
331     } else {
332     $onerror->(node => $doc,
333     type => 'character encoding:'.$charset_name,
334     level => $self->{good_level});
335 wakaba 1.51 }
336     }
337 wakaba 1.52 } elsif ($doc->manakai_is_html) {
338     ## NOTE: MUST and SHOULD requirements above cannot be tested,
339     ## since the document has no input charset encoding information.
340     $onerror->(node => $doc,
341     type => 'character encoding:',
342     level => 'unsupported');
343 wakaba 1.51 }
344    
345     return $return;
346 wakaba 1.42 } # check_document
347 wakaba 1.1
348 wakaba 1.56 sub check_element ($$$;$) {
349     my ($self, $el, $onerror, $onsubdoc) = @_;
350 wakaba 1.42 $self = bless {}, $self unless ref $self;
351     $self->{onerror} = $onerror;
352 wakaba 1.56 $self->{onsubdoc} = $onsubdoc || sub {
353     warn "A subdocument is not conformance-checked";
354     };
355 wakaba 1.2
356 wakaba 1.48 $self->{must_level} = 'm';
357     $self->{fact_level} = 'f';
358     $self->{should_level} = 's';
359 wakaba 1.51 $self->{good_level} = 'w';
360 wakaba 1.67 $self->{info_level} = 'i';
361 wakaba 1.71 $self->{unsupported_level} = 'u';
362 wakaba 1.48
363 wakaba 1.61 $self->{plus_elements} = {};
364     $self->{minus_elements} = {};
365 wakaba 1.42 $self->{id} = {};
366     $self->{term} = {};
367     $self->{usemap} = [];
368     $self->{contextmenu} = [];
369     $self->{map} = {};
370     $self->{menu} = {};
371     $self->{has_link_type} = {};
372 wakaba 1.60 $self->{flag} = {};
373 wakaba 1.46 #$self->{has_uri_attr};
374     #$self->{has_hyperlink_element};
375 wakaba 1.51 #$self->{has_charset};
376 wakaba 1.57 #$self->{has_base};
377 wakaba 1.42 $self->{return} = {
378     class => {},
379     id => $self->{id}, table => [], term => $self->{term},
380     };
381 wakaba 1.4
382 wakaba 1.60 my @item = ({type => 'element', node => $el, parent_state => {}});
383 wakaba 1.66 $item[-1]->{real_parent_state} = $item[-1]->{parent_state};
384 wakaba 1.60 while (@item) {
385     my $item = shift @item;
386     if (ref $item eq 'ARRAY') {
387     my $code = shift @$item;
388     next unless $code;## TODO: temp.
389     $code->(@$item);
390     } elsif ($item->{type} eq 'element') {
391     my $el_nsuri = $item->{node}->namespace_uri;
392     $el_nsuri = '' unless defined $el_nsuri;
393     my $el_ln = $item->{node}->manakai_local_name;
394    
395     unless ($Namespace->{$el_nsuri}->{loaded}) {
396     if ($Namespace->{$el_nsuri}->{module}) {
397     eval qq{ require $Namespace->{$el_nsuri}->{module} } or die $@;
398 wakaba 1.42 } else {
399 wakaba 1.60 $Namespace->{$el_nsuri}->{loaded} = 1;
400 wakaba 1.1 }
401     }
402 wakaba 1.63
403     my $element_state = {};
404 wakaba 1.60 my $eldef = $Element->{$el_nsuri}->{$el_ln} ||
405     $Element->{$el_nsuri}->{''} ||
406 wakaba 1.42 $ElementDefault;
407 wakaba 1.61 my $content_def = $item->{transparent}
408     ? $item->{parent_def} || $eldef : $eldef;
409 wakaba 1.63 my $content_state = $item->{transparent}
410 wakaba 1.65 ? $item->{parent_def}
411     ? $item->{parent_state} || $element_state : $element_state
412     : $element_state;
413 wakaba 1.60
414 wakaba 1.67 unless ($eldef->{status} & FEATURE_STATUS_REC) {
415     my $status = $eldef->{status} & FEATURE_STATUS_CR ? 'cr' :
416     $eldef->{status} & FEATURE_STATUS_LC ? 'lc' :
417     $eldef->{status} & FEATURE_STATUS_WD ? 'wd' : 'non-standard';
418     $self->{onerror}->(node => $item->{node},
419     type => 'status:'.$status.':element',
420     level => $self->{info_level});
421     }
422 wakaba 1.70 if (not ($eldef->{status} & FEATURE_ALLOWED)) {
423     $self->{onerror}->(node => $item->{node},
424     type => 'element not defined',
425     level => $self->{must_level});
426     } elsif ($eldef->{status} & FEATURE_DEPRECATED_SHOULD) {
427     $self->{onerror}->(node => $item->{node},
428     type => 'deprecated:element',
429     level => $self->{should_level});
430     } elsif ($eldef->{status} & FEATURE_DEPRECATED_INFO) {
431     $self->{onerror}->(node => $item->{node},
432     type => 'deprecated:element',
433     level => $self->{info_level});
434     }
435 wakaba 1.67
436 wakaba 1.60 my @new_item;
437     push @new_item, [$eldef->{check_start}, $self, $item, $element_state];
438     push @new_item, [$eldef->{check_attrs}, $self, $item, $element_state];
439 wakaba 1.61
440 wakaba 1.60 my @child = @{$item->{node}->child_nodes};
441     while (@child) {
442     my $child = shift @child;
443     my $child_nt = $child->node_type;
444     if ($child_nt == 1) { # ELEMENT_NODE
445     my $child_nsuri = $child->namespace_uri;
446     $child_nsuri = '' unless defined $child_nsuri;
447     my $child_ln = $child->manakai_local_name;
448     if ($HTMLTransparentElements->{$child_nsuri}->{$child_ln} and
449     not (($self->{flag}->{in_head} or
450 wakaba 1.61 ($el_nsuri eq $HTML_NS and $el_ln eq 'head')) and
451     $child_nsuri eq $HTML_NS and $child_ln eq 'noscript')) {
452 wakaba 1.60 push @new_item, [$content_def->{check_child_element},
453     $self, $item, $child,
454 wakaba 1.66 $child_nsuri, $child_ln, 1,
455     $content_state, $element_state];
456 wakaba 1.60 push @new_item, {type => 'element', node => $child,
457 wakaba 1.65 parent_state => $content_state,
458 wakaba 1.61 parent_def => $content_def,
459 wakaba 1.66 real_parent_state => $element_state,
460 wakaba 1.60 transparent => 1};
461     } else {
462 wakaba 1.65 if ($item->{parent_def} and # has parent
463     $el_nsuri eq $HTML_NS) { ## $HTMLSemiTransparentElements
464 wakaba 1.61 if ($el_ln eq 'object') {
465     if ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
466     #
467     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
468     #
469     } else {
470 wakaba 1.62 $content_def = $item->{parent_def} || $content_def;
471 wakaba 1.63 $content_state = $item->{parent_state} || $content_state;
472 wakaba 1.62 }
473     } elsif ($el_ln eq 'video' or $el_ln eq 'audio') {
474     if ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
475     #
476     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
477     $element_state->{has_source} = 1;
478     } else {
479     $content_def = $item->{parent_def} || $content_def;
480 wakaba 1.63 $content_state = $item->{parent_state} || $content_state;
481 wakaba 1.61 }
482     }
483     }
484    
485 wakaba 1.60 push @new_item, [$content_def->{check_child_element},
486     $self, $item, $child,
487 wakaba 1.64 $child_nsuri, $child_ln,
488     $HTMLSemiTransparentElements
489     ->{$child_nsuri}->{$child_ln},
490 wakaba 1.66 $content_state, $element_state];
491 wakaba 1.60 push @new_item, {type => 'element', node => $child,
492 wakaba 1.65 parent_def => $content_def,
493 wakaba 1.66 real_parent_state => $element_state,
494 wakaba 1.65 parent_state => $content_state};
495 wakaba 1.60 }
496    
497     if ($HTMLEmbeddedContent->{$child_nsuri}->{$child_ln}) {
498     $element_state->{has_significant} = 1;
499     }
500     } elsif ($child_nt == 3 or # TEXT_NODE
501     $child_nt == 4) { # CDATA_SECTION_NODE
502     my $has_significant = ($child->data =~ /[^\x09-\x0D\x20]/);
503     push @new_item, [$content_def->{check_child_text},
504     $self, $item, $child, $has_significant,
505 wakaba 1.66 $content_state, $element_state];
506     $element_state->{has_significant} ||= $has_significant;
507 wakaba 1.61 if ($has_significant and
508     $HTMLSemiTransparentElements->{$el_nsuri}->{$el_ln}) {
509     $content_def = $item->{parent_def} || $content_def;
510     }
511 wakaba 1.60 } elsif ($child_nt == 5) { # ENTITY_REFERENCE_NODE
512     push @child, @{$child->child_nodes};
513 wakaba 1.1 }
514 wakaba 1.60 ## TODO: PI_NODE
515     ## TODO: Unknown node type
516 wakaba 1.1 }
517 wakaba 1.60
518     push @new_item, [$eldef->{check_end}, $self, $item, $element_state];
519    
520     unshift @item, @new_item;
521 wakaba 1.30 } else {
522 wakaba 1.60 die "$0: Internal error: Unsupported checking action type |$item->{type}|";
523 wakaba 1.4 }
524 wakaba 1.1 }
525 wakaba 1.17
526     for (@{$self->{usemap}}) {
527     unless ($self->{map}->{$_->[0]}) {
528     $self->{onerror}->(node => $_->[1], type => 'no referenced map');
529     }
530     }
531    
532 wakaba 1.32 for (@{$self->{contextmenu}}) {
533     unless ($self->{menu}->{$_->[0]}) {
534     $self->{onerror}->(node => $_->[1], type => 'no referenced menu');
535     }
536     }
537    
538 wakaba 1.61 delete $self->{plus_elements};
539     delete $self->{minus_elements};
540 wakaba 1.17 delete $self->{onerror};
541     delete $self->{id};
542     delete $self->{usemap};
543     delete $self->{map};
544 wakaba 1.33 return $self->{return};
545 wakaba 1.1 } # check_element
546    
547 wakaba 1.60 sub _add_minus_elements ($$@) {
548     my $self = shift;
549     my $element_state = shift;
550     for my $elements (@_) {
551     for my $nsuri (keys %$elements) {
552     for my $ln (keys %{$elements->{$nsuri}}) {
553     unless ($self->{minus_elements}->{$nsuri}->{$ln}) {
554     $element_state->{minus_elements_original}->{$nsuri}->{$ln} = 0;
555     $self->{minus_elements}->{$nsuri}->{$ln} = 1;
556     }
557     }
558     }
559     }
560     } # _add_minus_elements
561    
562     sub _remove_minus_elements ($$) {
563     my $self = shift;
564     my $element_state = shift;
565     for my $nsuri (keys %{$element_state->{minus_elements_original}}) {
566     for my $ln (keys %{$element_state->{minus_elements_original}->{$nsuri}}) {
567     delete $self->{minus_elements}->{$nsuri}->{$ln};
568     }
569     }
570     } # _remove_minus_elements
571    
572     sub _add_plus_elements ($$@) {
573     my $self = shift;
574     my $element_state = shift;
575     for my $elements (@_) {
576     for my $nsuri (keys %$elements) {
577     for my $ln (keys %{$elements->{$nsuri}}) {
578     unless ($self->{plus_elements}->{$nsuri}->{$ln}) {
579     $element_state->{plus_elements_original}->{$nsuri}->{$ln} = 0;
580     $self->{plus_elements}->{$nsuri}->{$ln} = 1;
581     }
582     }
583     }
584     }
585     } # _add_plus_elements
586    
587     sub _remove_plus_elements ($$) {
588     my $self = shift;
589     my $element_state = shift;
590     for my $nsuri (keys %{$element_state->{plus_elements_original}}) {
591     for my $ln (keys %{$element_state->{plus_elements_original}->{$nsuri}}) {
592     delete $self->{plus_elements}->{$nsuri}->{$ln};
593     }
594     }
595     } # _remove_plus_elements
596    
597 wakaba 1.68 sub _attr_status_info ($$$) {
598     my ($self, $attr, $status_code) = @_;
599 wakaba 1.70
600     if (not ($status_code & FEATURE_ALLOWED)) {
601     $self->{onerror}->(node => $attr,
602     type => 'attribute not defined',
603     level => $self->{must_level});
604     } elsif ($status_code & FEATURE_DEPRECATED_SHOULD) {
605     $self->{onerror}->(node => $attr,
606     type => 'deprecated:attr',
607     level => $self->{should_level});
608     } elsif ($status_code & FEATURE_DEPRECATED_INFO) {
609     $self->{onerror}->(node => $attr,
610     type => 'deprecated:attr',
611     level => $self->{info_level});
612     }
613    
614 wakaba 1.68 my $status;
615     if ($status_code & FEATURE_STATUS_REC) {
616     return;
617     } elsif ($status_code & FEATURE_STATUS_CR) {
618     $status = 'cr';
619     } elsif ($status_code & FEATURE_STATUS_LC) {
620     $status = 'lc';
621     } elsif ($status_code & FEATURE_STATUS_WD) {
622     $status = 'wd';
623     } else {
624     $status = 'non-standard';
625     }
626     $self->{onerror}->(node => $attr,
627     type => 'status:'.$status.':attr',
628     level => $self->{info_level});
629     } # _attr_status_info
630    
631 wakaba 1.2 sub _add_minuses ($@) {
632     my $self = shift;
633     my $r = {};
634     for my $list (@_) {
635     for my $ns (keys %$list) {
636     for my $ln (keys %{$list->{$ns}}) {
637     unless ($self->{minuses}->{$ns}->{$ln}) {
638     $self->{minuses}->{$ns}->{$ln} = 1;
639     $r->{$ns}->{$ln} = 1;
640     }
641     }
642     }
643     }
644 wakaba 1.4 return {type => 'plus', list => $r};
645 wakaba 1.2 } # _add_minuses
646    
647 wakaba 1.50 sub _add_pluses ($@) {
648     my $self = shift;
649     my $r = {};
650     for my $list (@_) {
651     for my $ns (keys %$list) {
652     for my $ln (keys %{$list->{$ns}}) {
653     unless ($self->{pluses}->{$ns}->{$ln}) {
654     $self->{pluses}->{$ns}->{$ln} = 1;
655     $r->{$ns}->{$ln} = 1;
656     }
657     }
658     }
659     }
660     return {type => 'minus', list => $r};
661     } # _add_pluses
662    
663 wakaba 1.2 sub _remove_minuses ($$) {
664 wakaba 1.4 my ($self, $todo) = @_;
665 wakaba 1.50 if ($todo->{type} eq 'minus') {
666     for my $ns (keys %{$todo->{list}}) {
667     for my $ln (keys %{$todo->{list}->{$ns}}) {
668     delete $self->{pluses}->{$ns}->{$ln} if $todo->{list}->{$ns}->{$ln};
669     }
670 wakaba 1.2 }
671 wakaba 1.50 } elsif ($todo->{type} eq 'plus') {
672     for my $ns (keys %{$todo->{list}}) {
673     for my $ln (keys %{$todo->{list}->{$ns}}) {
674     delete $self->{minuses}->{$ns}->{$ln} if $todo->{list}->{$ns}->{$ln};
675     }
676     }
677     } else {
678     die "$0: Unknown +- type: $todo->{type}";
679 wakaba 1.2 }
680     1;
681     } # _remove_minuses
682    
683 wakaba 1.50 ## NOTE: Priority for "minuses" and "pluses" are currently left
684     ## undefined and implemented inconsistently; it is not a problem for
685     ## now, since no element belongs to both lists.
686    
687 wakaba 1.30 sub _check_get_children ($$$) {
688     my ($self, $node, $parent_todo) = @_;
689 wakaba 1.4 my $new_todos = [];
690 wakaba 1.2 my $sib = [];
691     TP: {
692     my $node_ns = $node->namespace_uri;
693     $node_ns = '' unless defined $node_ns;
694     my $node_ln = $node->manakai_local_name;
695 wakaba 1.45 if ($HTMLTransparentElements->{$node_ns}->{$node_ln}) {
696     if ($node_ns eq $HTML_NS and $node_ln eq 'noscript') {
697     if ($parent_todo->{flag}->{in_head}) {
698     #
699     } else {
700     my $end = $self->_add_minuses ({$HTML_NS, {noscript => 1}});
701     push @$sib, $end;
702    
703     unshift @$sib, @{$node->child_nodes};
704     push @$new_todos, {type => 'element-attributes', node => $node};
705     last TP;
706     }
707 wakaba 1.58 } elsif ($node_ns eq $HTML_NS and $node_ln eq 'del') {
708     my $sig_flag = $parent_todo->{flag}->{has_descendant}->{significant};
709     unshift @$sib, @{$node->child_nodes};
710     push @$new_todos, {type => 'element-attributes', node => $node};
711     push @$new_todos,
712     {type => 'code',
713     code => sub {
714     $parent_todo->{flag}->{has_descendant}->{significant} = 0
715     if not $sig_flag;
716     }};
717     last TP;
718 wakaba 1.45 } else {
719     unshift @$sib, @{$node->child_nodes};
720     push @$new_todos, {type => 'element-attributes', node => $node};
721     last TP;
722 wakaba 1.2 }
723     }
724 wakaba 1.8 if ($node_ns eq $HTML_NS and ($node_ln eq 'video' or $node_ln eq 'audio')) {
725 wakaba 1.2 if ($node->has_attribute_ns (undef, 'src')) {
726     unshift @$sib, @{$node->child_nodes};
727 wakaba 1.9 push @$new_todos, {type => 'element-attributes', node => $node};
728 wakaba 1.2 last TP;
729     } else {
730     my @cn = @{$node->child_nodes};
731     CN: while (@cn) {
732     my $cn = shift @cn;
733     my $cnt = $cn->node_type;
734     if ($cnt == 1) {
735 wakaba 1.8 my $cn_nsuri = $cn->namespace_uri;
736     $cn_nsuri = '' unless defined $cn_nsuri;
737     if ($cn_nsuri eq $HTML_NS and $cn->manakai_local_name eq 'source') {
738 wakaba 1.2 #
739     } else {
740     last CN;
741     }
742     } elsif ($cnt == 3 or $cnt == 4) {
743     if ($cn->data =~ /[^\x09-\x0D\x20]/) {
744     last CN;
745     }
746     }
747     } # CN
748     unshift @$sib, @cn;
749     }
750 wakaba 1.57 } elsif ($node_ns eq $HTML_NS and $node_ln eq 'object') {
751     my @cn = @{$node->child_nodes};
752     CN: while (@cn) {
753     my $cn = shift @cn;
754     my $cnt = $cn->node_type;
755     if ($cnt == 1) {
756     my $cn_nsuri = $cn->namespace_uri;
757     $cn_nsuri = '' unless defined $cn_nsuri;
758     if ($cn_nsuri eq $HTML_NS and $cn->manakai_local_name eq 'param') {
759     #
760     } else {
761     last CN;
762     }
763     } elsif ($cnt == 3 or $cnt == 4) {
764     if ($cn->data =~ /[^\x09-\x0D\x20]/) {
765     last CN;
766     }
767     }
768     } # CN
769     unshift @$sib, @cn;
770 wakaba 1.2 }
771 wakaba 1.4 push @$new_todos, {type => 'element', node => $node};
772 wakaba 1.2 } # TP
773 wakaba 1.30
774     for my $new_todo (@$new_todos) {
775     $new_todo->{flag} = {%{$parent_todo->{flag} or {}}};
776     }
777    
778 wakaba 1.4 return ($sib, $new_todos);
779 wakaba 1.2 } # _check_get_children
780    
781 wakaba 1.44 =head1 LICENSE
782    
783 wakaba 1.56 Copyright 2007-2008 Wakaba <[email protected]>
784 wakaba 1.44
785     This library is free software; you can redistribute it
786     and/or modify it under the same terms as Perl itself.
787    
788     =cut
789    
790 wakaba 1.1 1;
791 wakaba 1.72 # $Date: 2008/03/02 11:16:34 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24