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