177d40427748dcee59de09c833f2e0e7a6140b65
[WebKit-https.git] / Source / WebCore / bindings / scripts / CodeGeneratorGObject.pm
1 # Copyright (C) 2008 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
2 # Copyright (C) 2008 Martin Soto <soto@freedesktop.org>
3 # Copyright (C) 2008 Alp Toker <alp@atoker.com>
4 # Copyright (C) 2009 Adam Dingle <adam@yorba.org>
5 # Copyright (C) 2009 Jim Nelson <jim@yorba.org>
6 # Copyright (C) 2009, 2010 Igalia S.L.
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Library General Public
10 # License as published by the Free Software Foundation; either
11 # version 2 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Library General Public License for more details.
17 #
18 # You should have received a copy of the GNU Library General Public License
19 # along with this library; see the file COPYING.LIB.  If not, write to
20 # the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 # Boston, MA 02111-1307, USA.
22
23 package CodeGeneratorGObject;
24
25 use constant FileNamePrefix => "WebKitDOM";
26
27 # Global Variables
28 my %implIncludes = ();
29 my %hdrIncludes = ();
30
31 my $defineTypeMacro = "G_DEFINE_TYPE";
32 my $defineTypeInterfaceImplementation = ")";
33 my @txtEventListeners = ();
34 my @txtInstallEventListeners = ();
35 my @txtInstallSignals = ();
36 my @txtInstallProps = ();
37 my @txtSetProps = ();
38 my @txtGetProps = ();
39
40 my $className = "";
41
42 # Default constructor
43 sub new {
44     my $object = shift;
45     my $reference = { };
46
47     $codeGenerator = shift;
48     $outputDir = shift;
49     mkdir $outputDir;
50
51     bless($reference, $object);
52 }
53
54 my $licenceTemplate = << "EOF";
55 /*
56     This file is part of the WebKit open source project.
57     This file has been generated by generate-bindings.pl. DO NOT MODIFY!
58
59     This library is free software; you can redistribute it and/or
60     modify it under the terms of the GNU Library General Public
61     License as published by the Free Software Foundation; either
62     version 2 of the License, or (at your option) any later version.
63
64     This library is distributed in the hope that it will be useful,
65     but WITHOUT ANY WARRANTY; without even the implied warranty of
66     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
67     Library General Public License for more details.
68
69     You should have received a copy of the GNU Library General Public License
70     along with this library; see the file COPYING.LIB.  If not, write to
71     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
72     Boston, MA 02110-1301, USA.
73 */
74 EOF
75
76 sub GenerateModule {
77 }
78
79 sub GetParentClassName {
80     my $dataNode = shift;
81
82     return "WebKitDOMObject" if @{$dataNode->parents} eq 0;
83     return "WebKitDOM" . $codeGenerator->StripModule($dataNode->parents(0));
84 }
85
86 # From String::CamelCase 0.01
87 sub camelize
88 {
89         my $s = shift;
90         join('', map{ ucfirst $_ } split(/(?<=[A-Za-z])_(?=[A-Za-z])|\b/, $s));
91 }
92
93 sub decamelize
94 {
95         my $s = shift;
96         $s =~ s{([^a-zA-Z]?)([A-Z]*)([A-Z])([a-z]?)}{
97                 my $fc = pos($s)==0;
98                 my ($p0,$p1,$p2,$p3) = ($1,lc$2,lc$3,$4);
99                 my $t = $p0 || $fc ? $p0 : '_';
100                 $t .= $p3 ? $p1 ? "${p1}_$p2$p3" : "$p2$p3" : "$p1$p2";
101                 $t;
102         }ge;
103         $s;
104 }
105
106 sub FixUpDecamelizedName {
107     my $classname = shift;
108
109     # FIXME: try to merge this somehow with the fixes in ClassNameToGobjectType
110     $classname =~ s/x_path/xpath/;
111     $classname =~ s/web_kit/webkit/;
112     $classname =~ s/htmli_frame/html_iframe/;
113
114     return $classname;
115 }
116
117 sub HumanReadableConditional {
118     my @conditional = split('_', shift);
119     my @upperCaseExceptions = ("SQL", "API");
120     my @humanReadable;
121
122     for $part (@conditional) {
123         if (!grep {$_ eq $part} @upperCaseExceptions) {
124             $part = camelize(lc($part));
125         }
126         push(@humanReadable, $part);
127     }
128
129     return join(' ', @humanReadable);
130 }
131
132 sub ClassNameToGObjectType {
133     my $className = shift;
134     my $CLASS_NAME = uc(decamelize($className));
135     # Fixup: with our prefix being 'WebKitDOM' decamelize can't get
136     # WebKitDOMCSS and similar names right, so we have to fix it
137     # manually.
138     $CLASS_NAME =~ s/DOMCSS/DOM_CSS/;
139     $CLASS_NAME =~ s/DOMHTML/DOM_HTML/;
140     $CLASS_NAME =~ s/DOMDOM/DOM_DOM/;
141     $CLASS_NAME =~ s/DOMCDATA/DOM_CDATA/;
142     $CLASS_NAME =~ s/DOMX_PATH/DOM_XPATH/;
143     $CLASS_NAME =~ s/DOM_WEB_KIT/DOM_WEBKIT/;
144     $CLASS_NAME =~ s/DOMUI/DOM_UI/;
145     $CLASS_NAME =~ s/HTMLI_FRAME/HTML_IFRAME/;
146     return $CLASS_NAME;
147 }
148
149 sub GetParentGObjType {
150     my $dataNode = shift;
151
152     return "WEBKIT_TYPE_DOM_OBJECT" if @{$dataNode->parents} eq 0;
153     return "WEBKIT_TYPE_DOM_" . ClassNameToGObjectType($codeGenerator->StripModule($dataNode->parents(0)));
154 }
155
156 sub GetClassName {
157     my $name = $codeGenerator->StripModule(shift);
158
159     return "WebKitDOM$name";
160 }
161
162 sub GetCoreObject {
163     my ($interfaceName, $name, $parameter) = @_;
164
165     return "WebCore::${interfaceName}* $name = WebKit::core($parameter);";
166 }
167
168 sub SkipAttribute {
169     my $attribute = shift;
170
171     if ($attribute->signature->extendedAttributes->{"Custom"}
172         || $attribute->signature->extendedAttributes->{"CustomGetter"}
173         || $attribute->signature->extendedAttributes->{"CustomSetter"}) {
174         return 1;
175     }
176
177     my $propType = $attribute->signature->type;
178     if ($propType =~ /Constructor$/) {
179         return 1;
180     }
181
182     if ($codeGenerator->GetArrayType($propType)) {
183         return 1;
184     }
185
186     # This is for DOMWindow.idl location attribute
187     if ($attribute->signature->name eq "location") {
188         return 1;
189     }
190
191     # This is for HTMLInput.idl valueAsDate
192     if ($attribute->signature->name eq "valueAsDate") {
193         return 1;
194     }
195
196     # This is for DOMWindow.idl Crypto attribute
197     if ($attribute->signature->type eq "Crypto") {
198         return 1;
199     }
200
201     return 0;
202 }
203
204 sub SkipFunction {
205     my $function = shift;
206     my $decamelize = shift;
207     my $prefix = shift;
208
209     my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($function->signature->name);
210     my $functionReturnType = $prefix eq "set_" ? "void" : $function->signature->type;
211     my $isCustomFunction = $function->signature->extendedAttributes->{"Custom"};
212     my $callWith = $function->signature->extendedAttributes->{"CallWith"};
213     my $isUnsupportedCallWith = $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments") || $codeGenerator->ExtendedAttributeContains($callWith, "CallStack");
214
215     if (($isCustomFunction || $isUnsupportedCallWith) &&
216         $functionName ne "webkit_dom_node_replace_child" &&
217         $functionName ne "webkit_dom_node_insert_before" &&
218         $functionName ne "webkit_dom_node_remove_child" &&
219         $functionName ne "webkit_dom_node_append_child" &&
220         $functionName ne "webkit_dom_html_collection_item" &&
221         $functionName ne "webkit_dom_html_collection_named_item") {
222         return 1;
223     }
224
225     if ($function->signature->name eq "getSVGDocument") {
226         return 1;
227     }
228
229     if ($function->signature->name eq "getCSSCanvasContext") {
230         return 1;
231     }
232
233     if ($codeGenerator->GetArrayType($functionReturnType)) {
234         return 1;
235     }
236
237     # Skip functions that have ["Callback"] parameters, because this
238     # code generator doesn't know how to auto-generate callbacks.
239     # Skip functions that have "MediaQueryListListener" or sequence<T> parameters, because this
240     # code generator doesn't know how to auto-generate MediaQueryListListener or sequence<T>.
241     foreach my $param (@{$function->parameters}) {
242         if ($param->extendedAttributes->{"Callback"} ||
243             $param->type eq "MediaQueryListListener" ||
244             $codeGenerator->GetArrayType($param->type)) {
245             return 1;
246         }
247     }
248
249     return 0;
250 }
251
252 # Name type used in the g_value_{set,get}_* functions
253 sub GetGValueTypeName {
254     my $type = shift;
255
256     my %types = ("DOMString", "string",
257                  "DOMTimeStamp", "uint",
258                  "float", "float",
259                  "double", "double",
260                  "boolean", "boolean",
261                  "char", "char",
262                  "long", "long",
263                  "long long", "int64",
264                  "short", "int",
265                  "uchar", "uchar",
266                  "unsigned", "uint",
267                  "int", "int",
268                  "unsigned int", "uint",
269                  "unsigned long long", "uint64", 
270                  "unsigned long", "ulong",
271                  "unsigned short", "uint");
272
273     return $types{$type} ? $types{$type} : "object";
274 }
275
276 # Name type used in C declarations
277 sub GetGlibTypeName {
278     my $type = shift;
279     my $name = GetClassName($type);
280
281     my %types = ("DOMString", "gchar*",
282                  "DOMTimeStamp", "guint32",
283                  "CompareHow", "gushort",
284                  "float", "gfloat",
285                  "double", "gdouble",
286                  "boolean", "gboolean",
287                  "char", "gchar",
288                  "long", "glong",
289                  "long long", "gint64",
290                  "short", "gshort",
291                  "uchar", "guchar",
292                  "unsigned", "guint",
293                  "int", "gint",
294                  "unsigned int", "guint",
295                  "unsigned long", "gulong",
296                  "unsigned long long", "guint64",
297                  "unsigned short", "gushort",
298                  "void", "void");
299
300     return $types{$type} ? $types{$type} : "$name*";
301 }
302
303 sub IsGDOMClassType {
304     my $type = shift;
305
306     return 0 if $codeGenerator->IsNonPointerType($type) || $codeGenerator->IsStringType($type);
307     return 1;
308 }
309
310 sub GetReadableProperties {
311     my $properties = shift;
312
313     my @result = ();
314
315     foreach my $property (@{$properties}) {
316         if (!SkipAttribute($property)) {
317             push(@result, $property);
318         }
319     }
320
321     return @result;
322 }
323
324 sub GetWriteableProperties {
325     my $properties = shift;
326     my @result = ();
327
328     foreach my $property (@{$properties}) {
329         my $writeable = $property->type !~ /^readonly/;
330         my $gtype = GetGValueTypeName($property->signature->type);
331         my $hasGtypeSignature = ($gtype eq "boolean" || $gtype eq "float" || $gtype eq "double" ||
332                                  $gtype eq "uint64" || $gtype eq "ulong" || $gtype eq "long" || 
333                                  $gtype eq "uint" || $gtype eq "ushort" || $gtype eq "uchar" ||
334                                  $gtype eq "char" || $gtype eq "string");
335         # FIXME: We are not generating setters for 'Replaceable'
336         # attributes now, but we should somehow.
337         my $replaceable = $property->signature->extendedAttributes->{"Replaceable"};
338         if ($writeable && $hasGtypeSignature && !$replaceable) {
339             push(@result, $property);
340         }
341     }
342
343     return @result;
344 }
345
346 sub GenerateConditionalWarning
347 {
348     my $node = shift;
349     my $indentSize = shift;
350     if (!$indentSize) {
351         $indentSize = 4;
352     }
353
354     my $conditional = $node->extendedAttributes->{"Conditional"};
355     my @warn;
356
357     if ($conditional) {
358         if ($conditional =~ /&/) {
359             my @splitConditionals = split(/&/, $conditional);
360             foreach $condition (@splitConditionals) {
361                 push(@warn, "#if !ENABLE($condition)\n");
362                 push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n");
363                 push(@warn, "#endif\n");
364             }
365         } elsif ($conditional =~ /\|/) {
366             foreach $condition (split(/\|/, $conditional)) {
367                 push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n");
368             }
369         } else {
370             push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($conditional) . "\")\n");
371         }
372     }
373
374     return @warn;
375 }
376
377 sub GenerateProperty {
378     my $attribute = shift;
379     my $interfaceName = shift;
380     my @writeableProperties = @{shift @_};
381     my $parentNode = shift;
382
383     my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature);
384     my @conditionalWarn = GenerateConditionalWarning($attribute->signature, 8);
385     my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode);
386     my @parentConditionalWarn = GenerateConditionalWarning($parentNode, 8);
387     my $camelPropName = $attribute->signature->name;
388     my $setPropNameFunction = $codeGenerator->WK_ucfirst($camelPropName);
389     my $getPropNameFunction = $codeGenerator->WK_lcfirst($camelPropName);
390
391     my $propName = decamelize($camelPropName);
392     my $propNameCaps = uc($propName);
393     $propName =~ s/_/-/g;
394     my ${propEnum} = "PROP_${propNameCaps}";
395     push(@cBodyProperties, "    ${propEnum},\n");
396
397     my $propType = $attribute->signature->type;
398     my ${propGType} = decamelize($propType);
399     my ${ucPropGType} = uc($propGType);
400
401     my $gtype = GetGValueTypeName($propType);
402     my $gparamflag = "WEBKIT_PARAM_READABLE";
403     my $writeable = $attribute->type !~ /^readonly/;
404     my $const = "read-only ";
405     my $custom = $attribute->signature->extendedAttributes->{"Custom"};
406     if ($writeable && $custom) {
407         $const = "read-only (due to custom functions needed in webkitdom)";
408         return;
409     }
410     if ($writeable && !$custom) {
411         $gparamflag = "WEBKIT_PARAM_READWRITE";
412         $const = "read-write ";
413     }
414
415     my $type = GetGlibTypeName($propType);
416     $nick = decamelize("${interfaceName}_${propName}");
417     $long = "${const} ${type} ${interfaceName}.${propName}";
418
419     my $convertFunction = "";
420     if ($gtype eq "string") {
421         $convertFunction = "WTF::String::fromUTF8";
422     }
423
424     my ($getterFunctionName, @getterArguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute);
425     my ($setterFunctionName, @setterArguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute);
426
427     if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) {
428         my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"};
429         $implIncludes{"${implementedBy}.h"} = 1;
430         push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))");
431         unshift(@getterArguments, "coreSelf");
432         unshift(@setterArguments, "coreSelf");
433         $getterFunctionName = "WebCore::${implementedBy}::$getterFunctionName";
434         $setterFunctionName = "WebCore::${implementedBy}::$setterFunctionName";
435     } else {
436         push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))");
437         $getterFunctionName = "coreSelf->$getterFunctionName";
438         $setterFunctionName = "coreSelf->$setterFunctionName";
439     }
440     push(@getterArguments, "ec") if @{$attribute->getterExceptions};
441     push(@setterArguments, "ec") if @{$attribute->setterExceptions};
442
443     if (grep {$_ eq $attribute} @writeableProperties) {
444         push(@txtSetProps, "    case ${propEnum}: {\n");
445         push(@txtSetProps, "#if ${parentConditionalString}\n") if $parentConditionalString;
446         push(@txtSetProps, "#if ${conditionalString}\n") if $conditionalString;
447         push(@txtSetProps, "        WebCore::ExceptionCode ec = 0;\n") if @{$attribute->setterExceptions};
448         push(@txtSetProps, "        ${setterFunctionName}(" . join(", ", @setterArguments) . ");\n");
449         push(@txtSetProps, "#else\n") if $conditionalString;
450         push(@txtSetProps, @conditionalWarn) if scalar(@conditionalWarn);
451         push(@txtSetProps, "#endif /* ${conditionalString} */\n") if $conditionalString;
452         push(@txtSetProps, "#else\n") if $parentConditionalString;
453         push(@txtSetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn);
454         push(@txtSetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString;
455         push(@txtSetProps, "        break;\n    }\n");
456     }
457
458     push(@txtGetProps, "    case ${propEnum}: {\n");
459     push(@txtGetProps, "#if ${parentConditionalString}\n") if $parentConditionalString;
460     push(@txtGetProps, "#if ${conditionalString}\n") if $conditionalString;
461     push(@txtGetProps, "        WebCore::ExceptionCode ec = 0;\n") if @{$attribute->getterExceptions};
462
463     my $postConvertFunction = "";
464     my $done = 0;
465     if ($gtype eq "string") {
466         push(@txtGetProps, "        g_value_take_string(value, convertToUTF8String(${getterFunctionName}(" . join(", ", @getterArguments) . ")));\n");
467         $done = 1;
468     } elsif ($gtype eq "object") {
469         push(@txtGetProps, "        RefPtr<WebCore::${propType}> ptr = ${getterFunctionName}(" . join(", ", @getterArguments) . ");\n");
470         push(@txtGetProps, "        g_value_set_object(value, WebKit::kit(ptr.get()));\n");
471         $done = 1;
472     }
473
474     # FIXME: get rid of this glitch?
475     my $_gtype = $gtype;
476     if ($gtype eq "ushort") {
477         $_gtype = "uint";
478     }
479
480     if (!$done) {
481         if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) {
482             my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"};
483             $implIncludes{"${implementedBy}.h"} = 1;
484             push(@txtGetProps, "        g_value_set_$_gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) .  ")${postConvertFunction});\n");
485         } else {
486             push(@txtGetProps, "        g_value_set_$_gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) . ")${postConvertFunction});\n");
487         }
488     }
489
490     push(@txtGetProps, "#else\n") if $conditionalString;
491     push(@txtGetProps, @conditionalWarn) if scalar(@conditionalWarn);
492     push(@txtGetProps, "#endif /* ${conditionalString} */\n") if $conditionalString;
493     push(@txtGetProps, "#else\n") if $parentConditionalString;
494     push(@txtGetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn);
495     push(@txtGetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString;
496     push(@txtGetProps, "        break;\n    }\n");
497
498     my %param_spec_options = ("int", "G_MININT, /* min */\nG_MAXINT, /* max */\n0, /* default */",
499                               "boolean", "FALSE, /* default */",
500                               "float", "-G_MAXFLOAT, /* min */\nG_MAXFLOAT, /* max */\n0.0, /* default */",
501                               "double", "-G_MAXDOUBLE, /* min */\nG_MAXDOUBLE, /* max */\n0.0, /* default */",
502                               "uint64", "0, /* min */\nG_MAXUINT64, /* min */\n0, /* default */",
503                               "long", "G_MINLONG, /* min */\nG_MAXLONG, /* max */\n0, /* default */",
504                               "int64", "G_MININT64, /* min */\nG_MAXINT64, /* max */\n0, /* default */",
505                               "ulong", "0, /* min */\nG_MAXULONG, /* max */\n0, /* default */",
506                               "uint", "0, /* min */\nG_MAXUINT, /* max */\n0, /* default */",
507                               "ushort", "0, /* min */\nG_MAXUINT16, /* max */\n0, /* default */",
508                               "uchar", "G_MININT8, /* min */\nG_MAXINT8, /* max */\n0, /* default */",
509                               "char", "0, /* min */\nG_MAXUINT8, /* max */\n0, /* default */",
510                               "string", "\"\", /* default */",
511                               "object", "WEBKIT_TYPE_DOM_${ucPropGType}, /* gobject type */");
512
513     my $txtInstallProp = << "EOF";
514     g_object_class_install_property(gobjectClass,
515                                     ${propEnum},
516                                     g_param_spec_${_gtype}("${propName}", /* name */
517                                                            "$nick", /* short description */
518                                                            "$long", /* longer - could do with some extra doc stuff here */
519                                                            $param_spec_options{$gtype}
520                                                            ${gparamflag}));
521 EOF
522     push(@txtInstallProps, $txtInstallProp);
523 }
524
525 sub GenerateProperties {
526     my ($object, $interfaceName, $dataNode) = @_;
527
528     my $clsCaps = substr(ClassNameToGObjectType($className), 12);
529     my $lowerCaseIfaceName = "webkit_dom_" . (FixUpDecamelizedName(decamelize($interfaceName)));
530
531     my $conditionGuardStart = "";
532     my $conditionGuardEnd = "";
533     my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
534     if ($conditionalString) {
535         $conditionGuardStart = "#if ${conditionalString}";
536         $conditionGuardEnd = "#endif // ${conditionalString}";
537     }
538
539     # Properties
540     my $implContent = "";
541
542     # Properties
543     $implContent = << "EOF";
544 enum {
545     PROP_0,
546 EOF
547     push(@cBodyProperties, $implContent);
548
549     my @readableProperties = GetReadableProperties($dataNode->attributes);
550
551     my $privFunction = GetCoreObject($interfaceName, "coreSelf", "self");
552
553     my $txtGetProp = << "EOF";
554 static void ${lowerCaseIfaceName}_get_property(GObject* object, guint propertyId, GValue* value, GParamSpec* pspec)
555 {
556     WebCore::JSMainThreadNullState state;
557 EOF
558     push(@txtGetProps, $txtGetProp);
559     if (scalar @readableProperties > 0) {
560         $txtGetProp = << "EOF";
561 $conditionGuardStart
562     ${className}* self = WEBKIT_DOM_${clsCaps}(object);
563     $privFunction
564 $conditionGuardEnd
565 EOF
566         push(@txtGetProps, $txtGetProp);
567     }
568
569     $txtGetProp = << "EOF";
570     switch (propertyId) {
571 EOF
572     push(@txtGetProps, $txtGetProp);
573
574     my @writeableProperties = GetWriteableProperties(\@readableProperties);
575
576     my $txtSetProps = << "EOF";
577 static void ${lowerCaseIfaceName}_set_property(GObject* object, guint propertyId, const GValue* value, GParamSpec* pspec)
578 {
579     WebCore::JSMainThreadNullState state;
580 EOF
581     push(@txtSetProps, $txtSetProps);
582
583     if (scalar @writeableProperties > 0) {
584         $txtSetProps = << "EOF";
585 $conditionGuardStart
586     ${className}* self = WEBKIT_DOM_${clsCaps}(object);
587     $privFunction
588 $conditionGuardEnd
589 EOF
590         push(@txtSetProps, $txtSetProps);
591     }
592
593     $txtSetProps = << "EOF";
594     switch (propertyId) {
595 EOF
596     push(@txtSetProps, $txtSetProps);
597
598     foreach my $attribute (@readableProperties) {
599         if ($attribute->signature->type ne "EventListener" &&
600             $attribute->signature->type ne "MediaQueryListListener") {
601             GenerateProperty($attribute, $interfaceName, \@writeableProperties, $dataNode);
602         }
603     }
604
605     push(@cBodyProperties, "};\n\n");
606
607     $txtGetProp = << "EOF";
608     default:
609         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
610         break;
611     }
612 }
613 EOF
614     push(@txtGetProps, $txtGetProp);
615
616     $txtSetProps = << "EOF";
617     default:
618         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
619         break;
620     }
621 }
622 EOF
623     push(@txtSetProps, $txtSetProps);
624
625     # Do not insert extra spaces when interpolating array variables
626     $" = "";
627
628     $implContent = << "EOF";
629 static void ${lowerCaseIfaceName}_finalize(GObject* object)
630 {
631 $conditionGuardStart
632     WebKitDOMObject* domObject = WEBKIT_DOM_OBJECT(object);
633     
634     if (domObject->coreObject) {
635         WebCore::${interfaceName}* coreObject = static_cast<WebCore::${interfaceName}*>(domObject->coreObject);
636
637         WebKit::DOMObjectCache::forget(coreObject);
638         coreObject->deref();
639
640         domObject->coreObject = 0;
641     }
642 $conditionGuardEnd
643
644     G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->finalize(object);
645 }
646
647 @txtSetProps
648
649 @txtGetProps
650
651 static void ${lowerCaseIfaceName}_constructed(GObject* object)
652 {
653 EOF
654     push(@cBodyProperties, $implContent);
655
656     $implContent = << "EOF";
657 @txtInstallEventListeners
658     if (G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->constructed)
659         G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->constructed(object);
660 }
661
662 static void ${lowerCaseIfaceName}_class_init(${className}Class* requestClass)
663 {
664     GObjectClass* gobjectClass = G_OBJECT_CLASS(requestClass);
665     gobjectClass->finalize = ${lowerCaseIfaceName}_finalize;
666     gobjectClass->set_property = ${lowerCaseIfaceName}_set_property;
667     gobjectClass->get_property = ${lowerCaseIfaceName}_get_property;
668     gobjectClass->constructed = ${lowerCaseIfaceName}_constructed;
669
670 @txtInstallProps
671 @txtInstallSignals
672 }
673
674 static void ${lowerCaseIfaceName}_init(${className}* request)
675 {
676 }
677
678 EOF
679     push(@cBodyProperties, $implContent);
680 }
681
682 sub GenerateHeader {
683     my ($object, $interfaceName, $parentClassName) = @_;
684
685     my $implContent = "";
686
687     # Add the default header template
688     @hPrefix = split("\r", $licenceTemplate);
689     push(@hPrefix, "\n");
690
691     #Header guard
692     my $guard = $className . "_h";
693
694     @hPrefixGuard = << "EOF";
695 #ifndef $guard
696 #define $guard
697
698 EOF
699
700     $implContent = << "EOF";
701 G_BEGIN_DECLS
702 EOF
703
704     push(@hBodyPre, $implContent);
705
706     my $decamelize = FixUpDecamelizedName(decamelize($interfaceName));
707     my $clsCaps = uc($decamelize);
708     my $lowerCaseIfaceName = "webkit_dom_" . ($decamelize);
709
710     $implContent = << "EOF";
711 #define WEBKIT_TYPE_DOM_${clsCaps}            (${lowerCaseIfaceName}_get_type())
712 #define WEBKIT_DOM_${clsCaps}(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_DOM_${clsCaps}, ${className}))
713 #define WEBKIT_DOM_${clsCaps}_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class)
714 #define WEBKIT_DOM_IS_${clsCaps}(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_DOM_${clsCaps}))
715 #define WEBKIT_DOM_IS_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_DOM_${clsCaps}))
716 #define WEBKIT_DOM_${clsCaps}_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class))
717
718 struct _${className} {
719     ${parentClassName} parent_instance;
720 };
721
722 struct _${className}Class {
723     ${parentClassName}Class parent_class;
724 };
725
726 WEBKIT_API GType
727 ${lowerCaseIfaceName}_get_type (void);
728
729 EOF
730
731     push(@hBody, $implContent);
732 }
733
734 sub getIncludeHeader {
735     my $type = shift;
736     my $name = GetClassName($type);
737
738     return "" if $type eq "int";
739     return "" if $type eq "long";
740     return "" if $type eq "long long";
741     return "" if $type eq "short";
742     return "" if $type eq "char";
743     return "" if $type eq "float";
744     return "" if $type eq "double";
745     return "" if $type eq "unsigned";
746     return "" if $type eq "unsigned int";
747     return "" if $type eq "unsigned long";
748     return "" if $type eq "unsigned long long";
749     return "" if $type eq "unsigned short";
750     return "" if $type eq "DOMTimeStamp";
751     return "" if $type eq "EventListener";
752     return "" if $type eq "MediaQueryListListener";
753     return "" if $type eq "unsigned char";
754     return "" if $type eq "DOMString";
755     return "" if $type eq "float";
756     return "" if $type eq "boolean";
757     return "" if $type eq "void";
758     return "" if $type eq "CompareHow";
759
760     return "$name.h";
761 }
762
763 sub addIncludeInBody {
764     my $type = shift;
765
766     if ($type eq "DOMObject") {
767         return;
768     }
769
770     my $header = getIncludeHeader($type);
771     if ($header eq "") {
772         return;
773     }
774     
775     if (IsGDOMClassType($type)) {
776         $implIncludes{"webkit/$header"} = 1;
777     } else {
778         $implIncludes{$header} = 1
779     }
780 }
781
782 sub GenerateFunction {
783     my ($object, $interfaceName, $function, $prefix, $parentNode) = @_;
784
785     my $decamelize = FixUpDecamelizedName(decamelize($interfaceName));
786
787     if ($object eq "MediaQueryListListener") {
788         return;
789     }
790
791     if (SkipFunction($function, $decamelize, $prefix)) {
792         return;
793     }
794
795     my $functionSigName = $function->signature->name;
796     my $functionSigType = $prefix eq "set_" ? "void" : $function->signature->type;
797     my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($functionSigName);
798     my $returnType = GetGlibTypeName($functionSigType);
799     my $returnValueIsGDOMType = IsGDOMClassType($functionSigType);
800
801     my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
802     my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode);
803     my @conditionalWarn = GenerateConditionalWarning($function->signature);
804     my @parentConditionalWarn = GenerateConditionalWarning($parentNode);
805
806     my $functionSig = "${className}* self";
807
808     my @callImplParams;
809
810     foreach my $param (@{$function->parameters}) {
811         my $paramIDLType = $param->type;
812         if ($paramIDLType eq "EventListener" || $paramIDLType eq "MediaQueryListListener") {
813             # EventListeners are handled elsewhere.
814             return;
815         }
816         addIncludeInBody($paramIDLType);
817         my $paramType = GetGlibTypeName($paramIDLType);
818         my $const = $paramType eq "gchar*" ? "const " : "";
819         my $paramName = $param->name;
820
821         $functionSig .= ", ${const}$paramType $paramName";
822
823         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
824         if ($paramIsGDOMType) {
825             if ($paramIDLType ne "DOMObject") {
826                 $implIncludes{"webkit/WebKitDOM${paramIDLType}Private.h"} = 1;
827             }
828         }
829         if ($paramIsGDOMType || ($paramIDLType eq "DOMString") || ($paramIDLType eq "CompareHow")) {
830             $paramName = "converted" . $codeGenerator->WK_ucfirst($paramName);
831         }
832         push(@callImplParams, $paramName);
833     }
834
835     if ($returnType ne "void" && $returnValueIsGDOMType && $functionSigType ne "DOMObject") {
836         if ($functionSigType ne "EventTarget") {
837             $implIncludes{"webkit/WebKitDOM${functionSigType}Private.h"} = 1;
838             $implIncludes{"webkit/WebKitDOM${functionSigType}.h"} = 1;
839         } else {
840             $implIncludes{"WebKitDOM${functionSigType}.h"} = 1;
841         }
842
843         $implIncludes{"${functionSigType}.h"} = 1;
844     }
845
846     if (@{$function->raisesExceptions}) {
847         $functionSig .= ", GError** error";
848     }
849
850     # Insert introspection annotations
851     push(@hBody, "/**\n");
852     push(@hBody, " * ${functionName}:\n");
853     push(@hBody, " * \@self: A #${className}\n");
854
855     foreach my $param (@{$function->parameters}) {
856         my $paramType = GetGlibTypeName($param->type);
857         # $paramType can have a trailing * in some cases
858         $paramType =~ s/\*$//;
859         my $paramName = $param->name;
860         push(@hBody, " * \@${paramName}: A #${paramType}\n");
861     }
862     if(@{$function->raisesExceptions}) {
863         push(@hBody, " * \@error: #GError\n");
864     }
865     push(@hBody, " *\n");
866     if (IsGDOMClassType($function->signature->type)) {
867         push(@hBody, " * Returns: (transfer none):\n");
868     } else {
869         push(@hBody, " * Returns:\n");
870     }
871     push(@hBody, " *\n");
872     push(@hBody, "**/\n");
873
874     push(@hBody, "WEBKIT_API $returnType\n$functionName($functionSig);\n");
875     push(@hBody, "\n");
876
877     push(@cBody, "$returnType\n$functionName($functionSig)\n{\n");
878     push(@cBody, "#if ${parentConditionalString}\n") if $parentConditionalString;
879     push(@cBody, "#if ${conditionalString}\n") if $conditionalString;
880
881     if ($returnType ne "void") {
882         # TODO: return proper default result
883         push(@cBody, "    g_return_val_if_fail(self, 0);\n");
884     } else {
885         push(@cBody, "    g_return_if_fail(self);\n");
886     }
887
888     push(@cBody, "    WebCore::JSMainThreadNullState state;\n");
889
890     # The WebKit::core implementations check for null already; no need to duplicate effort.
891     push(@cBody, "    WebCore::${interfaceName}* item = WebKit::core(self);\n");
892
893     foreach my $param (@{$function->parameters}) {
894         my $paramName = $param->name;
895         my $paramIDLType = $param->type;
896         my $paramTypeIsPrimitive = $codeGenerator->IsPrimitiveType($paramIDLType);
897         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
898         if (!$paramTypeIsPrimitive) {
899             if ($returnType ne "void") {
900                 # TODO: return proper default result
901                 # FIXME: Temporary hack for generating a proper implementation
902                 #        of the webkit_dom_document_evaluate function (Bug-ID: 42115)
903                 if (!(($functionName eq "webkit_dom_document_evaluate") && ($paramIDLType eq "XPathResult"))) {
904                     push(@cBody, "    g_return_val_if_fail($paramName, 0);\n");
905                 }
906             } else {
907                 push(@cBody, "    g_return_if_fail($paramName);\n");
908             }
909         }
910     }
911
912     $returnParamName = "";
913     foreach my $param (@{$function->parameters}) {
914         my $paramIDLType = $param->type;
915         my $paramName = $param->name;
916
917         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
918         $convertedParamName = "converted" . $codeGenerator->WK_ucfirst($paramName);
919         if ($paramIDLType eq "DOMString") {
920             push(@cBody, "    WTF::String ${convertedParamName} = WTF::String::fromUTF8($paramName);\n");
921         } elsif ($paramIDLType eq "CompareHow") {
922             push(@cBody, "    WebCore::Range::CompareHow ${convertedParamName} = static_cast<WebCore::Range::CompareHow>($paramName);\n");
923         } elsif ($paramIsGDOMType) {
924             push(@cBody, "    WebCore::${paramIDLType}* ${convertedParamName} = 0;\n");
925             push(@cBody, "    if (${paramName}) {\n");
926             push(@cBody, "        ${convertedParamName} = WebKit::core($paramName);\n");
927
928             if ($returnType ne "void") {
929                 # TODO: return proper default result
930                 push(@cBody, "        g_return_val_if_fail(${convertedParamName}, 0);\n");
931             } else {
932                 push(@cBody, "        g_return_if_fail(${convertedParamName});\n");
933             }
934
935             push(@cBody, "    }\n");
936         }
937         $returnParamName = $convertedParamName if $param->extendedAttributes->{"CustomReturn"};
938     }
939
940     my $assign = "";
941     my $assignPre = "";
942     my $assignPost = "";
943
944     # We need to special-case these Node methods because their C++
945     # signature is different from what we'd expect given their IDL
946     # description; see Node.h.
947     my $functionHasCustomReturn = $functionName eq "webkit_dom_node_append_child" ||
948         $functionName eq "webkit_dom_node_insert_before" ||
949         $functionName eq "webkit_dom_node_replace_child" ||
950         $functionName eq "webkit_dom_node_remove_child";
951          
952     if ($returnType ne "void" && !$functionHasCustomReturn) {
953         if ($returnValueIsGDOMType) {
954             $assign = "RefPtr<WebCore::${functionSigType}> gobjectResult = ";
955             $assignPre = "WTF::getPtr(";
956             $assignPost = ")";
957         } else {
958             $assign = "${returnType} result = ";
959         }
960     }
961
962     if (@{$function->raisesExceptions}) {
963         push(@cBody, "    WebCore::ExceptionCode ec = 0;\n") ;
964         push(@callImplParams, "ec");
965     }
966
967     if ($functionHasCustomReturn) {
968         push(@cBody, "    bool ok = item->${functionSigName}(" . join(", ", @callImplParams) . ");\n");
969         my $customNodeAppendChild = << "EOF";
970     if (ok)
971     {
972         ${returnType} result = WebKit::kit($returnParamName);
973         return result;
974     }
975 EOF
976         push(@cBody, $customNodeAppendChild);
977     
978         if(@{$function->raisesExceptions}) {
979             my $exceptionHandling = << "EOF";
980
981     WebCore::ExceptionCodeDescription ecdesc(ec);
982     g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name);
983 EOF
984             push(@cBody, $exceptionHandling);
985         }
986         push(@cBody, "return 0;");
987         push(@cBody, "}\n\n");
988         return;
989     } elsif ($functionSigType eq "DOMString") {
990         my $getterContentHead;
991         if ($prefix) {
992             my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function);
993             push(@arguments, @callImplParams);
994             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
995                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
996                 $implIncludes{"${implementedBy}.h"} = 1;
997                 unshift(@arguments, "item");
998                 $functionName = "WebCore::${implementedBy}::${functionName}";
999             } else {
1000                 $functionName = "item->${functionName}";
1001             }
1002             $getterContentHead = "${assign}convertToUTF8String(${functionName}(" . join(", ", @arguments) . "));\n";
1003         } else {
1004             my @arguments = @callImplParams;
1005             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1006                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1007                 $implIncludes{"${implementedBy}.h"} = 1;
1008                 unshift(@arguments, "item");
1009                 $getterContentHead = "${assign}convertToUTF8String(WebCore::${implementedBy}::${functionSigName}(" . join(", ", @arguments) . "));\n";
1010             } else {
1011                 $getterContentHead = "${assign}convertToUTF8String(item->${functionSigName}(" . join(", ", @arguments) . "));\n";
1012             }
1013         }
1014         push(@cBody, "    ${getterContentHead}");
1015     } else {
1016         my $contentHead;
1017         if ($prefix eq "get_") {
1018             my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function);
1019             push(@arguments, @callImplParams);
1020             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1021                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1022                 $implIncludes{"${implementedBy}.h"} = 1;
1023                 unshift(@arguments, "item");
1024                 $functionName = "WebCore::${implementedBy}::${functionName}";
1025             } else {
1026                 $functionName = "item->${functionName}";
1027             }
1028             $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1029         } elsif ($prefix eq "set_") {
1030             my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $function);
1031             push(@arguments, @callImplParams);
1032             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1033                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1034                 $implIncludes{"${implementedBy}.h"} = 1;
1035                 unshift(@arguments, "item");
1036                 $functionName = "WebCore::${implementedBy}::${functionName}";
1037                 $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1038             } else {
1039                 $functionName = "item->${functionName}";
1040                 $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1041             }
1042         } else {
1043             my @arguments = @callImplParams;
1044             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1045                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1046                 $implIncludes{"${implementedBy}.h"} = 1;
1047                 unshift(@arguments, "item");
1048                 $contentHead = "${assign}${assignPre}WebCore::${implementedBy}::${functionSigName}(" . join(", ", @arguments) . "${assignPost});\n";
1049             } else {
1050                 $contentHead = "${assign}${assignPre}item->${functionSigName}(" . join(", ", @arguments) . "${assignPost});\n";
1051             }
1052         }
1053         push(@cBody, "    ${contentHead}");
1054         
1055         if(@{$function->raisesExceptions}) {
1056             my $exceptionHandling = << "EOF";
1057     if (ec) {
1058         WebCore::ExceptionCodeDescription ecdesc(ec);
1059         g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name);
1060     }
1061 EOF
1062             push(@cBody, $exceptionHandling);
1063         }
1064     }
1065
1066     if ($returnType ne "void" && !$functionHasCustomReturn) {
1067         if ($functionSigType ne "DOMObject") {
1068             if ($returnValueIsGDOMType) {
1069                 push(@cBody, "    ${returnType} result = WebKit::kit(gobjectResult.get());\n");
1070             }
1071         }
1072         if ($functionSigType eq "DOMObject") {
1073             push(@cBody, "    return 0; // TODO: return canvas object\n");
1074         } else {
1075             push(@cBody, "    return result;\n");
1076         }
1077     }
1078
1079     if ($conditionalString) {
1080         push(@cBody, "#else\n");
1081         push(@cBody, @conditionalWarn) if scalar(@conditionalWarn);
1082         if ($returnType ne "void") {
1083             if ($codeGenerator->IsNonPointerType($functionSigType)) {
1084                 push(@cBody, "    return static_cast<${returnType}>(0);\n");
1085             } else {
1086                 push(@cBody, "    return 0;\n");
1087             }
1088         }
1089         push(@cBody, "#endif /* ${conditionalString} */\n");
1090     }
1091
1092     if ($parentConditionalString) {
1093         push(@cBody, "#else\n");
1094         push(@cBody, @parentConditionalWarn) if scalar(@parentConditionalWarn);
1095         if ($returnType ne "void") {
1096             if ($codeGenerator->IsNonPointerType($functionSigType)) {
1097                 push(@cBody, "    return static_cast<${returnType}>(0);\n");
1098             } else {
1099                 push(@cBody, "    return 0;\n");
1100             }
1101         }
1102         push(@cBody, "#endif /* ${parentConditionalString} */\n");
1103     }
1104
1105     push(@cBody, "}\n\n");
1106 }
1107
1108 sub ClassHasFunction {
1109     my ($class, $name) = @_;
1110
1111     foreach my $function (@{$class->functions}) {
1112         if ($function->signature->name eq $name) {
1113             return 1;
1114         }
1115     }
1116
1117     return 0;
1118 }
1119
1120 sub GenerateFunctions {
1121     my ($object, $interfaceName, $dataNode) = @_;
1122
1123     foreach my $function (@{$dataNode->functions}) {
1124         $object->GenerateFunction($interfaceName, $function, "", $dataNode);
1125     }
1126
1127     TOP:
1128     foreach my $attribute (@{$dataNode->attributes}) {
1129         if (SkipAttribute($attribute) ||
1130             $attribute->signature->type eq "EventListener" ||
1131             $attribute->signature->type eq "MediaQueryListListener") {
1132             next TOP;
1133         }
1134         
1135         if ($attribute->signature->name eq "type"
1136             # This will conflict with the get_type() function we define to return a GType
1137             # according to GObject conventions.  Skip this for now.
1138             || $attribute->signature->name eq "URL"     # TODO: handle this
1139             ) {
1140             next TOP;
1141         }
1142             
1143         my $attrNameUpper = $codeGenerator->WK_ucfirst($attribute->signature->name);
1144         my $getname = "get${attrNameUpper}";
1145         my $setname = "set${attrNameUpper}";
1146         if (ClassHasFunction($dataNode, $getname) || ClassHasFunction($dataNode, $setname)) {
1147             # Very occasionally an IDL file defines getter/setter functions for one of its
1148             # attributes; in this case we don't need to autogenerate the getter/setter.
1149             next TOP;
1150         }
1151         
1152         # Generate an attribute getter.  For an attribute "foo", this is a function named
1153         # "get_foo" which calls a DOM class method named foo().
1154         my $function = new domFunction();
1155         $function->signature($attribute->signature);
1156         $function->raisesExceptions($attribute->getterExceptions);
1157         $object->GenerateFunction($interfaceName, $function, "get_", $dataNode);
1158
1159         # FIXME: We are not generating setters for 'Replaceable'
1160         # attributes now, but we should somehow.
1161         if ($attribute->type =~ /^readonly/ ||
1162             $attribute->signature->extendedAttributes->{"Replaceable"}) {
1163             next TOP;
1164         }
1165         
1166         # Generate an attribute setter.  For an attribute, "foo", this is a function named
1167         # "set_foo" which calls a DOM class method named setFoo().
1168         $function = new domFunction();
1169         
1170         $function->signature(new domSignature());
1171         $function->signature->name($attribute->signature->name);
1172         $function->signature->type($attribute->signature->type);
1173         $function->signature->extendedAttributes($attribute->signature->extendedAttributes);
1174         
1175         my $param = new domSignature();
1176         $param->name("value");
1177         $param->type($attribute->signature->type);
1178         my %attributes = ();
1179         $param->extendedAttributes(\%attributes);
1180         my $arrayRef = $function->parameters;
1181         push(@$arrayRef, $param);
1182         
1183         $function->raisesExceptions($attribute->setterExceptions);
1184         
1185         $object->GenerateFunction($interfaceName, $function, "set_", $dataNode);
1186     }
1187 }
1188
1189 sub GenerateCFile {
1190     my ($object, $interfaceName, $parentClassName, $parentGObjType, $dataNode) = @_;
1191
1192     if ($dataNode->extendedAttributes->{"EventTarget"}) {
1193         $object->GenerateEventTargetIface($dataNode);
1194     }
1195
1196     my $implContent = "";
1197
1198     my $clsCaps = uc(FixUpDecamelizedName(decamelize($interfaceName)));
1199     my $lowerCaseIfaceName = "webkit_dom_" . FixUpDecamelizedName(decamelize($interfaceName));
1200
1201     $implContent = << "EOF";
1202 ${defineTypeMacro}(${className}, ${lowerCaseIfaceName}, ${parentGObjType}${defineTypeInterfaceImplementation}
1203
1204 EOF
1205     push(@cBodyProperties, $implContent);
1206
1207     $implContent = << "EOF";
1208 WebCore::${interfaceName}* core(${className}* request)
1209 {
1210     g_return_val_if_fail(request, 0);
1211
1212     WebCore::${interfaceName}* coreObject = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(request)->coreObject);
1213     g_return_val_if_fail(coreObject, 0);
1214
1215     return coreObject;
1216 }
1217
1218 EOF
1219     push(@cBodyPriv, $implContent);
1220
1221     $object->GenerateProperties($interfaceName, $dataNode);
1222     $object->GenerateFunctions($interfaceName, $dataNode);
1223
1224     my $wrapMethod = << "EOF";
1225 ${className}* wrap${interfaceName}(WebCore::${interfaceName}* coreObject)
1226 {
1227     g_return_val_if_fail(coreObject, 0);
1228
1229     // We call ref() rather than using a C++ smart pointer because we can't store a C++ object
1230     // in a C-allocated GObject structure. See the finalize() code for the matching deref().
1231     coreObject->ref();
1232
1233     return WEBKIT_DOM_${clsCaps}(g_object_new(WEBKIT_TYPE_DOM_${clsCaps}, "core-object", coreObject, NULL));
1234 }
1235
1236 EOF
1237     push(@cBodyPriv, $wrapMethod);
1238 }
1239
1240 sub GenerateEndHeader {
1241     my ($object) = @_;
1242
1243     #Header guard
1244     my $guard = $className . "_h";
1245
1246     push(@hBody, "G_END_DECLS\n\n");
1247     push(@hPrefixGuardEnd, "#endif /* $guard */\n");
1248 }
1249
1250 sub UsesManualKitImplementation {
1251     my $type = shift;
1252
1253     return 1 if $type eq "Node" or $type eq "Element" or $type eq "Event";
1254     return 0;
1255 }
1256
1257 sub GenerateEventTargetIface {
1258     my $object = shift;
1259     my $dataNode = shift;
1260
1261     my $interfaceName = $dataNode->name;
1262     my $decamelize = FixUpDecamelizedName(decamelize($interfaceName));
1263
1264     $implIncludes{"GObjectEventListener.h"} = 1;
1265     $implIncludes{"WebKitDOMEventTarget.h"} = 1;
1266     $implIncludes{"WebKitDOMEventPrivate.h"} = 1;
1267
1268     my $impl = << "EOF";
1269 static void webkit_dom_${decamelize}_dispatch_event(WebKitDOMEventTarget* target, WebKitDOMEvent* event, GError** error)
1270 {
1271     WebCore::Event* coreEvent = WebKit::core(event);
1272     WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);
1273
1274     WebCore::ExceptionCode ec = 0;
1275     coreTarget->dispatchEvent(coreEvent, ec);
1276     if (ec) {
1277         WebCore::ExceptionCodeDescription description(ec);
1278         g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), description.code, description.name);
1279     }
1280 }
1281
1282 static gboolean webkit_dom_${decamelize}_add_event_listener(WebKitDOMEventTarget* target, const char* eventName, GCallback handler, gboolean bubble, gpointer userData)
1283 {
1284     WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);
1285     return WebCore::GObjectEventListener::addEventListener(G_OBJECT(target), coreTarget, eventName, handler, bubble, userData);
1286 }
1287
1288 static gboolean webkit_dom_${decamelize}_remove_event_listener(WebKitDOMEventTarget* target, const char* eventName, GCallback handler, gboolean bubble)
1289 {
1290     WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);
1291     return WebCore::GObjectEventListener::removeEventListener(G_OBJECT(target), coreTarget, eventName, handler, bubble);
1292 }
1293
1294 static void webkit_dom_event_target_init(WebKitDOMEventTargetIface* iface)
1295 {
1296     iface->dispatch_event = webkit_dom_${decamelize}_dispatch_event;
1297     iface->add_event_listener = webkit_dom_${decamelize}_add_event_listener;
1298     iface->remove_event_listener = webkit_dom_${decamelize}_remove_event_listener;
1299 }
1300
1301 EOF
1302
1303     push(@cBodyProperties, $impl);
1304
1305     $defineTypeMacro = "G_DEFINE_TYPE_WITH_CODE";
1306     $defineTypeInterfaceImplementation = ", G_IMPLEMENT_INTERFACE(WEBKIT_TYPE_DOM_EVENT_TARGET, webkit_dom_event_target_init))";
1307 }
1308
1309 sub Generate {
1310     my ($object, $dataNode) = @_;
1311
1312     my $parentClassName = GetParentClassName($dataNode);
1313     my $parentGObjType = GetParentGObjType($dataNode);
1314     my $interfaceName = $dataNode->name;
1315
1316     # Add the default impl header template
1317     @cPrefix = split("\r", $licenceTemplate);
1318     push(@cPrefix, "\n");
1319
1320     $implIncludes{"webkitdefines.h"} = 1;
1321     $implIncludes{"webkitglobalsprivate.h"} = 1;
1322     $implIncludes{"webkitmarshal.h"} = 1;
1323     $implIncludes{"DOMObjectCache.h"} = 1;
1324     $implIncludes{"WebKitDOMBinding.h"} = 1;
1325     $implIncludes{"gobject/ConvertToUTF8String.h"} = 1;
1326     $implIncludes{"webkit/$className.h"} = 1;
1327     $implIncludes{"webkit/${className}Private.h"} = 1;
1328     $implIncludes{"${interfaceName}.h"} = 1;
1329     $implIncludes{"JSMainThreadExecState.h"} = 1;
1330     $implIncludes{"ExceptionCode.h"} = 1;
1331
1332     $hdrIncludes{"webkit/${parentClassName}.h"} = 1;
1333
1334     if (!UsesManualKitImplementation($interfaceName)) {
1335         my $converter = << "EOF";
1336 ${className}* kit(WebCore::$interfaceName* obj)
1337 {
1338     g_return_val_if_fail(obj, 0);
1339
1340     if (gpointer ret = DOMObjectCache::get(obj))
1341         return static_cast<${className}*>(ret);
1342
1343     return static_cast<${className}*>(DOMObjectCache::put(obj, WebKit::wrap${interfaceName}(obj)));
1344 }
1345
1346 EOF
1347     push(@cBodyPriv, $converter);
1348     }
1349
1350     $object->GenerateHeader($interfaceName, $parentClassName);
1351     $object->GenerateCFile($interfaceName, $parentClassName, $parentGObjType, $dataNode);
1352     $object->GenerateEndHeader();
1353 }
1354
1355 # Internal helper
1356 sub WriteData {
1357     my $object = shift;
1358     my $dataNode = shift;
1359
1360     # Write a private header.
1361     my $interfaceName = $dataNode->name;
1362     my $filename = "$outputDir/" . $className . "Private.h";
1363     my $guard = "${className}Private_h";
1364     my $parentClassName = GetParentClassName($dataNode);
1365
1366     # Add the guard if the 'Conditional' extended attribute exists
1367     my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
1368
1369     open(PRIVHEADER, ">$filename") or die "Couldn't open file $filename for writing";
1370
1371     print PRIVHEADER split("\r", $licenceTemplate);
1372     print PRIVHEADER "\n";
1373
1374     my $text = << "EOF";
1375 #ifndef $guard
1376 #define $guard
1377
1378 #include "${interfaceName}.h"
1379 #include <glib-object.h>
1380 #include <webkit/${parentClassName}.h>
1381 EOF
1382
1383     print PRIVHEADER $text;
1384     print PRIVHEADER "#if ${conditionalString}\n" if $conditionalString;
1385     print PRIVHEADER map { "#include \"$_\"\n" } sort keys(%hdrPropIncludes);
1386     print PRIVHEADER "\n";
1387     $text = << "EOF";
1388 namespace WebKit {
1389 ${className}* wrap${interfaceName}(WebCore::${interfaceName}*);
1390 WebCore::${interfaceName}* core(${className}* request);
1391 EOF
1392
1393     print PRIVHEADER $text;
1394
1395     if ($className ne "WebKitDOMNode") {
1396         print PRIVHEADER "${className}* kit(WebCore::${interfaceName}* node);\n"
1397     }
1398
1399     $text = << "EOF";
1400 } // namespace WebKit
1401
1402 EOF
1403
1404     print PRIVHEADER $text;
1405     print PRIVHEADER "#endif /* ${conditionalString} */\n\n" if $conditionalString;
1406     print PRIVHEADER "#endif /* ${guard} */\n";
1407
1408     close(PRIVHEADER);
1409
1410     my $basename = FileNamePrefix . $interfaceName;
1411     $basename =~ s/_//g;
1412
1413     # Write public header.
1414     my $fullHeaderFilename = "$outputDir/" . $basename . ".h";
1415     my $installedHeaderFilename = "${basename}.h";
1416     open(HEADER, ">$fullHeaderFilename") or die "Couldn't open file $fullHeaderFilename";
1417
1418     print HEADER @hPrefix;
1419     print HEADER @hPrefixGuard;
1420     print HEADER "#include <glib-object.h>\n";
1421     print HEADER map { "#include <$_>\n" } sort keys(%hdrIncludes);
1422     print HEADER "#include <webkit/webkitdefines.h>\n";
1423     print HEADER "#include <webkit/webkitdomdefines.h>\n\n";
1424     print HEADER @hBodyPre;
1425     print HEADER @hBody;
1426     print HEADER @hPrefixGuardEnd;
1427
1428     close(HEADER);
1429
1430     # Write the implementation sources
1431     my $implFileName = "$outputDir/" . $basename . ".cpp";
1432     open(IMPL, ">$implFileName") or die "Couldn't open file $implFileName";
1433
1434     print IMPL @cPrefix;
1435     print IMPL "#include \"config.h\"\n";
1436     print IMPL "#include \"$installedHeaderFilename\"\n\n";
1437
1438     # Remove the implementation header from the list of included files.
1439     %includesCopy = %implIncludes;
1440     delete ($includesCopy{"webkit/$installedHeaderFilename"});
1441     print IMPL map { "#include \"$_\"\n" } sort keys(%includesCopy);
1442
1443     print IMPL "#include <glib-object.h>\n";
1444     print IMPL "#include <wtf/GetPtr.h>\n";
1445     print IMPL "#include <wtf/RefPtr.h>\n\n";
1446     print IMPL "#if ${conditionalString}\n\n" if $conditionalString;
1447
1448     print IMPL "namespace WebKit {\n\n";
1449     print IMPL @cBodyPriv;
1450     print IMPL "} // namespace WebKit\n\n";
1451     print IMPL "#endif // ${conditionalString}\n\n" if $conditionalString;
1452
1453     print IMPL @cBodyProperties;
1454     print IMPL @cBody;
1455
1456     close(IMPL);
1457
1458     %implIncludes = ();
1459     %hdrIncludes = ();
1460     @hPrefix = ();
1461     @hBody = ();
1462
1463     @cPrefix = ();
1464     @cBody = ();
1465     @cBodyPriv = ();
1466     @cBodyProperties = ();
1467 }
1468
1469 sub GenerateInterface {
1470     my ($object, $dataNode, $defines) = @_;
1471
1472     # Set up some global variables
1473     $className = GetClassName($dataNode->name);
1474
1475     $object->Generate($dataNode);
1476     $object->WriteData($dataNode);
1477 }
1478
1479 1;