#!/usr/bin/env perl # Copyright (C) 2005-2007, 2009, 2013-2014 Apple Inc. All rights reserved. # Copyright (C) 2009, Julien Chaffraix # Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) # Copyright (C) 2011 Ericsson AB. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of Apple Inc. ("Apple") nor the names of # its contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../bindings/scripts"; use StaticString; use Config; use Getopt::Long; use File::Path; use File::Spec; use IO::File; use InFilesParser; sub readTags($); sub readAttrs($); my $printFactory = 0; my $printWrapperFactory = 0; my $fontNamesIn = ""; my $tagsFile = ""; my $attrsFile = ""; my $outputDir = "."; my %parsedTags = (); my %parsedAttrs = (); my %allTags = (); my %allAttrs = (); my %allStrings = (); my %parameters = (); my $initDefaults = 1; my %extensionAttrs = (); require Config; my $ccLocation = ""; if ($ENV{CC}) { $ccLocation = $ENV{CC}; } elsif ($Config::Config{"osname"} eq "darwin" && $ENV{SDKROOT}) { chomp($ccLocation = `xcrun -find cc -sdk '$ENV{SDKROOT}'`); } else { $ccLocation = "/usr/bin/cc"; } GetOptions( 'tags=s' => \$tagsFile, 'attrs=s' => \$attrsFile, 'factory' => \$printFactory, 'outputDir=s' => \$outputDir, 'wrapperFactory' => \$printWrapperFactory, 'fonts=s' => \$fontNamesIn ); mkpath($outputDir); if (length($fontNamesIn)) { my $names = new IO::File; my $familyNamesFileBase = "WebKitFontFamily"; open($names, $fontNamesIn) or die "Failed to open file: $fontNamesIn"; $initDefaults = 0; my $Parser = InFilesParser->new(); my $dummy; $Parser->parse($names, \¶metersHandler, \&dummy); my $F; my $header = File::Spec->catfile($outputDir, "${familyNamesFileBase}Names.h"); open F, ">$header" or die "Unable to open $header for writing."; printLicenseHeader($F); printHeaderHead($F, "CSS", $familyNamesFileBase, < #include #include #include END print F "enum class FamilyNamesIndex {\n"; for my $name (sort keys %parameters) { print F " ", ucfirst(${name}), ",\n"; } print F "};\n\n"; print F "template\n"; print F "class FamilyNamesList : public Vector {\n"; print F "public:\n"; print F " T& at(FamilyNamesIndex i)\n"; print F " {\n"; print F " return Vector::at(static_cast(i));\n"; print F " }\n"; print F "};\n\n"; print F "extern LazyNeverDestroyed> familyNamesData;\n"; print F "extern MainThreadLazyNeverDestroyed> familyNames;\n\n"; printMacros($F, "extern MainThreadLazyNeverDestroyed", "", \%parameters); print F "\n"; print F "#endif\n\n"; printInit($F, 1); close F; my $source = File::Spec->catfile($outputDir, "${familyNamesFileBase}Names.cpp"); open F, ">$source" or die "Unable to open $source for writing."; printLicenseHeader($F); printCppHead($F, "CSS", $familyNamesFileBase, "WTF"); print F StaticString::GenerateStrings(\%parameters); print F "LazyNeverDestroyed> familyNamesData;\n"; print F "MainThreadLazyNeverDestroyed> familyNames;\n\n"; printMacros($F, "MainThreadLazyNeverDestroyed", "", \%parameters); printInit($F, 0); print F "\n"; print F StaticString::GenerateStringAsserts(\%parameters); print F " familyNamesData.construct();\n"; for my $name (sort keys %parameters) { print F " familyNamesData->uncheckedAppend(&${name}Data);\n"; } print F "\n"; for my $name (sort keys %parameters) { print F " ${name}.construct(&${name}Data);\n"; } print F "\n"; print F " familyNames.construct();\n"; for my $name (sort keys %parameters) { print F " familyNames->uncheckedAppend(${name}->impl());\n"; } print F "}\n}\n}\n"; close F; exit 0; } die "You must specify at least one of --tags or --attrs " unless (length($tagsFile) || length($attrsFile)); if (length($tagsFile)) { %allTags = %{readTags($tagsFile)}; namesToStrings(\%allTags, \%allStrings); } if (length($attrsFile)) { %allAttrs = %{readAttrs($attrsFile)}; namesToStrings(\%allAttrs, \%allStrings); } die "You must specify a namespace (e.g. SVG) for Names.h" unless $parameters{namespace}; die "You must specify a namespaceURI (e.g. http://www.w3.org/2000/svg)" unless $parameters{namespaceURI}; $parameters{namespacePrefix} = $parameters{namespace} unless $parameters{namespacePrefix}; $parameters{fallbackJSInterfaceName} = $parameters{fallbackInterfaceName} unless $parameters{fallbackJSInterfaceName}; my $typeHelpersBasePath = "$outputDir/$parameters{namespace}ElementTypeHelpers"; my $namesBasePath = "$outputDir/$parameters{namespace}Names"; my $factoryBasePath = "$outputDir/$parameters{namespace}ElementFactory"; my $wrapperFactoryFileName = "$parameters{namespace}ElementWrapperFactory"; printNamesHeaderFile("$namesBasePath.h"); printNamesCppFile("$namesBasePath.cpp"); printTypeHelpersHeaderFile("$typeHelpersBasePath.h"); if ($printFactory) { printFactoryCppFile("$factoryBasePath.cpp"); printFactoryHeaderFile("$factoryBasePath.h"); } if ($printWrapperFactory) { printWrapperFactoryCppFile($outputDir, $wrapperFactoryFileName); printWrapperFactoryHeaderFile($outputDir, $wrapperFactoryFileName); } ### Hash initialization sub defaultTagPropertyHash { return ( 'constructorNeedsCreatedByParser' => 0, 'constructorNeedsFormElement' => 0, 'noConstructor' => 0, 'interfaceName' => defaultInterfaceName($_[0]), # By default, the JSInterfaceName is the same as the interfaceName. 'JSInterfaceName' => defaultInterfaceName($_[0]), 'mapToTagName' => '', 'wrapperOnlyIfMediaIsAvailable' => 0, 'settingsConditional' => 0, 'conditional' => 0, 'runtimeEnabled' => 0, 'customTypeHelper' => 0, ); } sub defaultParametersHash { return ( 'namespace' => '', 'namespacePrefix' => '', 'namespaceURI' => '', 'guardFactoryWith' => '', 'tagsNullNamespace' => 0, 'attrsNullNamespace' => 0, 'fallbackInterfaceName' => '', 'fallbackJSInterfaceName' => '', 'customElementInterfaceName' => '', ); } sub defaultInterfaceName { die "No namespace found" if !$parameters{namespace}; return $parameters{namespace} . upperCaseName($_[0]) . "Element" } ### Parsing handlers sub valueForName { my $name = shift; my $value = $extensionAttrs{$name}; if (!$value) { $value = $name; $value =~ s/_/-/g; } return $value; } sub namesToStrings { my $namesRef = shift; my $stringsRef = shift; my %names = %$namesRef; for my $name (keys %names) { $stringsRef->{$name} = valueForName($name); } } sub tagsHandler { my ($tag, $property, $value) = @_; $tag =~ s/-/_/g; # Initialize default property values. $parsedTags{$tag} = { defaultTagPropertyHash($tag) } if !defined($parsedTags{$tag}); if ($property) { die "Unknown property $property for tag $tag\n" if !defined($parsedTags{$tag}{$property}); # The code relies on JSInterfaceName deriving from interfaceName to check for custom JSInterfaceName. # So override JSInterfaceName if it was not already set. $parsedTags{$tag}{JSInterfaceName} = $value if $property eq "interfaceName" && $parsedTags{$tag}{JSInterfaceName} eq $parsedTags{$tag}{interfaceName}; $parsedTags{$tag}{$property} = $value; } } sub attrsHandler { my ($attr, $property, $value) = @_; # Translate HTML5 extension attributes of the form 'x-webkit-feature' to 'webkitfeature'. # We don't just check for the 'x-' prefix because there are attributes such as x-height # which should follow the default path below. if ($attr =~ m/^x-webkit-(.*)/) { my $newAttr = "webkit$1"; $extensionAttrs{$newAttr} = $attr; $attr = $newAttr; } $attr =~ s/-/_/g; # Initialize default properties' values. $parsedAttrs{$attr} = {} if !defined($parsedAttrs{$attr}); if ($property) { die "Unknown property $property for attribute $attr\n" if !defined($parsedAttrs{$attr}{$property}); $parsedAttrs{$attr}{$property} = $value; } } sub parametersHandler { my ($parameter, $value) = @_; # Initialize default properties' values. %parameters = defaultParametersHash() if (!(keys %parameters) && $initDefaults); die "Unknown parameter $parameter for tags/attrs\n" if (!defined($parameters{$parameter}) && $initDefaults); $parameters{$parameter} = $value; } ## Support routines sub readNames($$$) { my ($namesFile, $hashToFillRef, $handler) = @_; my $names = new IO::File; open($names, $namesFile) or die "Failed to open file: $namesFile"; my $InParser = InFilesParser->new(); $InParser->parse($names, \¶metersHandler, $handler); close($names); die "Failed to read names from file: $namesFile" if (keys %{$hashToFillRef} == 0); return $hashToFillRef; } sub readAttrs($) { my ($namesFile) = @_; %parsedAttrs = (); return readNames($namesFile, \%parsedAttrs, \&attrsHandler); } sub readTags($) { my ($namesFile) = @_; %parsedTags = (); return readNames($namesFile, \%parsedTags, \&tagsHandler); } sub printMacros { my ($F, $macro, $suffix, $namesRef) = @_; my %names = %$namesRef; for my $name (sort keys %names) { print F "$macro $name","$suffix;\n"; } } sub usesDefaultWrapper { my $tagName = shift; return $tagName eq $parameters{namespace} . "Element"; } # Build a direct mapping from the tags to the Element to create. sub buildConstructorMap { my %tagConstructorMap = (); for my $tagName (keys %allTags) { my $interfaceName = $allTags{$tagName}{interfaceName}; if ($allTags{$tagName}{mapToTagName}) { die "Cannot handle multiple mapToTagName for $tagName\n" if $allTags{$allTags{$tagName}{mapToTagName}}{mapToTagName}; $interfaceName = $allTags{ $allTags{$tagName}{mapToTagName} }{interfaceName}; } # Chop the string to keep the interesting part. $interfaceName =~ s/$parameters{namespace}(.*)Element/$1/; $tagConstructorMap{$tagName} = lc($interfaceName); } return %tagConstructorMap; } # Helper method that print the constructor's signature avoiding # unneeded arguments. sub printConstructorSignature { my ($F, $tagName, $constructorName, $constructorTagName) = @_; print F "static Ref<$parameters{namespace}Element> ${constructorName}Constructor(const QualifiedName& $constructorTagName, Document& document"; if ($parameters{namespace} eq "HTML") { print F ", HTMLFormElement*"; print F " formElement" if $allTags{$tagName}{constructorNeedsFormElement}; } print F ", bool"; print F " createdByParser" if $allTags{$tagName}{constructorNeedsCreatedByParser}; print F ")\n{\n"; } # Helper method to dump the constructor interior and call the # Element constructor with the right arguments. # The variable names should be kept in sync with the previous method. sub printConstructorInterior { my ($F, $tagName, $interfaceName, $constructorTagName) = @_; # Handle media elements. # Note that wrapperOnlyIfMediaIsAvailable is a misnomer, because media availability # does not just control the wrapper; it controls the element object that is created. # FIXME: Could we instead do this entirely in the wrapper, and use custom wrappers # instead of having all the support for this here in this script? if ($allTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { print F <localName(), tagName.namespaceURI())"); } } } sub printFunctionTable { my ($F, $tagConstructorMap) = @_; my %tagConstructorMap = %$tagConstructorMap; for my $tagName (sort keys %tagConstructorMap) { next if $allTags{$tagName}{noConstructor}; my $conditional = $allTags{$tagName}{conditional}; if ($conditional) { my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; print F "#if ${conditionalString}\n"; } if ($allTags{$tagName}{mapToTagName}) { print F " { $parameters{namespace}Names::${tagName}Tag, $allTags{$tagName}{mapToTagName}To${tagName}Constructor },\n"; } else { print F " { $parameters{namespace}Names::${tagName}Tag, $tagConstructorMap{$tagName}Constructor },\n"; } if ($conditional) { print F "#endif\n"; } } } sub svgCapitalizationHacks { my $name = shift; $name = "FE" . ucfirst $1 if $name =~ /^fe(.+)$/; return $name; } sub upperCaseName { my $name = shift; $name = svgCapitalizationHacks($name) if ($parameters{namespace} eq "SVG"); while ($name =~ /^(.*?)_(.*)/) { $name = $1 . ucfirst $2; } return ucfirst $name; } sub printHeaderHead { my ($F, $prefix, $namespace, $includes, $definitions) = @_; print F< 1; print F < class TypeCastTraits { public: static bool isOfType(ArgType& node) { return checkTagName(node); } private: END ; if ($parameters{namespace} eq "HTML" && ($parsedTags{$name}{wrapperOnlyIfMediaIsAvailable} || $parsedTags{$name}{settingsConditional} || $parsedTags{$name}{runtimeEnabled})) { print F <(node) && checkTagName(downcast(node)); } END ; } else { print F <(target) && checkTagName(downcast(target)); } }; } END ; print F "\n"; } } sub printTypeHelpersHeaderFile { my ($headerPath) = shift; my $F; open F, ">$headerPath"; printLicenseHeader($F); print F "#pragma once\n\n"; print F "#include \"".$parameters{namespace}."Names.h\"\n\n"; # FIXME: Remove `if` condition below once HTMLElementTypeHeaders.h is made inline. if ($parameters{namespace} eq "SVG") { print F "#include \"".$parameters{namespace}."ElementInlines.h\"\n\n"; } printTypeHelpers($F, \%allTags); close F; } sub printNamesHeaderFile { my ($headerPath) = shift; my $F; open F, ">$headerPath"; printLicenseHeader($F); printHeaderHead($F, "DOM", $parameters{namespace}, < #include #include #include "QualifiedName.h" END my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); print F "// Namespace\n"; print F "WEBCORE_EXPORT extern MainThreadLazyNeverDestroyed ${lowercaseNamespacePrefix}NamespaceURI;\n\n"; if (keys %allTags) { print F "// Tags\n"; printMacros($F, "WEBCORE_EXPORT extern LazyNeverDestroyed", "Tag", \%allTags); } if (keys %allAttrs) { print F "// Attributes\n"; printMacros($F, "WEBCORE_EXPORT extern LazyNeverDestroyed", "Attr", \%allAttrs); } print F "#endif\n\n"; if (keys %allTags) { print F "const unsigned $parameters{namespace}TagsCount = ", scalar(keys %allTags), ";\n"; print F "const WebCore::$parameters{namespace}QualifiedName* const* get$parameters{namespace}Tags();\n"; if ($parameters{namespace} eq "HTML") { print F "AtomString find$parameters{namespace}Tag(Span);\n" } } if (keys %allAttrs) { print F "const unsigned $parameters{namespace}AttrsCount = ", scalar(keys %allAttrs), ";\n"; print F "const WebCore::QualifiedName* const* get$parameters{namespace}Attrs();\n"; } printInit($F, 1); close F; } sub findMaxTagLength { my $allTags = shift; my $maxLength = 0; foreach my $tagName (keys %{$allTags}) { my $tagLength = length($tagName); $maxLength = $tagLength if $tagLength > $maxLength; } return $maxLength; } sub tagsWithLength { my $allAttrs = shift; my $expectedLength = shift; my @tags = (); foreach my $tagName (sort keys %{$allAttrs}) { push(@tags, $tagName) if length($tagName) == $expectedLength; } return @tags; } sub generateFindTagForLength { my $indent = shift; my $tagsRef = shift; my $length = shift; my $currentIndex = shift; my @tags = @{$tagsRef}; my $tagCount = @tags; if ($tagCount == 1) { my $tag = $tags[0]; my $needsIfCheck = $currentIndex < $length; if ($needsIfCheck) { my $lengthToCompare = $length - $currentIndex; if ($lengthToCompare == 1) { my $letter = substr($tag, $currentIndex, 1); print F "${indent}if (buffer[$currentIndex] == '$letter') {\n"; } else { my $bufferStart = $currentIndex > 0 ? "buffer.data() + $currentIndex" : "buffer.data()"; print F "${indent}static constexpr UChar ${tag}Rest[] = { "; for (my $index = $currentIndex; $index < $length; $index = $index + 1) { my $letter = substr($tag, $index, 1); print F "'$letter', "; } print F "};\n"; print F "${indent}if (!memcmp($bufferStart, ${tag}Rest, $lengthToCompare * sizeof(UChar))) {\n"; } print F "$indent return ${tag}Tag->localName();\n"; print F "$indent}\n"; print F "${indent}return { };\n"; } else { print F "${indent}return ${tag}Tag->localName();\n"; } return; } for (my $i = 0; $i < $tagCount;) { my $tag = $tags[$i]; my $letterAtIndex = substr($tag, $currentIndex, 1); print F "${indent}if (buffer[$currentIndex] == '$letterAtIndex') {\n"; my @tagsWithPrefix = ($tag); for ($i = $i + 1; $i < $tagCount; $i = $i + 1) { my $nextTag = $tags[$i]; if (substr($nextTag, $currentIndex, 1) eq $letterAtIndex) { push(@tagsWithPrefix, $nextTag); } else { last; } } generateFindTagForLength($indent . " ", \@tagsWithPrefix, $length, $currentIndex + 1); if (scalar @tagsWithPrefix > 1) { print F "${indent} return { };\n"; } print F "$indent}\n"; } } sub printNamesCppFile { my $cppPath = shift; my $F; open F, ">$cppPath"; printLicenseHeader($F); printCppHead($F, "DOM", $parameters{namespace}, "WebCore"); my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); print F "MainThreadLazyNeverDestroyed ${lowercaseNamespacePrefix}NamespaceURI;\n\n"; print F StaticString::GenerateStrings(\%allStrings); if (keys %allTags) { print F "// Tags\n"; for my $name (sort keys %allTags) { print F "WEBCORE_EXPORT LazyNeverDestroyed ${name}Tag;\n"; } print F "\n\nconst WebCore::$parameters{namespace}QualifiedName* const* get$parameters{namespace}Tags()\n"; print F "{\n static const WebCore::$parameters{namespace}QualifiedName* const $parameters{namespace}Tags[] = {\n"; for my $name (sort keys %allTags) { print F " &${name}Tag.get(),\n"; } print F " };\n"; print F " return $parameters{namespace}Tags;\n"; print F "}\n"; if ($parameters{namespace} eq "HTML") { print F "\nAtomString find$parameters{namespace}Tag(Span buffer)\n{\n"; my $maxTagLength = findMaxTagLength(\%allTags); print F " switch (buffer.size()) {\n"; for (my $length = 1; $length <= $maxTagLength; $length = $length + 1) { my @tags = tagsWithLength(\%allTags, $length); next unless scalar @tags > 0; print F " case $length: {\n"; generateFindTagForLength(" ", \@tags, $length, 0); print F " break;\n"; print F " }\n"; } print F " default:\n"; print F " break;\n"; print F " };\n"; print F " return { };\n"; print F "}\n"; } } if (keys %allAttrs) { print F "\n// Attributes\n"; for my $name (sort keys %allAttrs) { print F "WEBCORE_EXPORT LazyNeverDestroyed ${name}Attr;\n"; } print F "\n\nconst WebCore::QualifiedName* const* get$parameters{namespace}Attrs()\n"; print F "{\n static const WebCore::QualifiedName* const $parameters{namespace}Attrs[] = {\n"; for my $name (sort keys %allAttrs) { print F " &${name}Attr.get(),\n"; } print F " };\n"; print F " return $parameters{namespace}Attrs;\n"; print F "}\n"; } printInit($F, 0); print(F " AtomString ${lowercaseNamespacePrefix}NS(\"$parameters{namespaceURI}\"_s);\n\n"); print(F " // Namespace\n"); print(F " ${lowercaseNamespacePrefix}NamespaceURI.construct(${lowercaseNamespacePrefix}NS);\n"); print(F "\n"); print F StaticString::GenerateStringAsserts(\%allStrings); if (keys %allTags) { my $tagsNamespace = $parameters{tagsNullNamespace} ? "nullAtom()" : "${lowercaseNamespacePrefix}NS"; printDefinitions($F, \%allTags, "tags", $tagsNamespace); } if (keys %allAttrs) { my $attrsNamespace = $parameters{attrsNullNamespace} ? "nullAtom()" : "${lowercaseNamespacePrefix}NS"; printDefinitions($F, \%allAttrs, "attributes", $attrsNamespace); } print F "}\n\n} }\n\n"; close F; } sub printJSElementIncludes { my $F = shift; my %tagsSeen; for my $tagName (sort keys %allTags) { my $JSInterfaceName = $allTags{$tagName}{JSInterfaceName}; next if defined($tagsSeen{$JSInterfaceName}) || usesDefaultJSWrapper($tagName); if ($allTags{$tagName}{conditional}) { # We skip feature-define-specific #includes here since we handle them separately. next; } $tagsSeen{$JSInterfaceName} = 1; print F "#include \"JS${JSInterfaceName}.h\"\n"; } print F "#include \"JS$parameters{fallbackJSInterfaceName}.h\"\n"; } sub printElementIncludes { my $F = shift; my %tagsSeen; for my $tagName (sort keys %allTags) { my $interfaceName = $allTags{$tagName}{interfaceName}; next if defined($tagsSeen{$interfaceName}); if ($allTags{$tagName}{conditional}) { # We skip feature-define-specific #includes here since we handle them separately. next; } $tagsSeen{$interfaceName} = 1; print F "#include \"${interfaceName}.h\"\n"; } print F "#include \"$parameters{fallbackInterfaceName}.h\"\n"; } sub printConditionalElementIncludes { my ($F, $wrapperIncludes) = @_; my %conditionals; my %unconditionalElementIncludes; my %unconditionalJSElementIncludes; for my $tagName (keys %allTags) { my $conditional = $allTags{$tagName}{conditional}; my $interfaceName = $allTags{$tagName}{interfaceName}; my $JSInterfaceName = $allTags{$tagName}{JSInterfaceName}; if ($conditional) { $conditionals{$conditional}{interfaceNames}{$interfaceName} = 1; $conditionals{$conditional}{JSInterfaceNames}{$JSInterfaceName} = 1; } else { $unconditionalElementIncludes{$interfaceName} = 1; $unconditionalJSElementIncludes{$JSInterfaceName} = 1; } } for my $conditional (sort keys %conditionals) { print F "\n#if ENABLE($conditional)\n"; for my $interfaceName (sort keys %{$conditionals{$conditional}{interfaceNames}}) { next if $unconditionalElementIncludes{$interfaceName}; print F "#include \"$interfaceName.h\"\n"; } if ($wrapperIncludes) { for my $JSInterfaceName (sort keys %{$conditionals{$conditional}{JSInterfaceNames}}) { next if $unconditionalJSElementIncludes{$JSInterfaceName}; print F "#include \"JS$JSInterfaceName.h\"\n"; } } print F "#endif\n"; } } sub printDefinitions { my ($F, $namesRef, $type, $namespaceURI) = @_; my $shortCamelType = ucfirst(substr(substr($type, 0, -1), 0, 4)); my $capitalizedType = ucfirst($type); print F <* targetAddress; const StaticStringImpl& name; }; static const ${capitalizedType}TableEntry ${type}Table[] = { END my $cast = $type eq "tags" ? "(LazyNeverDestroyed*)" : ""; for my $name (sort keys %$namesRef) { print F " { $cast&$name$shortCamelType, *(&${name}Data) },\n"; } print F <construct(nullAtom(), AtomString(&entry.name), $namespaceURI); END } ## ElementFactory routines sub printFactoryCppFile { my $cppPath = shift; my $F; open F, ">$cppPath"; my $formElementArgumentForDeclaration = ""; my $formElementArgumentForDefinition = ""; $formElementArgumentForDeclaration = ", HTMLFormElement*" if $parameters{namespace} eq "HTML"; $formElementArgumentForDefinition = ", HTMLFormElement* formElement" if $parameters{namespace} eq "HTML"; printLicenseHeader($F); print F < #include namespace WebCore { using $parameters{namespace}ConstructorFunction = Ref<$parameters{namespace}Element> (*)(const QualifiedName&, Document&$formElementArgumentForDeclaration, bool createdByParser); END ; my %tagConstructorMap = buildConstructorMap(); my $argumentList; if ($parameters{namespace} eq "HTML") { $argumentList = "name, document, formElement, createdByParser"; } else { $argumentList = "name, document, createdByParser"; } my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); printConstructors($F, \%tagConstructorMap); my $firstTag; for my $tag (sort keys %tagConstructorMap) { $firstTag = $tag; last; } print F < create$parameters{namespace}FactoryMap() { struct TableEntry { decltype($parameters{namespace}Names::${firstTag}Tag)& name; $parameters{namespace}ConstructorFunction function; }; static constexpr TableEntry table[] = { END ; printFunctionTable($F, \%tagConstructorMap); print F < map; for (auto& entry : table) map.add(entry.name.get().localName(), $parameters{namespace}ConstructorFunctionMapEntry { entry.function, &entry.name.get() }); return map; } static $parameters{namespace}ConstructorFunctionMapEntry find$parameters{namespace}ElementConstructorFunction(const AtomString& localName) { static NeverDestroyed map = create$parameters{namespace}FactoryMap(); return map.get().get(localName); } RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const AtomString& localName, Document& document$formElementArgumentForDefinition, bool createdByParser) { const $parameters{namespace}ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(localName); if (LIKELY(entry.function)) { ASSERT(entry.qualifiedName); const auto& name = *entry.qualifiedName; return entry.function($argumentList); } return nullptr; } RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser) { const $parameters{namespace}ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName()); if (LIKELY(entry.function)) return entry.function($argumentList); return nullptr; } Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const AtomString& localName, Document& document$formElementArgumentForDefinition, bool createdByParser) { const $parameters{namespace}ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(localName); if (LIKELY(entry.function)) { ASSERT(entry.qualifiedName); const auto& name = *entry.qualifiedName; return entry.function($argumentList); } return $parameters{fallbackInterfaceName}::create(QualifiedName(nullAtom(), localName, $parameters{namespace}Names::${lowercaseNamespacePrefix}NamespaceURI), document); } Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser) { const $parameters{namespace}ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName()); if (LIKELY(entry.function)) return entry.function($argumentList); return $parameters{fallbackInterfaceName}::create(name, document); } } // namespace WebCore END ; print F "#endif\n" if $parameters{guardFactoryWith}; close F; } sub printFactoryHeaderFile { my $headerPath = shift; my $F; open F, ">$headerPath"; printLicenseHeader($F); print F< namespace WebCore { class Document; class HTMLFormElement; class QualifiedName; class $parameters{namespace}Element; class $parameters{namespace}ElementFactory { public: END ; print F "static RefPtr<$parameters{namespace}Element> createKnownElement(const AtomString&, Document&"; print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; print F ", bool createdByParser = false);\n"; print F "static RefPtr<$parameters{namespace}Element> createKnownElement(const QualifiedName&, Document&"; print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; print F ", bool createdByParser = false);\n"; print F "static Ref<$parameters{namespace}Element> createElement(const AtomString&, Document&"; print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; print F ", bool createdByParser = false);\n"; print F "static Ref<$parameters{namespace}Element> createElement(const QualifiedName&, Document&"; print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; print F ", bool createdByParser = false);\n"; printf F<&& element) { if (element->is$parameters{fallbackInterfaceName}()) return createWrapper<$parameters{fallbackInterfaceName}>(globalObject, WTFMove(element)); return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END ; } elsif ($allTags{$tagName}{settingsConditional}) { print F <&& element) { if (element->is$parameters{fallbackInterfaceName}()) return createWrapper<$parameters{fallbackInterfaceName}>(globalObject, WTFMove(element)); return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END ; } elsif ($allTags{$tagName}{runtimeEnabled}) { my $runtimeEnabled = $allTags{$tagName}{runtimeEnabled}; print F <&& element) { if (element->is$parameters{fallbackInterfaceName}()) return createWrapper<$parameters{fallbackJSInterfaceName}>(globalObject, WTFMove(element)); return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END ; } else { print F <&& element) { return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END ; } if ($conditional) { print F "#endif\n\n"; } } } sub printWrapperFactoryCppFile { my $outputDir = shift; my $wrapperFactoryFileName = shift; my $F; open F, ">" . $outputDir . "/JS" . $wrapperFactoryFileName . ".cpp"; printLicenseHeader($F); print F "#include \"config.h\"\n"; print F "#include \"JS$parameters{namespace}ElementWrapperFactory.h\"\n\n"; print F "\n#if $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith}; printJSElementIncludes($F); printElementIncludes($F); print F "\n#include \"$parameters{namespace}Names.h\"\n"; print F < #include #include END ; printConditionalElementIncludes($F, 1); print F <&&); END ; printWrapperFunctions($F); my $firstTag; for my $tag (sort keys %allTags) { $firstTag = $tag; last; } print F < create$parameters{namespace}WrapperMap() { struct TableEntry { decltype($parameters{namespace}Names::${firstTag}Tag)& name; Create$parameters{namespace}ElementWrapperFunction function; }; static constexpr TableEntry table[] = { END ; for my $tag (sort keys %allTags) { # Do not add the name to the map if it does not have a JS wrapper constructor or uses the default wrapper. next if (usesDefaultJSWrapper($tag, \%allTags) && ($parameters{fallbackJSInterfaceName} eq $parameters{namespace} . "Element")); my $conditional = $allTags{$tag}{conditional}; if ($conditional) { my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; print F "#if ${conditionalString}\n"; } my $ucTag; if ($allTags{$tag}{settingsConditional}) { $ucTag = $allTags{$tag}{interfaceName}; } else { $ucTag = $allTags{$tag}{JSInterfaceName}; } print F " { $parameters{namespace}Names::${tag}Tag, create${ucTag}Wrapper },\n"; if ($conditional) { print F "#endif\n"; } } print F < map; for (auto& entry : table) map.add(entry.name.get().localName(), entry.function); return map; } JSDOMObject* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) { static NeverDestroyed functions = create$parameters{namespace}WrapperMap(); if (auto function = functions.get().get(element->localName())) return function(globalObject, WTFMove(element)); END ; if ($parameters{customElementInterfaceName}) { print F <isUnknownElement()) return createWrapper<$parameters{customElementInterfaceName}>(globalObject, WTFMove(element)); END ; } if ("$parameters{namespace}Element" eq $parameters{fallbackJSInterfaceName}) { print F <is$parameters{fallbackJSInterfaceName}()); END ; } print F <(globalObject, WTFMove(element)); } } END ; print F "\n#endif\n" if $parameters{guardFactoryWith}; close F; } sub printWrapperFactoryHeaderFile { my $outputDir = shift; my $wrapperFactoryFileName = shift; my $F; open F, ">" . $outputDir . "/JS" . $wrapperFactoryFileName . ".h"; printLicenseHeader($F); print F "#pragma once\n\n"; print F < namespace WebCore { class JSDOMObject; class JSDOMGlobalObject; class $parameters{namespace}Element; JSDOMObject* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject*, Ref<$parameters{namespace}Element>&&); } END ; close F; }