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