[GTK] Unstable headers should include webkitdomdefines-unstable.h
[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                  "float", "gfloat",
384                  "unrestricted float", "gfloat",
385                  "double", "gdouble",
386                  "unrestricted double", "gdouble",
387                  "boolean", "gboolean",
388                  "char", "gchar",
389                  "long", "glong",
390                  "long long", "gint64",
391                  "byte", "gint8",
392                  "octet", "guint8",
393                  "short", "gshort",
394                  "uchar", "guchar",
395                  "unsigned", "guint",
396                  "int", "gint",
397                  "unsigned int", "guint",
398                  "unsigned long", "gulong",
399                  "unsigned long long", "guint64",
400                  "unsigned short", "gushort",
401                  "void", "void");
402
403     return $types{$type} ? $types{$type} : "$name*";
404 }
405
406 sub IsGDOMClassType {
407     my $type = shift;
408
409     return 0 if $codeGenerator->IsNonPointerType($type) || $codeGenerator->IsStringType($type);
410     return 1;
411 }
412
413 sub IsPropertyReadable {
414     my $property = shift;
415     return !SkipAttribute($property);
416 }
417
418 sub IsPropertyWriteable {
419     my $property = shift;
420
421     if (!IsPropertyReadable($property)) {
422         return 0;
423     }
424
425     if ($property->isReadOnly) {
426         return 0;
427     }
428
429     my $gtype = GetGValueTypeName($property->signature->type);
430     my $hasGtypeSignature = $gtype eq "boolean" || $gtype eq "float" || $gtype eq "double" ||
431                             $gtype eq "int64" || $gtype eq "uint64" ||
432                             $gtype eq "long" || $gtype eq "ulong" ||
433                             $gtype eq "int" || $gtype eq "uint" ||
434                             $gtype eq "short" || $gtype eq "ushort" ||
435                             $gtype eq "int8" || $gtype eq "uint8" ||
436                             $gtype eq "char" || $gtype eq "uchar" ||
437                             $gtype eq "string";
438     if (!$hasGtypeSignature) {
439         return 0;
440     }
441
442     # FIXME: We are not generating setters for 'Replaceable' attributes now, but we should somehow.
443     if ($property->signature->extendedAttributes->{"Replaceable"}) {
444         return 0;
445     }
446
447     if ($property->signature->extendedAttributes->{"CustomSetter"}) {
448         return 0;
449     }
450
451     return 1;
452 }
453
454 sub GenerateConditionalWarning
455 {
456     my $node = shift;
457     my $indentSize = shift;
458     if (!$indentSize) {
459         $indentSize = 4;
460     }
461
462     my $conditional = $node->extendedAttributes->{"Conditional"};
463     my @warn;
464
465     if ($conditional) {
466         if ($conditional =~ /&/) {
467             my @splitConditionals = split(/&/, $conditional);
468             foreach $condition (@splitConditionals) {
469                 push(@warn, "#if !ENABLE($condition)\n");
470                 push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n");
471                 push(@warn, "#endif\n");
472             }
473         } elsif ($conditional =~ /\|/) {
474             foreach $condition (split(/\|/, $conditional)) {
475                 push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n");
476             }
477         } else {
478             push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($conditional) . "\")\n");
479         }
480     }
481
482     return @warn;
483 }
484
485 sub GenerateProperty {
486     my $attribute = shift;
487     my $interfaceName = shift;
488     my @writeableProperties = @{shift @_};
489     my $parentNode = shift;
490
491     my $hasGetterException = $attribute->signature->extendedAttributes->{"GetterRaisesException"};
492     my $hasSetterException = $attribute->signature->extendedAttributes->{"SetterRaisesException"};
493
494     my $decamelizeInterfaceName = decamelize($interfaceName);
495     my $propName = decamelize($attribute->signature->name);
496     my $propFunctionName = GetFunctionSignatureName($interfaceName, $attribute);
497     my $propNameCaps = uc($propName);
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 @getterArguments = ();
519     push(@getterArguments, "self");
520     push(@getterArguments, "nullptr") if $hasGetterException;
521
522     my @setterArguments = ();
523     push(@setterArguments, "self, g_value_get_$gtype(value)");
524     push(@setterArguments, "nullptr") if $hasSetterException;
525
526     if (grep {$_ eq $attribute} @writeableProperties) {
527         push(@txtSetProps, "    case ${propEnum}:\n");
528         push(@txtSetProps, "        webkit_dom_${decamelizeInterfaceName}_set_" . $propFunctionName . "(" . join(", ", @setterArguments) . ");\n");
529         push(@txtSetProps, "        break;\n");
530     }
531
532     push(@txtGetProps, "    case ${propEnum}:\n");
533
534     # FIXME: Should we return a default value when isNull == true?
535
536     my $postConvertFunction = "";
537     if ($gtype eq "string") {
538         push(@txtGetProps, "        g_value_take_string(value, webkit_dom_${decamelizeInterfaceName}_get_" . $propFunctionName . "(" . join(", ", @getterArguments) . "));\n");
539     } else {
540         push(@txtGetProps, "        g_value_set_$gtype(value, webkit_dom_${decamelizeInterfaceName}_get_" . $propFunctionName . "(" . join(", ", @getterArguments) . "));\n");
541     }
542
543     push(@txtGetProps, "        break;\n");
544
545     my %parameterSpecOptions = ("int" =>     [ "G_MININT", "G_MAXINT", "0" ],
546                                 "int8" =>    [ "G_MININT8", "G_MAXINT8", "0" ],
547                                 "boolean" => [ "FALSE" ],
548                                 "float" =>   [ "-G_MAXFLOAT", "G_MAXFLOAT", "0" ],
549                                 "double" =>  [ "-G_MAXDOUBLE", "G_MAXDOUBLE", "0" ],
550                                 "uint64" =>  [ "0", "G_MAXUINT64", "0" ],
551                                 "long" =>    [ "G_MINLONG", "G_MAXLONG", "0" ],
552                                 "int64" =>   [ "G_MININT64", "G_MAXINT64", "0" ],
553                                 "ulong" =>   [ "0", "G_MAXULONG", "0" ],
554                                 "uint" =>    [ "0", "G_MAXUINT", "0" ],
555                                 "uint8" =>   [ "0", "G_MAXUINT8", "0" ],
556                                 "ushort" =>  [ "0", "G_MAXUINT16", "0" ],
557                                 "uchar" =>   [ "G_MININT8", "G_MAXINT8", "0" ],
558                                 "char" =>    [ "0", "G_MAXUINT8", "0" ],
559                                 "string" =>  [ '""', ],
560                                 "object" =>  [ "WEBKIT_DOM_TYPE_${ucPropGType}" ]);
561
562     my $extraParameters = join(", ", @{$parameterSpecOptions{$gtype}});
563     my $glibTypeName = GetGlibTypeName($propType);
564     $propName =~ s/_/-/g;
565     my $txtInstallProp = << "EOF";
566     g_object_class_install_property(
567         gobjectClass,
568         $propEnum,
569         g_param_spec_$gtype(
570             "$propName",
571             "$interfaceName:$propName",
572             "$mutableString $glibTypeName $interfaceName:$propName",
573             $extraParameters,
574             $gparamflag));
575
576 EOF
577     push(@txtInstallProps, $txtInstallProp);
578 }
579
580 sub GenerateProperties {
581     my ($object, $interfaceName, $interface) = @_;
582
583     my $decamelize = decamelize($interfaceName);
584     my $clsCaps = uc($decamelize);
585     my $lowerCaseIfaceName = "webkit_dom_$decamelize";
586     my $parentImplClassName = GetParentImplClassName($interface);
587
588     my $conditionGuardStart = "";
589     my $conditionGuardEnd = "";
590     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
591     if ($conditionalString) {
592         $conditionGuardStart = "#if ${conditionalString}";
593         $conditionGuardEnd = "#endif // ${conditionalString}";
594     }
595
596     # Properties
597     my $implContent = "";
598     my @readableProperties = grep { IsPropertyReadable($_) } @{$interface->attributes};
599     my @writeableProperties = grep { IsPropertyWriteable($_) } @{$interface->attributes};
600     my $numProperties = scalar @readableProperties;
601
602     # Properties
603     if ($numProperties > 0) {
604         $implContent = << "EOF";
605 enum {
606     PROP_0,
607 EOF
608         push(@cBodyProperties, $implContent);
609
610         push(@txtGetProps, "static void ${lowerCaseIfaceName}_get_property(GObject* object, guint propertyId, GValue* value, GParamSpec* pspec)\n");
611         push(@txtGetProps, "{\n");
612         push(@txtGetProps, "    ${className}* self = WEBKIT_DOM_${clsCaps}(object);\n");
613         push(@txtGetProps, "\n");
614         push(@txtGetProps, "    switch (propertyId) {\n");
615
616         if (scalar @writeableProperties > 0) {
617             push(@txtSetProps, "static void ${lowerCaseIfaceName}_set_property(GObject* object, guint propertyId, const GValue* value, GParamSpec* pspec)\n");
618             push(@txtSetProps, "{\n");
619             push(@txtSetProps, "    ${className}* self = WEBKIT_DOM_${clsCaps}(object);\n");
620             push(@txtSetProps, "\n");
621             push(@txtSetProps, "    switch (propertyId) {\n");
622         }
623
624         foreach my $attribute (@readableProperties) {
625             GenerateProperty($attribute, $interfaceName, \@writeableProperties, $interface);
626         }
627
628         push(@cBodyProperties, "};\n\n");
629
630         $txtGetProp = << "EOF";
631     default:
632         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
633         break;
634     }
635 }
636 EOF
637         push(@txtGetProps, $txtGetProp);
638
639         if (scalar @writeableProperties > 0) {
640             $txtSetProps = << "EOF";
641     default:
642         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
643         break;
644     }
645 }
646 EOF
647             push(@txtSetProps, $txtSetProps);
648         }
649     }
650
651     # Do not insert extra spaces when interpolating array variables
652     $" = "";
653
654     if ($parentImplClassName eq "Object") {
655         $implContent = << "EOF";
656 static void ${lowerCaseIfaceName}_finalize(GObject* object)
657 {
658     ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object);
659 $conditionGuardStart
660     WebKit::DOMObjectCache::forget(priv->coreObject.get());
661 $conditionGuardEnd
662     priv->~${className}Private();
663     G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->finalize(object);
664 }
665
666 EOF
667         push(@cBodyProperties, $implContent);
668     }
669
670     if ($numProperties > 0) {
671         if (scalar @writeableProperties > 0) {
672             push(@cBodyProperties, @txtSetProps);
673             push(@cBodyProperties, "\n");
674         }
675         push(@cBodyProperties, @txtGetProps);
676         push(@cBodyProperties, "\n");
677     }
678
679     # Add a constructor implementation only for direct subclasses of Object to make sure
680     # that the WebCore wrapped object is added only once to the DOM cache. The DOM garbage
681     # collector works because Node is a direct subclass of Object and the version of
682     # DOMObjectCache::put() that receives a Node (which is the one setting the frame) is
683     # always called for DOM objects derived from Node.
684     if ($parentImplClassName eq "Object") {
685         $implContent = << "EOF";
686 static GObject* ${lowerCaseIfaceName}_constructor(GType type, guint constructPropertiesCount, GObjectConstructParam* constructProperties)
687 {
688     GObject* object = G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->constructor(type, constructPropertiesCount, constructProperties);
689 $conditionGuardStart
690     ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object);
691     priv->coreObject = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(object)->coreObject);
692     WebKit::DOMObjectCache::put(priv->coreObject.get(), object);
693 $conditionGuardEnd
694     return object;
695 }
696
697 EOF
698         push(@cBodyProperties, $implContent);
699     }
700
701     $implContent = << "EOF";
702 static void ${lowerCaseIfaceName}_class_init(${className}Class* requestClass)
703 {
704 EOF
705     push(@cBodyProperties, $implContent);
706
707     if ($parentImplClassName eq "Object" || $numProperties > 0) {
708         push(@cBodyProperties, "    GObjectClass* gobjectClass = G_OBJECT_CLASS(requestClass);\n");
709
710         if ($parentImplClassName eq "Object") {
711             push(@cBodyProperties, "    g_type_class_add_private(gobjectClass, sizeof(${className}Private));\n");
712             push(@cBodyProperties, "    gobjectClass->constructor = ${lowerCaseIfaceName}_constructor;\n");
713             push(@cBodyProperties, "    gobjectClass->finalize = ${lowerCaseIfaceName}_finalize;\n");
714         }
715
716         if ($numProperties > 0) {
717             if (scalar @writeableProperties > 0) {
718                 push(@cBodyProperties, "    gobjectClass->set_property = ${lowerCaseIfaceName}_set_property;\n");
719             }
720             push(@cBodyProperties, "    gobjectClass->get_property = ${lowerCaseIfaceName}_get_property;\n");
721             push(@cBodyProperties, "\n");
722             push(@cBodyProperties, @txtInstallProps);
723         }
724     } else {
725         push(@cBodyProperties, "    UNUSED_PARAM(requestClass);\n");
726     }
727     $implContent = << "EOF";
728 }
729
730 static void ${lowerCaseIfaceName}_init(${className}* request)
731 {
732 EOF
733     push(@cBodyProperties, $implContent);
734
735     if ($parentImplClassName eq "Object") {
736         $implContent = << "EOF";
737     ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(request);
738     new (priv) ${className}Private();
739 EOF
740         push(@cBodyProperties, $implContent);
741     } else {
742         push(@cBodyProperties, "    UNUSED_PARAM(request);\n");
743     }
744     $implContent = << "EOF";
745 }
746
747 EOF
748     push(@cBodyProperties, $implContent);
749 }
750
751 sub GenerateConstants {
752     my ($interface, $prefix) = @_;
753
754     my $isStableClass = scalar(@stableSymbols);
755
756     if (@{$interface->constants}) {
757         my @constants = @{$interface->constants};
758         foreach my $constant (@constants) {
759             my $conditionalString = $codeGenerator->GenerateConditionalString($constant);
760             my $constantName = $prefix . $constant->name;
761             my $constantValue = $constant->value;
762             my $isStableSymbol = grep {$_ eq $constantName} @stableSymbols;
763             if ($isStableSymbol) {
764                 push(@symbols, "$constantName\n");
765             }
766
767             my @constantHeader = ();
768             push(@constantHeader, "#if ${conditionalString}") if $conditionalString;
769             push(@constantHeader, "/**");
770             push(@constantHeader, " * ${constantName}:");
771             push(@constantHeader, " */");
772             push(@constantHeader, "#define $constantName $constantValue");
773             push(@constantHeader, "#endif /* ${conditionalString} */") if $conditionalString;
774             push(@constantHeader, "\n");
775
776             if ($isStableSymbol or !$isStableClass) {
777                 push(@hBody, join("\n", @constantHeader));
778             } else {
779                 push(@hBodyUnstable, join("\n", @constantHeader));
780             }
781         }
782     }
783 }
784
785 sub GenerateHeader {
786     my ($object, $interfaceName, $parentClassName, $interface) = @_;
787
788     my $implContent = "";
789
790     # Add the default header template
791     @hPrefix = split("\r", $licenceTemplate);
792     push(@hPrefix, "\n");
793
794     my $isStableClass = scalar(@stableSymbols);
795
796     if ($isStableClass) {
797         # Force single header include.
798         my $headerCheck = << "EOF";
799 #if !defined(__WEBKITDOM_H_INSIDE__) && !defined(BUILDING_WEBKIT)
800 #error "Only <webkitdom/webkitdom.h> can be included directly."
801 #endif
802
803 EOF
804         push(@hPrefix, $headerCheck);
805     }
806
807     # Header guard
808     my $guard = $className . "_h";
809
810     @hPrefixGuard = << "EOF";
811 #ifndef $guard
812 #define $guard
813
814 EOF
815     if (!$isStableClass) {
816         push(@hPrefixGuard, "#ifdef WEBKIT_DOM_USE_UNSTABLE_API\n\n");
817     }
818
819     $implContent = << "EOF";
820 G_BEGIN_DECLS
821
822 EOF
823
824     push(@hBodyPre, $implContent);
825
826     my $decamelize = decamelize($interfaceName);
827     my $clsCaps = uc($decamelize);
828     my $lowerCaseIfaceName = "webkit_dom_$decamelize";
829
830     $implContent = << "EOF";
831 #define WEBKIT_DOM_TYPE_${clsCaps}            (${lowerCaseIfaceName}_get_type())
832 #define WEBKIT_DOM_${clsCaps}(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_DOM_TYPE_${clsCaps}, ${className}))
833 #define WEBKIT_DOM_${clsCaps}_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_DOM_TYPE_${clsCaps}, ${className}Class)
834 #define WEBKIT_DOM_IS_${clsCaps}(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_DOM_TYPE_${clsCaps}))
835 #define WEBKIT_DOM_IS_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_DOM_TYPE_${clsCaps}))
836 #define WEBKIT_DOM_${clsCaps}_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_DOM_TYPE_${clsCaps}, ${className}Class))
837
838 EOF
839
840     push(@hBody, $implContent);
841
842     if ($isStableClass) {
843         push(@symbols, "GType ${lowerCaseIfaceName}_get_type(void)\n");
844     }
845
846     GenerateConstants($interface, "WEBKIT_DOM_${clsCaps}_");
847
848     $implContent = << "EOF";
849 struct _${className} {
850     ${parentClassName} parent_instance;
851 };
852
853 struct _${className}Class {
854     ${parentClassName}Class parent_class;
855 };
856
857 EOF
858
859     push(@hBody, $implContent);
860
861     push(@hBody, "WEBKIT_API GType\n${lowerCaseIfaceName}_get_type(void);\n");
862     push(@hBody, "\n");
863 }
864
865 sub GetGReturnMacro {
866     my ($paramName, $paramIDLType, $returnType, $functionName) = @_;
867
868     my $condition;
869     if ($paramIDLType eq "GError") {
870         $condition = "!$paramName || !*$paramName";
871     } elsif (IsGDOMClassType($paramIDLType)) {
872         my $paramTypeCaps = uc(decamelize($paramIDLType));
873         $condition = "WEBKIT_DOM_IS_${paramTypeCaps}($paramName)";
874         if (ParamCanBeNull($functionName, $paramName)) {
875             $condition = "!$paramName || $condition";
876         }
877     } else {
878         if (ParamCanBeNull($functionName, $paramName)) {
879             return "";
880         }
881         $condition = "$paramName";
882     }
883
884     my $macro;
885     if ($returnType ne "void") {
886         $defaultReturn = $returnType eq "gboolean" ? "FALSE" : 0;
887         $macro = "    g_return_val_if_fail($condition, $defaultReturn);\n";
888     } else {
889         $macro = "    g_return_if_fail($condition);\n";
890     }
891
892     return $macro;
893 }
894
895 sub ParamCanBeNull {
896     my($functionName, $paramName) = @_;
897
898     if (defined($functionName)) {
899         return scalar(grep {$_ eq $paramName} @{$canBeNullParams->{$functionName}});
900     }
901     return 0;
902 }
903
904 sub GetFunctionSignatureName {
905     my ($interfaceName, $function) = @_;
906
907     my $signatureName = decamelize($function->signature->name);
908
909     return $signatureName if $signatureName ne "type";
910
911     # For HTML type attribute use type_attr.
912     # Example: webkit_dom_html_link_element_get_type_attr()
913     my $contentAttributeName = $codeGenerator->ContentAttributeName(\%implIncludes, $interfaceName, $function);
914     if ($contentAttributeName) {
915         return "type_attr" if $contentAttributeName eq "WebCore::HTMLNames::typeAttr";
916     }
917
918     # For methods returning a MIME type use content_type.
919     # Examples: webkit_dom_style_sheet_get_content_type(), webkit_dom_dom_mime_type_get_content_type()
920     if ($interfaceName eq "StyleSheet" || $interfaceName eq "DOMMimeType") {
921         return "content_type";
922     }
923
924     # For HTMLFieldSet use field_set_type.
925     # Example: webkit_dom_html_field_set_element_get_field_set_type()
926     if ($interfaceName eq "HTMLFieldSet") {
927         return "field_set_type";
928     }
929
930     # For any other cases use the last word of the interface name.
931     # Examples: webkit_dom_blob_get_blob_type(), webkit_dom_event_get_event_type()
932     my @nameTokens = split('_', decamelize($interfaceName));
933     my $name = $nameTokens[-1];
934
935     # If the last word is element and there are more words, use the previous one.
936     # Example: webkit_dom_html_button_element_get_button_type()
937     if (scalar(@nameTokens) > 1 && $name eq "element") {
938         $name = $nameTokens[-2];
939     }
940
941     return "${name}_type";
942 }
943
944 sub GenerateFunction {
945     my ($object, $interfaceName, $function, $prefix, $parentNode) = @_;
946
947     my $decamelize = decamelize($interfaceName);
948
949     if (SkipFunction($object, $function, $parentNode, $decamelize, $prefix)) {
950         return;
951     }
952
953     my $functionSigType = $prefix eq "set_" ? "void" : $function->signature->type;
954     my $functionSigName = GetFunctionSignatureName($interfaceName, $function);
955     my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . $functionSigName;
956     my $returnType = GetGlibTypeName($functionSigType);
957     my $returnValueIsGDOMType = IsGDOMClassType($functionSigType);
958     my $raisesException = $function->signature->extendedAttributes->{"RaisesException"};
959
960     my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
961     my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode);
962     my @conditionalWarn = GenerateConditionalWarning($function->signature);
963     my @parentConditionalWarn = GenerateConditionalWarning($parentNode);
964
965     my $functionSig = "${className}* self";
966     my $symbolSig = "${className}*";
967
968     my @callImplParams;
969     foreach my $param (@{$function->parameters}) {
970         my $paramIDLType = $param->type;
971         my $paramType = GetGlibTypeName($paramIDLType);
972         my $const = $paramType eq "gchar*" ? "const " : "";
973         my $paramName = $param->name;
974
975         $functionSig .= ", ${const}$paramType $paramName";
976         $symbolSig .= ", ${const}$paramType";
977
978         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
979         if ($paramIsGDOMType) {
980             if ($paramIDLType ne "any") {
981                 $implIncludes{"WebKitDOM${paramIDLType}Private.h"} = 1;
982             }
983         }
984         if ($paramIsGDOMType || ($paramIDLType eq "DOMString") || ($paramIDLType eq "CompareHow")) {
985             $paramName = "converted" . $codeGenerator->WK_ucfirst($paramName);
986         }
987         if ($paramIDLType eq "NodeFilter" || $paramIDLType eq "XPathNSResolver") {
988             $paramName = "WTF::getPtr(" . $paramName . ")";
989         }
990         push(@callImplParams, $paramName);
991     }
992
993     if ($returnType ne "void" && $returnValueIsGDOMType && $functionSigType ne "any") {
994         $implIncludes{"WebKitDOM${functionSigType}Private.h"} = 1;
995     }
996
997     $functionSig .= ", GError** error" if $raisesException;
998     $symbolSig .= ", GError**" if $raisesException;
999
1000     my $symbol = "$returnType $functionName($symbolSig)";
1001     my $isStableClass = scalar(@stableSymbols);
1002     my $isStableSymbol = grep {$_ eq $symbol} @stableSymbols;
1003     if ($isStableSymbol and $isStableClass) {
1004         push(@symbols, "$symbol\n");
1005     }
1006
1007     my @functionHeader = ();
1008     # Insert introspection annotations
1009     push(@functionHeader, "/**");
1010     push(@functionHeader, " * ${functionName}:");
1011     push(@functionHeader, " * \@self: A #${className}");
1012
1013     foreach my $param (@{$function->parameters}) {
1014         my $paramType = GetGlibTypeName($param->type);
1015         # $paramType can have a trailing * in some cases
1016         $paramType =~ s/\*$//;
1017         my $paramName = $param->name;
1018         my $paramAnnotations = "";
1019         if (ParamCanBeNull($functionName, $paramName)) {
1020             $paramAnnotations = " (allow-none):";
1021         }
1022         push(@functionHeader, " * \@${paramName}:${paramAnnotations} A #${paramType}");
1023     }
1024     push(@functionHeader, " * \@error: #GError") if $raisesException;
1025     push(@functionHeader, " *");
1026     my $returnTypeName = $returnType;
1027     my $hasReturnTag = 0;
1028     $returnTypeName =~ s/\*$//;
1029     if ($returnValueIsGDOMType) {
1030         push(@functionHeader, " * Returns: (transfer none): A #${returnTypeName}");
1031         $hasReturnTag = 1;
1032     } elsif ($returnType ne "void") {
1033         push(@functionHeader, " * Returns: A #${returnTypeName}");
1034         $hasReturnTag = 1;
1035     }
1036     if (!$isStableSymbol) {
1037         if ($hasReturnTag) {
1038             push(@functionHeader, " *");
1039         }
1040         push(@functionHeader, " * Stability: Unstable");
1041     }
1042     push(@functionHeader, "**/");
1043
1044     push(@functionHeader, "WEBKIT_API $returnType\n$functionName($functionSig);");
1045     push(@functionHeader, "\n");
1046     if ($isStableSymbol or !$isStableClass) {
1047         push(@hBody, join("\n", @functionHeader));
1048     } else {
1049         push(@hBodyUnstable, join("\n", @functionHeader));
1050     }
1051
1052     push(@cBody, "$returnType $functionName($functionSig)\n{\n");
1053     push(@cBody, "#if ${parentConditionalString}\n") if $parentConditionalString;
1054     push(@cBody, "#if ${conditionalString}\n") if $conditionalString;
1055
1056     push(@cBody, "    WebCore::JSMainThreadNullState state;\n");
1057
1058     # g_return macros to check parameters of public methods.
1059     $gReturnMacro = GetGReturnMacro("self", $interfaceName, $returnType);
1060     push(@cBody, $gReturnMacro);
1061
1062     foreach my $param (@{$function->parameters}) {
1063         my $paramName = $param->name;
1064         my $paramIDLType = $param->type;
1065         my $paramTypeIsPointer = !$codeGenerator->IsNonPointerType($paramIDLType);
1066         if ($paramTypeIsPointer) {
1067             $gReturnMacro = GetGReturnMacro($paramName, $paramIDLType, $returnType, $functionName);
1068             push(@cBody, $gReturnMacro);
1069         }
1070     }
1071
1072     if ($raisesException) {
1073         $gReturnMacro = GetGReturnMacro("error", "GError", $returnType);
1074         push(@cBody, $gReturnMacro);
1075     }
1076
1077     # The WebKit::core implementations check for null already; no need to duplicate effort.
1078     push(@cBody, "    WebCore::${interfaceName}* item = WebKit::core(self);\n");
1079
1080     $returnParamName = "";
1081     foreach my $param (@{$function->parameters}) {
1082         my $paramIDLType = $param->type;
1083         my $paramName = $param->name;
1084
1085         my $paramIsGDOMType = IsGDOMClassType($paramIDLType);
1086         $convertedParamName = "converted" . $codeGenerator->WK_ucfirst($paramName);
1087         if ($paramIDLType eq "DOMString") {
1088             push(@cBody, "    WTF::String ${convertedParamName} = WTF::String::fromUTF8($paramName);\n");
1089         } elsif ($paramIDLType eq "CompareHow") {
1090             push(@cBody, "    WebCore::Range::CompareHow ${convertedParamName} = static_cast<WebCore::Range::CompareHow>($paramName);\n");
1091         } elsif ($paramIDLType eq "NodeFilter" || $paramIDLType eq "XPathNSResolver") {
1092             push(@cBody, "    RefPtr<WebCore::$paramIDLType> ${convertedParamName} = WebKit::core($paramName);\n");
1093         } elsif ($paramIsGDOMType) {
1094             push(@cBody, "    WebCore::${paramIDLType}* ${convertedParamName} = WebKit::core($paramName);\n");
1095         }
1096         $returnParamName = $convertedParamName if $param->extendedAttributes->{"CustomReturn"};
1097     }
1098
1099     my $assign = "";
1100     my $assignPre = "";
1101     my $assignPost = "";
1102
1103     # We need to special-case these Node methods because their C++
1104     # signature is different from what we'd expect given their IDL
1105     # description; see Node.h.
1106     my $functionHasCustomReturn = $functionName eq "webkit_dom_node_append_child" ||
1107         $functionName eq "webkit_dom_node_insert_before" ||
1108         $functionName eq "webkit_dom_node_replace_child" ||
1109         $functionName eq "webkit_dom_node_remove_child";
1110          
1111     if ($returnType ne "void" && !$functionHasCustomReturn) {
1112         if ($returnValueIsGDOMType) {
1113             $assign = "RefPtr<WebCore::${functionSigType}> gobjectResult = ";
1114             $assignPre = "WTF::getPtr(";
1115             $assignPost = ")";
1116         } else {
1117             $assign = "${returnType} result = ";
1118         }
1119     }
1120
1121     # FIXME: Should we return a default value when isNull == true?
1122     if ($function->signature->isNullable) {
1123         push(@cBody, "    bool isNull = false;\n");
1124         push(@callImplParams, "isNull");
1125     }
1126
1127     if ($raisesException) {
1128         push(@cBody, "    WebCore::ExceptionCode ec = 0;\n");
1129         push(@callImplParams, "ec");
1130     }
1131
1132     my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $function->signature->name;
1133
1134     if ($functionHasCustomReturn) {
1135         push(@cBody, "    bool ok = item->${functionImplementationName}(" . join(", ", @callImplParams) . ");\n");
1136         my $customNodeAppendChild = << "EOF";
1137     if (ok)
1138         return WebKit::kit($returnParamName);
1139 EOF
1140         push(@cBody, $customNodeAppendChild);
1141     
1142         if($raisesException) {
1143             my $exceptionHandling = << "EOF";
1144
1145     WebCore::ExceptionCodeDescription ecdesc(ec);
1146     g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name);
1147 EOF
1148             push(@cBody, $exceptionHandling);
1149         }
1150         push(@cBody, "    return 0;\n");
1151         push(@cBody, "}\n\n");
1152         return;
1153     } elsif ($functionSigType eq "DOMString") {
1154         my $getterContentHead;
1155         if ($prefix) {
1156             my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function);
1157             push(@arguments, @callImplParams);
1158             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1159                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1160                 $implIncludes{"${implementedBy}.h"} = 1;
1161                 unshift(@arguments, "item");
1162                 $functionName = "WebCore::${implementedBy}::${functionName}";
1163             } else {
1164                 $functionName = "item->${functionName}";
1165             }
1166             $getterContentHead = "${assign}convertToUTF8String(${functionName}(" . join(", ", @arguments) . "));\n";
1167         } else {
1168             my @arguments = @callImplParams;
1169             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1170                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1171                 $implIncludes{"${implementedBy}.h"} = 1;
1172                 unshift(@arguments, "item");
1173                 $getterContentHead = "${assign}convertToUTF8String(WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "));\n";
1174             } else {
1175                 $getterContentHead = "${assign}convertToUTF8String(item->${functionImplementationName}(" . join(", ", @arguments) . "));\n";
1176             }
1177         }
1178         push(@cBody, "    ${getterContentHead}");
1179     } else {
1180         my $contentHead;
1181         if ($prefix eq "get_") {
1182             my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function);
1183             push(@arguments, @callImplParams);
1184             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1185                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1186                 $implIncludes{"${implementedBy}.h"} = 1;
1187                 unshift(@arguments, "item");
1188                 $functionName = "WebCore::${implementedBy}::${functionName}";
1189             } else {
1190                 $functionName = "item->${functionName}";
1191             }
1192             $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1193         } elsif ($prefix eq "set_") {
1194             my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $function);
1195             push(@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                 $functionName = "WebCore::${implementedBy}::${functionName}";
1201                 $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1202             } else {
1203                 $functionName = "item->${functionName}";
1204                 $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n";
1205             }
1206         } else {
1207             my @arguments = @callImplParams;
1208             if ($function->signature->extendedAttributes->{"ImplementedBy"}) {
1209                 my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
1210                 $implIncludes{"${implementedBy}.h"} = 1;
1211                 unshift(@arguments, "item");
1212                 $contentHead = "${assign}${assignPre}WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n";
1213             } else {
1214                 $contentHead = "${assign}${assignPre}item->${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n";
1215             }
1216         }
1217         push(@cBody, "    ${contentHead}");
1218         
1219         if($raisesException) {
1220             my $exceptionHandling = << "EOF";
1221     if (ec) {
1222         WebCore::ExceptionCodeDescription ecdesc(ec);
1223         g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name);
1224     }
1225 EOF
1226             push(@cBody, $exceptionHandling);
1227         }
1228     }
1229
1230     if ($returnType ne "void" && !$functionHasCustomReturn) {
1231         if ($functionSigType ne "any") {
1232             if ($returnValueIsGDOMType) {
1233                 push(@cBody, "    return WebKit::kit(gobjectResult.get());\n");
1234             } else {
1235                 push(@cBody, "    return result;\n");
1236             }
1237         } else {
1238             push(@cBody, "    return 0; // TODO: return canvas object\n");
1239         }
1240     }
1241
1242     if ($conditionalString) {
1243         push(@cBody, "#else\n");
1244
1245         push(@cBody, "    UNUSED_PARAM(self);\n");
1246         foreach my $param (@{$function->parameters}) {
1247             push(@cBody, "    UNUSED_PARAM(" . $param->name . ");\n");
1248         }
1249         push(@cBody, "    UNUSED_PARAM(error);\n") if $raisesException;
1250
1251         push(@cBody, @conditionalWarn) if scalar(@conditionalWarn);
1252         if ($returnType ne "void") {
1253             if ($codeGenerator->IsNonPointerType($functionSigType)) {
1254                 push(@cBody, "    return static_cast<${returnType}>(0);\n");
1255             } else {
1256                 push(@cBody, "    return 0;\n");
1257             }
1258         }
1259         push(@cBody, "#endif /* ${conditionalString} */\n");
1260     }
1261
1262     if ($parentConditionalString) {
1263         push(@cBody, "#else\n");
1264
1265         push(@cBody, "    UNUSED_PARAM(self);\n");
1266         foreach my $param (@{$function->parameters}) {
1267             push(@cBody, "    UNUSED_PARAM(" . $param->name . ");\n");
1268         }
1269         push(@cBody, "    UNUSED_PARAM(error);\n") if $raisesException;
1270
1271         push(@cBody, @parentConditionalWarn) if scalar(@parentConditionalWarn);
1272         if ($returnType ne "void") {
1273             if ($codeGenerator->IsNonPointerType($functionSigType)) {
1274                 push(@cBody, "    return static_cast<${returnType}>(0);\n");
1275             } else {
1276                 push(@cBody, "    return 0;\n");
1277             }
1278         }
1279         push(@cBody, "#endif /* ${parentConditionalString} */\n");
1280     }
1281
1282     push(@cBody, "}\n\n");
1283 }
1284
1285 sub ClassHasFunction {
1286     my ($class, $name) = @_;
1287
1288     foreach my $function (@{$class->functions}) {
1289         if ($function->signature->name eq $name) {
1290             return 1;
1291         }
1292     }
1293
1294     return 0;
1295 }
1296
1297 sub GenerateFunctions {
1298     my ($object, $interfaceName, $interface) = @_;
1299
1300     foreach my $function (@{$interface->functions}) {
1301         $object->GenerateFunction($interfaceName, $function, "", $interface);
1302     }
1303
1304     TOP:
1305     foreach my $attribute (@{$interface->attributes}) {
1306         if (SkipAttribute($attribute)) {
1307             next TOP;
1308         }
1309
1310         my $attrNameUpper = $codeGenerator->WK_ucfirst($attribute->signature->name);
1311         my $getname = "get${attrNameUpper}";
1312         my $setname = "set${attrNameUpper}";
1313         if (ClassHasFunction($interface, $getname) || ClassHasFunction($interface, $setname)) {
1314             # Very occasionally an IDL file defines getter/setter functions for one of its
1315             # attributes; in this case we don't need to autogenerate the getter/setter.
1316             next TOP;
1317         }
1318         
1319         # Generate an attribute getter.  For an attribute "foo", this is a function named
1320         # "get_foo" which calls a DOM class method named foo().
1321         my $function = new domFunction();
1322         $function->signature($attribute->signature);
1323         $function->signature->extendedAttributes({%{$attribute->signature->extendedAttributes}});
1324         if ($attribute->signature->extendedAttributes->{"GetterRaisesException"}) {
1325             $function->signature->extendedAttributes->{"RaisesException"} = "VALUE_IS_MISSING";
1326         }
1327         $object->GenerateFunction($interfaceName, $function, "get_", $interface);
1328
1329         # FIXME: We are not generating setters for 'Replaceable'
1330         # attributes now, but we should somehow.
1331         my $custom = $attribute->signature->extendedAttributes->{"CustomSetter"};
1332         if ($attribute->isReadOnly || $attribute->signature->extendedAttributes->{"Replaceable"} || $custom) {
1333             next TOP;
1334         }
1335         
1336         # Generate an attribute setter.  For an attribute, "foo", this is a function named
1337         # "set_foo" which calls a DOM class method named setFoo().
1338         $function = new domFunction();
1339         
1340         $function->signature(new domSignature());
1341         $function->signature->name($attribute->signature->name);
1342         $function->signature->type($attribute->signature->type);
1343         $function->signature->extendedAttributes({%{$attribute->signature->extendedAttributes}});
1344         
1345         my $param = new domSignature();
1346         $param->name("value");
1347         $param->type($attribute->signature->type);
1348         my %attributes = ();
1349         $param->extendedAttributes(\%attributes);
1350         my $arrayRef = $function->parameters;
1351         push(@$arrayRef, $param);
1352         
1353         if ($attribute->signature->extendedAttributes->{"SetterRaisesException"}) {
1354             $function->signature->extendedAttributes->{"RaisesException"} = "VALUE_IS_MISSING";
1355         } else {
1356             delete $function->signature->extendedAttributes->{"RaisesException"};
1357         }
1358         
1359         $object->GenerateFunction($interfaceName, $function, "set_", $interface);
1360     }
1361 }
1362
1363 sub GenerateCFile {
1364     my ($object, $interfaceName, $parentClassName, $parentGObjType, $interface) = @_;
1365
1366     if ($interface->extendedAttributes->{"EventTarget"}) {
1367         $object->GenerateEventTargetIface($interface);
1368     }
1369
1370     my $implContent = "";
1371
1372     my $decamelize = decamelize($interfaceName);
1373     my $clsCaps = uc($decamelize);
1374     my $lowerCaseIfaceName = "webkit_dom_$decamelize";
1375     my $parentImplClassName = GetParentImplClassName($interface);
1376     my $baseClassName = GetBaseClass($parentImplClassName, $interface);
1377
1378     # Add a private struct only for direct subclasses of Object so that we can use RefPtr
1379     # for the WebCore wrapped object and make sure we only increment the reference counter once.
1380     if ($parentImplClassName eq "Object") {
1381         my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1382         push(@cStructPriv, "#define WEBKIT_DOM_${clsCaps}_GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE(obj, WEBKIT_DOM_TYPE_${clsCaps}, ${className}Private)\n\n");
1383         push(@cStructPriv, "typedef struct _${className}Private {\n");
1384         push(@cStructPriv, "#if ${conditionalString}\n") if $conditionalString;
1385         push(@cStructPriv, "    RefPtr<WebCore::${interfaceName}> coreObject;\n");
1386         push(@cStructPriv, "#endif // ${conditionalString}\n") if $conditionalString;
1387         push(@cStructPriv, "} ${className}Private;\n\n");
1388     }
1389
1390     $implContent = << "EOF";
1391 ${defineTypeMacro}(${className}, ${lowerCaseIfaceName}, ${parentGObjType}${defineTypeInterfaceImplementation}
1392
1393 EOF
1394     push(@cBodyProperties, $implContent);
1395
1396     if ($parentImplClassName eq "Object") {
1397         push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n");
1398         push(@cBodyPriv, "{\n");
1399         push(@cBodyPriv, "    if (!obj)\n");
1400         push(@cBodyPriv, "        return 0;\n\n");
1401         push(@cBodyPriv, "    if (gpointer ret = DOMObjectCache::get(obj))\n");
1402         push(@cBodyPriv, "        return WEBKIT_DOM_${clsCaps}(ret);\n\n");
1403         if (IsPolymorphic($interfaceName)) {
1404             push(@cBodyPriv, "    return wrap(obj);\n");
1405         } else {
1406             push(@cBodyPriv, "    return wrap${interfaceName}(obj);\n");
1407         }
1408         push(@cBodyPriv, "}\n\n");
1409     } else {
1410         push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n");
1411         push(@cBodyPriv, "{\n");
1412         if (!IsPolymorphic($baseClassName)) {
1413             push(@cBodyPriv, "    if (!obj)\n");
1414             push(@cBodyPriv, "        return 0;\n\n");
1415             push(@cBodyPriv, "    if (gpointer ret = DOMObjectCache::get(obj))\n");
1416             push(@cBodyPriv, "        return WEBKIT_DOM_${clsCaps}(ret);\n\n");
1417             push(@cBodyPriv, "    return wrap${interfaceName}(obj);\n");
1418         } else {
1419             push(@cBodyPriv, "    return WEBKIT_DOM_${clsCaps}(kit(static_cast<WebCore::$baseClassName*>(obj)));\n");
1420         }
1421         push(@cBodyPriv, "}\n\n");
1422     }
1423
1424     $implContent = << "EOF";
1425 WebCore::${interfaceName}* core(${className}* request)
1426 {
1427     return request ? static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(request)->coreObject) : 0;
1428 }
1429
1430 ${className}* wrap${interfaceName}(WebCore::${interfaceName}* coreObject)
1431 {
1432     ASSERT(coreObject);
1433     return WEBKIT_DOM_${clsCaps}(g_object_new(WEBKIT_DOM_TYPE_${clsCaps}, "core-object", coreObject, nullptr));
1434 }
1435
1436 EOF
1437     push(@cBodyPriv, $implContent);
1438
1439     $object->GenerateProperties($interfaceName, $interface);
1440     $object->GenerateFunctions($interfaceName, $interface);
1441 }
1442
1443 sub GenerateEndHeader {
1444     my ($object) = @_;
1445
1446     my $isStableClass = scalar(@stableSymbols);
1447     if (!$isStableClass) {
1448         push(@hPrefixGuardEnd, "#endif /* WEBKIT_DOM_USE_UNSTABLE_API */\n");
1449     }
1450
1451     #Header guard
1452     my $guard = $className . "_h";
1453
1454     push(@hBody, "G_END_DECLS\n\n");
1455     push(@hPrefixGuardEnd, "#endif /* $guard */\n");
1456 }
1457
1458 sub IsPolymorphic {
1459     my $type = shift;
1460
1461     return scalar(grep {$_ eq $type} qw(Blob Event HTMLCollection Node StyleSheet TextTrackCue));
1462 }
1463
1464 sub GenerateEventTargetIface {
1465     my $object = shift;
1466     my $interface = shift;
1467
1468     my $interfaceName = $interface->name;
1469     my $decamelize = decamelize($interfaceName);
1470     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1471     my @conditionalWarn = GenerateConditionalWarning($interface);
1472
1473     $implIncludes{"GObjectEventListener.h"} = 1;
1474     $implIncludes{"WebKitDOMEventTarget.h"} = 1;
1475     $implIncludes{"WebKitDOMEventPrivate.h"} = 1;
1476
1477     push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_dispatch_event(WebKitDOMEventTarget* target, WebKitDOMEvent* event, GError** error)\n{\n");
1478     push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString;
1479     push(@cBodyProperties, "    WebCore::Event* coreEvent = WebKit::core(event);\n");
1480     push(@cBodyProperties, "    WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n\n");
1481     push(@cBodyProperties, "    WebCore::ExceptionCode ec = 0;\n");
1482     push(@cBodyProperties, "    gboolean result = coreTarget->dispatchEvent(coreEvent, ec);\n");
1483     push(@cBodyProperties, "    if (ec) {\n        WebCore::ExceptionCodeDescription description(ec);\n");
1484     push(@cBodyProperties, "        g_set_error_literal(error, g_quark_from_string(\"WEBKIT_DOM\"), description.code, description.name);\n    }\n");
1485     push(@cBodyProperties, "    return result;\n");
1486     if ($conditionalString) {
1487         push(@cBodyProperties, "#else\n");
1488         push(@cBodyProperties, "    UNUSED_PARAM(target);\n");
1489         push(@cBodyProperties, "    UNUSED_PARAM(event);\n");
1490         push(@cBodyProperties, "    UNUSED_PARAM(error);\n");
1491         push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn);
1492         push(@cBodyProperties, "    return false;\n#endif // ${conditionalString}\n");
1493     }
1494     push(@cBodyProperties, "}\n\n");
1495
1496     push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_add_event_listener(WebKitDOMEventTarget* target, const char* eventName, GClosure* handler, gboolean useCapture)\n{\n");
1497     push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString;
1498     push(@cBodyProperties, "    WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n");
1499     push(@cBodyProperties, "    return WebCore::GObjectEventListener::addEventListener(G_OBJECT(target), coreTarget, eventName, handler, useCapture);\n");
1500     if ($conditionalString) {
1501         push(@cBodyProperties, "#else\n");
1502         push(@cBodyProperties, "    UNUSED_PARAM(target);\n");
1503         push(@cBodyProperties, "    UNUSED_PARAM(eventName);\n");
1504         push(@cBodyProperties, "    UNUSED_PARAM(handler);\n");
1505         push(@cBodyProperties, "    UNUSED_PARAM(useCapture);\n");
1506         push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn);
1507         push(@cBodyProperties, "    return false;\n#endif // ${conditionalString}\n");
1508     }
1509     push(@cBodyProperties, "}\n\n");
1510
1511     push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_remove_event_listener(WebKitDOMEventTarget* target, const char* eventName, GClosure* handler, gboolean useCapture)\n{\n");
1512     push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString;
1513     push(@cBodyProperties, "    WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n");
1514     push(@cBodyProperties, "    return WebCore::GObjectEventListener::removeEventListener(G_OBJECT(target), coreTarget, eventName, handler, useCapture);\n");
1515     if ($conditionalString) {
1516         push(@cBodyProperties, "#else\n");
1517         push(@cBodyProperties, "    UNUSED_PARAM(target);\n");
1518         push(@cBodyProperties, "    UNUSED_PARAM(eventName);\n");
1519         push(@cBodyProperties, "    UNUSED_PARAM(handler);\n");
1520         push(@cBodyProperties, "    UNUSED_PARAM(useCapture);\n");
1521         push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn);
1522         push(@cBodyProperties, "    return false;\n#endif // ${conditionalString}\n");
1523     }
1524     push(@cBodyProperties, "}\n\n");
1525
1526     push(@cBodyProperties, "static void webkit_dom_event_target_init(WebKitDOMEventTargetIface* iface)\n{\n");
1527     push(@cBodyProperties, "    iface->dispatch_event = webkit_dom_${decamelize}_dispatch_event;\n");
1528     push(@cBodyProperties, "    iface->add_event_listener = webkit_dom_${decamelize}_add_event_listener;\n");
1529     push(@cBodyProperties, "    iface->remove_event_listener = webkit_dom_${decamelize}_remove_event_listener;\n}\n\n");
1530
1531     $defineTypeMacro = "G_DEFINE_TYPE_WITH_CODE";
1532     $defineTypeInterfaceImplementation = ", G_IMPLEMENT_INTERFACE(WEBKIT_DOM_TYPE_EVENT_TARGET, webkit_dom_event_target_init))";
1533 }
1534
1535 sub Generate {
1536     my ($object, $interface) = @_;
1537
1538     my $parentClassName = GetParentClassName($interface);
1539     my $parentGObjType = GetParentGObjType($interface);
1540     my $interfaceName = $interface->name;
1541     my $parentImplClassName = GetParentImplClassName($interface);
1542     my $baseClassName = GetBaseClass($parentImplClassName, $interface);
1543
1544     # Add the default impl header template
1545     @cPrefix = split("\r", $licenceTemplate);
1546     push(@cPrefix, "\n");
1547
1548     $implIncludes{"DOMObjectCache.h"} = 1;
1549     $implIncludes{"WebKitDOMPrivate.h"} = 1;
1550     $implIncludes{"gobject/ConvertToUTF8String.h"} = 1;
1551     $implIncludes{"${className}Private.h"} = 1;
1552     $implIncludes{"Document.h"} = 1;
1553     $implIncludes{"JSMainThreadExecState.h"} = 1;
1554     $implIncludes{"ExceptionCode.h"} = 1;
1555     $implIncludes{"CSSImportRule.h"} = 1;
1556     if ($parentImplClassName ne "Object" and IsPolymorphic($baseClassName)) {
1557         $implIncludes{"WebKitDOM${baseClassName}Private.h"} = 1;
1558     }
1559
1560     $hdrIncludes{"webkitdom/${parentClassName}.h"} = 1;
1561
1562     $object->GenerateHeader($interfaceName, $parentClassName, $interface);
1563     $object->GenerateCFile($interfaceName, $parentClassName, $parentGObjType, $interface);
1564     $object->GenerateEndHeader();
1565 }
1566
1567 sub WriteData {
1568     my $object = shift;
1569     my $interface = shift;
1570     my $outputDir = shift;
1571     mkdir $outputDir;
1572
1573     my $isStableClass = scalar(@stableSymbols);
1574
1575     # Write a private header.
1576     my $interfaceName = $interface->name;
1577     my $filename = "$outputDir/" . $className . "Private.h";
1578     my $guard = "${className}Private_h";
1579
1580     # Add the guard if the 'Conditional' extended attribute exists
1581     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1582
1583     open(PRIVHEADER, ">$filename") or die "Couldn't open file $filename for writing";
1584
1585     print PRIVHEADER split("\r", $licenceTemplate);
1586     print PRIVHEADER "\n";
1587
1588     my $text = << "EOF";
1589 #ifndef $guard
1590 #define $guard
1591
1592 #include "${interfaceName}.h"
1593 #include <webkitdom/${className}.h>
1594 EOF
1595
1596     print PRIVHEADER $text;
1597     print PRIVHEADER "#if ${conditionalString}\n" if $conditionalString;
1598     print PRIVHEADER "\n";
1599     $text = << "EOF";
1600 namespace WebKit {
1601 ${className}* wrap${interfaceName}(WebCore::${interfaceName}*);
1602 ${className}* kit(WebCore::${interfaceName}*);
1603 WebCore::${interfaceName}* core(${className}*);
1604 EOF
1605
1606     print PRIVHEADER $text;
1607
1608     $text = << "EOF";
1609 } // namespace WebKit
1610
1611 EOF
1612
1613     print PRIVHEADER $text;
1614     print PRIVHEADER "#endif /* ${conditionalString} */\n\n" if $conditionalString;
1615     print PRIVHEADER "#endif /* ${guard} */\n";
1616
1617     close(PRIVHEADER);
1618
1619     my $basename = FileNamePrefix . $interfaceName;
1620     $basename =~ s/_//g;
1621
1622     # Write public header.
1623     my $fullHeaderFilename = "$outputDir/" . $basename . ".h";
1624     my $installedHeaderFilename = "${basename}.h";
1625     open(HEADER, ">$fullHeaderFilename") or die "Couldn't open file $fullHeaderFilename";
1626
1627     print HEADER @hPrefix;
1628     print HEADER @hPrefixGuard;
1629     print HEADER "#include <glib-object.h>\n";
1630     print HEADER map { "#include <$_>\n" } sort keys(%hdrIncludes);
1631     if ($isStableClass) {
1632         print HEADER "#include <webkitdom/webkitdomdefines.h>\n\n";
1633     } else {
1634         print HEADER "#include <webkitdom/webkitdomdefines-unstable.h>\n\n";
1635     }
1636     print HEADER @hBodyPre;
1637     print HEADER @hBody;
1638     print HEADER @hPrefixGuardEnd;
1639
1640     close(HEADER);
1641
1642     # Write the unstable header if needed.
1643     if ($isStableClass and scalar(@hBodyUnstable)) {
1644         my $fullUnstableHeaderFilename = "$outputDir/" . $className . "Unstable.h";
1645         open(UNSTABLE, ">$fullUnstableHeaderFilename") or die "Couldn't open file $fullUnstableHeaderFilename";
1646
1647         print UNSTABLE split("\r", $licenceTemplate);
1648         print UNSTABLE "\n";
1649
1650         $guard = "${className}Unstable_h";
1651         $text = << "EOF";
1652 #ifndef $guard
1653 #define $guard
1654
1655 #ifdef WEBKIT_DOM_USE_UNSTABLE_API
1656
1657 #include <webkitdom/webkitdomdefines-unstable.h>
1658 EOF
1659
1660         print UNSTABLE $text;
1661         print UNSTABLE "\n";
1662         print UNSTABLE "#if ${conditionalString}\n\n" if $conditionalString;
1663         print UNSTABLE "G_BEGIN_DECLS\n";
1664         print UNSTABLE "\n";
1665         print UNSTABLE @hBodyUnstable;
1666         print UNSTABLE "\n";
1667         print UNSTABLE "G_END_DECLS\n";
1668         print UNSTABLE "\n";
1669         print UNSTABLE "#endif /* ${conditionalString} */\n\n" if $conditionalString;
1670         print UNSTABLE "#endif /* WEBKIT_DOM_USE_UNSTABLE_API */\n";
1671         print UNSTABLE "#endif /* ${guard} */\n";
1672
1673         close(UNSTABLE);
1674     }
1675
1676     # Write the implementation sources
1677     my $implFileName = "$outputDir/" . $basename . ".cpp";
1678     open(IMPL, ">$implFileName") or die "Couldn't open file $implFileName";
1679
1680     print IMPL @cPrefix;
1681     print IMPL "#include \"config.h\"\n";
1682     print IMPL "#include \"$installedHeaderFilename\"\n\n";
1683
1684     # Remove the implementation header from the list of included files.
1685     %includesCopy = %implIncludes;
1686     print IMPL map { "#include \"$_\"\n" } sort keys(%includesCopy);
1687     if ($isStableClass and scalar(@hBodyUnstable)) {
1688         print IMPL "#include \"${className}Unstable.h\"\n";
1689     }
1690
1691     print IMPL "#include <wtf/GetPtr.h>\n";
1692     print IMPL "#include <wtf/RefPtr.h>\n\n";
1693     print IMPL @cStructPriv;
1694     print IMPL "#if ${conditionalString}\n\n" if $conditionalString;
1695
1696     print IMPL "namespace WebKit {\n\n";
1697     print IMPL @cBodyPriv;
1698     print IMPL "} // namespace WebKit\n\n";
1699     print IMPL "#endif // ${conditionalString}\n\n" if $conditionalString;
1700
1701     print IMPL @cBodyProperties;
1702     print IMPL @cBody;
1703
1704     close(IMPL);
1705
1706     # Write a symbols file.
1707     if ($isStableClass) {
1708         my $symbolsFileName = "$outputDir/" . $basename . ".symbols";
1709         open(SYM, ">$symbolsFileName") or die "Couldn't open file $symbolsFileName";
1710         print SYM @symbols;
1711         close(SYM);
1712     }
1713
1714     %implIncludes = ();
1715     %hdrIncludes = ();
1716     @hPrefix = ();
1717     @hBody = ();
1718     @hBodyUnstable = ();
1719
1720     @cPrefix = ();
1721     @cBody = ();
1722     @cBodyPriv = ();
1723     @cBodyProperties = ();
1724     @cStructPriv = ();
1725
1726     @symbols = ();
1727     @stableSymbols = ();
1728 }
1729
1730 sub IsInterfaceSymbol {
1731     my ($line, $lowerCaseIfaceName) = @_;
1732
1733     # Function.
1734     return 1 if $line =~ /^[a-zA-Z0-9\*]+\s${lowerCaseIfaceName}_.+$/;
1735
1736     # Constant.
1737     my $prefix = uc($lowerCaseIfaceName);
1738     return 1 if $line =~ /^${prefix}_[A-Z_]+$/;
1739     return 0;
1740 }
1741
1742 sub ReadStableSymbols {
1743     my $interfaceName = shift;
1744
1745     @stableSymbols = ();
1746
1747     my $bindingsDir = dirname($FindBin::Bin);
1748     my $fileName = "$bindingsDir/gobject/webkitdom.symbols";
1749     open FILE, "<", $fileName or die "Could not open $fileName";
1750     my @lines = <FILE>;
1751     close FILE;
1752
1753     my $decamelize = decamelize($interfaceName);
1754     my $lowerCaseIfaceName = "webkit_dom_$decamelize";
1755
1756     foreach $line (@lines) {
1757         $line =~ s/\n$//;
1758
1759         if ($line eq "GType ${lowerCaseIfaceName}_get_type(void)") {
1760             push(@stableSymbols, $line);
1761             next;
1762         }
1763
1764         if (scalar(@stableSymbols) and IsInterfaceSymbol($line, $lowerCaseIfaceName) and $line !~ /^GType/) {
1765             push(@stableSymbols, $line);
1766             next;
1767         }
1768
1769         if (scalar(@stableSymbols) and $line !~ /^GType/) {
1770             warn "Symbol %line found, but a get_type was expected";
1771         }
1772
1773         last if scalar(@stableSymbols);
1774     }
1775 }
1776
1777 sub GenerateInterface {
1778     my ($object, $interface, $defines) = @_;
1779
1780     # Set up some global variables
1781     $className = GetClassName($interface->name);
1782
1783     ReadStableSymbols($interface->name);
1784
1785     $object->Generate($interface);
1786 }
1787
1788 1;