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