| #!/usr/bin/env perl |
| |
| # Copyright (C) 2005-2007, 2009, 2013-2014 Apple Inc. All rights reserved. |
| # Copyright (C) 2009, Julien Chaffraix <jchaffraix@webkit.org> |
| # 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, <<END, ""); |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/RobinHoodHashMap.h> |
| #include <wtf/Vector.h> |
| #include <wtf/text/AtomString.h> |
| 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<typename T, size_t inlineCapacity = 0>\n"; |
| print F "class FamilyNamesList : public Vector<T, inlineCapacity> {\n"; |
| print F "public:\n"; |
| print F " T& at(FamilyNamesIndex i)\n"; |
| print F " {\n"; |
| print F " return Vector<T, inlineCapacity>::at(static_cast<size_t>(i));\n"; |
| print F " }\n"; |
| print F "};\n\n"; |
| print F "extern LazyNeverDestroyed<FamilyNamesList<const StaticStringImpl*, ", scalar(keys %parameters), ">> familyNamesData;\n"; |
| print F "extern MainThreadLazyNeverDestroyed<FamilyNamesList<AtomStringImpl*, ", scalar(keys %parameters), ">> familyNames;\n\n"; |
| printMacros($F, "extern MainThreadLazyNeverDestroyed<const AtomString>", "", \%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<FamilyNamesList<const StaticStringImpl*, ", scalar(keys %parameters), ">> familyNamesData;\n"; |
| print F "MainThreadLazyNeverDestroyed<FamilyNamesList<AtomStringImpl*, ", scalar(keys %parameters), ">> familyNames;\n\n"; |
| |
| printMacros($F, "MainThreadLazyNeverDestroyed<const AtomString>", "", \%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 <file> or --attrs <file>" 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 <namespace>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 <<END |
| if (!document.settings().mediaEnabled()) |
| return $parameters{fallbackInterfaceName}::create($constructorTagName, document); |
| |
| END |
| ; |
| } |
| |
| my $runtimeCondition; |
| my $settingsConditional = $allTags{$tagName}{settingsConditional}; |
| my $runtimeEnabled = $allTags{$tagName}{runtimeEnabled}; |
| if ($settingsConditional) { |
| $runtimeCondition = "document.settings().${settingsConditional}()"; |
| } elsif ($runtimeEnabled) { |
| $runtimeCondition = "RuntimeEnabledFeatures::sharedFeatures().${runtimeEnabled}Enabled()"; |
| } |
| |
| if ($runtimeCondition) { |
| print F <<END |
| if (!$runtimeCondition) |
| return $parameters{fallbackInterfaceName}::create($constructorTagName, document); |
| END |
| ; |
| } |
| |
| # Call the constructor with the right parameters. |
| print F " return ${interfaceName}::create($constructorTagName, document"; |
| print F ", formElement" if $allTags{$tagName}{constructorNeedsFormElement}; |
| print F ", createdByParser" if $allTags{$tagName}{constructorNeedsCreatedByParser}; |
| print F ");\n}\n"; |
| } |
| |
| sub printConstructors |
| { |
| my ($F, $tagConstructorMapRef) = @_; |
| my %tagConstructorMap = %$tagConstructorMapRef; |
| |
| # This is to avoid generating the same constructor several times. |
| my %uniqueTags = (); |
| for my $tagName (sort keys %tagConstructorMap) { |
| my $interfaceName = $allTags{$tagName}{interfaceName}; |
| |
| # Ignore the mapped tag |
| # FIXME: It could be moved inside this loop but was split for readibility. |
| next if (defined($uniqueTags{$interfaceName}) || $allTags{$tagName}{mapToTagName}); |
| # Tags can have wrappers without constructors. |
| # This is useful to make user-agent shadow elements internally testable |
| # while keeping them from being avaialble in the HTML markup. |
| next if $allTags{$tagName}{noConstructor}; |
| |
| $uniqueTags{$interfaceName} = '1'; |
| |
| my $conditional = $allTags{$tagName}{conditional}; |
| if ($conditional) { |
| my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; |
| print F "#if ${conditionalString}\n"; |
| } |
| |
| printConstructorSignature($F, $tagName, $tagConstructorMap{$tagName}, "tagName"); |
| printConstructorInterior($F, $tagName, $interfaceName, "tagName"); |
| |
| if ($conditional) { |
| print F "#endif\n"; |
| } |
| |
| print F "\n"; |
| } |
| |
| # Mapped tag name uses a special wrapper to keep their prefix and namespaceURI while using the mapped localname. |
| for my $tagName (sort keys %tagConstructorMap) { |
| if ($allTags{$tagName}{mapToTagName}) { |
| my $mappedName = $allTags{$tagName}{mapToTagName}; |
| printConstructorSignature($F, $mappedName, $mappedName . "To" . $tagName, "tagName"); |
| printConstructorInterior($F, $mappedName, $allTags{$mappedName}{interfaceName}, "QualifiedName(tagName.prefix(), ${mappedName}Tag->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<<END |
| #pragma once |
| |
| $includes |
| |
| namespace WebCore { |
| |
| ${definitions}namespace ${namespace}Names { |
| |
| #ifndef ${prefix}_${namespace}_NAMES_HIDE_GLOBALS |
| |
| END |
| ; |
| } |
| |
| sub printCppHead |
| { |
| my ($F, $prefix, $namespace, $usedNamespace) = @_; |
| |
| print F "#include \"config.h\"\n\n"; |
| print F "#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC\n"; |
| print F "#define ${prefix}_${namespace}_NAMES_HIDE_GLOBALS 1\n"; |
| print F "#else\n"; |
| print F "#define QNAME_DEFAULT_CONSTRUCTOR 1\n"; |
| print F "#endif\n\n"; |
| |
| print F "#include \"${namespace}Names.h\"\n\n"; |
| |
| print F "namespace WebCore {\n\n"; |
| print F "namespace ${namespace}Names {\n\n"; |
| print F "using namespace $usedNamespace;\n\n"; |
| } |
| |
| sub printInit |
| { |
| my ($F, $isDefinition) = @_; |
| |
| if ($isDefinition) { |
| print F "\nWEBCORE_EXPORT void init();\n\n"; |
| print F "} }\n\n"; |
| return; |
| } |
| |
| print F "\nvoid init() |
| { |
| static bool initialized = false; |
| if (initialized) |
| return; |
| initialized = true; |
| |
| // Use placement new to initialize the globals. |
| "; |
| } |
| |
| sub printLicenseHeader |
| { |
| my $F = shift; |
| print F "/* |
| * THIS FILE WAS AUTOMATICALLY GENERATED, DO NOT EDIT. |
| * |
| * This file was generated by the dom/make_names.pl script. |
| * |
| * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR |
| * 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. |
| */ |
| |
| "; |
| } |
| |
| sub printTypeHelpers |
| { |
| my ($F, $namesRef) = @_; |
| my %names = %$namesRef; |
| |
| # Do a first pass to discard classes that map to several tags. |
| my %classToTags = (); |
| for my $name (keys %names) { |
| my $class = $parsedTags{$name}{interfaceName}; |
| push(@{$classToTags{$class}}, $name) if defined $class; |
| } |
| |
| for my $class (sort keys %classToTags) { |
| my $name = $classToTags{$class}[0]; |
| next if $parsedTags{$name}{customTypeHelper}; |
| # Skip classes that map to more than 1 tag. |
| my $tagCount = scalar @{$classToTags{$class}}; |
| next if $tagCount > 1; |
| |
| print F <<END |
| namespace WebCore { |
| class $class; |
| } |
| namespace WTF { |
| template<typename ArgType> class TypeCastTraits<const WebCore::$class, ArgType, false /* isBaseType */> { |
| 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 <<END |
| static bool checkTagName(const WebCore::HTMLElement& element) { return !element.isHTMLUnknownElement() && element.hasTagName(WebCore::$parameters{namespace}Names::${name}Tag); } |
| static bool checkTagName(const WebCore::Node& node) { return is<WebCore::HTMLElement>(node) && checkTagName(downcast<WebCore::HTMLElement>(node)); } |
| END |
| ; |
| } else { |
| print F <<END |
| static bool checkTagName(const WebCore::$parameters{namespace}Element& element) { return element.hasTagName(WebCore::$parameters{namespace}Names::${name}Tag); } |
| static bool checkTagName(const WebCore::Node& node) { return node.hasTagName(WebCore::$parameters{namespace}Names::${name}Tag); } |
| END |
| ; |
| } |
| print F <<END |
| static bool checkTagName(const WebCore::EventTarget& target) { return is<WebCore::Node>(target) && checkTagName(downcast<WebCore::Node>(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}, <<END, "class $parameters{namespace}QualifiedName : public QualifiedName { };\n\n"); |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/RobinHoodHashMap.h> |
| #include <wtf/text/AtomString.h> |
| #include "QualifiedName.h" |
| END |
| |
| my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); |
| |
| print F "// Namespace\n"; |
| print F "WEBCORE_EXPORT extern MainThreadLazyNeverDestroyed<const AtomString> ${lowercaseNamespacePrefix}NamespaceURI;\n\n"; |
| |
| if (keys %allTags) { |
| print F "// Tags\n"; |
| printMacros($F, "WEBCORE_EXPORT extern LazyNeverDestroyed<const WebCore::$parameters{namespace}QualifiedName>", "Tag", \%allTags); |
| } |
| |
| if (keys %allAttrs) { |
| print F "// Attributes\n"; |
| printMacros($F, "WEBCORE_EXPORT extern LazyNeverDestroyed<const WebCore::QualifiedName>", "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<const UChar>);\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<const AtomString> ${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<const $parameters{namespace}QualifiedName> ${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<const UChar> 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<const QualifiedName> ${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 <<END; |
| |
| struct ${capitalizedType}TableEntry { |
| LazyNeverDestroyed<const QualifiedName>* targetAddress; |
| const StaticStringImpl& name; |
| }; |
| |
| static const ${capitalizedType}TableEntry ${type}Table[] = { |
| END |
| |
| my $cast = $type eq "tags" ? "(LazyNeverDestroyed<const QualifiedName>*)" : ""; |
| for my $name (sort keys %$namesRef) { |
| print F " { $cast&$name$shortCamelType, *(&${name}Data) },\n"; |
| } |
| |
| print F <<END; |
| }; |
| |
| for (auto& entry : ${type}Table) |
| entry.targetAddress->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 <<END |
| #include "config.h" |
| END |
| ; |
| |
| print F "\n#if $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith}; |
| |
| print F <<END |
| #include "$parameters{namespace}ElementFactory.h" |
| |
| #include "$parameters{namespace}Names.h" |
| |
| END |
| ; |
| |
| printElementIncludes($F); |
| printConditionalElementIncludes($F, 0); |
| |
| print F <<END |
| |
| #include "Document.h" |
| #include "RuntimeEnabledFeatures.h" |
| #include "Settings.h" |
| #include <wtf/RobinHoodHashMap.h> |
| #include <wtf/NeverDestroyed.h> |
| |
| 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 <<END |
| |
| struct $parameters{namespace}ConstructorFunctionMapEntry { |
| $parameters{namespace}ConstructorFunction function { nullptr }; |
| const QualifiedName* qualifiedName { nullptr }; // Use pointer instead of reference so that emptyValue() in HashMap is cheap to create. |
| }; |
| |
| static NEVER_INLINE MemoryCompactLookupOnlyRobinHoodHashMap<AtomString, $parameters{namespace}ConstructorFunctionMapEntry> 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 <<END |
| }; |
| |
| MemoryCompactLookupOnlyRobinHoodHashMap<AtomString, $parameters{namespace}ConstructorFunctionMapEntry> 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<<END |
| #pragma once |
| |
| #include <wtf/Forward.h> |
| |
| 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<<END |
| }; |
| |
| } |
| |
| END |
| ; |
| |
| close F; |
| } |
| |
| ## Wrapper Factory routines |
| |
| sub usesDefaultJSWrapper |
| { |
| my $name = shift; |
| |
| # A tag reuses the default wrapper if its JSInterfaceName matches the default namespace Element. |
| return $allTags{$name}{JSInterfaceName} eq $parameters{namespace} . "Element"; |
| } |
| |
| sub printWrapperFunctions |
| { |
| my $F = shift; |
| |
| my %tagsSeen; |
| for my $tagName (sort keys %allTags) { |
| # Avoid defining the same wrapper method twice. |
| my $JSInterfaceName = $allTags{$tagName}{JSInterfaceName}; |
| next if (defined($tagsSeen{$JSInterfaceName}) || (usesDefaultJSWrapper($tagName) && ($parameters{fallbackJSInterfaceName} eq $parameters{namespace} . "Element"))) && !$allTags{$tagName}{settingsConditional}; |
| $tagsSeen{$JSInterfaceName} = 1; |
| |
| my $conditional = $allTags{$tagName}{conditional}; |
| if ($conditional) { |
| my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; |
| print F "#if ${conditionalString}\n\n"; |
| } |
| |
| if ($allTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { |
| print F <<END |
| static JSDOMObject* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& 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 <<END |
| static JSDOMObject* create$allTags{$tagName}{interfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& 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 <<END |
| static JSDOMObject* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) |
| { |
| if (element->is$parameters{fallbackInterfaceName}()) |
| return createWrapper<$parameters{fallbackJSInterfaceName}>(globalObject, WTFMove(element)); |
| return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); |
| } |
| END |
| ; |
| } else { |
| print F <<END |
| static JSDOMObject* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& 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 <<END |
| |
| #include "Document.h" |
| #include "RuntimeEnabledFeatures.h" |
| #include "Settings.h" |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/RobinHoodHashMap.h> |
| #include <wtf/StdLibExtras.h> |
| END |
| ; |
| |
| printConditionalElementIncludes($F, 1); |
| |
| print F <<END |
| |
| using namespace JSC; |
| |
| namespace WebCore { |
| |
| using Create$parameters{namespace}ElementWrapperFunction = JSDOMObject* (*)(JSDOMGlobalObject*, Ref<$parameters{namespace}Element>&&); |
| |
| END |
| ; |
| |
| printWrapperFunctions($F); |
| |
| my $firstTag; |
| for my $tag (sort keys %allTags) { |
| $firstTag = $tag; |
| last; |
| } |
| |
| print F <<END |
| |
| static NEVER_INLINE MemoryCompactLookupOnlyRobinHoodHashMap<AtomString, Create$parameters{namespace}ElementWrapperFunction> 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 <<END |
| }; |
| |
| MemoryCompactLookupOnlyRobinHoodHashMap<AtomString, Create$parameters{namespace}ElementWrapperFunction> 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 <<END |
| if (!element->isUnknownElement()) |
| return createWrapper<$parameters{customElementInterfaceName}>(globalObject, WTFMove(element)); |
| END |
| ; |
| } |
| |
| if ("$parameters{namespace}Element" eq $parameters{fallbackJSInterfaceName}) { |
| print F <<END |
| ASSERT(element->is$parameters{fallbackJSInterfaceName}()); |
| END |
| ; |
| } |
| |
| print F <<END |
| return createWrapper<$parameters{fallbackJSInterfaceName}>(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 <<END |
| #include <wtf/Forward.h> |
| |
| namespace WebCore { |
| |
| class JSDOMObject; |
| class JSDOMGlobalObject; |
| class $parameters{namespace}Element; |
| |
| JSDOMObject* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject*, Ref<$parameters{namespace}Element>&&); |
| |
| } |
| |
| END |
| ; |
| |
| close F; |
| } |