# Copyright (C) 2008 Luke Kenneth Casson Leighton # Copyright (C) 2008 Martin Soto # Copyright (C) 2008 Alp Toker # Copyright (C) 2009 Adam Dingle # Copyright (C) 2009 Jim Nelson # Copyright (C) 2009, 2010 Igalia S.L. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this library; see the file COPYING.LIB. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. package CodeGeneratorGObject; use constant FileNamePrefix => "WebKitDOM"; # Global Variables my %implIncludes = (); my %hdrIncludes = (); my $defineTypeMacro = "G_DEFINE_TYPE"; my $defineTypeInterfaceImplementation = ")"; my @txtEventListeners = (); my @txtInstallProps = (); my @txtSetProps = (); my @txtGetProps = (); my $className = ""; # FIXME: this should be replaced with a function that recurses up the tree # to find the actual base type. my %baseTypeHash = ("Object" => 1, "Node" => 1, "NodeList" => 1, "NamedNodeMap" => 1, "DOMImplementation" => 1, "Event" => 1, "CSSRule" => 1, "CSSValue" => 1, "StyleSheet" => 1, "MediaList" => 1, "Counter" => 1, "Rect" => 1, "RGBColor" => 1, "XPathExpression" => 1, "XPathResult" => 1, "NodeIterator" => 1, "TreeWalker" => 1, "AbstractView" => 1, "Blob" => 1, "DOMTokenList" => 1, "HTMLCollection" => 1); # List of function parameters that are allowed to be NULL my $canBeNullParams = { 'webkit_dom_document_evaluate' => ['inResult', 'resolver'], 'webkit_dom_node_insert_before' => ['refChild'], 'webkit_dom_dom_window_get_computed_style' => ['pseudoElement'] }; # Default constructor sub new { my $object = shift; my $reference = { }; $codeGenerator = shift; bless($reference, $object); } my $licenceTemplate = << "EOF"; /* This file is part of the WebKit open source project. This file has been generated by generate-bindings.pl. DO NOT MODIFY! This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ EOF sub GetParentClassName { my $interface = shift; return "WebKitDOMObject" if @{$interface->parents} eq 0; return "WebKitDOM" . $interface->parents(0); } sub GetParentImplClassName { my $interface = shift; return "Object" if @{$interface->parents} eq 0; return $interface->parents(0); } sub IsBaseType { my $type = shift; return 1 if $baseTypeHash{$type}; return 0; } sub GetBaseClass { $parent = shift; return $parent if $parent eq "Object" or IsBaseType($parent); return "Event" if $parent eq "UIEvent" or $parent eq "MouseEvent"; return "CSSValue" if $parent eq "SVGColor" or $parent eq "CSSValueList"; return "Node"; } # From String::CamelCase 0.01 sub camelize { my $s = shift; join('', map{ ucfirst $_ } split(/(?<=[A-Za-z])_(?=[A-Za-z])|\b/, $s)); } sub decamelize { my $s = shift; $s =~ s{([^a-zA-Z]?)([A-Z]*)([A-Z])([a-z]?)}{ my $fc = pos($s)==0; my ($p0,$p1,$p2,$p3) = ($1,lc$2,lc$3,$4); my $t = $p0 || $fc ? $p0 : '_'; $t .= $p3 ? $p1 ? "${p1}_$p2$p3" : "$p2$p3" : "$p1$p2"; $t; }ge; $s; } sub FixUpDecamelizedName { my $classname = shift; # FIXME: try to merge this somehow with the fixes in ClassNameToGobjectType $classname =~ s/x_path/xpath/; $classname =~ s/web_kit/webkit/; $classname =~ s/htmli_frame/html_iframe/; return $classname; } sub HumanReadableConditional { my @conditional = split('_', shift); my @upperCaseExceptions = ("SQL", "API"); my @humanReadable; for $part (@conditional) { if (!grep {$_ eq $part} @upperCaseExceptions) { $part = camelize(lc($part)); } push(@humanReadable, $part); } return join(' ', @humanReadable); } sub ClassNameToGObjectType { my $className = shift; my $CLASS_NAME = uc(decamelize($className)); # Fixup: with our prefix being 'WebKitDOM' decamelize can't get # WebKitDOMCSS and similar names right, so we have to fix it # manually. $CLASS_NAME =~ s/DOMCSS/DOM_CSS/; $CLASS_NAME =~ s/DOMHTML/DOM_HTML/; $CLASS_NAME =~ s/DOMDOM/DOM_DOM/; $CLASS_NAME =~ s/DOMCDATA/DOM_CDATA/; $CLASS_NAME =~ s/DOMX_PATH/DOM_XPATH/; $CLASS_NAME =~ s/DOM_WEB_KIT/DOM_WEBKIT/; $CLASS_NAME =~ s/DOMUI/DOM_UI/; $CLASS_NAME =~ s/HTMLI_FRAME/HTML_IFRAME/; return $CLASS_NAME; } sub GetParentGObjType { my $interface = shift; return "WEBKIT_TYPE_DOM_OBJECT" if @{$interface->parents} eq 0; return "WEBKIT_TYPE_DOM_" . ClassNameToGObjectType($interface->parents(0)); } sub GetClassName { my $name = shift; return "WebKitDOM$name"; } sub GetCoreObject { my ($interfaceName, $name, $parameter) = @_; return "WebCore::${interfaceName}* $name = WebKit::core($parameter);"; } sub SkipAttribute { my $attribute = shift; if ($attribute->signature->extendedAttributes->{"Custom"} || $attribute->signature->extendedAttributes->{"CustomGetter"} || $attribute->signature->extendedAttributes->{"CustomSetter"}) { return 1; } my $propType = $attribute->signature->type; if ($propType =~ /Constructor$/) { return 1; } return 1 if $attribute->isStatic; return 1 if $codeGenerator->IsTypedArrayType($propType); $codeGenerator->AssertNotSequenceType($propType); if ($codeGenerator->GetArrayType($propType)) { return 1; } if ($codeGenerator->IsEnumType($propType)) { return 1; } # This is for DOMWindow.idl location attribute if ($attribute->signature->name eq "location") { return 1; } # This is for HTMLInput.idl valueAsDate if ($attribute->signature->name eq "valueAsDate") { return 1; } # This is for DOMWindow.idl Crypto attribute if ($attribute->signature->type eq "Crypto") { return 1; } # Skip indexed database attributes for now, they aren't yet supported for the GObject generator. if ($attribute->signature->name =~ /^(?:webkit)?[Ii]ndexedDB/ or $attribute->signature->name =~ /^(?:webkit)?IDB/) { return 1; } return 0; } sub SkipFunction { my $function = shift; my $decamelize = shift; my $prefix = shift; my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($function->signature->name); my $functionReturnType = $prefix eq "set_" ? "void" : $function->signature->type; my $isCustomFunction = $function->signature->extendedAttributes->{"Custom"}; my $callWith = $function->signature->extendedAttributes->{"CallWith"}; my $isUnsupportedCallWith = $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments") || $codeGenerator->ExtendedAttributeContains($callWith, "CallStack"); if (($isCustomFunction || $isUnsupportedCallWith) && $functionName ne "webkit_dom_node_replace_child" && $functionName ne "webkit_dom_node_insert_before" && $functionName ne "webkit_dom_node_remove_child" && $functionName ne "webkit_dom_node_append_child" && $functionName ne "webkit_dom_html_collection_item" && $functionName ne "webkit_dom_html_collection_named_item") { return 1; } if ($function->signature->name eq "getSVGDocument") { return 1; } if ($function->signature->name eq "getCSSCanvasContext") { return 1; } if ($function->signature->name eq "setRangeText" && @{$function->parameters} == 1) { return 1; } # This is for DataTransferItemList.idl add(File) method if ($functionName eq "webkit_dom_data_transfer_item_list_add" && @{$function->parameters} == 1) { return 1; } if ($function->signature->name eq "timeEnd") { return 1; } if ($codeGenerator->GetSequenceType($functionReturnType)) { return 1; } if ($function->signature->name eq "supports" && @{$function->parameters} == 1) { return 1; } # Skip functions that have callback parameters, because this # code generator doesn't know how to auto-generate callbacks. # Skip functions that have "MediaQueryListListener" or sequence parameters, because this # code generator doesn't know how to auto-generate MediaQueryListListener or sequence. foreach my $param (@{$function->parameters}) { if ($codeGenerator->IsCallbackInterface($param->type) || $param->extendedAttributes->{"Clamp"} || $param->type eq "MediaQueryListListener" || $codeGenerator->GetSequenceType($param->type)) { return 1; } } return 0; } # Name type used in the g_value_{set,get}_* functions sub GetGValueTypeName { my $type = shift; my %types = ("DOMString", "string", "DOMTimeStamp", "uint", "float", "float", "double", "double", "boolean", "boolean", "char", "char", "long", "long", "long long", "int64", "byte", "int8", "octet", "uint8", "short", "int", "uchar", "uchar", "unsigned", "uint", "int", "int", "unsigned int", "uint", "unsigned long long", "uint64", "unsigned long", "ulong", "unsigned short", "uint"); return $types{$type} ? $types{$type} : "object"; } # Name type used in C declarations sub GetGlibTypeName { my $type = shift; my $name = GetClassName($type); my %types = ("DOMString", "gchar*", "DOMTimeStamp", "guint32", "CompareHow", "gushort", "float", "gfloat", "double", "gdouble", "boolean", "gboolean", "char", "gchar", "long", "glong", "long long", "gint64", "byte", "gint8", "octet", "guint8", "short", "gshort", "uchar", "guchar", "unsigned", "guint", "int", "gint", "unsigned int", "guint", "unsigned long", "gulong", "unsigned long long", "guint64", "unsigned short", "gushort", "void", "void"); return $types{$type} ? $types{$type} : "$name*"; } sub IsGDOMClassType { my $type = shift; return 0 if $codeGenerator->IsNonPointerType($type) || $codeGenerator->IsStringType($type); return 1; } sub GetReadableProperties { my $properties = shift; my @result = (); foreach my $property (@{$properties}) { if (!SkipAttribute($property)) { push(@result, $property); } } return @result; } sub GetWriteableProperties { my $properties = shift; my @result = (); foreach my $property (@{$properties}) { my $gtype = GetGValueTypeName($property->signature->type); my $hasGtypeSignature = ($gtype eq "boolean" || $gtype eq "float" || $gtype eq "double" || $gtype eq "uint64" || $gtype eq "ulong" || $gtype eq "long" || $gtype eq "uint" || $gtype eq "ushort" || $gtype eq "int8" || $gtype eq "uint8" || $gtype eq "uchar" || $gtype eq "char" || $gtype eq "string"); # FIXME: We are not generating setters for 'Replaceable' # attributes now, but we should somehow. my $replaceable = $property->signature->extendedAttributes->{"Replaceable"}; if (!$property->isReadOnly && $hasGtypeSignature && !$replaceable) { push(@result, $property); } } return @result; } sub GenerateConditionalWarning { my $node = shift; my $indentSize = shift; if (!$indentSize) { $indentSize = 4; } my $conditional = $node->extendedAttributes->{"Conditional"}; my @warn; if ($conditional) { if ($conditional =~ /&/) { my @splitConditionals = split(/&/, $conditional); foreach $condition (@splitConditionals) { push(@warn, "#if !ENABLE($condition)\n"); push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n"); push(@warn, "#endif\n"); } } elsif ($conditional =~ /\|/) { foreach $condition (split(/\|/, $conditional)) { push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n"); } } else { push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($conditional) . "\")\n"); } } return @warn; } sub GenerateProperty { my $attribute = shift; my $interfaceName = shift; my @writeableProperties = @{shift @_}; my $parentNode = shift; my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); my @conditionalWarn = GenerateConditionalWarning($attribute->signature, 8); my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode); my @parentConditionalWarn = GenerateConditionalWarning($parentNode, 8); my $camelPropName = $attribute->signature->name; my $setPropNameFunction = $codeGenerator->WK_ucfirst($camelPropName); my $getPropNameFunction = $codeGenerator->WK_lcfirst($camelPropName); my $hasGetterException = $attribute->signature->extendedAttributes->{"GetterRaisesException"}; my $hasSetterException = $attribute->signature->extendedAttributes->{"SetterRaisesException"}; my $propName = decamelize($camelPropName); my $propNameCaps = uc($propName); $propName =~ s/_/-/g; my ${propEnum} = "PROP_${propNameCaps}"; push(@cBodyProperties, " ${propEnum},\n"); my $propType = $attribute->signature->type; my ${propGType} = decamelize($propType); my ${ucPropGType} = uc($propGType); my $gtype = GetGValueTypeName($propType); my $gparamflag = "WEBKIT_PARAM_READABLE"; my $writeable = !$attribute->isReadOnly; my $const = "read-only "; my $custom = $attribute->signature->extendedAttributes->{"Custom"}; if ($writeable && $custom) { $const = "read-only (due to custom functions needed in webkitdom)"; return; } if ($writeable && !$custom) { $gparamflag = "WEBKIT_PARAM_READWRITE"; $const = "read-write "; } my $type = GetGlibTypeName($propType); $nick = decamelize("${interfaceName}_${propName}"); $long = "${const} ${type} ${interfaceName}.${propName}"; my $convertFunction = ""; if ($gtype eq "string") { $convertFunction = "WTF::String::fromUTF8"; } my ($getterFunctionName, @getterArguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); my ($setterFunctionName, @setterArguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute); if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))"); unshift(@getterArguments, "coreSelf"); unshift(@setterArguments, "coreSelf"); $getterFunctionName = "WebCore::${implementedBy}::$getterFunctionName"; $setterFunctionName = "WebCore::${implementedBy}::$setterFunctionName"; } else { push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))"); $getterFunctionName = "coreSelf->$getterFunctionName"; $setterFunctionName = "coreSelf->$setterFunctionName"; } push(@getterArguments, "isNull") if $attribute->signature->isNullable; push(@getterArguments, "ec") if $hasGetterException; push(@setterArguments, "ec") if $hasSetterException; if (grep {$_ eq $attribute} @writeableProperties) { push(@txtSetProps, " case ${propEnum}: {\n"); push(@txtSetProps, "#if ${parentConditionalString}\n") if $parentConditionalString; push(@txtSetProps, "#if ${conditionalString}\n") if $conditionalString; push(@txtSetProps, " WebCore::ExceptionCode ec = 0;\n") if $hasSetterException; push(@txtSetProps, " ${setterFunctionName}(" . join(", ", @setterArguments) . ");\n"); push(@txtSetProps, "#else\n") if $conditionalString; push(@txtSetProps, @conditionalWarn) if scalar(@conditionalWarn); push(@txtSetProps, "#endif /* ${conditionalString} */\n") if $conditionalString; push(@txtSetProps, "#else\n") if $parentConditionalString; push(@txtSetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn); push(@txtSetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString; push(@txtSetProps, " break;\n }\n"); } push(@txtGetProps, " case ${propEnum}: {\n"); push(@txtGetProps, "#if ${parentConditionalString}\n") if $parentConditionalString; push(@txtGetProps, "#if ${conditionalString}\n") if $conditionalString; push(@txtGetProps, " bool isNull = false;\n") if $attribute->signature->isNullable; push(@txtGetProps, " WebCore::ExceptionCode ec = 0;\n") if $hasGetterException; # FIXME: Should we return a default value when isNull == true? my $postConvertFunction = ""; my $done = 0; if ($gtype eq "string") { push(@txtGetProps, " g_value_take_string(value, convertToUTF8String(${getterFunctionName}(" . join(", ", @getterArguments) . ")));\n"); $done = 1; } elsif ($gtype eq "object") { push(@txtGetProps, " RefPtr ptr = ${getterFunctionName}(" . join(", ", @getterArguments) . ");\n"); push(@txtGetProps, " g_value_set_object(value, WebKit::kit(ptr.get()));\n"); $done = 1; } # FIXME: get rid of this glitch? my $_gtype = $gtype; if ($gtype eq "ushort") { $_gtype = "uint"; } if (!$done) { if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; push(@txtGetProps, " g_value_set_$_gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) . ")${postConvertFunction});\n"); } else { push(@txtGetProps, " g_value_set_$_gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) . ")${postConvertFunction});\n"); } } push(@txtGetProps, "#else\n") if $conditionalString; push(@txtGetProps, @conditionalWarn) if scalar(@conditionalWarn); push(@txtGetProps, "#endif /* ${conditionalString} */\n") if $conditionalString; push(@txtGetProps, "#else\n") if $parentConditionalString; push(@txtGetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn); push(@txtGetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString; push(@txtGetProps, " break;\n }\n"); my %param_spec_options = ("int", "G_MININT, /* min */\nG_MAXINT, /* max */\n0, /* default */", "int8", "G_MININT8, /* min */\nG_MAXINT8, /* max */\n0, /* default */", "boolean", "FALSE, /* default */", "float", "-G_MAXFLOAT, /* min */\nG_MAXFLOAT, /* max */\n0.0, /* default */", "double", "-G_MAXDOUBLE, /* min */\nG_MAXDOUBLE, /* max */\n0.0, /* default */", "uint64", "0, /* min */\nG_MAXUINT64, /* min */\n0, /* default */", "long", "G_MINLONG, /* min */\nG_MAXLONG, /* max */\n0, /* default */", "int64", "G_MININT64, /* min */\nG_MAXINT64, /* max */\n0, /* default */", "ulong", "0, /* min */\nG_MAXULONG, /* max */\n0, /* default */", "uint", "0, /* min */\nG_MAXUINT, /* max */\n0, /* default */", "uint8", "0, /* min */\nG_MAXUINT8, /* max */\n0, /* default */", "ushort", "0, /* min */\nG_MAXUINT16, /* max */\n0, /* default */", "uchar", "G_MININT8, /* min */\nG_MAXINT8, /* max */\n0, /* default */", "char", "0, /* min */\nG_MAXUINT8, /* max */\n0, /* default */", "string", "\"\", /* default */", "object", "WEBKIT_TYPE_DOM_${ucPropGType}, /* gobject type */"); my $txtInstallProp = << "EOF"; g_object_class_install_property(gobjectClass, ${propEnum}, g_param_spec_${_gtype}("${propName}", /* name */ "$nick", /* short description */ "$long", /* longer - could do with some extra doc stuff here */ $param_spec_options{$gtype} ${gparamflag})); EOF push(@txtInstallProps, $txtInstallProp); } sub GenerateProperties { my ($object, $interfaceName, $interface) = @_; my $clsCaps = substr(ClassNameToGObjectType($className), 12); my $lowerCaseIfaceName = "webkit_dom_" . (FixUpDecamelizedName(decamelize($interfaceName))); my $parentImplClassName = GetParentImplClassName($interface); my $conditionGuardStart = ""; my $conditionGuardEnd = ""; my $conditionalString = $codeGenerator->GenerateConditionalString($interface); if ($conditionalString) { $conditionGuardStart = "#if ${conditionalString}"; $conditionGuardEnd = "#endif // ${conditionalString}"; } # Properties my $implContent = ""; my @readableProperties = GetReadableProperties($interface->attributes); my @writeableProperties = GetWriteableProperties(\@readableProperties); my $numProperties = scalar @readableProperties; # Properties my $privFunction = GetCoreObject($interfaceName, "coreSelf", "self"); if ($numProperties > 0) { $implContent = << "EOF"; enum { PROP_0, EOF push(@cBodyProperties, $implContent); my $txtGetProp = << "EOF"; static void ${lowerCaseIfaceName}_get_property(GObject* object, guint propertyId, GValue* value, GParamSpec* pspec) { WebCore::JSMainThreadNullState state; EOF push(@txtGetProps, $txtGetProp); $txtGetProp = << "EOF"; $conditionGuardStart ${className}* self = WEBKIT_DOM_${clsCaps}(object); $privFunction $conditionGuardEnd EOF push(@txtGetProps, $txtGetProp); $txtGetProp = << "EOF"; switch (propertyId) { EOF push(@txtGetProps, $txtGetProp); if (scalar @writeableProperties > 0) { my $txtSetProps = << "EOF"; static void ${lowerCaseIfaceName}_set_property(GObject* object, guint propertyId, const GValue* value, GParamSpec* pspec) { WebCore::JSMainThreadNullState state; EOF push(@txtSetProps, $txtSetProps); $txtSetProps = << "EOF"; $conditionGuardStart ${className}* self = WEBKIT_DOM_${clsCaps}(object); $privFunction $conditionGuardEnd EOF push(@txtSetProps, $txtSetProps); $txtSetProps = << "EOF"; switch (propertyId) { EOF push(@txtSetProps, $txtSetProps); } foreach my $attribute (@readableProperties) { if ($attribute->signature->type ne "EventListener" && $attribute->signature->type ne "MediaQueryListListener") { GenerateProperty($attribute, $interfaceName, \@writeableProperties, $interface); } } push(@cBodyProperties, "};\n\n"); $txtGetProp = << "EOF"; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec); break; } } EOF push(@txtGetProps, $txtGetProp); if (scalar @writeableProperties > 0) { $txtSetProps = << "EOF"; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec); break; } } EOF push(@txtSetProps, $txtSetProps); } } # Do not insert extra spaces when interpolating array variables $" = ""; if ($parentImplClassName eq "Object") { $implContent = << "EOF"; static void ${lowerCaseIfaceName}_finalize(GObject* object) { ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object); $conditionGuardStart WebKit::DOMObjectCache::forget(priv->coreObject.get()); $conditionGuardEnd priv->~${className}Private(); G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->finalize(object); } EOF push(@cBodyProperties, $implContent); } if ($numProperties > 0) { if (scalar @writeableProperties > 0) { push(@cBodyProperties, @txtSetProps); push(@cBodyProperties, "\n"); } push(@cBodyProperties, @txtGetProps); push(@cBodyProperties, "\n"); } # Add a constructor implementation only for direct subclasses of Object to make sure # that the WebCore wrapped object is added only once to the DOM cache. The DOM garbage # collector works because Node is a direct subclass of Object and the version of # DOMObjectCache::put() that receives a Node (which is the one setting the frame) is # always called for DOM objects derived from Node. if ($parentImplClassName eq "Object") { $implContent = << "EOF"; static GObject* ${lowerCaseIfaceName}_constructor(GType type, guint constructPropertiesCount, GObjectConstructParam* constructProperties) { GObject* object = G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->constructor(type, constructPropertiesCount, constructProperties); $conditionGuardStart ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object); priv->coreObject = static_cast(WEBKIT_DOM_OBJECT(object)->coreObject); WebKit::DOMObjectCache::put(priv->coreObject.get(), object); $conditionGuardEnd return object; } EOF push(@cBodyProperties, $implContent); } $implContent = << "EOF"; static void ${lowerCaseIfaceName}_class_init(${className}Class* requestClass) { EOF push(@cBodyProperties, $implContent); if ($parentImplClassName eq "Object" || $numProperties > 0) { push(@cBodyProperties, " GObjectClass* gobjectClass = G_OBJECT_CLASS(requestClass);\n"); if ($parentImplClassName eq "Object") { push(@cBodyProperties, " g_type_class_add_private(gobjectClass, sizeof(${className}Private));\n"); push(@cBodyProperties, " gobjectClass->constructor = ${lowerCaseIfaceName}_constructor;\n"); push(@cBodyProperties, " gobjectClass->finalize = ${lowerCaseIfaceName}_finalize;\n"); } if ($numProperties > 0) { if (scalar @writeableProperties > 0) { push(@cBodyProperties, " gobjectClass->set_property = ${lowerCaseIfaceName}_set_property;\n"); } push(@cBodyProperties, " gobjectClass->get_property = ${lowerCaseIfaceName}_get_property;\n"); push(@cBodyProperties, "\n"); push(@cBodyProperties, @txtInstallProps); } } $implContent = << "EOF"; } static void ${lowerCaseIfaceName}_init(${className}* request) { EOF push(@cBodyProperties, $implContent); if ($parentImplClassName eq "Object") { $implContent = << "EOF"; ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(request); new (priv) ${className}Private(); EOF push(@cBodyProperties, $implContent); } $implContent = << "EOF"; } EOF push(@cBodyProperties, $implContent); } sub GenerateHeader { my ($object, $interfaceName, $parentClassName) = @_; my $implContent = ""; # Add the default header template @hPrefix = split("\r", $licenceTemplate); push(@hPrefix, "\n"); # Force single header include. my $headerCheck = << "EOF"; #if !defined(__WEBKITDOM_H_INSIDE__) && !defined(BUILDING_WEBKIT) #error "Only can be included directly." #endif EOF push(@hPrefix, $headerCheck); # Header guard my $guard = $className . "_h"; @hPrefixGuard = << "EOF"; #ifndef $guard #define $guard EOF $implContent = << "EOF"; G_BEGIN_DECLS EOF push(@hBodyPre, $implContent); my $decamelize = FixUpDecamelizedName(decamelize($interfaceName)); my $clsCaps = uc($decamelize); my $lowerCaseIfaceName = "webkit_dom_" . ($decamelize); $implContent = << "EOF"; #define WEBKIT_TYPE_DOM_${clsCaps} (${lowerCaseIfaceName}_get_type()) #define WEBKIT_DOM_${clsCaps}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_DOM_${clsCaps}, ${className})) #define WEBKIT_DOM_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class) #define WEBKIT_DOM_IS_${clsCaps}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_DOM_${clsCaps})) #define WEBKIT_DOM_IS_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_DOM_${clsCaps})) #define WEBKIT_DOM_${clsCaps}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class)) struct _${className} { ${parentClassName} parent_instance; }; struct _${className}Class { ${parentClassName}Class parent_class; }; WEBKIT_API GType ${lowerCaseIfaceName}_get_type (void); EOF push(@hBody, $implContent); } sub GetGReturnMacro { my ($paramName, $paramIDLType, $returnType, $functionName) = @_; my $condition; if ($paramIDLType eq "GError") { $condition = "!$paramName || !*$paramName"; } elsif (IsGDOMClassType($paramIDLType)) { my $paramTypeCaps = uc(FixUpDecamelizedName(decamelize($paramIDLType))); $condition = "WEBKIT_DOM_IS_${paramTypeCaps}($paramName)"; if (ParamCanBeNull($functionName, $paramName)) { $condition = "!$paramName || $condition"; } } else { if (ParamCanBeNull($functionName, $paramName)) { return; } $condition = "$paramName"; } my $macro; if ($returnType ne "void") { $defaultReturn = $returnType eq "gboolean" ? "FALSE" : 0; $macro = " g_return_val_if_fail($condition, $defaultReturn);\n"; } else { $macro = " g_return_if_fail($condition);\n"; } return $macro; } sub ParamCanBeNull { my($functionName, $paramName) = @_; if (defined($functionName)) { return scalar(grep(/$paramName/, @{$canBeNullParams->{$functionName}})); } return 0; } sub GenerateFunction { my ($object, $interfaceName, $function, $prefix, $parentNode) = @_; my $decamelize = FixUpDecamelizedName(decamelize($interfaceName)); if ($object eq "MediaQueryListListener") { return; } if (SkipFunction($function, $decamelize, $prefix)) { return; } return if ($function->signature->name eq "set" and $parentNode->extendedAttributes->{"TypedArray"}); my $functionSigType = $prefix eq "set_" ? "void" : $function->signature->type; my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($function->signature->name); my $returnType = GetGlibTypeName($functionSigType); my $returnValueIsGDOMType = IsGDOMClassType($functionSigType); my $raisesException = $function->signature->extendedAttributes->{"RaisesException"}; my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode); my @conditionalWarn = GenerateConditionalWarning($function->signature); my @parentConditionalWarn = GenerateConditionalWarning($parentNode); my $functionSig = "${className}* self"; my @callImplParams; foreach my $param (@{$function->parameters}) { my $paramIDLType = $param->type; if ($paramIDLType eq "EventListener" || $paramIDLType eq "MediaQueryListListener") { # EventListeners are handled elsewhere. return; } my $paramType = GetGlibTypeName($paramIDLType); my $const = $paramType eq "gchar*" ? "const " : ""; my $paramName = $param->name; $functionSig .= ", ${const}$paramType $paramName"; my $paramIsGDOMType = IsGDOMClassType($paramIDLType); if ($paramIsGDOMType) { if ($paramIDLType ne "any") { $implIncludes{"WebKitDOM${paramIDLType}Private.h"} = 1; } } if ($paramIsGDOMType || ($paramIDLType eq "DOMString") || ($paramIDLType eq "CompareHow")) { $paramName = "converted" . $codeGenerator->WK_ucfirst($paramName); } push(@callImplParams, $paramName); } if ($returnType ne "void" && $returnValueIsGDOMType && $functionSigType ne "any") { $implIncludes{"WebKitDOM${functionSigType}Private.h"} = 1; } $functionSig .= ", GError** error" if $raisesException; # Insert introspection annotations push(@hBody, "/**\n"); push(@hBody, " * ${functionName}:\n"); push(@hBody, " * \@self: A #${className}\n"); foreach my $param (@{$function->parameters}) { my $paramType = GetGlibTypeName($param->type); # $paramType can have a trailing * in some cases $paramType =~ s/\*$//; my $paramName = $param->name; push(@hBody, " * \@${paramName}: A #${paramType}\n"); } push(@hBody, " * \@error: #GError\n") if $raisesException; push(@hBody, " *\n"); if (IsGDOMClassType($function->signature->type)) { push(@hBody, " * Returns: (transfer none):\n"); } else { push(@hBody, " * Returns:\n"); } push(@hBody, " *\n"); push(@hBody, "**/\n"); push(@hBody, "WEBKIT_API $returnType\n$functionName($functionSig);\n"); push(@hBody, "\n"); push(@cBody, "$returnType\n$functionName($functionSig)\n{\n"); push(@cBody, "#if ${parentConditionalString}\n") if $parentConditionalString; push(@cBody, "#if ${conditionalString}\n") if $conditionalString; push(@cBody, " WebCore::JSMainThreadNullState state;\n"); # g_return macros to check parameters of public methods. $gReturnMacro = GetGReturnMacro("self", $interfaceName, $returnType); push(@cBody, $gReturnMacro); foreach my $param (@{$function->parameters}) { my $paramName = $param->name; my $paramIDLType = $param->type; my $paramTypeIsPrimitive = $codeGenerator->IsPrimitiveType($paramIDLType); my $paramIsGDOMType = IsGDOMClassType($paramIDLType); if (!$paramTypeIsPrimitive) { $gReturnMacro = GetGReturnMacro($paramName, $paramIDLType, $returnType, $functionName); push(@cBody, $gReturnMacro); } } if ($raisesException) { $gReturnMacro = GetGReturnMacro("error", "GError", $returnType); push(@cBody, $gReturnMacro); } # The WebKit::core implementations check for null already; no need to duplicate effort. push(@cBody, " WebCore::${interfaceName}* item = WebKit::core(self);\n"); $returnParamName = ""; foreach my $param (@{$function->parameters}) { my $paramIDLType = $param->type; my $paramName = $param->name; my $paramIsGDOMType = IsGDOMClassType($paramIDLType); $convertedParamName = "converted" . $codeGenerator->WK_ucfirst($paramName); if ($paramIDLType eq "DOMString") { push(@cBody, " WTF::String ${convertedParamName} = WTF::String::fromUTF8($paramName);\n"); } elsif ($paramIDLType eq "CompareHow") { push(@cBody, " WebCore::Range::CompareHow ${convertedParamName} = static_cast($paramName);\n"); } elsif ($paramIsGDOMType) { push(@cBody, " WebCore::${paramIDLType}* ${convertedParamName} = WebKit::core($paramName);\n"); } $returnParamName = $convertedParamName if $param->extendedAttributes->{"CustomReturn"}; } my $assign = ""; my $assignPre = ""; my $assignPost = ""; # We need to special-case these Node methods because their C++ # signature is different from what we'd expect given their IDL # description; see Node.h. my $functionHasCustomReturn = $functionName eq "webkit_dom_node_append_child" || $functionName eq "webkit_dom_node_insert_before" || $functionName eq "webkit_dom_node_replace_child" || $functionName eq "webkit_dom_node_remove_child"; if ($returnType ne "void" && !$functionHasCustomReturn) { if ($returnValueIsGDOMType) { $assign = "RefPtr gobjectResult = "; $assignPre = "WTF::getPtr("; $assignPost = ")"; } else { $assign = "${returnType} result = "; } } # FIXME: Should we return a default value when isNull == true? if ($function->signature->isNullable) { push(@cBody, " bool isNull = false;\n"); push(@callImplParams, "isNull"); } if ($raisesException) { push(@cBody, " WebCore::ExceptionCode ec = 0;\n"); push(@callImplParams, "ec"); } my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $function->signature->name; if ($functionHasCustomReturn) { push(@cBody, " bool ok = item->${functionImplementationName}(" . join(", ", @callImplParams) . ");\n"); my $customNodeAppendChild = << "EOF"; if (ok) return WebKit::kit($returnParamName); EOF push(@cBody, $customNodeAppendChild); if($raisesException) { my $exceptionHandling = << "EOF"; WebCore::ExceptionCodeDescription ecdesc(ec); g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name); EOF push(@cBody, $exceptionHandling); } push(@cBody, " return 0;\n"); push(@cBody, "}\n\n"); return; } elsif ($functionSigType eq "DOMString") { my $getterContentHead; if ($prefix) { my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function); push(@arguments, @callImplParams); if ($function->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; unshift(@arguments, "item"); $functionName = "WebCore::${implementedBy}::${functionName}"; } else { $functionName = "item->${functionName}"; } $getterContentHead = "${assign}convertToUTF8String(${functionName}(" . join(", ", @arguments) . "));\n"; } else { my @arguments = @callImplParams; if ($function->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; unshift(@arguments, "item"); $getterContentHead = "${assign}convertToUTF8String(WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "));\n"; } else { $getterContentHead = "${assign}convertToUTF8String(item->${functionImplementationName}(" . join(", ", @arguments) . "));\n"; } } push(@cBody, " ${getterContentHead}"); } else { my $contentHead; if ($prefix eq "get_") { my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function); push(@arguments, @callImplParams); if ($function->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; unshift(@arguments, "item"); $functionName = "WebCore::${implementedBy}::${functionName}"; } else { $functionName = "item->${functionName}"; } $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n"; } elsif ($prefix eq "set_") { my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $function); push(@arguments, @callImplParams); if ($function->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; unshift(@arguments, "item"); $functionName = "WebCore::${implementedBy}::${functionName}"; $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n"; } else { $functionName = "item->${functionName}"; $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n"; } } else { my @arguments = @callImplParams; if ($function->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; $implIncludes{"${implementedBy}.h"} = 1; unshift(@arguments, "item"); $contentHead = "${assign}${assignPre}WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n"; } else { $contentHead = "${assign}${assignPre}item->${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n"; } } push(@cBody, " ${contentHead}"); if($raisesException) { my $exceptionHandling = << "EOF"; if (ec) { WebCore::ExceptionCodeDescription ecdesc(ec); g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name); } EOF push(@cBody, $exceptionHandling); } } if ($returnType ne "void" && !$functionHasCustomReturn) { if ($functionSigType ne "any") { if ($returnValueIsGDOMType) { push(@cBody, " return WebKit::kit(gobjectResult.get());\n"); } else { push(@cBody, " return result;\n"); } } else { push(@cBody, " return 0; // TODO: return canvas object\n"); } } if ($conditionalString) { push(@cBody, "#else\n"); push(@cBody, @conditionalWarn) if scalar(@conditionalWarn); if ($returnType ne "void") { if ($codeGenerator->IsNonPointerType($functionSigType)) { push(@cBody, " return static_cast<${returnType}>(0);\n"); } else { push(@cBody, " return 0;\n"); } } push(@cBody, "#endif /* ${conditionalString} */\n"); } if ($parentConditionalString) { push(@cBody, "#else\n"); push(@cBody, @parentConditionalWarn) if scalar(@parentConditionalWarn); if ($returnType ne "void") { if ($codeGenerator->IsNonPointerType($functionSigType)) { push(@cBody, " return static_cast<${returnType}>(0);\n"); } else { push(@cBody, " return 0;\n"); } } push(@cBody, "#endif /* ${parentConditionalString} */\n"); } push(@cBody, "}\n\n"); } sub ClassHasFunction { my ($class, $name) = @_; foreach my $function (@{$class->functions}) { if ($function->signature->name eq $name) { return 1; } } return 0; } sub GenerateFunctions { my ($object, $interfaceName, $interface) = @_; foreach my $function (@{$interface->functions}) { $object->GenerateFunction($interfaceName, $function, "", $interface); } TOP: foreach my $attribute (@{$interface->attributes}) { if (SkipAttribute($attribute) || $attribute->signature->type eq "EventListener" || $attribute->signature->type eq "MediaQueryListListener") { next TOP; } if ($attribute->signature->name eq "type" # This will conflict with the get_type() function we define to return a GType # according to GObject conventions. Skip this for now. || $attribute->signature->name eq "URL" # TODO: handle this ) { next TOP; } my $attrNameUpper = $codeGenerator->WK_ucfirst($attribute->signature->name); my $getname = "get${attrNameUpper}"; my $setname = "set${attrNameUpper}"; if (ClassHasFunction($interface, $getname) || ClassHasFunction($interface, $setname)) { # Very occasionally an IDL file defines getter/setter functions for one of its # attributes; in this case we don't need to autogenerate the getter/setter. next TOP; } # Generate an attribute getter. For an attribute "foo", this is a function named # "get_foo" which calls a DOM class method named foo(). my $function = new domFunction(); $function->signature($attribute->signature); $function->signature->extendedAttributes({%{$attribute->signature->extendedAttributes}}); if ($attribute->signature->extendedAttributes->{"GetterRaisesException"}) { $function->signature->extendedAttributes->{"RaisesException"} = "VALUE_IS_MISSING"; } $object->GenerateFunction($interfaceName, $function, "get_", $interface); # FIXME: We are not generating setters for 'Replaceable' # attributes now, but we should somehow. if ($attribute->isReadOnly || $attribute->signature->extendedAttributes->{"Replaceable"}) { next TOP; } # Generate an attribute setter. For an attribute, "foo", this is a function named # "set_foo" which calls a DOM class method named setFoo(). $function = new domFunction(); $function->signature(new domSignature()); $function->signature->name($attribute->signature->name); $function->signature->type($attribute->signature->type); $function->signature->extendedAttributes({%{$attribute->signature->extendedAttributes}}); my $param = new domSignature(); $param->name("value"); $param->type($attribute->signature->type); my %attributes = (); $param->extendedAttributes(\%attributes); my $arrayRef = $function->parameters; push(@$arrayRef, $param); if ($attribute->signature->extendedAttributes->{"SetterRaisesException"}) { $function->signature->extendedAttributes->{"RaisesException"} = "VALUE_IS_MISSING"; } else { delete $function->signature->extendedAttributes->{"RaisesException"}; } $object->GenerateFunction($interfaceName, $function, "set_", $interface); } } sub GenerateCFile { my ($object, $interfaceName, $parentClassName, $parentGObjType, $interface) = @_; if ($interface->extendedAttributes->{"EventTarget"}) { $object->GenerateEventTargetIface($interface); } my $implContent = ""; my $clsCaps = uc(FixUpDecamelizedName(decamelize($interfaceName))); my $lowerCaseIfaceName = "webkit_dom_" . FixUpDecamelizedName(decamelize($interfaceName)); my $parentImplClassName = GetParentImplClassName($interface); my $baseClassName = GetBaseClass($parentImplClassName); # Add a private struct only for direct subclasses of Object so that we can use RefPtr # for the WebCore wrapped object and make sure we only increment the reference counter once. if ($parentImplClassName eq "Object") { my $conditionalString = $codeGenerator->GenerateConditionalString($interface); push(@cStructPriv, "#define WEBKIT_DOM_${clsCaps}_GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE(obj, WEBKIT_TYPE_DOM_${clsCaps}, ${className}Private)\n\n"); push(@cStructPriv, "typedef struct _${className}Private {\n"); push(@cStructPriv, "#if ${conditionalString}\n") if $conditionalString; push(@cStructPriv, " RefPtr coreObject;\n"); push(@cStructPriv, "#endif // ${conditionalString}\n") if $conditionalString; push(@cStructPriv, "} ${className}Private;\n\n"); } $implContent = << "EOF"; ${defineTypeMacro}(${className}, ${lowerCaseIfaceName}, ${parentGObjType}${defineTypeInterfaceImplementation} EOF push(@cBodyProperties, $implContent); if ($parentImplClassName eq "Object") { push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n"); push(@cBodyPriv, "{\n"); push(@cBodyPriv, " if (!obj)\n"); push(@cBodyPriv, " return 0;\n\n"); push(@cBodyPriv, " if (gpointer ret = DOMObjectCache::get(obj))\n"); push(@cBodyPriv, " return WEBKIT_DOM_${clsCaps}(ret);\n\n"); if (IsPolymorphic($interfaceName)) { push(@cBodyPriv, " return wrap(obj);\n"); } else { push(@cBodyPriv, " return wrap${interfaceName}(obj);\n"); } push(@cBodyPriv, "}\n\n"); } else { push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n"); push(@cBodyPriv, "{\n"); if (!IsPolymorphic($baseClassName)) { push(@cBodyPriv, " if (!obj)\n"); push(@cBodyPriv, " return 0;\n\n"); push(@cBodyPriv, " if (gpointer ret = DOMObjectCache::get(obj))\n"); push(@cBodyPriv, " return WEBKIT_DOM_${clsCaps}(ret);\n\n"); push(@cBodyPriv, " return wrap${interfaceName}(obj);\n"); } else { push(@cBodyPriv, " return WEBKIT_DOM_${clsCaps}(kit(static_cast(obj)));\n"); } push(@cBodyPriv, "}\n\n"); } $implContent = << "EOF"; WebCore::${interfaceName}* core(${className}* request) { return request ? static_cast(WEBKIT_DOM_OBJECT(request)->coreObject) : 0; } ${className}* wrap${interfaceName}(WebCore::${interfaceName}* coreObject) { ASSERT(coreObject); return WEBKIT_DOM_${clsCaps}(g_object_new(WEBKIT_TYPE_DOM_${clsCaps}, "core-object", coreObject, NULL)); } EOF push(@cBodyPriv, $implContent); $object->GenerateProperties($interfaceName, $interface); $object->GenerateFunctions($interfaceName, $interface); } sub GenerateEndHeader { my ($object) = @_; #Header guard my $guard = $className . "_h"; push(@hBody, "G_END_DECLS\n\n"); push(@hPrefixGuardEnd, "#endif /* $guard */\n"); } sub IsPolymorphic { my $type = shift; return scalar(grep {$_ eq $type} qw(Blob Event HTMLCollection Node StyleSheet)); } sub GenerateEventTargetIface { my $object = shift; my $interface = shift; my $interfaceName = $interface->name; my $decamelize = FixUpDecamelizedName(decamelize($interfaceName)); my $conditionalString = $codeGenerator->GenerateConditionalString($interface); my @conditionalWarn = GenerateConditionalWarning($interface); $implIncludes{"GObjectEventListener.h"} = 1; $implIncludes{"WebKitDOMEventTarget.h"} = 1; $implIncludes{"WebKitDOMEventPrivate.h"} = 1; push(@cBodyProperties, "static void webkit_dom_${decamelize}_dispatch_event(WebKitDOMEventTarget* target, WebKitDOMEvent* event, GError** error)\n{\n"); push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString; push(@cBodyProperties, " WebCore::Event* coreEvent = WebKit::core(event);\n"); push(@cBodyProperties, " WebCore::${interfaceName}* coreTarget = static_cast(WEBKIT_DOM_OBJECT(target)->coreObject);\n\n"); push(@cBodyProperties, " WebCore::ExceptionCode ec = 0;\n"); push(@cBodyProperties, " coreTarget->dispatchEvent(coreEvent, ec);\n"); push(@cBodyProperties, " if (ec) {\n WebCore::ExceptionCodeDescription description(ec);\n"); push(@cBodyProperties, " g_set_error_literal(error, g_quark_from_string(\"WEBKIT_DOM\"), description.code, description.name);\n }\n"); push(@cBodyProperties, "#else\n") if $conditionalString; push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); push(@cBodyProperties, "#endif // ${conditionalString}\n") if $conditionalString; push(@cBodyProperties, "}\n\n"); push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_add_event_listener(WebKitDOMEventTarget* target, const char* eventName, GCallback handler, gboolean bubble, gpointer userData)\n{\n"); push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString; push(@cBodyProperties, " WebCore::${interfaceName}* coreTarget = static_cast(WEBKIT_DOM_OBJECT(target)->coreObject);\n"); push(@cBodyProperties, " return WebCore::GObjectEventListener::addEventListener(G_OBJECT(target), coreTarget, eventName, handler, bubble, userData);\n"); push(@cBodyProperties, "#else\n") if $conditionalString; push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n") if $conditionalString; push(@cBodyProperties, "}\n\n"); push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_remove_event_listener(WebKitDOMEventTarget* target, const char* eventName, GCallback handler, gboolean bubble)\n{\n"); push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString; push(@cBodyProperties, " WebCore::${interfaceName}* coreTarget = static_cast(WEBKIT_DOM_OBJECT(target)->coreObject);\n"); push(@cBodyProperties, " return WebCore::GObjectEventListener::removeEventListener(G_OBJECT(target), coreTarget, eventName, handler, bubble);\n"); push(@cBodyProperties, "#else\n") if $conditionalString; push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n") if $conditionalString; push(@cBodyProperties, "}\n\n"); push(@cBodyProperties, "static void webkit_dom_event_target_init(WebKitDOMEventTargetIface* iface)\n{\n"); push(@cBodyProperties, " iface->dispatch_event = webkit_dom_${decamelize}_dispatch_event;\n"); push(@cBodyProperties, " iface->add_event_listener = webkit_dom_${decamelize}_add_event_listener;\n"); push(@cBodyProperties, " iface->remove_event_listener = webkit_dom_${decamelize}_remove_event_listener;\n}\n\n"); $defineTypeMacro = "G_DEFINE_TYPE_WITH_CODE"; $defineTypeInterfaceImplementation = ", G_IMPLEMENT_INTERFACE(WEBKIT_TYPE_DOM_EVENT_TARGET, webkit_dom_event_target_init))"; } sub Generate { my ($object, $interface) = @_; my $parentClassName = GetParentClassName($interface); my $parentGObjType = GetParentGObjType($interface); my $interfaceName = $interface->name; my $parentImplClassName = GetParentImplClassName($interface); my $baseClassName = GetBaseClass($parentImplClassName); # Add the default impl header template @cPrefix = split("\r", $licenceTemplate); push(@cPrefix, "\n"); $implIncludes{"DOMObjectCache.h"} = 1; $implIncludes{"WebKitDOMPrivate.h"} = 1; $implIncludes{"gobject/ConvertToUTF8String.h"} = 1; $implIncludes{"${className}Private.h"} = 1; $implIncludes{"JSMainThreadExecState.h"} = 1; $implIncludes{"ExceptionCode.h"} = 1; $implIncludes{"CSSImportRule.h"} = 1; if ($parentImplClassName ne "Object" and IsPolymorphic($baseClassName)) { $implIncludes{"WebKitDOM${baseClassName}Private.h"} = 1; } $hdrIncludes{"webkitdom/${parentClassName}.h"} = 1; $object->GenerateHeader($interfaceName, $parentClassName); $object->GenerateCFile($interfaceName, $parentClassName, $parentGObjType, $interface); $object->GenerateEndHeader(); } sub WriteData { my $object = shift; my $interface = shift; my $outputDir = shift; mkdir $outputDir; # Write a private header. my $interfaceName = $interface->name; my $filename = "$outputDir/" . $className . "Private.h"; my $guard = "${className}Private_h"; # Add the guard if the 'Conditional' extended attribute exists my $conditionalString = $codeGenerator->GenerateConditionalString($interface); open(PRIVHEADER, ">$filename") or die "Couldn't open file $filename for writing"; print PRIVHEADER split("\r", $licenceTemplate); print PRIVHEADER "\n"; my $text = << "EOF"; #ifndef $guard #define $guard #include "${interfaceName}.h" #include EOF print PRIVHEADER $text; print PRIVHEADER "#if ${conditionalString}\n" if $conditionalString; print PRIVHEADER map { "#include \"$_\"\n" } sort keys(%hdrPropIncludes); print PRIVHEADER "\n"; $text = << "EOF"; namespace WebKit { ${className}* wrap${interfaceName}(WebCore::${interfaceName}*); ${className}* kit(WebCore::${interfaceName}*); WebCore::${interfaceName}* core(${className}*); EOF print PRIVHEADER $text; $text = << "EOF"; } // namespace WebKit EOF print PRIVHEADER $text; print PRIVHEADER "#endif /* ${conditionalString} */\n\n" if $conditionalString; print PRIVHEADER "#endif /* ${guard} */\n"; close(PRIVHEADER); my $basename = FileNamePrefix . $interfaceName; $basename =~ s/_//g; # Write public header. my $fullHeaderFilename = "$outputDir/" . $basename . ".h"; my $installedHeaderFilename = "${basename}.h"; open(HEADER, ">$fullHeaderFilename") or die "Couldn't open file $fullHeaderFilename"; print HEADER @hPrefix; print HEADER @hPrefixGuard; print HEADER "#include \n"; print HEADER map { "#include <$_>\n" } sort keys(%hdrIncludes); print HEADER "#include \n\n"; print HEADER @hBodyPre; print HEADER @hBody; print HEADER @hPrefixGuardEnd; close(HEADER); # Write the implementation sources my $implFileName = "$outputDir/" . $basename . ".cpp"; open(IMPL, ">$implFileName") or die "Couldn't open file $implFileName"; print IMPL @cPrefix; print IMPL "#include \"config.h\"\n"; print IMPL "#include \"$installedHeaderFilename\"\n\n"; # Remove the implementation header from the list of included files. %includesCopy = %implIncludes; print IMPL map { "#include \"$_\"\n" } sort keys(%includesCopy); print IMPL "#include \n"; print IMPL "#include \n\n"; print IMPL @cStructPriv; print IMPL "#if ${conditionalString}\n\n" if $conditionalString; print IMPL "namespace WebKit {\n\n"; print IMPL @cBodyPriv; print IMPL "} // namespace WebKit\n\n"; print IMPL "#endif // ${conditionalString}\n\n" if $conditionalString; print IMPL @cBodyProperties; print IMPL @cBody; close(IMPL); %implIncludes = (); %hdrIncludes = (); @hPrefix = (); @hBody = (); @cPrefix = (); @cBody = (); @cBodyPriv = (); @cBodyProperties = (); @cStructPriv = (); } sub GenerateInterface { my ($object, $interface, $defines) = @_; # Set up some global variables $className = GetClassName($interface->name); $object->Generate($interface); } 1;