# 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_create_attribute_ns' => ['namespaceURI'], 'webkit_dom_document_create_element_ns' => ['namespaceURI'], 'webkit_dom_document_create_entity_reference' => ['name'], 'webkit_dom_document_evaluate' => ['inResult', 'resolver'], 'webkit_dom_document_get_override_style' => ['pseudoElement'], 'webkit_dom_dom_implementation_create_document' => ['namespaceURI', 'doctype'], 'webkit_dom_dom_window_get_computed_style' => ['pseudoElement'], 'webkit_dom_element_set_attribute_ns' => ['namespaceURI'], 'webkit_dom_node_insert_before' => ['refChild'], }; # 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" unless $interface->parent; return "WebKitDOM" . $interface->parent; } sub GetParentImplClassName { my $interface = shift; return "Object" unless $interface->parent; return $interface->parent; } sub IsBaseType { my $type = shift; return 1 if $baseTypeHash{$type}; return 0; } sub GetBaseClass { $parent = shift; $interface = shift; return $parent if $parent eq "Object" or IsBaseType($parent); return "Event" if $codeGenerator->InheritsInterface($interface, "Event"); 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; # Some strings are not correctly decamelized, apply fix ups for ($s) { s/domcss/dom_css/; s/domhtml/dom_html/; s/domdom/dom_dom/; s/domcdata/dom_cdata/; s/domui/dom_ui/; s/x_path/xpath/; s/web_kit/webkit/; s/htmli_frame/html_iframe/; } return $s; } 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 GetParentGObjType { my $interface = shift; return "WEBKIT_TYPE_DOM_OBJECT" unless $interface->parent; return "WEBKIT_TYPE_DOM_" . uc(decamelize(($interface->parent))); } 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"}) { 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; } if ($attribute->signature->type eq "EventListener") { return 1; } if ($attribute->signature->type eq "MediaQueryListListener") { 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 $object = shift; my $function = shift; my $parentNode = 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; } # 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. Skip EventListeners because they are handled elsewhere. foreach my $param (@{$function->parameters}) { if ($codeGenerator->IsCallbackInterface($param->type) || $param->extendedAttributes->{"Clamp"} || $param->type eq "MediaQueryListListener" || $param->type eq "EventListener" || $codeGenerator->GetSequenceType($param->type)) { return 1; } } # Skip functions for which we have a custom implementation due to API breaks if ($functionName eq "webkit_dom_html_media_element_set_current_time") { 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; } # Skip dispatch_event methods, except the one already deprecated. if ($parentNode->extendedAttributes->{"EventTarget"} && $function->signature->name eq "dispatchEvent" && $functionName ne "webkit_dom_audio_track_list_dispatch_event" && $functionName ne "webkit_dom_battery_manager_dispatch_event" && $functionName ne "webkit_dom_dom_application_cache_dispatch_event" && $functionName ne "webkit_dom_dom_window_dispatch_event" && $functionName ne "webkit_dom_node_dispatch_event" && $functionName ne "webkit_dom_text_track_cue_dispatch_event" && $functionName ne "webkit_dom_text_track_dispatch_event" && $functionName ne "webkit_dom_text_track_list_dispatch_event" && $functionName ne "webkit_dom_video_track_list_dispatch_event" && $functionName ne "webkit_dom_webkit_named_flow_dispatch_event" && $functionName ne "webkit_dom_test_event_target_dispatch_event") { return 1; } # Skip Console::profile() and Console::profileEnd() as they're not correctly generated for the moment. if ($functionName eq "webkit_dom_console_profile" || $functionName eq "webkit_dom_console_profile_end") { return 1; } if ($function->signature->name eq "set" and $parentNode->extendedAttributes->{"TypedArray"}) { return 1; } if ($object eq "MediaQueryListListener") { 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; } 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; } 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"}; my $custom = $property->signature->extendedAttributes->{"CustomSetter"}; if (!$property->isReadOnly && $hasGtypeSignature && !$replaceable && !$custom) { 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 $mutableString = "read-only"; my $hasCustomSetter = $attribute->signature->extendedAttributes->{"CustomSetter"}; if ($writeable && $hasCustomSetter) { $mutableStringconst = "read-only (due to custom functions needed in webkitdom)"; } elsif ($writeable) { $gparamflag = "WEBKIT_PARAM_READWRITE"; $mutableStringconst = "read-write"; } 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 = ""; if ($gtype eq "string") { push(@txtGetProps, " g_value_take_string(value, convertToUTF8String(${getterFunctionName}(" . join(", ", @getterArguments) . ")));\n"); } elsif ($gtype eq "object") { push(@txtGetProps, " RefPtr ptr = ${getterFunctionName}(" . join(", ", @getterArguments) . ");\n"); push(@txtGetProps, " g_value_set_object(value, WebKit::kit(ptr.get()));\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 %parameterSpecOptions = ("int" => [ "G_MININT", "G_MAXINT", "0" ], "int8" => [ "G_MININT8", "G_MAXINT8", "0" ], "boolean" => [ "FALSE" ], "float" => [ "-G_MAXFLOAT", "G_MAXFLOAT", "0" ], "double" => [ "-G_MAXDOUBLE", "G_MAXDOUBLE", "0" ], "uint64" => [ "0", "G_MAXUINT64", "0" ], "long" => [ "G_MINLONG", "G_MAXLONG", "0" ], "int64" => [ "G_MININT64", "G_MAXINT64", "0" ], "ulong" => [ "0", "G_MAXULONG", "0" ], "uint" => [ "0", "G_MAXUINT", "0" ], "uint8" => [ "0", "G_MAXUINT8", "0" ], "ushort" => [ "0", "G_MAXUINT16", "0" ], "uchar" => [ "G_MININT8", "G_MAXINT8", "0" ], "char" => [ "0", "G_MAXUINT8", "0" ], "string" => [ '""', ], "object" => [ "WEBKIT_TYPE_DOM_${ucPropGType}" ]); my $extraParameters = join(", ", @{$parameterSpecOptions{$gtype}}); my $glibTypeName = GetGlibTypeName($propType); my $txtInstallProp = << "EOF"; g_object_class_install_property( gobjectClass, $propEnum, g_param_spec_$gtype( "$propName", "$interfaceName:$propName", "$mutableString $glibTypeName $interfaceName:$propName", $extraParameters, $gparamflag)); EOF push(@txtInstallProps, $txtInstallProp); } sub GenerateProperties { my ($object, $interfaceName, $interface) = @_; my $decamelize = decamelize($interfaceName); my $clsCaps = uc($decamelize); my $lowerCaseIfaceName = "webkit_dom_$decamelize"; 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) { 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 = 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(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 {$_ eq $paramName} @{$canBeNullParams->{$functionName}}); } return 0; } sub GetFunctionDeprecationInformation { my($function, $parentNode) = @_; my $version; my $replacement; if ($parentNode->extendedAttributes->{"EventTarget"} && $function->signature->name eq "dispatchEvent") { # dispatchEvent is implemented already as part fo the WebKitDOMEventTarget interface. # Mark it as deprecated for now in favor of the interface method, and skip it once # we break the API. All other methods of WebKitDOMEventTarget interface are already # skipped because they receive an EventListener as parameter. $version = "2.4"; $replacement = "webkit_dom_event_target_dispatch_event"; } return ($version, $replacement); } sub GenerateFunction { my ($object, $interfaceName, $function, $prefix, $parentNode) = @_; my $decamelize = decamelize($interfaceName); if (SkipFunction($object, $function, $parentNode, $decamelize, $prefix)) { return; } my ($deprecationVersion, $deprecationReplacement) = GetFunctionDeprecationInformation($function, $parentNode); 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 $symbolSig = "${className}*"; my @callImplParams; foreach my $param (@{$function->parameters}) { my $paramIDLType = $param->type; my $paramType = GetGlibTypeName($paramIDLType); my $const = $paramType eq "gchar*" ? "const " : ""; my $paramName = $param->name; $functionSig .= ", ${const}$paramType $paramName"; $symbolSig .= ", ${const}$paramType"; 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; $symbolSig .= ", GError**" if $raisesException; push(@symbols, "$returnType $functionName($symbolSig)\n"); if ($deprecationVersion) { push(@hBody, "#if !defined(WEBKIT_DISABLE_DEPRECATED)\n"); } # 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; my $paramAnnotations = ""; if (ParamCanBeNull($functionName, $paramName)) { $paramAnnotations = " (allow-none):"; } push(@hBody, " * \@${paramName}:${paramAnnotations} A #${paramType}\n"); } push(@hBody, " * \@error: #GError\n") if $raisesException; push(@hBody, " *\n"); my $returnTypeName = $returnType; $returnTypeName =~ s/\*$//; if ($returnValueIsGDOMType) { push(@hBody, " * Returns: (transfer none): A #${returnTypeName}\n"); } elsif ($returnType ne "void") { push(@hBody, " * Returns: A #${returnTypeName}\n"); } if ($deprecationVersion) { push(@hBody, " *\n"); push(@hBody, " * Deprecated: $deprecationVersion"); if ($deprecationReplacement) { push(@hBody, ": Use $deprecationReplacement() instead."); } push(@hBody, "\n"); } push(@hBody, "**/\n"); if ($deprecationVersion && $deprecationReplacement) { push(@hBody, "WEBKIT_DEPRECATED_FOR($deprecationReplacement) "); } elsif ($deprecationVersion) { push(@hBody, "WEBKIT_DEPRECATED "); } else { push(@hBody, "WEBKIT_API "); } push(@hBody, "$returnType\n$functionName($functionSig);\n"); if ($deprecationVersion) { push(@hBody, "#endif /* WEBKIT_DISABLE_DEPRECATED */\n"); } push(@hBody, "\n"); push(@cBody, "$returnType $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 $paramTypeIsPointer = !$codeGenerator->IsNonPointerType($paramIDLType); if ($paramTypeIsPointer) { $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)) { 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. 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. my $custom = $attribute->signature->extendedAttributes->{"CustomSetter"}; if ($attribute->isReadOnly || $attribute->signature->extendedAttributes->{"Replaceable"} || $custom) { 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 $decamelize = decamelize($interfaceName); my $clsCaps = uc($decamelize); my $lowerCaseIfaceName = "webkit_dom_$decamelize"; my $parentImplClassName = GetParentImplClassName($interface); my $baseClassName = GetBaseClass($parentImplClassName, $interface); # 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 = 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 gboolean 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, " gboolean result = 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, " return result;\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}_add_event_listener(WebKitDOMEventTarget* target, const char* eventName, GClosure* handler, gboolean useCapture)\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, useCapture);\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, GClosure* handler, gboolean useCapture)\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, useCapture);\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, $interface); # 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{"Document.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); # Write a symbols file. my $symbolsFileName = "$outputDir/" . $basename . ".symbols"; open(SYM, ">$symbolsFileName") or die "Couldn't open file $symbolsFileName"; print SYM @symbols; close(SYM); %implIncludes = (); %hdrIncludes = (); @hPrefix = (); @hBody = (); @cPrefix = (); @cBody = (); @cBodyPriv = (); @cBodyProperties = (); @cStructPriv = (); @symbols = (); } sub GenerateInterface { my ($object, $interface, $defines) = @_; # Set up some global variables $className = GetClassName($interface->name); $object->Generate($interface); } 1;