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