/[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.28 - (show annotations) (download)
Sun Jan 13 06:37:46 2008 UTC (18 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.27: +7 -2 lines
++ manakai/lib/Message/DOM/ChangeLog	13 Jan 2008 06:35:37 -0000
2008-01-13  Wakaba  <wakaba@suika.fam.cx>

	* Window.pm: New Perl module.

	* Document.pm (default_view): Implemented.

	* Element.pm (current_style): Implemented.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24