# 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, "TextTrackCue" => 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", "unrestricted float", "float", "double", "double", "unrestricted 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", "unrestricted float", "gfloat", "double", "gdouble", "unrestricted 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 IsPropertyReadable { my $property = shift; return !SkipAttribute($property); } sub IsPropertyWriteable { my $property = shift; if (!IsPropertyReadable($property)) { return 0; } if ($property->isReadOnly) { return 0; } my $gtype = GetGValueTypeName($property->signature->type); my $hasGtypeSignature = $gtype eq "boolean" || $gtype eq "float" || $gtype eq "double" || $gtype eq "int64" || $gtype eq "uint64" || $gtype eq "long" || $gtype eq "ulong" || $gtype eq "int" || $gtype eq "uint" || $gtype eq "short" || $gtype eq "ushort" || $gtype eq "int8" || $gtype eq "uint8" || $gtype eq "char" || $gtype eq "uchar" || $gtype eq "string"; if (!$hasGtypeSignature) { return 0; } # FIXME: We are not generating setters for 'Replaceable' attributes now, but we should somehow. if ($property->signature->extendedAttributes->{"Replaceable"}) { return 0; } if ($property->signature->extendedAttributes->{"CustomSetter"}) { return 0; } return 1; } 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 = IsPropertyWriteable($attribute); my $mutableString = "read-only"; my $hasCustomSetter = $attribute->signature->extendedAttributes->{"CustomSetter"}; if ($writeable && $hasCustomSetter) { $mutableString = "read-only (due to custom functions needed in webkitdom)"; } elsif ($writeable) { $gparamflag = "WEBKIT_PARAM_READWRITE"; $mutableString = "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 = grep { IsPropertyReadable($_) } @{$interface->attributes}; my @writeableProperties = grep { IsPropertyWriteable($_) } @{$interface->attributes}; my $numProperties = scalar @readableProperties; # Properties my $privFunction = GetCoreObject($interfaceName, "coreSelf", "self"); if ($numProperties > 0) { $implContent = << "EOF"; enum { PROP_0, EOF push(@cBodyProperties, $implContent); push(@txtGetProps, "static void ${lowerCaseIfaceName}_get_property(GObject* object, guint propertyId, GValue* value, GParamSpec* pspec)\n"); push(@txtGetProps, "{\n"); push(@txtGetProps, " WebCore::JSMainThreadNullState state;\n"); push(@txtGetProps, "${conditionGuardStart}\n") if $conditionalString; push(@txtGetProps, " ${className}* self = WEBKIT_DOM_${clsCaps}(object);\n"); push(@txtGetProps, " ${privFunction}\n"); if ($conditionalString) { push(@txtGetProps, "#else\n"); push(@txtGetProps, " UNUSED_PARAM(value);\n"); push(@txtGetProps, "${conditionGuardEnd}\n"); } push(@txtGetProps, "\n"); push(@txtGetProps, " switch (propertyId) {\n"); if (scalar @writeableProperties > 0) { push(@txtSetProps, "static void ${lowerCaseIfaceName}_set_property(GObject* object, guint propertyId, const GValue* value, GParamSpec* pspec)\n"); push(@txtSetProps, "{\n"); push(@txtSetProps, " WebCore::JSMainThreadNullState state;\n"); push(@txtSetProps, "${conditionGuardStart}\n") if $conditionalString; push(@txtSetProps, " ${className}* self = WEBKIT_DOM_${clsCaps}(object);\n"); push(@txtSetProps, " ${privFunction}\n"); if ($conditionalString) { push(@txtSetProps, "#else\n"); push(@txtSetProps, " UNUSED_PARAM(value);\n"); push(@txtSetProps, "${conditionGuardEnd}\n"); } push(@txtSetProps, "\n"); push(@txtSetProps, " switch (propertyId) {\n"); } 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); } } else { push(@cBodyProperties, " UNUSED_PARAM(requestClass);\n"); } $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); } else { push(@cBodyProperties, " UNUSED_PARAM(request);\n"); } $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 GetEffectiveFunctionName { my $functionName = shift; # Rename webkit_dom_html_input_element_[set|get]_capture functions since they were changed to set/receive a # boolean instead of a string in r163958. The old methods are now manually added as deprecated. if ($functionName eq "webkit_dom_html_input_element_set_capture") { return "webkit_dom_html_input_element_set_capture_enabled"; } if ($functionName eq "webkit_dom_html_input_element_get_capture") { return "webkit_dom_html_input_element_get_capture_enabled"; } # webkit_dom_text_track_add_cue raises an exception since r163974. We need to add a with_error version to # keep API backwards compatibility. if ($functionName eq "webkit_dom_text_track_add_cue") { return "webkit_dom_text_track_add_cue_with_error"; } return $functionName; } 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 = GetEffectiveFunctionName("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, " UNUSED_PARAM(self);\n"); foreach my $param (@{$function->parameters}) { push(@cBody, " UNUSED_PARAM(" . $param->name . ");\n"); } push(@cBody, " UNUSED_PARAM(error);\n") if $raisesException; 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, " UNUSED_PARAM(self);\n"); foreach my $param (@{$function->parameters}) { push(@cBody, " UNUSED_PARAM(" . $param->name . ");\n"); } push(@cBody, " UNUSED_PARAM(error);\n") if $raisesException; 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 TextTrackCue)); } 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"); if ($conditionalString) { push(@cBodyProperties, "#else\n"); push(@cBodyProperties, " UNUSED_PARAM(target);\n"); push(@cBodyProperties, " UNUSED_PARAM(event);\n"); push(@cBodyProperties, " UNUSED_PARAM(error);\n"); push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n"); } 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"); if ($conditionalString) { push(@cBodyProperties, "#else\n"); push(@cBodyProperties, " UNUSED_PARAM(target);\n"); push(@cBodyProperties, " UNUSED_PARAM(eventName);\n"); push(@cBodyProperties, " UNUSED_PARAM(handler);\n"); push(@cBodyProperties, " UNUSED_PARAM(useCapture);\n"); push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n"); } 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"); if ($conditionalString) { push(@cBodyProperties, "#else\n"); push(@cBodyProperties, " UNUSED_PARAM(target);\n"); push(@cBodyProperties, " UNUSED_PARAM(eventName);\n"); push(@cBodyProperties, " UNUSED_PARAM(handler);\n"); push(@cBodyProperties, " UNUSED_PARAM(useCapture);\n"); push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n"); } 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 "\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;