/[suikacvs]/messaging/manakai/lib/Message/DOM/Document.pm
Suika

Contents of /messaging/manakai/lib/Message/DOM/Document.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.31 - (show annotations) (download)
Tue Oct 21 07:51:59 2008 UTC (16 years, 1 month ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.30: +5 -4 lines
Error occurred while calculating annotation data.
++ ChangeLog	21 Oct 2008 07:51:49 -0000
2008-10-21  Wakaba  <wakaba@suika.fam.cx>

	* cvscommit.sh: Invoke |mkcommitfeed.pl|.

	* mkcommitfeed.pl: New script (copied from Whatpm repository).

++ manakai/lib/Message/DOM/ChangeLog	21 Oct 2008 07:48:11 -0000
2008-10-21  Wakaba  <wakaba@suika.fam.cx>

	* Document.pm (inner_html): Use Whatpm::XML::Parser for XML
	parsing.

	* Node.pm (manakai_html_language): New attribute.

1 package Message::DOM::Document;
2 use strict;
3 our $VERSION=do{my @r=(q$Revision: 1.30 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 push our @ISA, 'Message::DOM::Node', 'Message::IF::Document',
5 'Message::IF::DocumentTraversal', 'Message::IF::DocumentXDoctype',
6 'Message::IF::DocumentSelector', # MUST in Selectors API spec
7 'Message::IF::HTMLDocument';
8 require Message::DOM::Node;
9 use Char::Class::XML
10 qw/
11 InXML_NameStartChar10 InXMLNameStartChar11
12 InXMLNameChar10 InXMLNameChar11
13 InXML_NCNameStartChar10 InXMLNCNameStartChar11
14 InXMLNCNameChar10 InXMLNCNameChar11
15 /;
16
17 sub ____new ($$) {
18 my $self = shift->SUPER::____new (undef);
19 $$self->{implementation} = $_[0];
20 $$self->{strict_error_checking} = 1;
21 $$self->{child_nodes} = [];
22 $$self->{'http://suika.fam.cx/www/2006/dom-config/strict-document-children'} = 1;
23 $$self->{'http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute'} = 1;
24 $$self->{'http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree'} = 1;
25 $$self->{'error-handler'} = sub ($) {
26 ## NOTE: Same as one set by |setParameter| with |undef| value.
27 warn $_[0];
28 return $_[0]->severity != 3; # SEVERITY_FATAL_ERROR
29 };
30 return $self;
31 } # ____new
32
33 sub AUTOLOAD {
34 my $method_name = our $AUTOLOAD;
35 $method_name =~ s/.*:://;
36 return if $method_name eq 'DESTROY';
37
38 if ({
39 ## Read-only attributes (trivial accessors)
40 default_view => 1,
41 implementation => 1,
42 }->{$method_name}) {
43 no strict 'refs';
44 eval qq{
45 sub $method_name (\$) {
46 return \${\$_[0]}->{$method_name};
47 }
48 };
49 goto &{ $AUTOLOAD };
50 } elsif ({
51 ## Read-write attributes (DOMString, trivial accessors)
52 manakai_charset => 1,
53 document_uri => 1,
54 input_encoding => 1,
55 }->{$method_name}) {
56 no strict 'refs';
57 eval qq{
58 sub $method_name (\$;\$) {
59 if (\@_ > 1) {
60 if (\${\$_[0]}->{strict_error_checking} and
61 \${\$_[0]}->{manakai_read_only}) {
62 report Message::DOM::DOMException
63 -object => \$_[0],
64 -type => 'NO_MODIFICATION_ALLOWED_ERR',
65 -subtype => 'READ_ONLY_NODE_ERR';
66 }
67 if (defined \$_[1]) {
68 \${\$_[0]}->{$method_name} = ''.\$_[1];
69 } else {
70 delete \${\$_[0]}->{$method_name};
71 }
72 }
73 return \${\$_[0]}->{$method_name};
74 }
75 };
76 goto &{ $AUTOLOAD };
77 } elsif ({
78 ## Read-write attributes (boolean, trivial accessors)
79 all_declarations_processed => 1,
80 manakai_has_bom => 1,
81 }->{$method_name}) {
82 no strict 'refs';
83 eval qq{
84 sub $method_name (\$;\$) {
85 if (\@_ > 1) {
86 if (\${\$_[0]}->{manakai_strict_error_checking} and
87 \${\$_[0]}->{manakai_read_only}) {
88 report Message::DOM::DOMException
89 -object => \$_[0],
90 -type => 'NO_MODIFICATION_ALLOWED_ERR',
91 -subtype => 'READ_ONLY_NODE_ERR';
92 }
93 if (\$_[1]) {
94 \${\$_[0]}->{$method_name} = 1;
95 } else {
96 delete \${\$_[0]}->{$method_name};
97 }
98 }
99 return \${\$_[0]}->{$method_name};
100 }
101 };
102 goto &{ $AUTOLOAD };
103 } elsif (my $module_name = {
104 can_dispatch => 'Message::DOM::EventTargetNode',
105 create_attribute => 'Message::DOM::Attr',
106 create_attribute_ns => 'Message::DOM::Attr',
107 create_attribute_definition => 'Message::DOM::AttributeDefinition',
108 create_cdata_section => 'Message::DOM::Text',
109 create_comment => 'Message::DOM::CharacterData',
110 create_document_fragment => 'Message::DOM::DocumentFragment',
111 create_document_type_definition => 'Message::DOM::DocumentType',
112 create_element => 'Message::DOM::Element',
113 create_element_ns => 'Message::DOM::Element',
114 create_element_type_definition => 'Message::DOM::ElementTypeDefinition',
115 create_entity_reference => 'Message::DOM::EntityReference',
116 create_event => 'Message::DOM::EventTargetNode',
117 create_general_entity => 'Message::DOM::Entity',
118 create_notation => 'Message::DOM::Notation',
119 create_processing_instruction => 'Message::DOM::ProcessingInstruction',
120 manakai_create_serial_walker => 'Message::DOM::SerialWalker',
121 create_text_node => 'Message::DOM::Text',
122 create_tree_walker => 'Message::DOM::TreeWalker',
123 query_selector => 'Message::DOM::SelectorsAPI',
124 query_selector_all => 'Message::DOM::SelectorsAPI',
125 ___query_selector_all => 'Message::DOM::SelectorsAPI',
126 }->{$method_name}) {
127 eval qq{ require $module_name } or die $@;
128 goto &{ $AUTOLOAD };
129 } else {
130 require Carp;
131 Carp::croak (qq<Can't locate method "$AUTOLOAD">);
132 }
133 } # AUTOLOAD
134 sub implementation ($);
135 sub create_attribute ($$);
136 sub create_attribute_ns ($$$);
137 sub create_attribute_definition ($$);
138 sub create_cdata_section ($$);
139 sub create_comment ($$);
140 sub create_document_fragment ($);
141 sub create_document_type_definition ($$);
142 sub create_element ($$);
143 sub create_element_ns ($$$);
144 sub create_element_type_definition ($$);
145 sub create_entity_reference ($$);
146 sub create_general_entity ($$);
147 sub create_notation ($$);
148 sub create_processing_instruction ($$$);
149 sub create_text_node ($$);
150
151 ## |Node| attributes
152
153 sub base_uri ($) {
154 my $v = ${$_[0]}->{manakai_entity_base_uri};
155 if (defined $v) {
156 return $v;
157 } else {
158 return ${$_[0]}->{document_uri};
159 }
160 ## TODO: HTML5 <base>
161 } # base_uri
162
163 sub node_name () { '#document' }
164
165 sub node_type () { 9 } # DOCUMENT_NODE
166
167 sub text_content ($;$) {
168 my $self = shift;
169 if ($$self->{'http://suika.fam.cx/www/2006/dom-config/strict-document-children'}) {
170 return undef;
171 } else {
172 local $Error::Depth = $Error::Depth + 1;
173 return $self->SUPER::text_content (@_);
174 }
175 } # text_content
176
177 ## |Node| methods
178
179 sub adopt_node ($$) {
180 my ($self, $source) = @_;
181 ## TODO: Should we apply |copy-asis| configuration parameter to this method?
182
183 return undef unless UNIVERSAL::isa ($source, 'Message::DOM::Node');
184
185 my $strict = $self->strict_error_checking;
186 if ($strict and $$self->{manakai_read_only}) {
187 report Message::DOM::DOMException
188 -object => $self,
189 -type => 'NO_MODIFICATION_ALLOWED_ERR',
190 -subtype => 'READ_ONLY_NODE_ERR';
191 }
192
193 my $parent = $source->parent_node;
194 if ($strict and defined $parent and $$parent->{manakai_read_only}) {
195 report Message::DOM::DOMException
196 -object => $self,
197 -type => 'NO_MODIFICATION_ALLOWED_ERR',
198 -subtype => 'READ_ONLY_NODE_ERR';
199 }
200
201 my $nt = $source->node_type;
202 my $oe;
203 if ($nt == 2) { # ATTRIBUTE_NODE
204 $oe = $source->owner_element;
205 if ($strict and defined $oe and $$oe->{manakai_read_only}) {
206 report Message::DOM::DOMException
207 -object => $self,
208 -type => 'NO_MODIFICATION_ALLOWED_ERR',
209 -subtype => 'READ_ONLY_NODE_ERR';
210 }
211 } elsif ($nt == 9 or $nt == 10 or $nt == 6 or $nt == 12 or
212 $nt == 81001 or $nt == 81002) {
213 # DOCUMENT_NODE, DOCUMENT_TYPE_NODE, ENTITY_NODE, NOTATION_NODE,
214 # ELEMENT_TYPE_DEFINITION_NODE, ATTRIBUTE_DEFINITION_NODE
215 report Message::DOM::DOMException
216 -object => $self,
217 -type => 'NOT_SUPPORTED_ERR',
218 -subtype => 'ADOPT_NODE_TYPE_NOT_SUPPORTED_ERR';
219 }
220
221 my @change_od;
222 my @nodes = ($source);
223 while (@nodes) {
224 my $node = shift @nodes;
225 my $nt = $node->node_type;
226 if ($strict and $$node->{manakai_read_only}) {
227 report Message::DOM::DOMException
228 -object => $self,
229 -type => 'NO_MODIFICATION_ALLOWED_ERR',
230 -subtype => 'READ_ONLY_NODE_ERR';
231 }
232
233 push @change_od, $node;
234 push @nodes, @{$node->child_nodes}, @{$node->attributes or []};
235 } # @nodes
236
237 local $Error::Depth = $Error::Depth + 1;
238
239 if (defined $parent) {
240 $parent->remove_child ($source);
241 } elsif (defined $oe) {
242 $oe->remove_attribute_node ($source);
243 }
244
245 return $source if $self eq $change_od[0]->owner_document;
246 ## NOTE: The array must have more than zero
247 ## nodes by definition. In addition,
248 ## it cannot contain document or document
249 ## type nodes in current implementation.
250
251 my @ud_node;
252 for my $n (@change_od) {
253 $$n->{owner_document} = $self;
254 Scalar::Util::weaken ($$n->{owner_document});
255 if ($$n->{user_data}) {
256 push @ud_node, $n;
257 }
258 }
259
260 for my $src (@ud_node) {
261 my $src_ud = $$src->{user_data};
262 for my $key (keys %{$src_ud}) {
263 my $dh = $src_ud->{$key}->[1];
264 if ($dh) {
265 $dh->(5, $key, $src_ud->{$key}->[0], $src, undef); # NODE_ADOPTED
266 }
267 }
268 }
269
270 return $source;
271 } # adopt_node
272
273 sub append_child ($$) {
274 ## NOTE: Overrides |Node|'s implementation.
275 my $self = $_[0];
276
277 ## NOTE: |$self_od| code here in some $self->node_type.
278
279 ## -- Node Type check
280 my @new_child;
281 my $new_child_parent;
282 if ($_[1]->node_type == 11) { # DOCUMENT_FRAGMENT_NODE
283 push @new_child, @{$_[1]->child_nodes};
284 $new_child_parent = $_[1];
285 } else {
286 @new_child = ($_[1]);
287 $new_child_parent = $_[1]->parent_node;
288 }
289
290 ## NOTE: Depends on $self->node_type:
291 if ($$self->{strict_error_checking}) {
292 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
293 if ($self ne $child_od and $child_od->node_type != 10) {
294 report Message::DOM::DOMException # DOCUMENT_TYPE_NODE
295 -object => $self,
296 -type => 'WRONG_DOCUMENT_ERR',
297 -subtype => 'EXTERNAL_OBJECT_ERR';
298 }
299
300 if ($$self->{manakai_read_only} or
301 (@new_child and defined $new_child_parent and
302 $$new_child_parent->{manakai_read_only})) {
303 report Message::DOM::DOMException
304 -object => $self,
305 -type => 'NO_MODIFICATION_ALLOWED_ERR',
306 -subtype => 'READ_ONLY_NODE_ERR';
307 }
308
309 ## NOTE: Only in |Document|:
310 my $strict_children = $self->dom_config->get_parameter
311 (q<http://suika.fam.cx/www/2006/dom-config/strict-document-children>);
312 if ($strict_children) {
313 my $has_el;
314 my $has_dt;
315 my $child_nt = $_[1]->node_type;
316 if ($child_nt == 1) { # ELEMENT_NODE
317 $has_el = 1;
318 } elsif ($child_nt == 10) { # DOCUMENT_TYPE_NODE
319 $has_dt = 1;
320 } elsif ($child_nt == 11) { # DOCUMENT_FRAGMENT_NODE
321 for my $cn (@{$_[1]->child_nodes}) {
322 my $cnt = $cn->node_type;
323 if ($cnt == 1) { # ELEMENT_NODE
324 if ($has_el) {
325 report Message::DOM::DOMException
326 -object => $self,
327 -type => 'HIERARCHY_REQUEST_ERR',
328 -subtype => 'CHILD_NODE_TYPE_ERR';
329 }
330 $has_el = 1;
331 } elsif ($cnt == 10) { # DOCUMENT_TYPE_NODE
332 ## NOTE: |DocumentType| node cannot be contained in
333 ## |DocumentFragment| in strict mode.
334 if ($has_dt) {
335 report Message::DOM::DOMException
336 -object => $self,
337 -type => 'HIERARCHY_REQUEST_ERR',
338 -subtype => 'CHILD_NODE_TYPE_ERR';
339 }
340 $has_dt = 1;
341 }
342 }
343 }
344
345 if ($has_el) {
346 my $anode = $self->last_child;
347 while (defined $anode) {
348 if ($anode->node_type == 1) { # ELEMENT_NODE
349 report Message::DOM::DOMException
350 -object => $self,
351 -type => 'HIERARCHY_REQUEST_ERR',
352 -subtype => 'CHILD_NODE_TYPE_ERR';
353 }
354 $anode = $anode->previous_sibling;
355 }
356 } # has_el
357 if ($has_dt) {
358 my $anode = $self->last_child;
359 while (defined $anode) {
360 my $ant = $anode->node_type;
361 if ($ant == 1 or $ant == 10) { # ELEMENT_NODE or DOCUMENT_TYPE_NODE
362 report Message::DOM::DOMException
363 -object => $self,
364 -type => 'HIERARCHY_REQUEST_ERR',
365 -subtype => 'CHILD_NODE_TYPE_ERR';
366 }
367 $anode = $anode->previous_sibling;
368 }
369 } # has_dt
370 }
371
372 for my $cn (@new_child) {
373 unless ({
374 3, (not $strict_children), # TEXT_NODE
375 5, (not $strict_children), # ENTITY_REFERENCE_NODE
376 1, 1, # ELEMENT_NODE
377 4, (not $strict_children), # CDATA_SECTION_NODE
378 7, 1, # PROCESSING_INSTRUCTION_NODE
379 8, 1, # COMMENT_NODE
380 10, 1, # DOCUMENT_TYPE_NODE
381 }->{$cn->node_type}) {
382 report Message::DOM::DOMException
383 -object => $self,
384 -type => 'HIERARCHY_REQUEST_ERR',
385 -subtype => 'CHILD_NODE_TYPE_ERR';
386 }
387 }
388
389 ## NOTE: Ancestor check here in |Node|.
390 }
391
392 ## NOTE: "Insert at" code only in insert_before and replace_child
393
394 ## -- Removes from parent
395 if ($new_child_parent) {
396 if (@new_child == 1) {
397 my $v = $$new_child_parent->{child_nodes};
398 RP: for my $i (0..$#$v) {
399 if ($v->[$i] eq $new_child[0]) {
400 splice @$v, $i, 1, ();
401 last RP;
402 }
403 } # RP
404 } else {
405 @{$$new_child_parent->{child_nodes}} = ();
406 }
407 }
408
409 ## -- Rewrite the |parentNode| properties
410 for my $nc (@new_child) {
411 $$nc->{parent_node} = $self;
412 Scalar::Util::weaken ($$nc->{parent_node});
413 }
414
415 ## NOTE: Depends on method:
416 push @{$$self->{child_nodes}}, @new_child;
417
418 ## NOTE: Only in |Document|.
419 for (@new_child) {
420 delete $$_->{implementation};
421 $$_->{owner_document} = $self;
422 Scalar::Util::weaken ($$_->{owner_document});
423 }
424
425 return $_[1];
426 } # apepnd_child
427
428 sub manakai_append_text ($$) {
429 my $self = shift;
430 if ($$self->{'http://suika.fam.cx/www/2006/dom-config/strict-document-children'}) {
431 #
432 } else {
433 local $Error::Depth = $Error::Depth + 1;
434 return $self->SUPER::manakai_append_text (@_);
435 }
436 } # manakai_append_text
437
438 sub insert_before ($$) {
439 ## NOTE: Overrides |Node|'s implementation.
440 my $self = $_[0];
441
442 ## NOTE: |$self_od| code here depending on $self->node_type.
443
444 ## -- Node Type check
445 my @new_child;
446 my $new_child_parent;
447 if ($_[1]->node_type == 11) { # DOCUMENT_FRAGMENT_NODE
448 push @new_child, @{$_[1]->child_nodes};
449 $new_child_parent = $_[1];
450 } else {
451 @new_child = ($_[1]);
452 $new_child_parent = $_[1]->parent_node;
453 }
454
455 ## NOTE: Depends on $self->node_type:
456 if ($$self->{strict_error_checking}) {
457 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
458 if ($self ne $child_od and $child_od->node_type != 10) {
459 report Message::DOM::DOMException # DOCUMENT_TYPE_NODE
460 -object => $self,
461 -type => 'WRONG_DOCUMENT_ERR',
462 -subtype => 'EXTERNAL_OBJECT_ERR';
463 }
464
465 if ($$self->{manakai_read_only} or
466 (@new_child and defined $new_child_parent and
467 $$new_child_parent->{manakai_read_only})) {
468 report Message::DOM::DOMException
469 -object => $self,
470 -type => 'NO_MODIFICATION_ALLOWED_ERR',
471 -subtype => 'READ_ONLY_NODE_ERR';
472 }
473
474 ## NOTE: Only in |Document|:
475 my $strict_children = $self->dom_config->get_parameter
476 (q<http://suika.fam.cx/www/2006/dom-config/strict-document-children>);
477 if ($strict_children) {
478 my $has_el;
479 my $has_dt;
480 my $child_nt = $_[1]->node_type;
481 if ($child_nt == 1) { # ELEMENT_NODE
482 $has_el = 1;
483 } elsif ($child_nt == 10) { # DOCUMENT_TYPE_NODE
484 $has_dt = 1;
485 } elsif ($child_nt == 11) { # DOCUMENT_FRAGMENT_NODE
486 for my $cn (@{$_[1]->child_nodes}) {
487 my $cnt = $cn->node_type;
488 if ($cnt == 1) { # ELEMENT_NODE
489 if ($has_el) {
490 report Message::DOM::DOMException
491 -object => $self,
492 -type => 'HIERARCHY_REQUEST_ERR',
493 -subtype => 'CHILD_NODE_TYPE_ERR';
494 }
495 $has_el = 1;
496 } elsif ($cnt == 10) { # DOCUMENT_TYPE_NODE
497 ## NOTE: |DocumentType| node cannot be contained in
498 ## |DocumentFragment| in strict mode.
499 if ($has_dt) {
500 report Message::DOM::DOMException
501 -object => $self,
502 -type => 'HIERARCHY_REQUEST_ERR',
503 -subtype => 'CHILD_NODE_TYPE_ERR';
504 }
505 $has_dt = 1;
506 }
507 }
508 }
509
510 ## ISSUE: This code is wrong. Old manakai's implementation
511 ## is better, but it is also wrong in some edge cases.
512 ## Maybe we should remove these code entirely. DOM3Core
513 ## conformance is not important for this bit. It only makes
514 ## things too complex. Same for replace_child's code.
515 if ($has_el) {
516 my $anode = $self->last_child;
517 while (defined $anode) {
518 if ($anode->node_type == 1) { # ELEMENT_NODE
519 report Message::DOM::DOMException
520 -object => $self,
521 -type => 'HIERARCHY_REQUEST_ERR',
522 -subtype => 'CHILD_NODE_TYPE_ERR';
523 }
524 $anode = $anode->previous_sibling;
525 }
526 } # has_el
527 if ($has_dt) {
528 my $anode = $self->last_child;
529 while (defined $anode) {
530 my $ant = $anode->node_type;
531 if ($ant == 1 or $ant == 10) { # ELEMENT_NODE or DOCUMENT_TYPE_NODE
532 report Message::DOM::DOMException
533 -object => $self,
534 -type => 'HIERARCHY_REQUEST_ERR',
535 -subtype => 'CHILD_NODE_TYPE_ERR';
536 }
537 $anode = $anode->previous_sibling;
538 }
539 } # has_dt
540 }
541
542 for my $cn (@new_child) {
543 unless ({
544 3, (not $strict_children), # TEXT_NODE
545 5, (not $strict_children), # ENTITY_REFERENCE_NODE
546 1, 1, # ELEMENT_NODE
547 4, (not $strict_children), # CDATA_SECTION_NODE
548 7, 1, # PROCESSING_INSTRUCTION_NODE
549 8, 1, # COMMENT_NODE
550 10, 1, # DOCUMENT_TYPE_NODE
551 }->{$cn->node_type}) {
552 report Message::DOM::DOMException
553 -object => $self,
554 -type => 'HIERARCHY_REQUEST_ERR',
555 -subtype => 'CHILD_NODE_TYPE_ERR';
556 }
557 }
558
559 ## NOTE: Ancestor check here in |Node|.
560 }
561
562 ## -- Insert at... ## NOTE: Only in insert_before and replace_child
563 my $index = -1; # last
564 if (defined $_[2]) {
565 ## error if $_[1] eq $_[2];
566
567 my $cns = $self->child_nodes;
568 my $cnsl = @$cns;
569 C: {
570 $index = 0;
571 for my $i (0..($cnsl-1)) {
572 my $cn = $cns->[$i];
573 if ($cn eq $_[2]) {
574 $index += $i;
575 last C;
576 } elsif ($cn eq $_[1]) {
577 $index = -1; # offset
578 }
579 }
580
581 report Message::DOM::DOMException
582 -object => $self,
583 -type => 'NOT_FOUND_ERR',
584 -subtype => 'NOT_CHILD_ERR';
585 } # C
586 }
587 ## NOTE: "else" only in replace_child
588
589 ## -- Removes from parent
590 if ($new_child_parent) {
591 if (@new_child == 1) {
592 my $v = $$new_child_parent->{child_nodes};
593 RP: for my $i (0..$#$v) {
594 if ($v->[$i] eq $new_child[0]) {
595 splice @$v, $i, 1, ();
596 last RP;
597 }
598 } # RP
599 } else {
600 @{$$new_child_parent->{child_nodes}} = ();
601 }
602 }
603
604 ## -- Rewrite the |parentNode| properties
605 for my $nc (@new_child) {
606 $$nc->{parent_node} = $self;
607 Scalar::Util::weaken ($$nc->{parent_node});
608 }
609
610 ## NOTE: Depends on method:
611 if ($index == -1) {
612 push @{$$self->{child_nodes}}, @new_child;
613 } else {
614 splice @{$$self->{child_nodes}}, $index, 0, @new_child;
615 }
616
617 ## NOTE: Only in |Document|.
618 for (@new_child) {
619 delete $$_->{implementation};
620 $$_->{owner_document} = $self;
621 Scalar::Util::weaken ($$_->{owner_document});
622 }
623
624 return $_[1];
625 } # insert_before
626
627 sub replace_child ($$) {
628 ## NOTE: Overrides |Node|'s implementation.
629 my $self = $_[0];
630
631 ## NOTE: |$self_od| code here depending on $self->node_type.
632
633 ## -- Node Type check
634 my @new_child;
635 my $new_child_parent;
636 if ($_[1]->node_type == 11) { # DOCUMENT_FRAGMENT_NODE
637 push @new_child, @{$_[1]->child_nodes};
638 $new_child_parent = $_[1];
639 } else {
640 @new_child = ($_[1]);
641 $new_child_parent = $_[1]->parent_node;
642 }
643
644 ## NOTE: Depends on $self->node_type:
645 if ($$self->{strict_error_checking}) {
646 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
647 if ($self ne $child_od and $child_od->node_type != 10) {
648 report Message::DOM::DOMException # DOCUMENT_TYPE_NODE
649 -object => $self,
650 -type => 'WRONG_DOCUMENT_ERR',
651 -subtype => 'EXTERNAL_OBJECT_ERR';
652 }
653
654 if ($$self->{manakai_read_only} or
655 (@new_child and defined $new_child_parent and
656 $$new_child_parent->{manakai_read_only})) {
657 report Message::DOM::DOMException
658 -object => $self,
659 -type => 'NO_MODIFICATION_ALLOWED_ERR',
660 -subtype => 'READ_ONLY_NODE_ERR';
661 }
662
663 ## NOTE: Only in |Document|:
664 my $strict_children = $self->dom_config->get_parameter
665 (q<http://suika.fam.cx/www/2006/dom-config/strict-document-children>);
666 if ($strict_children) {
667 my $has_el;
668 my $has_dt;
669 my $child_nt = $_[1]->node_type;
670 if ($child_nt == 1) { # ELEMENT_NODE
671 $has_el = 1;
672 } elsif ($child_nt == 10) { # DOCUMENT_TYPE_NODE
673 $has_dt = 1;
674 } elsif ($child_nt == 11) { # DOCUMENT_FRAGMENT_NODE
675 for my $cn (@{$_[1]->child_nodes}) {
676 my $cnt = $cn->node_type;
677 if ($cnt == 1) { # ELEMENT_NODE
678 if ($has_el) {
679 report Message::DOM::DOMException
680 -object => $self,
681 -type => 'HIERARCHY_REQUEST_ERR',
682 -subtype => 'CHILD_NODE_TYPE_ERR';
683 }
684 $has_el = 1;
685 } elsif ($cnt == 10) { # DOCUMENT_TYPE_NODE
686 ## NOTE: |DocumentType| node cannot be contained in
687 ## |DocumentFragment| in strict mode.
688 if ($has_dt) {
689 report Message::DOM::DOMException
690 -object => $self,
691 -type => 'HIERARCHY_REQUEST_ERR',
692 -subtype => 'CHILD_NODE_TYPE_ERR';
693 }
694 $has_dt = 1;
695 }
696 }
697 }
698
699 if ($has_el) {
700 my $anode = $self->last_child;
701 while (defined $anode) {
702 if ($anode->node_type == 1) { # ELEMENT_NODE
703 report Message::DOM::DOMException
704 -object => $self,
705 -type => 'HIERARCHY_REQUEST_ERR',
706 -subtype => 'CHILD_NODE_TYPE_ERR';
707 }
708 $anode = $anode->previous_sibling;
709 }
710 } # has_el
711 if ($has_dt) {
712 my $anode = $self->last_child;
713 while (defined $anode) {
714 my $ant = $anode->node_type;
715 if ($ant == 1 or $ant == 10) { # ELEMENT_NODE or DOCUMENT_TYPE_NODE
716 report Message::DOM::DOMException
717 -object => $self,
718 -type => 'HIERARCHY_REQUEST_ERR',
719 -subtype => 'CHILD_NODE_TYPE_ERR';
720 }
721 $anode = $anode->previous_sibling;
722 }
723 } # has_dt
724 }
725
726 for my $cn (@new_child) {
727 unless ({
728 3, (not $strict_children), # TEXT_NODE
729 5, (not $strict_children), # ENTITY_REFERENCE_NODE
730 1, 1, # ELEMENT_NODE
731 4, (not $strict_children), # CDATA_SECTION_NODE
732 7, 1, # PROCESSING_INSTRUCTION_NODE
733 8, 1, # COMMENT_NODE
734 10, 1, # DOCUMENT_TYPE_NODE
735 }->{$cn->node_type}) {
736 report Message::DOM::DOMException
737 -object => $self,
738 -type => 'HIERARCHY_REQUEST_ERR',
739 -subtype => 'CHILD_NODE_TYPE_ERR';
740 }
741 }
742
743 ## NOTE: Ancestor check here in |Node|.
744 }
745
746 ## -- Insert at... ## NOTE: Only in insertBefore and replaceChild
747 my $index = -1; # last
748 if (defined $_[2]) {
749 ## error if $_[1] eq $_[2];
750
751 my $cns = $self->child_nodes;
752 my $cnsl = @$cns;
753 C: {
754 $index = 0;
755 for my $i (0..($cnsl-1)) {
756 my $cn = $cns->[$i];
757 if ($cn eq $_[2]) {
758 $index += $i;
759 last C;
760 } elsif ($cn eq $_[1]) {
761 $index = -1; # offset
762 }
763 }
764
765 report Message::DOM::DOMException
766 -object => $self,
767 -type => 'NOT_FOUND_ERR',
768 -subtype => 'NOT_CHILD_ERR';
769 } # C
770 } else {
771 ## NOTE: Only in replaceChild
772 report Message::DOM::DOMException
773 -object => $self,
774 -type => 'NOT_FOUND_ERR',
775 -subtype => 'NOT_CHILD_ERR';
776 }
777
778 ## -- Removes from parent
779 if ($new_child_parent) {
780 if (@new_child == 1) {
781 my $v = $$new_child_parent->{child_nodes};
782 RP: for my $i (0..$#$v) {
783 if ($v->[$i] eq $new_child[0]) {
784 splice @$v, $i, 1, ();
785 last RP;
786 }
787 } # RP
788 } else {
789 @{$$new_child_parent->{child_nodes}} = ();
790 }
791 }
792
793 ## -- Rewrite the |parentNode| properties
794 for my $nc (@new_child) {
795 $$nc->{parent_node} = $self;
796 Scalar::Util::weaken ($$nc->{parent_node});
797 }
798
799 ## NOTE: Depends on method:
800 splice @{$$self->{child_nodes}}, $index, 1, @new_child;
801 delete ${$_[2]}->{parent_node};
802
803 ## NOTE: Only in |Document|.
804 for (@new_child) {
805 delete $$_->{implementation};
806 $$_->{owner_document} = $self;
807 Scalar::Util::weaken ($$_->{owner_document});
808 }
809
810 return $_[2];
811 } # replace_child
812
813 ## |Document| attributes
814
815 ## NOTE: A manakai extension.
816 sub all_declarations_processed ($;$);
817
818 ## TODO: documentation
819 sub manakai_charset ($;$);
820
821 sub doctype ($) {
822 my $self = $_[0];
823 for (@{$self->child_nodes}) {
824 if ($_->node_type == 10) { # DOCUMENT_TYPE_NODE
825 return $_;
826 }
827 }
828 return undef;
829 } # doctype
830
831 sub document_element ($) {
832 my $self = shift;
833 for (@{$self->child_nodes}) {
834 if ($_->node_type == 1) { # ELEMENT_NODE
835 return $_;
836 }
837 }
838 return undef;
839 } # document_element
840
841 sub document_uri ($;$);
842
843 sub dom_config ($) {
844 require Message::DOM::DOMConfiguration;
845 return bless \\($_[0]), 'Message::DOM::DOMConfiguration';
846 } # dom_config
847
848 sub manakai_entity_base_uri ($;$) {
849 my $self = $_[0];
850 if (@_ > 1) {
851 if ($$self->{strict_error_checking}) {
852 if ($$self->{manakai_read_only}) {
853 report Message::DOM::DOMException
854 -object => $self,
855 -type => 'NO_MODIFICATION_ALLOWED_ERR',
856 -subtype => 'READ_ONLY_NODE_ERR';
857 }
858 }
859 if (defined $_[1]) {
860 $$self->{manakai_entity_base_uri} = ''.$_[1];
861 } else {
862 delete $$self->{manakai_entity_base_uri};
863 }
864 }
865
866 if (defined $$self->{manakai_entity_base_uri}) {
867 return $$self->{manakai_entity_base_uri};
868 } else {
869 return $$self->{document_uri};
870 }
871 } # manakai_entity_base_uri
872
873 ## TODO: documentation
874 sub manakai_has_bom ($;$);
875
876 sub input_encoding ($;$);
877
878 sub strict_error_checking ($;$) {
879 ## NOTE: Same as trivial boolean accessor, except no read-only checking.
880 if (@_ > 1) {
881 if ($_[1]) {
882 ${$_[0]}->{strict_error_checking} = 1;
883 } else {
884 delete ${$_[0]}->{strict_error_checking};
885 }
886 }
887 return ${$_[0]}->{strict_error_checking};
888 } # strict_error_checking
889
890 ## ISSUE: Setting manakai_is_html true shadows
891 ## xml_* properties. Is this desired?
892
893 sub xml_encoding ($;$) {
894 my $self = $_[0];
895 if (@_ > 1) {
896 ## NOTE: A manakai extension.
897 if ($$self->{strict_error_checking}) {
898 if ($$self->{manakai_is_html}) {
899 report Message::DOM::DOMException
900 -object => $self,
901 -type => 'NOT_SUPPORTED_ERR',
902 -subtype => 'NON_HTML_OPERATION_ERR';
903 }
904 if ($$self->{manakai_read_only}) {
905 report Message::DOM::DOMException
906 -object => $self,
907 -type => 'NO_MODIFICATION_ALLOWED_ERR',
908 -subtype => 'READ_ONLY_NODE_ERR';
909 }
910 }
911 if (defined $_[1]) {
912 $$self->{xml_encoding} = ''.$_[1];
913 } else {
914 delete $$self->{xml_encoding};
915 }
916 }
917
918 if ($$self->{manakai_is_html}) {
919 return undef;
920 } else {
921 return $$self->{xml_encoding};
922 }
923 } # xml_encoding
924
925 sub xml_standalone ($;$) {
926 my $self = $_[0];
927 if (@_ > 1) {
928 if ($$self->{strict_error_checking}) {
929 if ($$self->{manakai_is_html}) {
930 report Message::DOM::DOMException
931 -object => $self,
932 -type => 'NOT_SUPPORTED_ERR',
933 -subtype => 'NON_HTML_OPERATION_ERR';
934 }
935 ## NOTE: Not in DOM3.
936 if ($$self->{manakai_read_only}) {
937 report Message::DOM::DOMException
938 -object => $self,
939 -type => 'NO_MODIFICATION_ALLOWED_ERR',
940 -subtype => 'READ_ONLY_NODE_ERR';
941 }
942 }
943 if ($_[1]) {
944 $$self->{xml_standalone} = 1;
945 } else {
946 delete $$self->{xml_standalone};
947 }
948 }
949
950 if ($$self->{manakai_is_html}) {
951 return 0;
952 } else {
953 return $$self->{xml_standalone};
954 }
955 } # xml_standalone
956
957 sub xml_version ($;$) {
958 my $self = $_[0];
959 if (@_ > 1) {
960 my $v = ''.$_[1];
961 if ($$self->{strict_error_checking}) {
962 if ($$self->{manakai_is_html}) {
963 report Message::DOM::DOMException
964 -object => $self,
965 -type => 'NOT_SUPPORTED_ERR',
966 -subtype => 'NON_HTML_OPERATION_ERR';
967 }
968 if ($v ne '1.0' and $v ne '1.1') {
969 report Message::DOM::DOMException
970 -object => $self,
971 -type => 'NOT_SUPPORTED_ERR',
972 -subtype => 'UNKNOWN_XML_VERSION_ERR';
973 }
974 if ($$self->{manakai_read_only}) {
975 ## ISSUE: Not in DOM3.
976 report Message::DOM::DOMException
977 -object => $self,
978 -type => 'NO_MODIFICATION_ALLOWED_ERR',
979 -subtype => 'READ_ONLY_NODE_ERR';
980 }
981 }
982 $$self->{xml_version} = $v;
983 }
984
985 if (defined wantarray) {
986 if ($$self->{manakai_is_html}) {
987 return undef;
988 } elsif (defined $$self->{xml_version}) {
989 return $$self->{xml_version};
990 } else {
991 return '1.0';
992 }
993 }
994 } # xml_version
995
996 ## |Document| methods
997
998 sub get_element_by_id ($$) {
999 local $Error::Depth = $Error::Depth + 1;
1000 my @nodes = @{$_[0]->child_nodes};
1001 N: while (@nodes) {
1002 my $node = shift @nodes;
1003 next N unless $node->node_type == 1; # ELEMENT_NODE
1004 for my $attr (@{$node->attributes}) {
1005 if ($attr->is_id and $attr->value eq $_[1]) {
1006 return $node;
1007 }
1008 }
1009 unshift @nodes, @{$node->child_nodes};
1010 } # N
1011 return undef;
1012 } # get_element_by_id
1013
1014 ## TODO: HTML5 case normalization
1015 sub get_elements_by_tag_name ($$) {
1016 my $name = ''.$_[1];
1017 my $chk;
1018 if ($name eq '*') {
1019 $chk = sub () { 1 };
1020 } else {
1021 $chk = sub ($) {
1022 return $_[0]->manakai_tag_name eq $name;
1023 };
1024 }
1025
1026 require Message::DOM::NodeList;
1027 return bless \[$_[0], $chk], 'Message::DOM::NodeList::GetElementsList';
1028 } # get_elements_by_tag_name
1029
1030 sub get_elements_by_tag_name_ns ($$$) {
1031 my $nsuri = defined $_[1] ? ''.$_[1] : '';
1032 my $lname = ''.$_[2];
1033 my $chk;
1034 if ($nsuri eq '*') {
1035 if ($lname eq '*') {
1036 $chk = sub () { 1 };
1037 } else {
1038 $chk = sub ($) {
1039 return $_[0]->manakai_local_name eq $lname;
1040 };
1041 }
1042 } elsif ($nsuri eq '') {
1043 if ($lname eq '*') {
1044 $chk = sub ($) {
1045 return not defined $_[0]->namespace_uri;
1046 };
1047 } else {
1048 $chk = sub ($) {
1049 return (not defined $_[0]->namespace_uri and
1050 $_[0]->manakai_local_name eq $lname);
1051 };
1052 }
1053 } else {
1054 if ($lname eq '*') {
1055 $chk = sub ($) {
1056 my $ns = $_[0]->namespace_uri;
1057 return (defined $ns and $ns eq $nsuri);
1058 };
1059 } else {
1060 $chk = sub ($) {
1061 my $ns = $_[0]->namespace_uri;
1062 return (defined $ns and $ns eq $nsuri and
1063 $_[0]->manakai_local_name eq $lname);
1064 };
1065 }
1066 }
1067
1068 require Message::DOM::NodeList;
1069 return bless \[$_[0], $chk], 'Message::DOM::NodeList::GetElementsList';
1070 } # get_elements_by_tag_name
1071
1072 ## TODO: import_node
1073
1074 ## TODO: normalize_document
1075
1076 ## TODO: rename_node
1077
1078 ## |DocumentTraversal| methods
1079
1080 ## TODO: create_node_iterator
1081
1082 sub manakai_create_serial_walker ($$;$$$);
1083
1084 sub create_tree_walker ($$;$$$);
1085
1086 ## |DocumentView| attribute
1087
1088 sub default_view ($);
1089
1090 ## |HTMLDocument| attributes
1091
1092 sub compat_mode ($) {
1093 if (${$_[0]}->{manakai_is_html}) {
1094 if (defined ${$_[0]}->{manakai_compat_mode} and
1095 ${$_[0]}->{manakai_compat_mode} eq 'quirks') {
1096 return 'BackCompat';
1097 }
1098 }
1099 return 'CSS1Compat';
1100 } # compat_mode
1101
1102 sub manakai_compat_mode ($;$) {
1103 if (${$_[0]}->{manakai_is_html}) {
1104 if (@_ > 1 and defined $_[1] and
1105 {'no quirks' => 1, 'limited quirks' => 1, 'quirks' => 1}->{$_[1]}) {
1106 ${$_[0]}->{manakai_compat_mode} = $_[1];
1107 }
1108 return ${$_[0]}->{manakai_compat_mode} || 'no quirks';
1109 } else {
1110 return 'no quirks';
1111 }
1112 } # manakai_compat_mode
1113
1114 ## TODO: documentation
1115 sub manakai_head ($) {
1116 local $Error::Depth = $Error::Depth + 1;
1117 my $html = $_[0]->manakai_html;
1118 return undef unless defined $html;
1119 for my $el (@{$html->child_nodes}) {
1120 next unless $el->node_type == 1; # ELEMENT_NODE
1121 my $nsuri = $el->namespace_uri;
1122 next unless defined $nsuri;
1123 next unless $nsuri eq q<http://www.w3.org/1999/xhtml>;
1124 next unless $el->manakai_local_name eq 'head';
1125 return $el;
1126 }
1127 return undef;
1128 } # manakai_head
1129
1130 ## TODO: documentation
1131 sub manakai_html ($) {
1132 local $Error::Depth = $Error::Depth + 1;
1133 my $de = $_[0]->document_element;
1134 my $nsuri = $de->namespace_uri;
1135 if (defined $nsuri and $nsuri eq q<http://www.w3.org/1999/xhtml> and
1136 $de->manakai_local_name eq 'html') {
1137 return $de;
1138 } else {
1139 return undef;
1140 }
1141 } # manakai_html
1142
1143 sub inner_html ($;$) {
1144 my $self = $_[0];
1145 local $Error::Depth = $Error::Depth + 1;
1146
1147 if ($$self->{manakai_is_html}) {
1148 require Whatpm::HTML;
1149 if (@_ > 1) {
1150 ## Step 1
1151 ## TODO: Stop parsing and ...
1152
1153 ## Step 2
1154 my @cn = @{$self->child_nodes};
1155 for (@cn) { ## NOTE: Might throw a |NO_MODIFICATION_ALLOWED_ERR|.
1156 $self->remove_child ($_); #
1157 }
1158
1159 ## Step 3, 4, 5
1160 Whatpm::HTML->parse_string ($_[1] => $self);
1161
1162 ## TODO:
1163 ## <script>var input = function_to_do_xmlhttprequest (location.href);
1164 ## document.innerHTML = input</script>
1165
1166 return unless defined wantarray;
1167 }
1168
1169 require Whatpm::HTML::Serializer;
1170 return ${ Whatpm::HTML::Serializer->get_inner_html ($self) };
1171 } else {
1172 if (@_ > 1) {
1173 ## Step 1
1174 require Whatpm::XML::Parser; # MUST
1175 my $doc = $self->implementation->create_document;
1176
1177 ## Step 2
1178 #
1179
1180 ## Step 3
1181 $doc = Whatpm::XML::Parser->parse_char_string ($_[1] => $doc); # MUST
1182
1183 ## Step 4
1184 #
1185
1186 ## Step 5
1187 ## TODO: ill-formed -> SYNTAX_ERR # MUST
1188
1189 ## Step 6 # MUST
1190 my @cn = @{$self->child_nodes}; ## TODO: If read-only
1191 for (@cn) {
1192 $self->remove_child ($_);
1193 }
1194 ## TODO: strict-document-children option?
1195
1196 ## Step 7, 8, 9, 10
1197 for my $node (@{$doc->child_nodes}) {
1198 $self->append_child ($self->adopt_node ($node));
1199 }
1200
1201 return unless defined wantarray;
1202 }
1203
1204 ## TODO: This serializer is currently not conformant to HTML5 spec.
1205 require Whatpm::XMLSerializer;
1206 my $r = '';
1207 for my $node (@{$self->child_nodes}) {
1208 $r .= ${ Whatpm::XMLSerializer->get_outer_xml ($node, sub {
1209 ## TODO: INVALID_STATE_ERR
1210 }) };
1211 }
1212 return $r;
1213 }
1214 } # inner_html
1215
1216 sub manakai_is_html ($;$) {
1217 if (@_ > 1) {
1218 if ($_[1]) {
1219 ${$_[0]}->{manakai_is_html} = 1;
1220 } else {
1221 delete ${$_[0]}->{manakai_is_html};
1222 delete ${$_[0]}->{manakai_compat_mode};
1223 }
1224 }
1225 return ${$_[0]}->{manakai_is_html};
1226 } # manakai_is_html
1227
1228 package Message::IF::Document;
1229 package Message::IF::DocumentTraversal;
1230 package Message::IF::DocumentXDoctype;
1231 package Message::IF::DocumentSelector;
1232 package Message::IF::HTMLDocument;
1233
1234 package Message::DOM::DOMImplementation;
1235
1236 sub create_document ($;$$$) {
1237 my $r = Message::DOM::Document->____new ($_[0]);
1238
1239 if (defined $_[2]) {
1240 local $Error::Depth = $Error::Depth + 1;
1241 $r->append_child ($r->create_element_ns ($_[1], $_[2])); # NAMESPACE_ERR
1242 ## NOTE: manakai might raise DOMExceptions in cases not defined
1243 ## in DOM3Core spec: XMLNSPREFIX_NONXMLNSNS_ERR,
1244 ## XMLNS_NONXMLNSNS_ERR, and NONXMLNSPREFIX_XMLNSNS_ERR.
1245 } elsif (defined $_[1]) {
1246 report Message::DOM::DOMException
1247 -object => $_[0],
1248 -type => 'NAMESPACE_ERR',
1249 -subtype => 'QNAME_NULLNS_ERR';
1250 }
1251
1252 if (defined $_[3]) {
1253 if ($_[3]->parent_node) {
1254 report Message::DOM::DOMException
1255 -object => $_[0],
1256 -type => 'WRONG_DOCUMENT_ERR',
1257 -subtype => 'INUSE_DOCTYPE_ERR';
1258 }
1259 local $Error::Depth = $Error::Depth + 1;
1260 $r->insert_before ($_[3], $r->first_child); # EXTERNAL_OBJECT_ERR
1261 }
1262
1263 return $r;
1264 } # create_document
1265
1266 =head1 LICENSE
1267
1268 Copyright 2007 Wakaba <w@suika.fam.cx>
1269
1270 This program is free software; you can redistribute it and/or
1271 modify it under the same terms as Perl itself.
1272
1273 =cut
1274
1275 1;
1276 ## $Date: 2008/04/12 15:58:41 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24