02c2d3db6d5cc1f521fdfa8679828420587091c4
[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 $writeable = $property->type !~ /^readonly/;
390         my $gtype = GetGValueTypeName($property->signature->type);
391         my $hasGtypeSignature = ($gtype eq "boolean" || $gtype eq "float" || $gtype eq "double" ||
392                                  $gtype eq "uint64" || $gtype eq "ulong" || $gtype eq "long" || 
393                                  $gtype eq "uint" || $gtype eq "ushort" || $gtype eq "uchar" ||
394                                  $gtype eq "char" || $gtype eq "string");
395         # FIXME: We are not generating setters for 'Replaceable'
396         # attributes now, but we should somehow.
397         my $replaceable = $property->signature->extendedAttributes->{"Replaceable"};
398         if ($writeable && $hasGtypeSignature && !$replaceable) {
399             push(@result, $property);
400         }
401     }
402
403     return @result;
404 }
405
406 sub GenerateConditionalWarning
407 {
408     my $node = shift;
409     my $indentSize = shift;
410     if (!$indentSize) {
411         $indentSize = 4;
412     }
413
414     my $conditional = $node->extendedAttributes->{"Conditional"};
415     my @warn;
416
417     if ($conditional) {
418         if ($conditional =~ /&/) {
419             my @splitConditionals = split(/&/, $conditional);
420             foreach $condition (@splitConditionals) {
421                 push(@warn, "#if !ENABLE($condition)\n");
422                 push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n");
423                 push(@warn, "#endif\n");
424             }
425         } elsif ($conditional =~ /\|/) {
426             foreach $condition (split(/\|/, $conditional)) {
427                 push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n");
428             }
429         } else {
430             push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($conditional) . "\")\n");
431         }
432     }
433
434     return @warn;
435 }
436
437 sub GenerateProperty {
438     my $attribute = shift;
439     my $interfaceName = shift;
440     my @writeableProperties = @{shift @_};
441     my $parentNode = shift;
442
443     my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature);
444     my @conditionalWarn = GenerateConditionalWarning($attribute->signature, 8);
445     my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode);
446     my @parentConditionalWarn = GenerateConditionalWarning($parentNode, 8);
447     my $camelPropName = $attribute->signature->name;
448     my $setPropNameFunction = $codeGenerator->WK_ucfirst($camelPropName);
449     my $getPropNameFunction = $codeGenerator->WK_lcfirst($camelPropName);
450
451     my $propName = decamelize($camelPropName);
452     my $propNameCaps = uc($propName);
453     $propName =~ s/_/-/g;
454     my ${propEnum} = "PROP_${propNameCaps}";
455     push(@cBodyProperties, "    ${propEnum},\n");
456
457     my $propType = $attribute->signature->type;
458     my ${propGType} = decamelize($propType);
459     my ${ucPropGType} = uc($propGType);
460
461     my $gtype = GetGValueTypeName($propType);
462     my $gparamflag = "WEBKIT_PARAM_READABLE";
463     my $writeable = $attribute->type !~ /^readonly/;
464     my $const = "read-only ";
465     my $custom = $attribute->signature->extendedAttributes->{"Custom"};
466     if ($writeable && $custom) {
467         $const = "read-only (due to custom functions needed in webkitdom)";
468         return;
469     }
470     if ($writeable && !$custom) {
471         $gparamflag = "WEBKIT_PARAM_READWRITE";
472         $const = "read-write ";
473     }
474
475     my $type = GetGlibTypeName($propType);
476     $nick = decamelize("${interfaceName}_${propName}");
477     $long = "${const} ${type} ${interfaceName}.${propName}";
478
479     my $convertFunction = "";
480     if ($gtype eq "string") {
481         $convertFunction = "WTF::String::fromUTF8";
482     }
483
484     my ($getterFunctionName, @getterArguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute);
485     my ($setterFunctionName, @setterArguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute);
486
487     if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) {
488         my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"};
489         $implIncludes{"${implementedBy}.h"} = 1;
490         push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))");
491         unshift(@getterArguments, "coreSelf");
492         unshift(@setterArguments, "coreSelf");
493         $getterFunctionName = "WebCore::${implementedBy}::$getterFunctionName";
494         $setterFunctionName = "WebCore::${implementedBy}::$setterFunctionName";
495     } else {
496         push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))");
497         $getterFunctionName = "coreSelf->$getterFunctionName";
498         $setterFunctionName = "coreSelf->$setterFunctionName";
499     }
500     push(@getterArguments, "isNull") if $attribute->signature->isNullable;
501     push(@getterArguments, "ec") if @{$attribute->getterExceptions};
502     push(@setterArguments, "ec") if @{$attribute->setterExceptions};
503
504     if (grep {$_ eq $attribute} @writeableProperties) {
505         push(@txtSetProps, "    case ${propEnum}: {\n");
506         push(@txtSetProps, "#if ${parentConditionalString}\n") if $parentConditionalString;
507         push(@txtSetProps, "#if ${conditionalString}\n") if $conditionalString;
508         push(@txtSetProps, "        WebCore::ExceptionCode ec = 0;\n") if @{$attribute->setterExceptions};
509         push(@txtSetProps, "        ${setterFunctionName}(" . join(", ", @setterArguments) . ");\n");
510         push(@txtSetProps, "#else\n") if $conditionalString;
511         push(@txtSetProps, @conditionalWarn) if scalar(@conditionalWarn);
512         push(@txtSetProps, "#endif /* ${conditionalString} */\n") if $conditionalString;
513         push(@txtSetProps, "#else\n") if $parentConditionalString;
514         push(@txtSetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn);
515         push(@txtSetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString;
516         push(@txtSetProps, "        break;\n    }\n");
517     }
518
519     push(@txtGetProps, "    case ${propEnum}: {\n");
520     push(@txtGetProps, "#if ${parentConditionalString}\n") if $parentConditionalString;
521     push(@txtGetProps, "#if ${conditionalString}\n") if $conditionalString;
522     push(@txtGetProps, "        bool isNull = false;\n") if $attribute->signature->isNullable;
523     push(@txtGetProps, "        WebCore::ExceptionCode ec = 0;\n") if @{$attribute->getterExceptions};
524
525     # FIXME: Should we return a default value when isNull == true?
526
527     my $postConvertFunction = "";
528     my $done = 0;
529     if ($gtype eq "string") {
530         push(@txtGetProps, "        g_value_take_string(value, convertToUTF8String(${getterFunctionName}(" . join(", ", @getterArguments) . ")));\n");
531         $done = 1;
532     } elsif ($gtype eq "object") {
533         push(@txtGetProps, "        RefPtr<WebCore::${propType}> ptr = ${getterFunctionName}(" . join(", ", @getterArguments) . ");\n");
534         push(@txtGetProps, "        g_value_set_object(value, WebKit::kit(ptr.get()));\n");
535         $done = 1;
536     }
537
538     # FIXME: get rid of this glitch?
539     my $_gtype = $gtype;
540     if ($gtype eq "ushort") {
541         $_gtype = "uint";
542     }
543
544     if (!$done) {
545         if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) {
546             my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"};
547             $implIncludes{"${implementedBy}.h"} = 1;
548             push(@txtGetProps, "        g_value_set_$_gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) .  ")${postConvertFunction});\n");
549         } else {
550             push(@txtGetProps, "        g_value_set_$_gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) . ")${postConvertFunction});\n");
551         }
552     }
553
554     push(@txtGetProps, "#else\n") if $conditionalString;
555     push(@txtGetProps, @conditionalWarn) if scalar(@conditionalWarn);
556     push(@txtGetProps, "#endif /* ${conditionalString} */\n") if $conditionalString;
557     push(@txtGetProps, "#else\n") if $parentConditionalString;
558     push(@txtGetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn);
559     push(@txtGetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString;
560     push(@txtGetProps, "        break;\n    }\n");
561
562     my %param_spec_options = ("int", "G_MININT, /* min */\nG_MAXINT, /* max */\n0, /* default */",
563                               "boolean", "FALSE, /* default */",
564                               "float", "-G_MAXFLOAT, /* min */\nG_MAXFLOAT, /* max */\n0.0, /* default */",
565                               "double", "-G_MAXDOUBLE, /* min */\nG_MAXDOUBLE, /* max */\n0.0, /* default */",
566                               "uint64", "0, /* min */\nG_MAXUINT64, /* min */\n0, /* default */",
567                               "long", "G_MINLONG, /* min */\nG_MAXLONG, /* max */\n0, /* default */",
568                               "int64", "G_MININT64, /* min */\nG_MAXINT64, /* max */\n0, /* default */",
569                               "ulong", "0, /* min */\nG_MAXULONG, /* max */\n0, /* default */",
570                               "uint", "0, /* min */\nG_MAXUINT, /* max */\n0, /* default */",
571                               "ushort", "0, /* min */\nG_MAXUINT16, /* max */\n0, /* default */",
572                               "uchar", "G_MININT8, /* min */\nG_MAXINT8, /* max */\n0, /* default */",
573                               "char", "0, /* min */\nG_MAXUINT8, /* max */\n0, /* default */",
574                               "string", "\"\", /* default */",
575                               "object", "WEBKIT_TYPE_DOM_${ucPropGType}, /* gobject type */");
576
577     my $txtInstallProp = << "EOF";
578     g_object_class_install_property(gobjectClass,
579                                     ${propEnum},
580                                     g_param_spec_${_gtype}("${propName}", /* name */
581                                                            "$nick", /* short description */
582                                                            "$long", /* longer - could do with some extra doc stuff here */
583                                                            $param_spec_options{$gtype}
584                                                            ${gparamflag}));
585 EOF
586     push(@txtInstallProps, $txtInstallProp);
587 }
588
589 sub GenerateProperties {
590     my ($object, $interfaceName, $interface) = @_;
591
592     my $clsCaps = substr(ClassNameToGObjectType($className), 12);
593     my $lowerCaseIfaceName = "webkit_dom_" . (FixUpDecamelizedName(decamelize($interfaceName)));
594     my $parentImplClassName = GetParentImplClassName($interface);
595
596     my $conditionGuardStart = "";
597     my $conditionGuardEnd = "";
598     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
599     if ($conditionalString) {
600         $conditionGuardStart = "#if ${conditionalString}";
601         $conditionGuardEnd = "#endif // ${conditionalString}";
602     }
603
604     # Properties
605     my $implContent = "";
606     my @readableProperties = GetReadableProperties($interface->attributes);
607     my @writeableProperties = GetWriteableProperties(\@readableProperties);
608     my $numProperties = scalar @readableProperties;
609
610     # Properties
611     my $privFunction = GetCoreObject($interfaceName, "coreSelf", "self");
612     if ($numProperties > 0) {
613         $implContent = << "EOF";
614 enum {
615     PROP_0,
616 EOF
617         push(@cBodyProperties, $implContent);
618
619         my $txtGetProp = << "EOF";
620 static void ${lowerCaseIfaceName}_get_property(GObject* object, guint propertyId, GValue* value, GParamSpec* pspec)
621 {
622     WebCore::JSMainThreadNullState state;
623 EOF
624         push(@txtGetProps, $txtGetProp);
625         $txtGetProp = << "EOF";
626 $conditionGuardStart
627     ${className}* self = WEBKIT_DOM_${clsCaps}(object);
628     $privFunction
629 $conditionGuardEnd
630 EOF
631         push(@txtGetProps, $txtGetProp);
632
633         $txtGetProp = << "EOF";
634     switch (propertyId) {
635 EOF
636         push(@txtGetProps, $txtGetProp);
637
638         if (scalar @writeableProperties > 0) {
639             my $txtSetProps = << "EOF";
640 static void ${lowerCaseIfaceName}_set_property(GObject* object, guint propertyId, const GValue* value, GParamSpec* pspec)
641 {
642     WebCore::JSMainThreadNullState state;
643 EOF
644             push(@txtSetProps, $txtSetProps);
645
646             $txtSetProps = << "EOF";
647 $conditionGuardStart
648     ${className}* self = WEBKIT_DOM_${clsCaps}(object);
649     $privFunction
650 $conditionGuardEnd
651 EOF
652             push(@txtSetProps, $txtSetProps);
653
654             $txtSetProps = << "EOF";
655     switch (propertyId) {
656 EOF
657             push(@txtSetProps, $txtSetProps);
658         }
659
660         foreach my $attribute (@readableProperties) {
661             if ($attribute->signature->type ne "EventListener" &&
662                 $attribute->signature->type ne "MediaQueryListListener") {
663                 GenerateProperty($attribute, $interfaceName, \@writeableProperties, $interface);
664             }
665         }
666
667         push(@cBodyProperties, "};\n\n");
668
669         $txtGetProp = << "EOF";
670     default:
671         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
672         break;
673     }
674 }
675 EOF
676         push(@txtGetProps, $txtGetProp);
677
678         if (scalar @writeableProperties > 0) {
679             $txtSetProps = << "EOF";
680     default:
681         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
682         break;
683     }
684 }
685 EOF
686             push(@txtSetProps, $txtSetProps);
687         }
688     }
689
690     # Do not insert extra spaces when interpolating array variables
691     $" = "";
692
693     if ($parentImplClassName eq "Object") {
694         $implContent = << "EOF";
695 static void ${lowerCaseIfaceName}_finalize(GObject* object)
696 {
697     ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object);
698 $conditionGuardStart
699     WebKit::DOMObjectCache::forget(priv->coreObject.get());
700 $conditionGuardEnd
701     priv->~${className}Private();
702     G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->finalize(object);
703 }
704
705 EOF
706         push(@cBodyProperties, $implContent);
707     }
708
709     if ($numProperties > 0) {
710         if (scalar @writeableProperties > 0) {
711             push(@cBodyProperties, @txtSetProps);
712             push(@cBodyProperties, "\n");
713         }
714         push(@cBodyProperties, @txtGetProps);
715         push(@cBodyProperties, "\n");
716     }
717
718     # Add a constructor implementation only for direct subclasses of Object to make sure
719     # that the WebCore wrapped object is added only once to the DOM cache. The DOM garbage
720     # collector works because Node is a direct subclass of Object and the version of
721     # DOMObjectCache::put() that receives a Node (which is the one setting the frame) is
722     # always called for DOM objects derived from Node.
723     if ($parentImplClassName eq "Object") {
724         $implContent = << "EOF";
725 static GObject* ${lowerCaseIfaceName}_constructor(GType type, guint constructPropertiesCount, GObjectConstructParam* constructProperties)
726 {
727     GObject* object = G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->constructor(type, constructPropertiesCount, constructProperties);
728 $conditionGuardStart
729     ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object);
730     priv->coreObject = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(object)->coreObject);
731     WebKit::DOMObjectCache::put(priv->coreObject.get(), object);
732 $conditionGuardEnd
733     return object;
734 }
735
736 EOF
737         push(@cBodyProperties, $implContent);
738     }
739
740     $implContent = << "EOF";
741 static void ${lowerCaseIfaceName}_class_init(${className}Class* requestClass)
742 {
743 EOF
744     push(@cBodyProperties, $implContent);
745
746     if ($parentImplClassName eq "Object" || $numProperties > 0) {
747         push(@cBodyProperties, "    GObjectClass* gobjectClass = G_OBJECT_CLASS(requestClass);\n");
748
749         if ($parentImplClassName eq "Object") {
750             push(@cBodyProperties, "    g_type_class_add_private(gobjectClass, sizeof(${className}Private));\n");
751             push(@cBodyProperties, "    gobjectClass->constructor = ${lowerCaseIfaceName}_constructor;\n");
752             push(@cBodyProperties, "    gobjectClass->finalize = ${lowerCaseIfaceName}_finalize;\n");
753         }
754
755         if ($numProperties > 0) {
756             if (scalar @writeableProperties > 0) {
757                 push(@cBodyProperties, "    gobjectClass->set_property = ${lowerCaseIfaceName}_set_property;\n");
758             }
759             push(@cBodyProperties, "    gobjectClass->get_property = ${lowerCaseIfaceName}_get_property;\n");
760             push(@cBodyProperties, "\n");
761             push(@cBodyProperties, @txtInstallProps);
762         }
763     }
764     $implContent = << "EOF";
765 }
766
767 static void ${lowerCaseIfaceName}_init(${className}* request)
768 {
769 EOF
770     push(@cBodyProperties, $implContent);
771
772     if ($parentImplClassName eq "Object") {
773         $implContent = << "EOF";
774     ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(request);
775     new (priv) ${className}Private();
776 EOF
777         push(@cBodyProperties, $implContent);
778     }
779     $implContent = << "EOF";
780 }
781
782 EOF
783     push(@cBodyProperties, $implContent);
784 }
785
786 sub GenerateHeader {
787     my ($object, $interfaceName, $parentClassName) = @_;
788
789     my $implContent = "";
790
791     # Add the default header template
792     @hPrefix = split("\r", $licenceTemplate);
793     push(@hPrefix, "\n");
794
795     # Force single header include.
796     my $headerCheck = << "EOF";
797 #if !defined(__WEBKITDOM_H_INSIDE__) && !defined(BUILDING_WEBKIT)
798 #error "Only <webkitdom/webkitdom.h> can be included directly."
799 #endif
800
801 EOF
802     push(@hPrefix, $headerCheck);
803
804     # Header guard
805     my $guard = $className . "_h";
806
807     @hPrefixGuard = << "EOF";
808 #ifndef $guard
809 #define $guard
810
811 EOF
812
813     $implContent = << "EOF";
814 G_BEGIN_DECLS
815
816 EOF
817
818     push(@hBodyPre, $implContent);
819
820     my $decamelize = FixUpDecamelizedName(decamelize($interfaceName));
821     my $clsCaps = uc($decamelize);
822     my $lowerCaseIfaceName = "webkit_dom_" . ($decamelize);
823
824     $implContent = << "EOF";
825 #define WEBKIT_TYPE_DOM_${clsCaps}            (${lowerCaseIfaceName}_get_type())
826 #define WEBKIT_DOM_${clsCaps}(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_DOM_${clsCaps}, ${className}))
827 #define WEBKIT_DOM_${clsCaps}_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class)
828 #define WEBKIT_DOM_IS_${clsCaps}(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_DOM_${clsCaps}))
829 #define WEBKIT_DOM_IS_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_DOM_${clsCaps}))
830 #define WEBKIT_DOM_${clsCaps}_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class))
831
832 struct _${className} {
833     ${parentClassName} parent_instance;
834 };
835
836 struct _${className}Class {
837     ${parentClassName}Class parent_class;
838 };
839
840 WEBKIT_API GType
841 ${lowerCaseIfaceName}_get_type (void);
842
843 EOF
844
845     push(@hBody, $implContent);
846 }
847
848 sub GetGReturnMacro {
849     my ($paramName, $paramIDLType, $returnType) = @_;
850
851     my $condition;
852     if ($paramIDLType eq "GError") {
853         $condition = "!$paramName || !*$paramName";
854     } elsif (IsGDOMClassType($paramIDLType)) {
855         my $paramTypeCaps = uc(FixUpDecamelizedName(decamelize($paramIDLType)));
856         $condition = "WEBKIT_DOM_IS_${paramTypeCaps}($paramName)";
857     } else {
858         $condition = "$paramName";
859     }
860
861     my $macro;
862     if ($returnType ne "void") {
863         $defaultReturn = $returnType eq "gboolean" ? "FALSE" : 0;
864         $macro = "    g_return_val_if_fail($condition, $defaultReturn);\n";
865     } else {
866         $macro = "    g_return_if_fail($condition);\n";
867     }
868
869     return $macro;
870 }
871
872 sub GenerateFunction {
873     my ($object, $interfaceName, $function, $prefix, $parentNode) = @_;
874
875     my $decamelize = FixUpDecamelizedName(decamelize($interfaceName));
876
877     if ($object eq "MediaQueryListListener") {
878         return;
879     }
880
881     if (SkipFunction($function, $decamelize, $prefix)) {
882         return;
883     }
884
885     return if ($function->signature->name eq "set" and $parentNode->extendedAttributes->{"TypedArray"});
886
887     my $functionSigType = $prefix eq "set_" ? "void" : $function->signature->type;
888     my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($function->signature->name);
889     my $returnType = GetGlibTypeName($functionSigType);
890     my $returnValueIsGDOMType = IsGDOMClassType($functionSigType);
891
892     my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
893     my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode);
894     my @conditionalWarn = GenerateConditionalWarning($function->signature);
895     my @parentConditionalWarn = GenerateConditionalWarning($parentNode);
896
897     my $functionSig = "${className}* self";
898
899     my @callImplParams;
900
901     foreach my $param (@{$function->parameters}) {
902         my $paramIDLType = $param->type;
903         if ($paramIDLType eq "EventListener" || $paramIDLType eq "MediaQueryListListener") {
904             # EventListeners are handled elsewhere.
905             return;
906         }
907
908         my $paramType = GetGlibTypeName($paramIDLType);
909         my $const = $paramType eq "gchar*" ? "const " : "";
910         my $paramName = $param->name;
911
912         $functionSig .= ", ${const}$paramType $paramName";
913
914         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
915         if ($paramIsGDOMType) {
916             if ($paramIDLType ne "any") {
917                 $implIncludes{"WebKitDOM${paramIDLType}Private.h"} = 1;
918             }
919         }
920         if ($paramIsGDOMType || ($paramIDLType eq "DOMString") || ($paramIDLType eq "CompareHow")) {
921             $paramName = "converted" . $codeGenerator->WK_ucfirst($paramName);
922         }
923         push(@callImplParams, $paramName);
924     }
925
926     if ($returnType ne "void" && $returnValueIsGDOMType && $functionSigType ne "any") {
927         $implIncludes{"WebKitDOM${functionSigType}Private.h"} = 1;
928     }
929
930     if (@{$function->raisesExceptions}) {
931         $functionSig .= ", GError** error";
932     }
933
934     # Insert introspection annotations
935     push(@hBody, "/**\n");
936     push(@hBody, " * ${functionName}:\n");
937     push(@hBody, " * \@self: A #${className}\n");
938
939     foreach my $param (@{$function->parameters}) {
940         my $paramType = GetGlibTypeName($param->type);
941         # $paramType can have a trailing * in some cases
942         $paramType =~ s/\*$//;
943         my $paramName = $param->name;
944         push(@hBody, " * \@${paramName}: A #${paramType}\n");
945     }
946     if(@{$function->raisesExceptions}) {
947         push(@hBody, " * \@error: #GError\n");
948     }
949     push(@hBody, " *\n");
950     if (IsGDOMClassType($function->signature->type)) {
951         push(@hBody, " * Returns: (transfer none):\n");
952     } else {
953         push(@hBody, " * Returns:\n");
954     }
955     push(@hBody, " *\n");
956     push(@hBody, "**/\n");
957
958     push(@hBody, "WEBKIT_API $returnType\n$functionName($functionSig);\n");
959     push(@hBody, "\n");
960
961     push(@cBody, "$returnType\n$functionName($functionSig)\n{\n");
962     push(@cBody, "#if ${parentConditionalString}\n") if $parentConditionalString;
963     push(@cBody, "#if ${conditionalString}\n") if $conditionalString;
964
965     push(@cBody, "    WebCore::JSMainThreadNullState state;\n");
966
967     # g_return macros to check parameters of public methods.
968     $gReturnMacro = GetGReturnMacro("self", $interfaceName, $returnType);
969     push(@cBody, $gReturnMacro);
970
971     foreach my $param (@{$function->parameters}) {
972         my $paramName = $param->name;
973         my $paramIDLType = $param->type;
974         my $paramTypeIsPrimitive = $codeGenerator->IsPrimitiveType($paramIDLType);
975         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
976         if (!$paramTypeIsPrimitive) {
977             # FIXME: Temporary hack for generating a proper implementation
978             #        of the webkit_dom_document_evaluate function (Bug-ID: 42115)
979             if (!(($functionName eq "webkit_dom_document_evaluate") && ($paramIDLType eq "XPathResult"))) {
980                 $gReturnMacro = GetGReturnMacro($paramName, $paramIDLType, $returnType);
981                 push(@cBody, $gReturnMacro);
982             }
983         }
984     }
985
986     if (@{$function->raisesExceptions}) {
987         $gReturnMacro = GetGReturnMacro("error", "GError", $returnType);
988         push(@cBody, $gReturnMacro);
989     }
990
991     # The WebKit::core implementations check for null already; no need to duplicate effort.
992     push(@cBody, "    WebCore::${interfaceName}* item = WebKit::core(self);\n");
993
994     $returnParamName = "";
995     foreach my $param (@{$function->parameters}) {
996         my $paramIDLType = $param->type;
997         my $paramName = $param->name;
998
999         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
1000         $convertedParamName = "converted" . $codeGenerator->WK_ucfirst($paramName);
1001         if ($paramIDLType eq "DOMString") {
1002             push(@cBody, "    WTF::String ${convertedParamName} = WTF::String::fromUTF8($paramName);\n");
1003         } elsif ($paramIDLType eq "CompareHow") {
1004             push(@cBody, "    WebCore::Range::CompareHow ${convertedParamName} = static_cast<WebCore::Range::CompareHow>($paramName);\n");
1005         } elsif ($paramIsGDOMType) {
1006             push(@cBody, "    WebCore::${paramIDLType}* ${convertedParamName} = WebKit::core($paramName);\n");
1007         }
1008         $returnParamName = $convertedParamName if $param->extendedAttributes->{"CustomReturn"};
1009     }
1010
1011     my $assign = "";
1012     my $assignPre = "";
1013     my $assignPost = "";
1014
1015     # We need to special-case these Node methods because their C++
1016     # signature is different from what we'd expect given their IDL
1017     # description; see Node.h.
1018     my $functionHasCustomReturn = $functionName eq "webkit_dom_node_append_child" ||
1019         $functionName eq "webkit_dom_node_insert_before" ||
1020         $functionName eq "webkit_dom_node_replace_child" ||
1021         $functionName eq "webkit_dom_node_remove_child";
1022          
1023     if ($returnType ne "void" && !$functionHasCustomReturn) {
1024         if ($returnValueIsGDOMType) {
1025             $assign = "RefPtr<WebCore::${functionSigType}> gobjectResult = ";
1026             $assignPre = "WTF::getPtr(";
1027             $assignPost = ")";
1028         } else {
1029             $assign = "${returnType} result = ";
1030         }
1031     }
1032
1033     # FIXME: Should we return a default value when isNull == true?
1034     if ($function->signature->isNullable) {
1035         push(@cBody, "    bool isNull = false;\n");
1036         push(@callImplParams, "isNull");
1037     }
1038
1039     if (@{$function->raisesExceptions}) {
1040         push(@cBody, "    WebCore::ExceptionCode ec = 0;\n");
1041         push(@callImplParams, "ec");
1042     }
1043
1044     my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $function->signature->name;
1045
1046     if ($functionHasCustomReturn) {
1047         push(@cBody, "    bool ok = item->${functionImplementationName}(" . join(", ", @callImplParams) . ");\n");
1048         my $customNodeAppendChild = << "EOF";
1049     if (ok)
1050         return WebKit::kit($returnParamName);
1051 EOF
1052         push(@cBody, $customNodeAppendChild);
1053     
1054         if(@{$function->raisesExceptions}) {
1055             my $exceptionHandling = << "EOF";
1056
1057     WebCore::ExceptionCodeDescription ecdesc(ec);
1058     g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name);
1059 EOF
1060             push(@cBody, $exceptionHandling);
1061         }
1062         push(@cBody, "    return 0;\n");
1063         push(@cBody, "}\n\n");
1064         return;
1065     } elsif ($functionSigType eq "DOMString") {
1066         my $getterContentHead;
1067         if ($prefix) {
1068             my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function);
1069             push(@arguments, @callImplParams);
1070             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1071                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1072                 $implIncludes{"${implementedBy}.h"} = 1;
1073                 unshift(@arguments, "item");
1074                 $functionName = "WebCore::${implementedBy}::${functionName}";
1075             } else {
1076                 $functionName = "item->${functionName}";
1077             }
1078             $getterContentHead = "${assign}convertToUTF8String(${functionName}(" . join(", ", @arguments) . "));\n";
1079         } else {
1080             my @arguments = @callImplParams;
1081             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1082                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1083                 $implIncludes{"${implementedBy}.h"} = 1;
1084                 unshift(@arguments, "item");
1085                 $getterContentHead = "${assign}convertToUTF8String(WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "));\n";
1086             } else {
1087                 $getterContentHead = "${assign}convertToUTF8String(item->${functionImplementationName}(" . join(", ", @arguments) . "));\n";
1088             }
1089         }
1090         push(@cBody, "    ${getterContentHead}");
1091     } else {
1092         my $contentHead;
1093         if ($prefix eq "get_") {
1094             my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function);
1095             push(@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                 $functionName = "WebCore::${implementedBy}::${functionName}";
1101             } else {
1102                 $functionName = "item->${functionName}";
1103             }
1104             $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1105         } elsif ($prefix eq "set_") {
1106             my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $function);
1107             push(@arguments, @callImplParams);
1108             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1109                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1110                 $implIncludes{"${implementedBy}.h"} = 1;
1111                 unshift(@arguments, "item");
1112                 $functionName = "WebCore::${implementedBy}::${functionName}";
1113                 $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1114             } else {
1115                 $functionName = "item->${functionName}";
1116                 $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1117             }
1118         } else {
1119             my @arguments = @callImplParams;
1120             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1121                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1122                 $implIncludes{"${implementedBy}.h"} = 1;
1123                 unshift(@arguments, "item");
1124                 $contentHead = "${assign}${assignPre}WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n";
1125             } else {
1126                 $contentHead = "${assign}${assignPre}item->${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n";
1127             }
1128         }
1129         push(@cBody, "    ${contentHead}");
1130         
1131         if(@{$function->raisesExceptions}) {
1132             my $exceptionHandling = << "EOF";
1133     if (ec) {
1134         WebCore::ExceptionCodeDescription ecdesc(ec);
1135         g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name);
1136     }
1137 EOF
1138             push(@cBody, $exceptionHandling);
1139         }
1140     }
1141
1142     if ($returnType ne "void" && !$functionHasCustomReturn) {
1143         if ($functionSigType ne "any") {
1144             if ($returnValueIsGDOMType) {
1145                 push(@cBody, "    return WebKit::kit(gobjectResult.get());\n");
1146             } else {
1147                 push(@cBody, "    return result;\n");
1148             }
1149         } else {
1150             push(@cBody, "    return 0; // TODO: return canvas object\n");
1151         }
1152     }
1153
1154     if ($conditionalString) {
1155         push(@cBody, "#else\n");
1156         push(@cBody, @conditionalWarn) if scalar(@conditionalWarn);
1157         if ($returnType ne "void") {
1158             if ($codeGenerator->IsNonPointerType($functionSigType)) {
1159                 push(@cBody, "    return static_cast<${returnType}>(0);\n");
1160             } else {
1161                 push(@cBody, "    return 0;\n");
1162             }
1163         }
1164         push(@cBody, "#endif /* ${conditionalString} */\n");
1165     }
1166
1167     if ($parentConditionalString) {
1168         push(@cBody, "#else\n");
1169         push(@cBody, @parentConditionalWarn) if scalar(@parentConditionalWarn);
1170         if ($returnType ne "void") {
1171             if ($codeGenerator->IsNonPointerType($functionSigType)) {
1172                 push(@cBody, "    return static_cast<${returnType}>(0);\n");
1173             } else {
1174                 push(@cBody, "    return 0;\n");
1175             }
1176         }
1177         push(@cBody, "#endif /* ${parentConditionalString} */\n");
1178     }
1179
1180     push(@cBody, "}\n\n");
1181 }
1182
1183 sub ClassHasFunction {
1184     my ($class, $name) = @_;
1185
1186     foreach my $function (@{$class->functions}) {
1187         if ($function->signature->name eq $name) {
1188             return 1;
1189         }
1190     }
1191
1192     return 0;
1193 }
1194
1195 sub GenerateFunctions {
1196     my ($object, $interfaceName, $interface) = @_;
1197
1198     foreach my $function (@{$interface->functions}) {
1199         $object->GenerateFunction($interfaceName, $function, "", $interface);
1200     }
1201
1202     TOP:
1203     foreach my $attribute (@{$interface->attributes}) {
1204         if (SkipAttribute($attribute) ||
1205             $attribute->signature->type eq "EventListener" ||
1206             $attribute->signature->type eq "MediaQueryListListener") {
1207             next TOP;
1208         }
1209         
1210         if ($attribute->signature->name eq "type"
1211             # This will conflict with the get_type() function we define to return a GType
1212             # according to GObject conventions.  Skip this for now.
1213             || $attribute->signature->name eq "URL"     # TODO: handle this
1214             ) {
1215             next TOP;
1216         }
1217             
1218         my $attrNameUpper = $codeGenerator->WK_ucfirst($attribute->signature->name);
1219         my $getname = "get${attrNameUpper}";
1220         my $setname = "set${attrNameUpper}";
1221         if (ClassHasFunction($interface, $getname) || ClassHasFunction($interface, $setname)) {
1222             # Very occasionally an IDL file defines getter/setter functions for one of its
1223             # attributes; in this case we don't need to autogenerate the getter/setter.
1224             next TOP;
1225         }
1226         
1227         # Generate an attribute getter.  For an attribute "foo", this is a function named
1228         # "get_foo" which calls a DOM class method named foo().
1229         my $function = new domFunction();
1230         $function->signature($attribute->signature);
1231         $function->raisesExceptions($attribute->getterExceptions);
1232         $object->GenerateFunction($interfaceName, $function, "get_", $interface);
1233
1234         # FIXME: We are not generating setters for 'Replaceable'
1235         # attributes now, but we should somehow.
1236         if ($attribute->type =~ /^readonly/ ||
1237             $attribute->signature->extendedAttributes->{"Replaceable"}) {
1238             next TOP;
1239         }
1240         
1241         # Generate an attribute setter.  For an attribute, "foo", this is a function named
1242         # "set_foo" which calls a DOM class method named setFoo().
1243         $function = new domFunction();
1244         
1245         $function->signature(new domSignature());
1246         $function->signature->name($attribute->signature->name);
1247         $function->signature->type($attribute->signature->type);
1248         $function->signature->extendedAttributes($attribute->signature->extendedAttributes);
1249         
1250         my $param = new domSignature();
1251         $param->name("value");
1252         $param->type($attribute->signature->type);
1253         my %attributes = ();
1254         $param->extendedAttributes(\%attributes);
1255         my $arrayRef = $function->parameters;
1256         push(@$arrayRef, $param);
1257         
1258         $function->raisesExceptions($attribute->setterExceptions);
1259         
1260         $object->GenerateFunction($interfaceName, $function, "set_", $interface);
1261     }
1262 }
1263
1264 sub GenerateCFile {
1265     my ($object, $interfaceName, $parentClassName, $parentGObjType, $interface) = @_;
1266
1267     if ($interface->extendedAttributes->{"EventTarget"}) {
1268         $object->GenerateEventTargetIface($interface);
1269     }
1270
1271     my $implContent = "";
1272
1273     my $clsCaps = uc(FixUpDecamelizedName(decamelize($interfaceName)));
1274     my $lowerCaseIfaceName = "webkit_dom_" . FixUpDecamelizedName(decamelize($interfaceName));
1275     my $parentImplClassName = GetParentImplClassName($interface);
1276     my $baseClassName = GetBaseClass($parentImplClassName);
1277
1278     # Add a private struct only for direct subclasses of Object so that we can use RefPtr
1279     # for the WebCore wrapped object and make sure we only increment the reference counter once.
1280     if ($parentImplClassName eq "Object") {
1281         my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1282         push(@cStructPriv, "#define WEBKIT_DOM_${clsCaps}_GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE(obj, WEBKIT_TYPE_DOM_${clsCaps}, ${className}Private)\n\n");
1283         push(@cStructPriv, "typedef struct _${className}Private {\n");
1284         push(@cStructPriv, "#if ${conditionalString}\n") if $conditionalString;
1285         push(@cStructPriv, "    RefPtr<WebCore::${interfaceName}> coreObject;\n");
1286         push(@cStructPriv, "#endif // ${conditionalString}\n") if $conditionalString;
1287         push(@cStructPriv, "} ${className}Private;\n\n");
1288     }
1289
1290     $implContent = << "EOF";
1291 ${defineTypeMacro}(${className}, ${lowerCaseIfaceName}, ${parentGObjType}${defineTypeInterfaceImplementation}
1292
1293 EOF
1294     push(@cBodyProperties, $implContent);
1295
1296     if ($parentImplClassName eq "Object") {
1297         push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n");
1298         push(@cBodyPriv, "{\n");
1299         push(@cBodyPriv, "    if (!obj)\n");
1300         push(@cBodyPriv, "        return 0;\n\n");
1301         push(@cBodyPriv, "    if (gpointer ret = DOMObjectCache::get(obj))\n");
1302         push(@cBodyPriv, "        return WEBKIT_DOM_${clsCaps}(ret);\n\n");
1303         if (IsPolymorphic($interfaceName)) {
1304             push(@cBodyPriv, "    return wrap(obj);\n");
1305         } else {
1306             push(@cBodyPriv, "    return wrap${interfaceName}(obj);\n");
1307         }
1308         push(@cBodyPriv, "}\n\n");
1309     } else {
1310         push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n");
1311         push(@cBodyPriv, "{\n");
1312         if (!IsPolymorphic($baseClassName)) {
1313             push(@cBodyPriv, "    if (!obj)\n");
1314             push(@cBodyPriv, "        return 0;\n\n");
1315             push(@cBodyPriv, "    if (gpointer ret = DOMObjectCache::get(obj))\n");
1316             push(@cBodyPriv, "        return WEBKIT_DOM_${clsCaps}(ret);\n\n");
1317             push(@cBodyPriv, "    return wrap${interfaceName}(obj);\n");
1318         } else {
1319             push(@cBodyPriv, "    return WEBKIT_DOM_${clsCaps}(kit(static_cast<WebCore::$baseClassName*>(obj)));\n");
1320         }
1321         push(@cBodyPriv, "}\n\n");
1322     }
1323
1324     $implContent = << "EOF";
1325 WebCore::${interfaceName}* core(${className}* request)
1326 {
1327     return request ? static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(request)->coreObject) : 0;
1328 }
1329
1330 ${className}* wrap${interfaceName}(WebCore::${interfaceName}* coreObject)
1331 {
1332     ASSERT(coreObject);
1333     return WEBKIT_DOM_${clsCaps}(g_object_new(WEBKIT_TYPE_DOM_${clsCaps}, "core-object", coreObject, NULL));
1334 }
1335
1336 EOF
1337     push(@cBodyPriv, $implContent);
1338
1339     $object->GenerateProperties($interfaceName, $interface);
1340     $object->GenerateFunctions($interfaceName, $interface);
1341 }
1342
1343 sub GenerateEndHeader {
1344     my ($object) = @_;
1345
1346     #Header guard
1347     my $guard = $className . "_h";
1348
1349     push(@hBody, "G_END_DECLS\n\n");
1350     push(@hPrefixGuardEnd, "#endif /* $guard */\n");
1351 }
1352
1353 sub IsPolymorphic {
1354     my $type = shift;
1355
1356     # FIXME: should we use ObjCPolymorphic attribute? or is it specific to ObjC bindings?
1357     return 1 if $type eq "Node" or $type eq "Event" or $type eq "HTMLCollection" or $type eq "StyleSheet" or $type eq "Blob";
1358     return 0;
1359 }
1360
1361 sub GenerateEventTargetIface {
1362     my $object = shift;
1363     my $interface = shift;
1364
1365     my $interfaceName = $interface->name;
1366     my $decamelize = FixUpDecamelizedName(decamelize($interfaceName));
1367     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1368     my @conditionalWarn = GenerateConditionalWarning($interface);
1369
1370     $implIncludes{"GObjectEventListener.h"} = 1;
1371     $implIncludes{"WebKitDOMEventTarget.h"} = 1;
1372     $implIncludes{"WebKitDOMEventPrivate.h"} = 1;
1373
1374     push(@cBodyProperties, "static void webkit_dom_${decamelize}_dispatch_event(WebKitDOMEventTarget* target, WebKitDOMEvent* event, GError** error)\n{\n");
1375     push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString;
1376     push(@cBodyProperties, "    WebCore::Event* coreEvent = WebKit::core(event);\n");
1377     push(@cBodyProperties, "    WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n\n");
1378     push(@cBodyProperties, "    WebCore::ExceptionCode ec = 0;\n");
1379     push(@cBodyProperties, "    coreTarget->dispatchEvent(coreEvent, ec);\n");
1380     push(@cBodyProperties, "    if (ec) {\n        WebCore::ExceptionCodeDescription description(ec);\n");
1381     push(@cBodyProperties, "        g_set_error_literal(error, g_quark_from_string(\"WEBKIT_DOM\"), description.code, description.name);\n    }\n");
1382     push(@cBodyProperties, "#else\n") if $conditionalString;
1383     push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn);
1384     push(@cBodyProperties, "#endif // ${conditionalString}\n") if $conditionalString;
1385     push(@cBodyProperties, "}\n\n");
1386
1387     push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_add_event_listener(WebKitDOMEventTarget* target, const char* eventName, GCallback handler, gboolean bubble, gpointer userData)\n{\n");
1388     push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString;
1389     push(@cBodyProperties, "    WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n");
1390     push(@cBodyProperties, "    return WebCore::GObjectEventListener::addEventListener(G_OBJECT(target), coreTarget, eventName, handler, bubble, userData);\n");
1391     push(@cBodyProperties, "#else\n") if $conditionalString;
1392     push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn);
1393     push(@cBodyProperties, "    return false;\n#endif // ${conditionalString}\n") if $conditionalString;
1394     push(@cBodyProperties, "}\n\n");
1395
1396     push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_remove_event_listener(WebKitDOMEventTarget* target, const char* eventName, GCallback handler, gboolean bubble)\n{\n");
1397     push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString;
1398     push(@cBodyProperties, "    WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n");
1399     push(@cBodyProperties, "    return WebCore::GObjectEventListener::removeEventListener(G_OBJECT(target), coreTarget, eventName, handler, bubble);\n");
1400     push(@cBodyProperties, "#else\n") if $conditionalString;
1401     push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn);
1402     push(@cBodyProperties, "    return false;\n#endif // ${conditionalString}\n") if $conditionalString;
1403     push(@cBodyProperties, "}\n\n");
1404
1405     push(@cBodyProperties, "static void webkit_dom_event_target_init(WebKitDOMEventTargetIface* iface)\n{\n");
1406     push(@cBodyProperties, "    iface->dispatch_event = webkit_dom_${decamelize}_dispatch_event;\n");
1407     push(@cBodyProperties, "    iface->add_event_listener = webkit_dom_${decamelize}_add_event_listener;\n");
1408     push(@cBodyProperties, "    iface->remove_event_listener = webkit_dom_${decamelize}_remove_event_listener;\n}\n\n");
1409
1410     $defineTypeMacro = "G_DEFINE_TYPE_WITH_CODE";
1411     $defineTypeInterfaceImplementation = ", G_IMPLEMENT_INTERFACE(WEBKIT_TYPE_DOM_EVENT_TARGET, webkit_dom_event_target_init))";
1412 }
1413
1414 sub Generate {
1415     my ($object, $interface) = @_;
1416
1417     my $parentClassName = GetParentClassName($interface);
1418     my $parentGObjType = GetParentGObjType($interface);
1419     my $interfaceName = $interface->name;
1420     my $parentImplClassName = GetParentImplClassName($interface);
1421     my $baseClassName = GetBaseClass($parentImplClassName);
1422
1423     # Add the default impl header template
1424     @cPrefix = split("\r", $licenceTemplate);
1425     push(@cPrefix, "\n");
1426
1427     $implIncludes{"DOMObjectCache.h"} = 1;
1428     $implIncludes{"WebKitDOMPrivate.h"} = 1;
1429     $implIncludes{"gobject/ConvertToUTF8String.h"} = 1;
1430     $implIncludes{"${className}Private.h"} = 1;
1431     $implIncludes{"JSMainThreadExecState.h"} = 1;
1432     $implIncludes{"ExceptionCode.h"} = 1;
1433     $implIncludes{"CSSImportRule.h"} = 1;
1434     if ($parentImplClassName ne "Object" and IsPolymorphic($baseClassName)) {
1435         $implIncludes{"WebKitDOM${baseClassName}Private.h"} = 1;
1436     }
1437
1438     $hdrIncludes{"webkitdom/${parentClassName}.h"} = 1;
1439
1440     $object->GenerateHeader($interfaceName, $parentClassName);
1441     $object->GenerateCFile($interfaceName, $parentClassName, $parentGObjType, $interface);
1442     $object->GenerateEndHeader();
1443 }
1444
1445 sub WriteData {
1446     my $object = shift;
1447     my $interface = shift;
1448     my $outputDir = shift;
1449     mkdir $outputDir;
1450
1451     # Write a private header.
1452     my $interfaceName = $interface->name;
1453     my $filename = "$outputDir/" . $className . "Private.h";
1454     my $guard = "${className}Private_h";
1455
1456     # Add the guard if the 'Conditional' extended attribute exists
1457     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1458
1459     open(PRIVHEADER, ">$filename") or die "Couldn't open file $filename for writing";
1460
1461     print PRIVHEADER split("\r", $licenceTemplate);
1462     print PRIVHEADER "\n";
1463
1464     my $text = << "EOF";
1465 #ifndef $guard
1466 #define $guard
1467
1468 #include "${interfaceName}.h"
1469 #include <webkitdom/${className}.h>
1470 EOF
1471
1472     print PRIVHEADER $text;
1473     print PRIVHEADER "#if ${conditionalString}\n" if $conditionalString;
1474     print PRIVHEADER map { "#include \"$_\"\n" } sort keys(%hdrPropIncludes);
1475     print PRIVHEADER "\n";
1476     $text = << "EOF";
1477 namespace WebKit {
1478 ${className}* wrap${interfaceName}(WebCore::${interfaceName}*);
1479 ${className}* kit(WebCore::${interfaceName}*);
1480 WebCore::${interfaceName}* core(${className}*);
1481 EOF
1482
1483     print PRIVHEADER $text;
1484
1485     $text = << "EOF";
1486 } // namespace WebKit
1487
1488 EOF
1489
1490     print PRIVHEADER $text;
1491     print PRIVHEADER "#endif /* ${conditionalString} */\n\n" if $conditionalString;
1492     print PRIVHEADER "#endif /* ${guard} */\n";
1493
1494     close(PRIVHEADER);
1495
1496     my $basename = FileNamePrefix . $interfaceName;
1497     $basename =~ s/_//g;
1498
1499     # Write public header.
1500     my $fullHeaderFilename = "$outputDir/" . $basename . ".h";
1501     my $installedHeaderFilename = "${basename}.h";
1502     open(HEADER, ">$fullHeaderFilename") or die "Couldn't open file $fullHeaderFilename";
1503
1504     print HEADER @hPrefix;
1505     print HEADER @hPrefixGuard;
1506     print HEADER "#include <glib-object.h>\n";
1507     print HEADER map { "#include <$_>\n" } sort keys(%hdrIncludes);
1508     print HEADER "#include <webkitdom/webkitdomdefines.h>\n\n";
1509     print HEADER @hBodyPre;
1510     print HEADER @hBody;
1511     print HEADER @hPrefixGuardEnd;
1512
1513     close(HEADER);
1514
1515     # Write the implementation sources
1516     my $implFileName = "$outputDir/" . $basename . ".cpp";
1517     open(IMPL, ">$implFileName") or die "Couldn't open file $implFileName";
1518
1519     print IMPL @cPrefix;
1520     print IMPL "#include \"config.h\"\n";
1521     print IMPL "#include \"$installedHeaderFilename\"\n\n";
1522
1523     # Remove the implementation header from the list of included files.
1524     %includesCopy = %implIncludes;
1525     print IMPL map { "#include \"$_\"\n" } sort keys(%includesCopy);
1526
1527     print IMPL "#include <wtf/GetPtr.h>\n";
1528     print IMPL "#include <wtf/RefPtr.h>\n\n";
1529     print IMPL @cStructPriv;
1530     print IMPL "#if ${conditionalString}\n\n" if $conditionalString;
1531
1532     print IMPL "namespace WebKit {\n\n";
1533     print IMPL @cBodyPriv;
1534     print IMPL "} // namespace WebKit\n\n";
1535     print IMPL "#endif // ${conditionalString}\n\n" if $conditionalString;
1536
1537     print IMPL @cBodyProperties;
1538     print IMPL @cBody;
1539
1540     close(IMPL);
1541
1542     %implIncludes = ();
1543     %hdrIncludes = ();
1544     @hPrefix = ();
1545     @hBody = ();
1546
1547     @cPrefix = ();
1548     @cBody = ();
1549     @cBodyPriv = ();
1550     @cBodyProperties = ();
1551     @cStructPriv = ();
1552 }
1553
1554 sub GenerateInterface {
1555     my ($object, $interface, $defines) = @_;
1556
1557     # Set up some global variables
1558     $className = GetClassName($interface->name);
1559
1560     $object->Generate($interface);
1561 }
1562
1563 1;