/[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.86 - (show annotations) (download)
Sat Aug 16 07:35:23 2008 UTC (18 years ago) by wakaba
Branch: MAIN
Changes since 1.85: +6 -4 lines
++ whatpm/Whatpm/ChangeLog	16 Aug 2008 07:34:18 -0000
	* CacheManifest.pm: Support for new style of error
	reports.

	* HTML.pm.src: Set line=1, column=1 to the document node.

2008-08-16  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/CSS/ChangeLog	16 Aug 2008 07:34:47 -0000
2008-08-16  Wakaba  <wakaba@suika.fam.cx>

	* MediaQueryParser.pm, SelectorsParser.pm, Parser.pm: Support
	for new style of error reports.

1 package Whatpm::ContentChecker;
2 use strict;
3 our $VERSION=do{my @r=(q$Revision: 1.85 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4
5 require Whatpm::URIChecker;
6
7 ## ISSUE: How XML and XML Namespaces conformance can (or cannot)
8 ## be applied to an in-memory representation (i.e. DOM)?
9
10 ## TODO: Conformance of an HTML document with non-html root element.
11
12 ## Stability
13 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 ## 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 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
26 my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
27 my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
28
29 my $Namespace = {
30 '' => {loaded => 1},
31 q<http://www.w3.org/2005/Atom> => {module => 'Whatpm::ContentChecker::Atom'},
32 q<http://purl.org/syndication/history/1.0>
33 => {module => 'Whatpm::ContentChecker::Atom'},
34 q<http://purl.org/syndication/threading/1.0>
35 => {module => 'Whatpm::ContentChecker::Atom'},
36 $HTML_NS => {module => 'Whatpm::ContentChecker::HTML'},
37 $XML_NS => {loaded => 1},
38 $XMLNS_NS => {loaded => 1},
39 q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {loaded => 1},
40 };
41
42 sub load_ns_module ($) {
43 my $nsuri = shift; # namespace URI or ''
44 unless ($Namespace->{$nsuri}->{loaded}) {
45 if ($Namespace->{$nsuri}->{module}) {
46 eval qq{ require $Namespace->{$nsuri}->{module} } or die $@;
47 } else {
48 $Namespace->{$nsuri}->{loaded} = 1;
49 }
50 }
51 } # load_ns_module
52
53 our $AttrChecker = {
54 $XML_NS => {
55 space => sub {
56 my ($self, $attr) = @_;
57 my $value = $attr->value;
58 if ($value eq 'default' or $value eq 'preserve') {
59 #
60 } else {
61 ## NOTE: An XML "error"
62 $self->{onerror}->(node => $attr, level => $self->{level}->{xml_error},
63 type => 'invalid attribute value');
64 }
65 },
66 lang => sub {
67 my ($self, $attr) = @_;
68 my $value = $attr->value;
69 if ($value eq '') {
70 #
71 } else {
72 require Whatpm::LangTag;
73 Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
74 $self->{onerror}->(@_, node => $attr);
75 }, $self->{level});
76 }
77
78 ## NOTE: "The values of the attribute are language identifiers
79 ## as defined by [IETF RFC 3066], Tags for the Identification
80 ## of Languages, or its successor; in addition, the empty string
81 ## may be specified." ("may" in lower case)
82 ## NOTE: Is an RFC 3066-valid (but RFC 4647-invalid) language tag
83 ## allowed today?
84
85 ## TODO: test data
86
87 if ($attr->owner_document->manakai_is_html) { # MUST NOT
88 $self->{onerror}->(node => $attr, type => 'in HTML:xml:lang',
89 level => $self->{level}->{must});
90 ## TODO: Test data...
91 }
92 },
93 base => sub {
94 my ($self, $attr) = @_;
95 my $value = $attr->value;
96 if ($value =~ /[^\x{0000}-\x{10FFFF}]/) { ## ISSUE: Should we disallow noncharacters?
97 $self->{onerror}->(node => $attr,
98 type => 'invalid attribute value',
99 level => $self->{level}->{fact}, ## TODO: correct?
100 );
101 }
102 ## NOTE: Conformance to URI standard is not checked since there is
103 ## no author requirement on conformance in the XML Base specification.
104 },
105 id => sub {
106 my ($self, $attr) = @_;
107 my $value = $attr->value;
108 $value =~ s/[\x09\x0A\x0D\x20]+/ /g;
109 $value =~ s/^\x20//;
110 $value =~ s/\x20$//;
111 ## TODO: NCName in XML 1.0 or 1.1
112 ## TODO: declared type is ID?
113 if ($self->{id}->{$value}) {
114 $self->{onerror}->(node => $attr,
115 type => 'duplicate ID',
116 level => $self->{level}->{xml_id_error});
117 push @{$self->{id}->{$value}}, $attr;
118 } else {
119 $self->{id}->{$value} = [$attr];
120 }
121 },
122 },
123 $XMLNS_NS => {
124 '' => sub {
125 my ($self, $attr) = @_;
126 my $ln = $attr->manakai_local_name;
127 my $value = $attr->value;
128 if ($value eq $XML_NS and $ln ne 'xml') {
129 $self->{onerror}
130 ->(node => $attr,
131 type => 'Reserved Prefixes and Namespace Names:Name',
132 text => $value,
133 level => $self->{level}->{nc});
134 } elsif ($value eq $XMLNS_NS) {
135 $self->{onerror}
136 ->(node => $attr,
137 type => 'Reserved Prefixes and Namespace Names:Name',
138 text => $value,
139 level => $self->{level}->{nc});
140 }
141 if ($ln eq 'xml' and $value ne $XML_NS) {
142 $self->{onerror}
143 ->(node => $attr,
144 type => 'Reserved Prefixes and Namespace Names:Prefix',
145 text => $ln,
146 level => $self->{level}->{nc});
147 } elsif ($ln eq 'xmlns') {
148 $self->{onerror}
149 ->(node => $attr,
150 type => 'Reserved Prefixes and Namespace Names:Prefix',
151 text => $ln,
152 level => $self->{level}->{nc});
153 }
154 ## TODO: If XML 1.0 and empty
155 },
156 xmlns => sub {
157 my ($self, $attr) = @_;
158 ## TODO: In XML 1.0, URI reference [RFC 3986] or an empty string
159 ## TODO: In XML 1.1, IRI reference [RFC 3987] or an empty string
160 ## TODO: relative references are deprecated
161 my $value = $attr->value;
162 if ($value eq $XML_NS) {
163 $self->{onerror}
164 ->(node => $attr,
165 type => 'Reserved Prefixes and Namespace Names:Name',
166 text => $value,
167 level => $self->{level}->{nc});
168 } elsif ($value eq $XMLNS_NS) {
169 $self->{onerror}
170 ->(node => $attr,
171 type => 'Reserved Prefixes and Namespace Names:Name',
172 text => $value,
173 level => $self->{level}->{nc});
174 }
175 },
176 },
177 };
178
179 ## ISSUE: Should we really allow these attributes?
180 $AttrChecker->{''}->{'xml:space'} = $AttrChecker->{$XML_NS}->{space};
181 $AttrChecker->{''}->{'xml:lang'} = $AttrChecker->{$XML_NS}->{lang};
182 $AttrChecker->{''}->{'xml:base'} = $AttrChecker->{$XML_NS}->{base};
183 $AttrChecker->{''}->{'xml:id'} = $AttrChecker->{$XML_NS}->{id};
184
185 our $AttrStatus;
186
187 for (qw/space lang base id/) {
188 $AttrStatus->{$XML_NS}->{$_} = FEATURE_STATUS_REC | FEATURE_ALLOWED;
189 $AttrStatus->{''}->{"xml:$_"} = FEATURE_STATUS_REC | FEATURE_ALLOWED;
190 ## XML 1.0: FEATURE_STATUS_CR
191 ## XML 1.1: FEATURE_STATUS_REC
192 ## XML Namespaces 1.0: FEATURE_STATUS_CR
193 ## XML Namespaces 1.1: FEATURE_STATUS_REC
194 ## XML Base: FEATURE_STATUS_REC
195 ## xml:id: FEATURE_STATUS_REC
196 }
197
198 $AttrStatus->{$XMLNS_NS}->{''} = FEATURE_STATUS_REC | FEATURE_ALLOWED;
199
200 ## TODO: xsi:schemaLocation for XHTML2 support (very, very low priority)
201
202 our %AnyChecker = (
203 check_start => sub { },
204 check_attrs => sub {
205 my ($self, $item, $element_state) = @_;
206 for my $attr (@{$item->{node}->attributes}) {
207 my $attr_ns = $attr->namespace_uri;
208 $attr_ns = '' unless defined $attr_ns;
209 my $attr_ln = $attr->manakai_local_name;
210
211 load_ns_module ($attr_ns);
212
213 my $checker = $AttrChecker->{$attr_ns}->{$attr_ln}
214 || $AttrChecker->{$attr_ns}->{''};
215 my $status = $AttrStatus->{$attr_ns}->{$attr_ln}
216 || $AttrStatus->{$attr_ns}->{''};
217 if (not defined $status) {
218 $status = FEATURE_ALLOWED;
219 ## NOTE: FEATURE_ALLOWED for all attributes, since the element
220 ## is not supported and therefore "attribute not defined" error
221 ## should not raised (too verbose) and global attributes should be
222 ## allowed anyway (if a global attribute has its specified creteria
223 ## for where it may be specified, then it should be checked in it's
224 ## checker function).
225 }
226 if ($checker) {
227 $checker->($self, $attr);
228 } else {
229 $self->{onerror}->(node => $attr,
230 type => 'unknown attribute',
231 level => $self->{level}->{uncertain});
232 }
233 $self->_attr_status_info ($attr, $status);
234 }
235 },
236 check_child_element => sub {
237 my ($self, $item, $child_el, $child_nsuri, $child_ln,
238 $child_is_transparent, $element_state) = @_;
239 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
240 $self->{onerror}->(node => $child_el,
241 type => 'element not allowed:minus',
242 level => $self->{level}->{must});
243 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
244 #
245 } else {
246 #
247 }
248 },
249 check_child_text => sub { },
250 check_end => sub {
251 my ($self, $item, $element_state) = @_;
252 ## NOTE: There is a modified copy of the code below for |html:ruby|.
253 if ($element_state->{has_significant}) {
254 $item->{real_parent_state}->{has_significant} = 1;
255 }
256 },
257 );
258
259 our $ElementDefault = {
260 %AnyChecker,
261 status => FEATURE_ALLOWED,
262 ## NOTE: No "element not defined" error - it is not supported anyway.
263 check_start => sub {
264 my ($self, $item, $element_state) = @_;
265 $self->{onerror}->(node => $item->{node},
266 type => 'unknown element',
267 level => $self->{level}->{uncertain});
268 },
269 };
270
271 our $HTMLEmbeddedContent = {
272 ## NOTE: All embedded content is also phrasing content.
273 $HTML_NS => {
274 img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
275 canvas => 1,
276 },
277 q<http://www.w3.org/1998/Math/MathML> => {math => 1},
278 q<http://www.w3.org/2000/svg> => {svg => 1},
279 ## NOTE: Foreign elements with content (but no metadata) are
280 ## embedded content.
281 };
282
283 my $HTMLTransparentElements = {
284 $HTML_NS => {qw/ins 1 del 1 font 1 noscript 1 canvas 1/},
285 ## NOTE: |html:noscript| is transparent if scripting is disabled
286 ## and not in |head|.
287 };
288
289 my $HTMLSemiTransparentElements = {
290 $HTML_NS => {object => 1, video => 1, audio => 1},
291 };
292
293 our $Element = {};
294
295 $Element->{q<http://www.w3.org/1999/02/22-rdf-syntax-ns#>}->{RDF} = {
296 %AnyChecker,
297 status => FEATURE_STATUS_REC | FEATURE_ALLOWED,
298 is_root => 1, ## ISSUE: Not explicitly allowed for non application/rdf+xml
299 check_start => sub {
300 my ($self, $item, $element_state) = @_;
301 my $triple = [];
302 push @{$self->{return}->{rdf}}, [$item->{node}, $triple];
303 require Whatpm::RDFXML;
304 my $rdf = Whatpm::RDFXML->new;
305 ## TODO: Should we make bnodeid unique in a document?
306 $rdf->{onerror} = $self->{onerror};
307 $rdf->{level} = $self->{level};
308 $rdf->{ontriple} = sub {
309 my %opt = @_;
310 push @$triple,
311 [$opt{node}, $opt{subject}, $opt{predicate}, $opt{object}];
312 if (defined $opt{id}) {
313 push @$triple,
314 [$opt{node},
315 $opt{id},
316 {uri => q<http://www.w3.org/1999/02/22-rdf-syntax-ns#subject>},
317 $opt{subject}];
318 push @$triple,
319 [$opt{node},
320 $opt{id},
321 {uri => q<http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate>},
322 $opt{predicate}];
323 push @$triple,
324 [$opt{node},
325 $opt{id},
326 {uri => q<http://www.w3.org/1999/02/22-rdf-syntax-ns#object>},
327 $opt{object}];
328 push @$triple,
329 [$opt{node},
330 $opt{id},
331 {uri => q<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>},
332 {uri => q<http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement>}];
333 }
334 };
335 $rdf->convert_rdf_element ($item->{node});
336 },
337 };
338
339 my $default_error_level = {
340 must => 'm',
341 should => 's',
342 warn => 'w',
343 good => 'w',
344 info => 'i',
345
346 uncertain => 'u',
347
348 html4_fact => 'm',
349 xml_error => 'm', ## TODO: correct?
350 nc => 'm', ## XML Namespace Constraints ## TODO: correct?
351
352 uri_syntax => 'm',
353 uri_fact => 'm',
354 uri_lc_must => 'm',
355 uri_lc_should => 'w',
356
357 langtag_fact => 'm',
358
359 rdf_fact => 'm',
360 rdf_grammer => 'm',
361 rdf_lc_must => 'm',
362 };
363
364 sub check_document ($$$;$) {
365 my ($self, $doc, $onerror, $onsubdoc) = @_;
366 $self = bless {}, $self unless ref $self;
367 $self->{onerror} = $onerror;
368 $self->{onsubdoc} = $onsubdoc || sub {
369 warn "A subdocument is not conformance-checked";
370 };
371
372 $self->{level} ||= $default_error_level;
373
374 ## TODO: If application/rdf+xml, RDF/XML mode should be invoked.
375
376 my $docel = $doc->document_element;
377 unless (defined $docel) {
378 ## ISSUE: Should we check content of Document node?
379 $onerror->(node => $doc, type => 'no document element',
380 level => $self->{level}->{must});
381 ## ISSUE: Is this non-conforming (to what spec)? Or just a warning?
382 return {
383 class => {},
384 id => {}, table => [], term => {},
385 };
386 }
387
388 ## ISSUE: Unexpanded entity references and HTML5 conformance
389
390 my $docel_nsuri = $docel->namespace_uri;
391 $docel_nsuri = '' unless defined $docel_nsuri;
392 load_ns_module ($docel_nsuri);
393 my $docel_def = $Element->{$docel_nsuri}->{$docel->manakai_local_name} ||
394 $Element->{$docel_nsuri}->{''} ||
395 $ElementDefault;
396 if ($docel_def->{is_root}) {
397 #
398 } elsif ($docel_def->{is_xml_root}) {
399 unless ($doc->manakai_is_html) {
400 #
401 } else {
402 $onerror->(node => $docel, type => 'element not allowed:root:xml',
403 level => $self->{level}->{must});
404 }
405 } else {
406 $onerror->(node => $docel, type => 'element not allowed:root',
407 level => $self->{level}->{must});
408 }
409
410 ## TODO: Check for other items other than document element
411 ## (second (errorous) element, text nodes, PI nodes, doctype nodes)
412
413 my $return = $self->check_element ($docel, $onerror, $onsubdoc);
414
415 ## TODO: Test for these checks are necessary.
416 my $charset_name = $doc->input_encoding;
417 if (defined $charset_name) {
418 require Message::Charset::Info;
419 my $charset = $Message::Charset::Info::IANACharset->{$charset_name};
420
421 if ($doc->manakai_is_html) {
422 if (not $doc->manakai_has_bom and
423 not defined $doc->manakai_charset) {
424 unless ($charset->{is_html_ascii_superset}) {
425 $onerror->(node => $doc,
426 level => $self->{level}->{must},
427 type => 'non ascii superset',
428 text => $charset_name);
429 }
430
431 if (not $self->{has_charset} and ## TODO: This does not work now.
432 not $charset->{iana_names}->{'us-ascii'}) {
433 $onerror->(node => $doc,
434 level => $self->{level}->{must},
435 type => 'no character encoding declaration',
436 text => $charset_name);
437 }
438 }
439
440 if ($charset->{iana_names}->{'utf-8'}) {
441 #
442 } elsif ($charset->{iana_names}->{'jis_x0212-1990'} or
443 $charset->{iana_names}->{'x-jis0208'} or
444 $charset->{iana_names}->{'utf-32'} or ## ISSUE: UTF-32BE? UTF-32LE?
445 $charset->{is_ebcdic_based}) {
446 $onerror->(node => $doc,
447 type => 'bad character encoding',
448 text => $charset_name,
449 level => $self->{level}->{should},
450 layer => 'encode');
451 } elsif ($charset->{iana_names}->{'cesu-8'} or
452 $charset->{iana_names}->{'utf-8'} or ## ISSUE: UNICODE-1-1-UTF-7?
453 $charset->{iana_names}->{'bocu-1'} or
454 $charset->{iana_names}->{'scsu'}) {
455 $onerror->(node => $doc,
456 type => 'disallowed character encoding',
457 text => $charset_name,
458 level => $self->{level}->{must},
459 layer => 'encode');
460 } else {
461 $onerror->(node => $doc,
462 type => 'non-utf-8 character encoding',
463 text => $charset_name,
464 level => $self->{level}->{good},
465 layer => 'encode');
466 }
467 }
468 } elsif ($doc->manakai_is_html) {
469 ## NOTE: MUST and SHOULD requirements above cannot be tested,
470 ## since the document has no input charset encoding information.
471 $onerror->(node => $doc,
472 type => 'character encoding unchecked',
473 level => $self->{level}->{info},
474 layer => 'encode');
475 }
476
477 return $return;
478 } # check_document
479
480 ## Check an element. The element is checked as if it is an orphan node (i.e.
481 ## an element without a parent node).
482 sub check_element ($$$;$) {
483 my ($self, $el, $onerror, $onsubdoc) = @_;
484 $self = bless {}, $self unless ref $self;
485 $self->{onerror} = $onerror;
486 $self->{onsubdoc} = $onsubdoc || sub {
487 warn "A subdocument is not conformance-checked";
488 };
489
490 $self->{level} ||= $default_error_level;
491
492 $self->{plus_elements} = {};
493 $self->{minus_elements} = {};
494 $self->{id} = {};
495 $self->{term} = {};
496 $self->{usemap} = [];
497 $self->{ref} = []; # datetemplate data references
498 $self->{template} = []; # datatemplate template references
499 $self->{contextmenu} = [];
500 $self->{map} = {};
501 $self->{menu} = {};
502 $self->{has_link_type} = {};
503 $self->{flag} = {};
504 #$self->{has_uri_attr};
505 #$self->{has_hyperlink_element};
506 #$self->{has_charset};
507 #$self->{has_base};
508 $self->{return} = {
509 class => {},
510 id => $self->{id},
511 table => [], # table objects returned by Whatpm::HTMLTable
512 term => $self->{term},
513 uri => {}, # URIs other than those in RDF triples
514 ## TODO: xmlns="", SYSTEM "", atom:* src="", xml:base=""
515 rdf => [],
516 };
517
518 my @item = ({type => 'element', node => $el, parent_state => {}});
519 $item[-1]->{real_parent_state} = $item[-1]->{parent_state};
520 while (@item) {
521 my $item = shift @item;
522 if (ref $item eq 'ARRAY') {
523 my $code = shift @$item;
524 next unless $code;## TODO: temp.
525 $code->(@$item);
526 } elsif ($item->{type} eq 'element') {
527 my $el_nsuri = $item->{node}->namespace_uri;
528 $el_nsuri = '' unless defined $el_nsuri;
529 my $el_ln = $item->{node}->manakai_local_name;
530
531 load_ns_module ($el_nsuri);
532
533 my $element_state = {};
534 my $eldef = $Element->{$el_nsuri}->{$el_ln} ||
535 $Element->{$el_nsuri}->{''} ||
536 $ElementDefault;
537 my $content_def = $item->{transparent}
538 ? $item->{parent_def} || $eldef : $eldef;
539 my $content_state = $item->{transparent}
540 ? $item->{parent_def}
541 ? $item->{parent_state} || $element_state : $element_state
542 : $element_state;
543
544 unless ($eldef->{status} & FEATURE_STATUS_REC) {
545 my $status = $eldef->{status} & FEATURE_STATUS_CR ? 'cr' :
546 $eldef->{status} & FEATURE_STATUS_LC ? 'lc' :
547 $eldef->{status} & FEATURE_STATUS_WD ? 'wd' : 'non-standard';
548 $self->{onerror}->(node => $item->{node},
549 type => 'status:'.$status.':element',
550 level => $self->{level}->{info});
551 }
552 if (not ($eldef->{status} & FEATURE_ALLOWED)) {
553 $self->{onerror}->(node => $item->{node},
554 type => 'element not defined',
555 level => $self->{level}->{must});
556 } elsif ($eldef->{status} & FEATURE_DEPRECATED_SHOULD) {
557 $self->{onerror}->(node => $item->{node},
558 type => 'deprecated:element',
559 level => $self->{level}->{should});
560 } elsif ($eldef->{status} & FEATURE_DEPRECATED_INFO) {
561 $self->{onerror}->(node => $item->{node},
562 type => 'deprecated:element',
563 level => $self->{level}->{info});
564 }
565
566 my @new_item;
567 push @new_item, [$eldef->{check_start}, $self, $item, $element_state];
568 push @new_item, [$eldef->{check_attrs}, $self, $item, $element_state];
569
570 my @child = @{$item->{node}->child_nodes};
571 while (@child) {
572 my $child = shift @child;
573 my $child_nt = $child->node_type;
574 if ($child_nt == 1) { # ELEMENT_NODE
575 my $child_nsuri = $child->namespace_uri;
576 $child_nsuri = '' unless defined $child_nsuri;
577 my $child_ln = $child->manakai_local_name;
578 if ($HTMLTransparentElements->{$child_nsuri}->{$child_ln} and
579 not (($self->{flag}->{in_head} or
580 ($el_nsuri eq $HTML_NS and $el_ln eq 'head')) and
581 $child_nsuri eq $HTML_NS and $child_ln eq 'noscript')) {
582 push @new_item, [$content_def->{check_child_element},
583 $self, $item, $child,
584 $child_nsuri, $child_ln, 1,
585 $content_state, $element_state];
586 push @new_item, {type => 'element', node => $child,
587 parent_state => $content_state,
588 parent_def => $content_def,
589 real_parent_state => $element_state,
590 transparent => 1};
591 } else {
592 if ($item->{parent_def} and # has parent
593 $el_nsuri eq $HTML_NS) { ## $HTMLSemiTransparentElements
594 if ($el_ln eq 'object') {
595 if ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
596 #
597 } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
598 #
599 } else {
600 $content_def = $item->{parent_def} || $content_def;
601 $content_state = $item->{parent_state} || $content_state;
602 }
603 } elsif ($el_ln eq 'video' or $el_ln eq 'audio') {
604 if ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
605 #
606 } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
607 $element_state->{has_source} = 1;
608 } else {
609 $content_def = $item->{parent_def} || $content_def;
610 $content_state = $item->{parent_state} || $content_state;
611 }
612 }
613 }
614
615 push @new_item, [$content_def->{check_child_element},
616 $self, $item, $child,
617 $child_nsuri, $child_ln,
618 $HTMLSemiTransparentElements
619 ->{$child_nsuri}->{$child_ln},
620 $content_state, $element_state];
621 push @new_item, {type => 'element', node => $child,
622 parent_def => $content_def,
623 real_parent_state => $element_state,
624 parent_state => $content_state};
625 }
626
627 if ($HTMLEmbeddedContent->{$child_nsuri}->{$child_ln}) {
628 $element_state->{has_significant} = 1;
629 }
630 } elsif ($child_nt == 3 or # TEXT_NODE
631 $child_nt == 4) { # CDATA_SECTION_NODE
632 my $has_significant = ($child->data =~ /[^\x09-\x0D\x20]/);
633 push @new_item, [$content_def->{check_child_text},
634 $self, $item, $child, $has_significant,
635 $content_state, $element_state];
636 $element_state->{has_significant} ||= $has_significant;
637 if ($has_significant and
638 $HTMLSemiTransparentElements->{$el_nsuri}->{$el_ln}) {
639 $content_def = $item->{parent_def} || $content_def;
640 }
641 } elsif ($child_nt == 5) { # ENTITY_REFERENCE_NODE
642 push @child, @{$child->child_nodes};
643 }
644 ## TODO: PI_NODE
645 ## TODO: Unknown node type
646 }
647
648 push @new_item, [$eldef->{check_end}, $self, $item, $element_state];
649
650 unshift @item, @new_item;
651 } else {
652 die "$0: Internal error: Unsupported checking action type |$item->{type}|";
653 }
654 }
655
656 for (@{$self->{template}}) {
657 ## TODO: If the document is an XML document, ...
658 ## NOTE: If the document is an HTML document:
659 ## ISSUE: We need to percent-decode?
660 F: {
661 if ($self->{id}->{$_->[0]}) {
662 my $el = $self->{id}->{$_->[0]}->[0]->owner_element;
663 if ($el->node_type == 1 and # ELEMENT_NODE
664 $el->manakai_local_name eq 'datatemplate') {
665 my $nsuri = $el->namespace_uri;
666 if (defined $nsuri and $nsuri eq $HTML_NS) {
667 if ($el eq $_->[1]->owner_element) {
668 $self->{onerror}->(node => $_->[1],
669 type => 'fragment points itself',
670 level => $self->{level}->{must});
671 }
672
673 last F;
674 }
675 }
676 }
677 ## TODO: Should we raise a "fragment points nothing" error instead
678 ## if the fragment identifier identifies no element?
679
680 $self->{onerror}->(node => $_->[1], type => 'template:not template',
681 level => $self->{level}->{must});
682 } # F
683 }
684
685 for (@{$self->{ref}}) {
686 ## TOOD: If XML
687 ## NOTE: If it is an HTML document:
688 if ($_->[0] eq '') {
689 ## NOTE: It points the top of the document.
690 } elsif ($self->{id}->{$_->[0]}) {
691 if ($self->{id}->{$_->[0]}->[0]->owner_element
692 eq $_->[1]->owner_element) {
693 $self->{onerror}->(node => $_->[1], type => 'fragment points itself',
694 level => $self->{level}->{must});
695 }
696 } else {
697 $self->{onerror}->(node => $_->[1], type => 'fragment points nothing',
698 level => $self->{level}->{must});
699 }
700 }
701
702 ## TODO: Maybe we should have $document->manakai_get_by_fragment or something
703
704 for (@{$self->{usemap}}) {
705 unless ($self->{map}->{$_->[0]}) {
706 $self->{onerror}->(node => $_->[1], type => 'no referenced map',
707 level => $self->{level}->{must});
708 }
709 }
710
711 for (@{$self->{contextmenu}}) {
712 unless ($self->{menu}->{$_->[0]}) {
713 $self->{onerror}->(node => $_->[1], type => 'no referenced menu',
714 level => $self->{level}->{must});
715 }
716 }
717
718 delete $self->{plus_elements};
719 delete $self->{minus_elements};
720 delete $self->{onerror};
721 delete $self->{id};
722 delete $self->{usemap};
723 delete $self->{ref};
724 delete $self->{template};
725 delete $self->{map};
726 return $self->{return};
727 } # check_element
728
729 sub _add_minus_elements ($$@) {
730 my $self = shift;
731 my $element_state = shift;
732 for my $elements (@_) {
733 for my $nsuri (keys %$elements) {
734 for my $ln (keys %{$elements->{$nsuri}}) {
735 unless ($self->{minus_elements}->{$nsuri}->{$ln}) {
736 $element_state->{minus_elements_original}->{$nsuri}->{$ln} = 0;
737 $self->{minus_elements}->{$nsuri}->{$ln} = 1;
738 }
739 }
740 }
741 }
742 } # _add_minus_elements
743
744 sub _remove_minus_elements ($$) {
745 my $self = shift;
746 my $element_state = shift;
747 for my $nsuri (keys %{$element_state->{minus_elements_original}}) {
748 for my $ln (keys %{$element_state->{minus_elements_original}->{$nsuri}}) {
749 delete $self->{minus_elements}->{$nsuri}->{$ln};
750 }
751 }
752 } # _remove_minus_elements
753
754 sub _add_plus_elements ($$@) {
755 my $self = shift;
756 my $element_state = shift;
757 for my $elements (@_) {
758 for my $nsuri (keys %$elements) {
759 for my $ln (keys %{$elements->{$nsuri}}) {
760 unless ($self->{plus_elements}->{$nsuri}->{$ln}) {
761 $element_state->{plus_elements_original}->{$nsuri}->{$ln} = 0;
762 $self->{plus_elements}->{$nsuri}->{$ln} = 1;
763 }
764 }
765 }
766 }
767 } # _add_plus_elements
768
769 sub _remove_plus_elements ($$) {
770 my $self = shift;
771 my $element_state = shift;
772 for my $nsuri (keys %{$element_state->{plus_elements_original}}) {
773 for my $ln (keys %{$element_state->{plus_elements_original}->{$nsuri}}) {
774 delete $self->{plus_elements}->{$nsuri}->{$ln};
775 }
776 }
777 } # _remove_plus_elements
778
779 sub _attr_status_info ($$$) {
780 my ($self, $attr, $status_code) = @_;
781
782 if (not ($status_code & FEATURE_ALLOWED)) {
783 $self->{onerror}->(node => $attr,
784 type => 'attribute not defined',
785 level => $self->{level}->{must});
786 } elsif ($status_code & FEATURE_DEPRECATED_SHOULD) {
787 $self->{onerror}->(node => $attr,
788 type => 'deprecated:attr',
789 level => $self->{level}->{should});
790 } elsif ($status_code & FEATURE_DEPRECATED_INFO) {
791 $self->{onerror}->(node => $attr,
792 type => 'deprecated:attr',
793 level => $self->{level}->{info});
794 }
795
796 my $status;
797 if ($status_code & FEATURE_STATUS_REC) {
798 return;
799 } elsif ($status_code & FEATURE_STATUS_CR) {
800 $status = 'cr';
801 } elsif ($status_code & FEATURE_STATUS_LC) {
802 $status = 'lc';
803 } elsif ($status_code & FEATURE_STATUS_WD) {
804 $status = 'wd';
805 } else {
806 $status = 'non-standard';
807 }
808 $self->{onerror}->(node => $attr,
809 type => 'status:'.$status.':attr',
810 level => $self->{level}->{info});
811 } # _attr_status_info
812
813 sub _add_minuses ($@) {
814 my $self = shift;
815 my $r = {};
816 for my $list (@_) {
817 for my $ns (keys %$list) {
818 for my $ln (keys %{$list->{$ns}}) {
819 unless ($self->{minuses}->{$ns}->{$ln}) {
820 $self->{minuses}->{$ns}->{$ln} = 1;
821 $r->{$ns}->{$ln} = 1;
822 }
823 }
824 }
825 }
826 return {type => 'plus', list => $r};
827 } # _add_minuses
828
829 sub _add_pluses ($@) {
830 my $self = shift;
831 my $r = {};
832 for my $list (@_) {
833 for my $ns (keys %$list) {
834 for my $ln (keys %{$list->{$ns}}) {
835 unless ($self->{pluses}->{$ns}->{$ln}) {
836 $self->{pluses}->{$ns}->{$ln} = 1;
837 $r->{$ns}->{$ln} = 1;
838 }
839 }
840 }
841 }
842 return {type => 'minus', list => $r};
843 } # _add_pluses
844
845 sub _remove_minuses ($$) {
846 my ($self, $todo) = @_;
847 if ($todo->{type} eq 'minus') {
848 for my $ns (keys %{$todo->{list}}) {
849 for my $ln (keys %{$todo->{list}->{$ns}}) {
850 delete $self->{pluses}->{$ns}->{$ln} if $todo->{list}->{$ns}->{$ln};
851 }
852 }
853 } elsif ($todo->{type} eq 'plus') {
854 for my $ns (keys %{$todo->{list}}) {
855 for my $ln (keys %{$todo->{list}->{$ns}}) {
856 delete $self->{minuses}->{$ns}->{$ln} if $todo->{list}->{$ns}->{$ln};
857 }
858 }
859 } else {
860 die "$0: Unknown +- type: $todo->{type}";
861 }
862 1;
863 } # _remove_minuses
864
865 ## NOTE: Priority for "minuses" and "pluses" are currently left
866 ## undefined and implemented inconsistently; it is not a problem for
867 ## now, since no element belongs to both lists.
868
869 sub _check_get_children ($$$) {
870 my ($self, $node, $parent_todo) = @_;
871 my $new_todos = [];
872 my $sib = [];
873 TP: {
874 my $node_ns = $node->namespace_uri;
875 $node_ns = '' unless defined $node_ns;
876 my $node_ln = $node->manakai_local_name;
877 if ($HTMLTransparentElements->{$node_ns}->{$node_ln}) {
878 if ($node_ns eq $HTML_NS and $node_ln eq 'noscript') {
879 if ($parent_todo->{flag}->{in_head}) {
880 #
881 } else {
882 my $end = $self->_add_minuses ({$HTML_NS, {noscript => 1}});
883 push @$sib, $end;
884
885 unshift @$sib, @{$node->child_nodes};
886 push @$new_todos, {type => 'element-attributes', node => $node};
887 last TP;
888 }
889 } elsif ($node_ns eq $HTML_NS and $node_ln eq 'del') {
890 my $sig_flag = $parent_todo->{flag}->{has_descendant}->{significant};
891 unshift @$sib, @{$node->child_nodes};
892 push @$new_todos, {type => 'element-attributes', node => $node};
893 push @$new_todos,
894 {type => 'code',
895 code => sub {
896 $parent_todo->{flag}->{has_descendant}->{significant} = 0
897 if not $sig_flag;
898 }};
899 last TP;
900 } else {
901 unshift @$sib, @{$node->child_nodes};
902 push @$new_todos, {type => 'element-attributes', node => $node};
903 last TP;
904 }
905 }
906 if ($node_ns eq $HTML_NS and ($node_ln eq 'video' or $node_ln eq 'audio')) {
907 if ($node->has_attribute_ns (undef, 'src')) {
908 unshift @$sib, @{$node->child_nodes};
909 push @$new_todos, {type => 'element-attributes', node => $node};
910 last TP;
911 } else {
912 my @cn = @{$node->child_nodes};
913 CN: while (@cn) {
914 my $cn = shift @cn;
915 my $cnt = $cn->node_type;
916 if ($cnt == 1) {
917 my $cn_nsuri = $cn->namespace_uri;
918 $cn_nsuri = '' unless defined $cn_nsuri;
919 if ($cn_nsuri eq $HTML_NS and $cn->manakai_local_name eq 'source') {
920 #
921 } else {
922 last CN;
923 }
924 } elsif ($cnt == 3 or $cnt == 4) {
925 if ($cn->data =~ /[^\x09-\x0D\x20]/) {
926 last CN;
927 }
928 }
929 } # CN
930 unshift @$sib, @cn;
931 }
932 } elsif ($node_ns eq $HTML_NS and $node_ln eq 'object') {
933 my @cn = @{$node->child_nodes};
934 CN: while (@cn) {
935 my $cn = shift @cn;
936 my $cnt = $cn->node_type;
937 if ($cnt == 1) {
938 my $cn_nsuri = $cn->namespace_uri;
939 $cn_nsuri = '' unless defined $cn_nsuri;
940 if ($cn_nsuri eq $HTML_NS and $cn->manakai_local_name eq 'param') {
941 #
942 } else {
943 last CN;
944 }
945 } elsif ($cnt == 3 or $cnt == 4) {
946 if ($cn->data =~ /[^\x09-\x0D\x20]/) {
947 last CN;
948 }
949 }
950 } # CN
951 unshift @$sib, @cn;
952 }
953 push @$new_todos, {type => 'element', node => $node};
954 } # TP
955
956 for my $new_todo (@$new_todos) {
957 $new_todo->{flag} = {%{$parent_todo->{flag} or {}}};
958 }
959
960 return ($sib, $new_todos);
961 } # _check_get_children
962
963 =head1 LICENSE
964
965 Copyright 2007-2008 Wakaba <[email protected]>
966
967 This library is free software; you can redistribute it
968 and/or modify it under the same terms as Perl itself.
969
970 =cut
971
972 1;
973 # $Date: 2008/08/15 16:02:02 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24