/[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.71 - (hide annotations) (download)
Sun Mar 2 11:16:34 2008 UTC (18 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.70: +38 -37 lines
++ whatpm/t/ChangeLog	2 Mar 2008 11:10:02 -0000
	* content-model-atom-1.dat: New test data are added.

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

++ whatpm/Whatpm/ChangeLog	2 Mar 2008 11:16:26 -0000
	* ContentChecker.pm: Typo fixed.  Don't raise "character encoding"
	and related errors unless it is an HTML document (though the spec
	is unclear on whether it is applied to XHTML document).

	* HTML.pm (%HTMLAttrStatus): WF2 repetition model attributes
	are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	2 Mar 2008 11:11:31 -0000
	* Atom.pm: Reimplemented.

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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24