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