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