1#!/usr/bin/perl -w
2
3# Copyright (C) 2005-2007, 2009, 2013-2014 Apple Inc. All rights reserved.
4# Copyright (C) 2009, Julien Chaffraix <jchaffraix@webkit.org>
5# Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
6# Copyright (C) 2011 Ericsson AB. All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11#
12# 1.  Redistributions of source code must retain the above copyright
13#     notice, this list of conditions and the following disclaimer.
14# 2.  Redistributions in binary form must reproduce the above copyright
15#     notice, this list of conditions and the following disclaimer in the
16#     documentation and/or other materials provided with the distribution.
17# 3.  Neither the name of Apple Inc. ("Apple") nor the names of
18#     its contributors may be used to endorse or promote products derived
19#     from this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
22# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
25# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32use strict;
33
34use StaticString;
35use Config;
36use Getopt::Long;
37use File::Path;
38use File::Spec;
39use IO::File;
40use InFilesParser;
41
42sub readTags($$);
43sub readAttrs($$);
44
45my $printFactory = 0;
46my $printWrapperFactory = 0;
47my $fontNamesIn = "";
48my $tagsFile = "";
49my $attrsFile = "";
50my $outputDir = ".";
51my %parsedTags = ();
52my %parsedAttrs = ();
53my %enabledTags = ();
54my %enabledAttrs = ();
55my %allTags = ();
56my %allAttrs = ();
57my %allStrings = ();
58my %parameters = ();
59my $extraDefines = 0;
60my $initDefaults = 1;
61my %extensionAttrs = ();
62
63require Config;
64
65my $gccLocation = "";
66if ($ENV{CC}) {
67    $gccLocation = $ENV{CC};
68} elsif (($Config::Config{"osname"}) =~ /solaris/i) {
69    $gccLocation = "/usr/sfw/bin/gcc";
70} elsif ($Config::Config{"osname"} eq "darwin" && $ENV{SDKROOT}) {
71    chomp($gccLocation = `xcrun -find cc -sdk '$ENV{SDKROOT}'`);
72} else {
73    $gccLocation = "/usr/bin/cc";
74}
75my $preprocessor = $gccLocation . " -E -x c++";
76
77GetOptions(
78    'tags=s' => \$tagsFile,
79    'attrs=s' => \$attrsFile,
80    'factory' => \$printFactory,
81    'outputDir=s' => \$outputDir,
82    'extraDefines=s' => \$extraDefines,
83    'preprocessor=s' => \$preprocessor,
84    'wrapperFactory' => \$printWrapperFactory,
85    'fonts=s' => \$fontNamesIn
86);
87
88mkpath($outputDir);
89
90if (length($fontNamesIn)) {
91    my $names = new IO::File;
92    my $familyNamesFileBase = "WebKitFontFamily";
93
94    open($names, $fontNamesIn) or die "Failed to open file: $fontNamesIn";
95
96    $initDefaults = 0;
97    my $Parser = InFilesParser->new();
98    my $dummy;
99    $Parser->parse($names, \&parametersHandler, \&dummy);
100
101    my $F;
102    my $header = File::Spec->catfile($outputDir, "${familyNamesFileBase}Names.h");
103    open F, ">$header" or die "Unable to open $header for writing.";
104
105    printLicenseHeader($F);
106    printHeaderHead($F, "CSS", $familyNamesFileBase, "#include <wtf/text/AtomicString.h>", "");
107
108    printMacros($F, "extern const WTF::AtomicString", "", \%parameters);
109    print F "#endif\n\n";
110
111    printInit($F, 1);
112    close F;
113
114    my $source = File::Spec->catfile($outputDir, "${familyNamesFileBase}Names.cpp");
115    open F, ">$source" or die "Unable to open $source for writing.";
116
117    printLicenseHeader($F);
118    printCppHead($F, "CSS", $familyNamesFileBase, "WTF");
119
120    print F StaticString::GenerateStrings(\%parameters);
121
122    while ( my ($name, $identifier) = each %parameters ) {
123        print F "DEFINE_GLOBAL(AtomicString, $name)\n";
124    }
125
126    printInit($F, 0);
127
128    print F "\n";
129    print F StaticString::GenerateStringAsserts(\%parameters);
130
131    while ( my ($name, $identifier) = each %parameters ) {
132        # FIXME: Would like to use static_cast here, but there are differences in const
133        # depending on whether SKIP_STATIC_CONSTRUCTORS_ON_GCC is used, so stick with a
134        # C-style cast for now.
135        print F "    new (NotNull, (void*)&$name) AtomicString(reinterpret_cast<StringImpl*>(&${name}Data));\n";
136    }
137
138    print F "}\n}\n}\n";
139    close F;
140    exit 0;
141}
142
143die "You must specify at least one of --tags <file> or --attrs <file>" unless (length($tagsFile) || length($attrsFile));
144
145if (length($tagsFile)) {
146    %allTags = %{readTags($tagsFile, 0)};
147    %enabledTags = %{readTags($tagsFile, 1)};
148    namesToStrings(\%allTags, \%allStrings);
149}
150
151if (length($attrsFile)) {
152    %allAttrs = %{readAttrs($attrsFile, 0)};
153    %enabledAttrs = %{readAttrs($attrsFile, 1)};
154    namesToStrings(\%allAttrs, \%allStrings);
155}
156
157die "You must specify a namespace (e.g. SVG) for <namespace>Names.h" unless $parameters{namespace};
158die "You must specify a namespaceURI (e.g. http://www.w3.org/2000/svg)" unless $parameters{namespaceURI};
159
160$parameters{namespacePrefix} = $parameters{namespace} unless $parameters{namespacePrefix};
161$parameters{fallbackJSInterfaceName} = $parameters{fallbackInterfaceName} unless $parameters{fallbackJSInterfaceName};
162
163my $typeHelpersBasePath = "$outputDir/$parameters{namespace}ElementTypeHelpers";
164my $namesBasePath = "$outputDir/$parameters{namespace}Names";
165my $factoryBasePath = "$outputDir/$parameters{namespace}ElementFactory";
166my $wrapperFactoryFileName = "$parameters{namespace}ElementWrapperFactory";
167
168printNamesHeaderFile("$namesBasePath.h");
169printNamesCppFile("$namesBasePath.cpp");
170printTypeHelpersHeaderFile("$typeHelpersBasePath.h");
171
172if ($printFactory) {
173    printFactoryCppFile("$factoryBasePath.cpp");
174    printFactoryHeaderFile("$factoryBasePath.h");
175}
176
177if ($printWrapperFactory) {
178    printWrapperFactoryCppFile($outputDir, $wrapperFactoryFileName);
179    printWrapperFactoryHeaderFile($outputDir, $wrapperFactoryFileName);
180}
181
182### Hash initialization
183
184sub defaultTagPropertyHash
185{
186    return (
187        'constructorNeedsCreatedByParser' => 0,
188        'constructorNeedsFormElement' => 0,
189        'noConstructor' => 0,
190        'interfaceName' => defaultInterfaceName($_[0]),
191        # By default, the JSInterfaceName is the same as the interfaceName.
192        'JSInterfaceName' => defaultInterfaceName($_[0]),
193        'mapToTagName' => '',
194        'wrapperOnlyIfMediaIsAvailable' => 0,
195        'conditional' => 0,
196        'runtimeConditional' => 0,
197        'generateTypeHelpers' => 0
198    );
199}
200
201sub defaultParametersHash
202{
203    return (
204        'namespace' => '',
205        'namespacePrefix' => '',
206        'namespaceURI' => '',
207        'guardFactoryWith' => '',
208        'tagsNullNamespace' => 0,
209        'attrsNullNamespace' => 0,
210        'fallbackInterfaceName' => '',
211        'fallbackJSInterfaceName' => '',
212    );
213}
214
215sub defaultInterfaceName
216{
217    die "No namespace found" if !$parameters{namespace};
218    return $parameters{namespace} . upperCaseName($_[0]) . "Element"
219}
220
221### Parsing handlers
222
223sub valueForName
224{
225    my $name = shift;
226    my $value = $extensionAttrs{$name};
227
228    if (!$value) {
229        $value = $name;
230        $value =~ s/_/-/g;
231    }
232
233    return $value;
234}
235
236sub namesToStrings
237{
238    my $namesRef = shift;
239    my $stringsRef = shift;
240
241    my %names = %$namesRef;
242
243    for my $name (keys %names) {
244        $stringsRef->{$name} = valueForName($name);
245    }
246}
247
248sub tagsHandler
249{
250    my ($tag, $property, $value) = @_;
251
252    $tag =~ s/-/_/g;
253
254    # Initialize default property values.
255    $parsedTags{$tag} = { defaultTagPropertyHash($tag) } if !defined($parsedTags{$tag});
256
257    if ($property) {
258        die "Unknown property $property for tag $tag\n" if !defined($parsedTags{$tag}{$property});
259
260        # The code relies on JSInterfaceName deriving from interfaceName to check for custom JSInterfaceName.
261        # So override JSInterfaceName if it was not already set.
262        $parsedTags{$tag}{JSInterfaceName} = $value if $property eq "interfaceName" && $parsedTags{$tag}{JSInterfaceName} eq $parsedTags{$tag}{interfaceName};
263
264        $parsedTags{$tag}{$property} = $value;
265    }
266}
267
268sub attrsHandler
269{
270    my ($attr, $property, $value) = @_;
271    # Translate HTML5 extension attributes of the form 'x-webkit-feature' to 'webkitfeature'.
272    # We don't just check for the 'x-' prefix because there are attributes such as x-height
273    # which should follow the default path below.
274    if ($attr =~ m/^x-webkit-(.*)/) {
275        my $newAttr = "webkit$1";
276        $extensionAttrs{$newAttr} = $attr;
277        $attr = $newAttr;
278    }
279    $attr =~ s/-/_/g;
280
281    # Initialize default properties' values.
282    $parsedAttrs{$attr} = {} if !defined($parsedAttrs{$attr});
283
284    if ($property) {
285        die "Unknown property $property for attribute $attr\n" if !defined($parsedAttrs{$attr}{$property});
286        $parsedAttrs{$attr}{$property} = $value;
287    }
288}
289
290sub parametersHandler
291{
292    my ($parameter, $value) = @_;
293
294    # Initialize default properties' values.
295    %parameters = defaultParametersHash() if (!(keys %parameters) && $initDefaults);
296
297    die "Unknown parameter $parameter for tags/attrs\n" if (!defined($parameters{$parameter}) && $initDefaults);
298    $parameters{$parameter} = $value;
299}
300
301## Support routines
302
303sub preprocessorCommand()
304{
305    return $preprocessor if $extraDefines eq 0;
306    return $preprocessor . " -D" . join(" -D", split(" ", $extraDefines));
307}
308
309sub readNames($$$$)
310{
311    my ($namesFile, $hashToFillRef, $handler, $usePreprocessor) = @_;
312
313    my $names = new IO::File;
314    if ($usePreprocessor) {
315        open($names, preprocessorCommand() . " " . $namesFile . "|") or die "Failed to open file: $namesFile";
316    } else {
317        open($names, $namesFile) or die "Failed to open file: $namesFile";
318    }
319
320    my $InParser = InFilesParser->new();
321    $InParser->parse($names, \&parametersHandler, $handler);
322
323    close($names);
324    die "Failed to read names from file: $namesFile" if (keys %{$hashToFillRef} == 0);
325    return $hashToFillRef;
326}
327
328sub readAttrs($$)
329{
330    my ($namesFile, $usePreprocessor) = @_;
331    %parsedAttrs = ();
332    return readNames($namesFile, \%parsedAttrs, \&attrsHandler, $usePreprocessor);
333}
334
335sub readTags($$)
336{
337    my ($namesFile, $usePreprocessor) = @_;
338    %parsedTags = ();
339    return readNames($namesFile, \%parsedTags, \&tagsHandler, $usePreprocessor);
340}
341
342sub printMacros
343{
344    my ($F, $macro, $suffix, $namesRef) = @_;
345    my %names = %$namesRef;
346
347    for my $name (sort keys %names) {
348        print F "$macro $name","$suffix;\n";
349    }
350}
351
352sub usesDefaultWrapper
353{
354    my $tagName = shift;
355    return $tagName eq $parameters{namespace} . "Element";
356}
357
358# Build a direct mapping from the tags to the Element to create.
359sub buildConstructorMap
360{
361    my %tagConstructorMap = ();
362    for my $tagName (keys %enabledTags) {
363        my $interfaceName = $enabledTags{$tagName}{interfaceName};
364
365        if ($enabledTags{$tagName}{mapToTagName}) {
366            die "Cannot handle multiple mapToTagName for $tagName\n" if $enabledTags{$enabledTags{$tagName}{mapToTagName}}{mapToTagName};
367            $interfaceName = $enabledTags{ $enabledTags{$tagName}{mapToTagName} }{interfaceName};
368        }
369
370        # Chop the string to keep the interesting part.
371        $interfaceName =~ s/$parameters{namespace}(.*)Element/$1/;
372        $tagConstructorMap{$tagName} = lc($interfaceName);
373    }
374
375    return %tagConstructorMap;
376}
377
378# Helper method that print the constructor's signature avoiding
379# unneeded arguments.
380sub printConstructorSignature
381{
382    my ($F, $tagName, $constructorName, $constructorTagName) = @_;
383
384    print F "static PassRefPtr<$parameters{namespace}Element> ${constructorName}Constructor(const QualifiedName& $constructorTagName, Document& document";
385    if ($parameters{namespace} eq "HTML") {
386        print F ", HTMLFormElement*";
387        print F " formElement" if $enabledTags{$tagName}{constructorNeedsFormElement};
388    }
389    print F ", bool";
390    print F " createdByParser" if $enabledTags{$tagName}{constructorNeedsCreatedByParser};
391    print F ")\n{\n";
392}
393
394# Helper method to dump the constructor interior and call the
395# Element constructor with the right arguments.
396# The variable names should be kept in sync with the previous method.
397sub printConstructorInterior
398{
399    my ($F, $tagName, $interfaceName, $constructorTagName) = @_;
400
401    # Handle media elements.
402    # Note that wrapperOnlyIfMediaIsAvailable is a misnomer, because media availability
403    # does not just control the wrapper; it controls the element object that is created.
404    # FIXME: Could we instead do this entirely in the wrapper, and use custom wrappers
405    # instead of having all the support for this here in this script?
406    if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) {
407        print F <<END
408    Settings* settings = document.settings();
409    if (!MediaPlayer::isAvailable() || (settings && !settings->mediaEnabled()))
410        return 0;
411
412END
413;
414    }
415
416    my $runtimeConditional = $enabledTags{$tagName}{runtimeConditional};
417    if ($runtimeConditional) {
418        print F <<END
419    if (!RuntimeEnabledFeatures::sharedFeatures().${runtimeConditional}Enabled())
420        return 0;
421END
422;
423    }
424
425    # Call the constructor with the right parameters.
426    print F "    return ${interfaceName}::create($constructorTagName, document";
427    print F ", formElement" if $enabledTags{$tagName}{constructorNeedsFormElement};
428    print F ", createdByParser" if $enabledTags{$tagName}{constructorNeedsCreatedByParser};
429    print F ");\n}\n";
430}
431
432sub printConstructors
433{
434    my ($F, $tagConstructorMapRef) = @_;
435    my %tagConstructorMap = %$tagConstructorMapRef;
436
437    # This is to avoid generating the same constructor several times.
438    my %uniqueTags = ();
439    for my $tagName (sort keys %tagConstructorMap) {
440        my $interfaceName = $enabledTags{$tagName}{interfaceName};
441
442        # Ignore the mapped tag
443        # FIXME: It could be moved inside this loop but was split for readibility.
444        next if (defined($uniqueTags{$interfaceName}) || $enabledTags{$tagName}{mapToTagName});
445        # Tags can have wrappers without constructors.
446        # This is useful to make user-agent shadow elements internally testable
447        # while keeping them from being avaialble in the HTML markup.
448        next if $enabledTags{$tagName}{noConstructor};
449
450        $uniqueTags{$interfaceName} = '1';
451
452        my $conditional = $enabledTags{$tagName}{conditional};
453        if ($conditional) {
454            my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")";
455            print F "#if ${conditionalString}\n";
456        }
457
458        printConstructorSignature($F, $tagName, $tagConstructorMap{$tagName}, "tagName");
459        printConstructorInterior($F, $tagName, $interfaceName, "tagName");
460
461        if ($conditional) {
462            print F "#endif\n";
463        }
464
465        print F "\n";
466    }
467
468    # Mapped tag name uses a special wrapper to keep their prefix and namespaceURI while using the mapped localname.
469    for my $tagName (sort keys %tagConstructorMap) {
470        if ($enabledTags{$tagName}{mapToTagName}) {
471            my $mappedName = $enabledTags{$tagName}{mapToTagName};
472            printConstructorSignature($F, $mappedName, $mappedName . "To" . $tagName, "tagName");
473            printConstructorInterior($F, $mappedName, $enabledTags{$mappedName}{interfaceName}, "QualifiedName(tagName.prefix(), ${mappedName}Tag.localName(), tagName.namespaceURI())");
474        }
475    }
476}
477
478sub printFunctionTable
479{
480    my ($F, $tagConstructorMap) = @_;
481    my %tagConstructorMap = %$tagConstructorMap;
482
483    for my $tagName (sort keys %tagConstructorMap) {
484        next if $enabledTags{$tagName}{noConstructor};
485
486        my $conditional = $enabledTags{$tagName}{conditional};
487        if ($conditional) {
488            my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")";
489            print F "#if ${conditionalString}\n";
490        }
491
492        if ($enabledTags{$tagName}{mapToTagName}) {
493            print F "        { ${tagName}Tag, $enabledTags{$tagName}{mapToTagName}To${tagName}Constructor },\n";
494        } else {
495            print F "        { ${tagName}Tag, $tagConstructorMap{$tagName}Constructor },\n";
496        }
497
498        if ($conditional) {
499            print F "#endif\n";
500        }
501    }
502}
503
504sub svgCapitalizationHacks
505{
506    my $name = shift;
507
508    $name = "FE" . ucfirst $1 if $name =~ /^fe(.+)$/;
509
510    return $name;
511}
512
513sub upperCaseName
514{
515    my $name = shift;
516
517    $name = svgCapitalizationHacks($name) if ($parameters{namespace} eq "SVG");
518
519    while ($name =~ /^(.*?)_(.*)/) {
520        $name = $1 . ucfirst $2;
521    }
522
523    return ucfirst $name;
524}
525
526sub printHeaderHead
527{
528    my ($F, $prefix, $namespace, $includes, $definitions) = @_;
529
530    print F<<END
531#ifndef ${prefix}_${namespace}Names_h
532
533#define ${prefix}_${namespace}Names_h
534
535$includes
536
537namespace WebCore {
538
539${definitions}namespace ${namespace}Names {
540
541#ifndef ${prefix}_${namespace}_NAMES_HIDE_GLOBALS
542
543END
544    ;
545}
546
547sub printCppHead
548{
549    my ($F, $prefix, $namespace, $usedNamespace) = @_;
550
551    print F "#include \"config.h\"\n\n";
552    print F "#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC\n";
553    print F "#define ${prefix}_${namespace}_NAMES_HIDE_GLOBALS 1\n";
554    print F "#else\n";
555    print F "#define QNAME_DEFAULT_CONSTRUCTOR 1\n";
556    print F "#endif\n\n";
557
558    print F "#include \"${namespace}Names.h\"\n\n";
559    print F "#include <wtf/StaticConstructors.h>\n";
560
561    print F "namespace WebCore {\n\n";
562    print F "namespace ${namespace}Names {\n\n";
563    print F "using namespace $usedNamespace;\n\n";
564}
565
566sub printInit
567{
568    my ($F, $isDefinition) = @_;
569
570    if ($isDefinition) {
571        print F "\nvoid init();\n\n";
572        print F "} }\n\n";
573        print F "#endif\n\n";
574        return;
575    }
576
577print F "\nvoid init()
578{
579    static bool initialized = false;
580    if (initialized)
581        return;
582    initialized = true;
583
584    // Use placement new to initialize the globals.
585
586    AtomicString::init();
587";
588}
589
590sub printLicenseHeader
591{
592    my $F = shift;
593    print F "/*
594 * THIS FILE WAS AUTOMATICALLY GENERATED, DO NOT EDIT.
595 *
596 * This file was generated by the dom/make_names.pl script.
597 *
598 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc.  All rights reserved.
599 *
600 * Redistribution and use in source and binary forms, with or without
601 * modification, are permitted provided that the following conditions
602 * are met:
603 * 1. Redistributions of source code must retain the above copyright
604 *    notice, this list of conditions and the following disclaimer.
605 * 2. Redistributions in binary form must reproduce the above copyright
606 *    notice, this list of conditions and the following disclaimer in the
607 *    documentation and/or other materials provided with the distribution.
608 *
609 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
610 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
611 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
612 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
613 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
614 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
615 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
616 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
617 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
618 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
619 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
620 */
621
622";
623}
624
625sub printTypeHelpers
626{
627    my ($F, $namesRef) = @_;
628    my %names = %$namesRef;
629
630    for my $name (sort keys %names) {
631        if (!$parsedTags{$name}{generateTypeHelpers}) {
632            next;
633        }
634
635        my $class = $parsedTags{$name}{interfaceName};
636        my $checkHelper = "is$class";
637
638        print F <<END
639class $class;
640void $checkHelper(const $class&); // Catch unnecessary runtime check of type known at compile time.
641void $checkHelper(const $class*); // Catch unnecessary runtime check of type known at compile time.
642END
643        ;
644
645        if ($parameters{namespace} eq "HTML") {
646            if ($parsedTags{$name}{wrapperOnlyIfMediaIsAvailable}) {
647                # We need to check for HTMLUnknownElement if it might have been created by the factory.
648                print F <<END
649inline bool $checkHelper(const HTMLElement& element) { return !element.isHTMLUnknownElement() && element.hasTagName($parameters{namespace}Names::${name}Tag); }
650inline bool $checkHelper(const HTMLElement* element) { ASSERT(element); return $checkHelper(*element); }
651END
652                ;
653            } else {
654                print F <<END
655inline bool $checkHelper(const HTMLElement& element) { return element.hasTagName(HTMLNames::${name}Tag); }
656inline bool $checkHelper(const HTMLElement* element) { ASSERT(element); return $checkHelper(*element); }
657END
658                ;
659            }
660
661                print F <<END
662inline bool $checkHelper(const Node& node) { return node.isHTMLElement() && $checkHelper(toHTMLElement(node)); }
663inline bool $checkHelper(const Node* node) { ASSERT(node); return $checkHelper(*node); }
664template <> inline bool isElementOfType<const $class>(const HTMLElement& element) { return $checkHelper(element); }
665template <> inline bool isElementOfType<const $class>(const Element& element) { return $checkHelper(element); }
666END
667                ;
668
669        } else {
670            print F <<END
671inline bool $checkHelper(const Element& element) { return element.hasTagName($parameters{namespace}Names::${name}Tag); }
672inline bool $checkHelper(const Element* element) { ASSERT(element); return $checkHelper(*element); }
673inline bool $checkHelper(const Node& node) { return node.isElementNode() && $checkHelper(toElement(node)); }
674inline bool $checkHelper(const Node* node) { ASSERT(node); return node->isElementNode() && $checkHelper(toElement(node)); }
675template <> inline bool isElementOfType<const $class>(const Element& element) { return $checkHelper(element); }
676END
677            ;
678        }
679
680        print F "\n";
681    }
682}
683
684sub printTypeHelpersHeaderFile
685{
686    my ($headerPath) = shift;
687    my $F;
688    open F, ">$headerPath";
689    printLicenseHeader($F);
690
691    print F "#ifndef ".$parameters{namespace}."ElementTypeHelpers_h\n";
692    print F "#define ".$parameters{namespace}."ElementTypeHelpers_h\n\n";
693    print F "#include \"".$parameters{namespace}."Names.h\"\n\n";
694    print F "namespace WebCore {\n\n";
695
696    printTypeHelpers($F, \%allTags);
697
698    print F "}\n\n";
699    print F "#endif\n";
700
701    close F;
702}
703
704sub printNamesHeaderFile
705{
706    my ($headerPath) = shift;
707    my $F;
708    open F, ">$headerPath";
709
710    printLicenseHeader($F);
711    printHeaderHead($F, "DOM", $parameters{namespace}, '#include "QualifiedName.h"', "class $parameters{namespace}QualifiedName : public QualifiedName { };\n\n");
712
713    my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix});
714
715    print F "// Namespace\n";
716    print F "extern const WTF::AtomicString ${lowercaseNamespacePrefix}NamespaceURI;\n\n";
717
718    if (keys %allTags) {
719        print F "// Tags\n";
720        printMacros($F, "extern const WebCore::$parameters{namespace}QualifiedName", "Tag", \%allTags);
721    }
722
723    if (keys %allAttrs) {
724        print F "// Attributes\n";
725        printMacros($F, "extern const WebCore::QualifiedName", "Attr", \%allAttrs);
726    }
727    print F "#endif\n\n";
728
729    if (keys %allTags) {
730        print F "const unsigned $parameters{namespace}TagsCount = ", scalar(keys %allTags), ";\n";
731        print F "const WebCore::$parameters{namespace}QualifiedName* const* get$parameters{namespace}Tags();\n";
732    }
733
734    if (keys %allAttrs) {
735        print F "const unsigned $parameters{namespace}AttrsCount = ", scalar(keys %allAttrs), ";\n";
736        print F "const WebCore::QualifiedName* const* get$parameters{namespace}Attrs();\n";
737    }
738
739    printInit($F, 1);
740    close F;
741}
742
743sub printNamesCppFile
744{
745    my $cppPath = shift;
746    my $F;
747    open F, ">$cppPath";
748
749    printLicenseHeader($F);
750    printCppHead($F, "DOM", $parameters{namespace}, "WebCore");
751
752    my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix});
753
754    print F "DEFINE_GLOBAL(AtomicString, ${lowercaseNamespacePrefix}NamespaceURI)\n\n";
755
756    print F StaticString::GenerateStrings(\%allStrings);
757
758    if (keys %allTags) {
759        print F "// Tags\n";
760        for my $name (sort keys %allTags) {
761            print F "DEFINE_GLOBAL($parameters{namespace}QualifiedName, ", $name, "Tag)\n";
762        }
763
764        print F "\n\nconst WebCore::$parameters{namespace}QualifiedName* const* get$parameters{namespace}Tags()\n";
765        print F "{\n    static const WebCore::$parameters{namespace}QualifiedName* const $parameters{namespace}Tags[] = {\n";
766        for my $name (sort keys %allTags) {
767            print F "        reinterpret_cast<const WebCore::$parameters{namespace}QualifiedName*>(&${name}Tag),\n";
768        }
769        print F "    };\n";
770        print F "    return $parameters{namespace}Tags;\n";
771        print F "}\n";
772    }
773
774    if (keys %allAttrs) {
775        print F "\n// Attributes\n";
776        for my $name (sort keys %allAttrs) {
777            print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Attr)\n";
778        }
779        print F "\n\nconst WebCore::QualifiedName* const* get$parameters{namespace}Attrs()\n";
780        print F "{\n    static const WebCore::QualifiedName* const $parameters{namespace}Attrs[] = {\n";
781        for my $name (sort keys %allAttrs) {
782            print F "        reinterpret_cast<const WebCore::QualifiedName*>(&${name}Attr),\n";
783        }
784        print F "    };\n";
785        print F "    return $parameters{namespace}Attrs;\n";
786        print F "}\n";
787    }
788
789    printInit($F, 0);
790
791    print(F "    AtomicString ${lowercaseNamespacePrefix}NS(\"$parameters{namespaceURI}\", AtomicString::ConstructFromLiteral);\n\n");
792
793    print(F "    // Namespace\n");
794    print(F "    new (NotNull, (void*)&${lowercaseNamespacePrefix}NamespaceURI) AtomicString(${lowercaseNamespacePrefix}NS);\n");
795    print(F "\n");
796    print F StaticString::GenerateStringAsserts(\%allStrings);
797
798    if (keys %allTags) {
799        my $tagsNamespace = $parameters{tagsNullNamespace} ? "nullAtom" : "${lowercaseNamespacePrefix}NS";
800        printDefinitions($F, \%allTags, "tags", $tagsNamespace);
801    }
802    if (keys %allAttrs) {
803        my $attrsNamespace = $parameters{attrsNullNamespace} ? "nullAtom" : "${lowercaseNamespacePrefix}NS";
804        printDefinitions($F, \%allAttrs, "attributes", $attrsNamespace);
805    }
806
807    print F "}\n\n} }\n\n";
808    close F;
809}
810
811sub printJSElementIncludes
812{
813    my $F = shift;
814
815    my %tagsSeen;
816    for my $tagName (sort keys %enabledTags) {
817        my $JSInterfaceName = $enabledTags{$tagName}{JSInterfaceName};
818        next if defined($tagsSeen{$JSInterfaceName}) || usesDefaultJSWrapper($tagName);
819        if ($enabledTags{$tagName}{conditional}) {
820            # We skip feature-define-specific #includes here since we handle them separately.
821            next;
822        }
823        $tagsSeen{$JSInterfaceName} = 1;
824
825        print F "#include \"JS${JSInterfaceName}.h\"\n";
826    }
827    print F "#include \"JS$parameters{fallbackJSInterfaceName}.h\"\n";
828}
829
830sub printElementIncludes
831{
832    my $F = shift;
833
834    my %tagsSeen;
835    for my $tagName (sort keys %enabledTags) {
836        my $interfaceName = $enabledTags{$tagName}{interfaceName};
837        next if defined($tagsSeen{$interfaceName});
838        if ($enabledTags{$tagName}{conditional}) {
839            # We skip feature-define-specific #includes here since we handle them separately.
840            next;
841        }
842        $tagsSeen{$interfaceName} = 1;
843
844        print F "#include \"${interfaceName}.h\"\n";
845    }
846    print F "#include \"$parameters{fallbackInterfaceName}.h\"\n";
847}
848
849sub printConditionalElementIncludes
850{
851    my ($F, $wrapperIncludes) = @_;
852
853    my %conditionals;
854    my %unconditionalElementIncludes;
855    my %unconditionalJSElementIncludes;
856
857    for my $tagName (keys %enabledTags) {
858        my $conditional = $enabledTags{$tagName}{conditional};
859        my $interfaceName = $enabledTags{$tagName}{interfaceName};
860        my $JSInterfaceName = $enabledTags{$tagName}{JSInterfaceName};
861
862        if ($conditional) {
863            $conditionals{$conditional}{interfaceNames}{$interfaceName} = 1;
864            $conditionals{$conditional}{JSInterfaceNames}{$JSInterfaceName} = 1;
865        } else {
866            $unconditionalElementIncludes{$interfaceName} = 1;
867            $unconditionalJSElementIncludes{$JSInterfaceName} = 1;
868        }
869    }
870
871    for my $conditional (sort keys %conditionals) {
872        print F "\n#if ENABLE($conditional)\n";
873        for my $interfaceName (sort keys %{$conditionals{$conditional}{interfaceNames}}) {
874            next if $unconditionalElementIncludes{$interfaceName};
875            print F "#include \"$interfaceName.h\"\n";
876        }
877        if ($wrapperIncludes) {
878            for my $JSInterfaceName (sort keys %{$conditionals{$conditional}{JSInterfaceNames}}) {
879                next if $unconditionalJSElementIncludes{$JSInterfaceName};
880                print F "#include \"JS$JSInterfaceName.h\"\n";
881            }
882        }
883        print F "#endif\n";
884    }
885}
886
887sub printDefinitions
888{
889    my ($F, $namesRef, $type, $namespaceURI) = @_;
890
891    my $shortCamelType = ucfirst(substr(substr($type, 0, -1), 0, 4));
892    my $capitalizedType = ucfirst($type);
893
894print F <<END
895
896    struct ${capitalizedType}TableEntry {
897        void* targetAddress;
898        StringImpl& name;
899    };
900
901    static const ${capitalizedType}TableEntry ${type}Table[] = {
902END
903;
904    for my $name (sort keys %$namesRef) {
905        print F "        { (void*)&$name$shortCamelType, *reinterpret_cast<StringImpl*>(&${name}Data) },\n";
906    }
907
908print F <<END
909    };
910
911    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(${type}Table); ++i)
912END
913;
914    if ($namespaceURI eq "nullAtom") {
915        print F "        createQualifiedName(${type}Table[i].targetAddress, &${type}Table[i].name);\n";
916    } else {
917        print F "        createQualifiedName(${type}Table[i].targetAddress, &${type}Table[i].name, $namespaceURI);\n";
918    }
919}
920
921## ElementFactory routines
922
923sub printFactoryCppFile
924{
925    my $cppPath = shift;
926    my $F;
927    open F, ">$cppPath";
928
929    my $formElementArgumentForDeclaration = "";
930    my $formElementArgumentForDefinition = "";
931    $formElementArgumentForDeclaration = ", HTMLFormElement*" if $parameters{namespace} eq "HTML";
932    $formElementArgumentForDefinition = ", HTMLFormElement* formElement" if $parameters{namespace} eq "HTML";
933
934    printLicenseHeader($F);
935
936    print F <<END
937#include "config.h"
938END
939    ;
940
941    print F "\n#if $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith};
942
943    print F <<END
944#include "$parameters{namespace}ElementFactory.h"
945
946#include "$parameters{namespace}Names.h"
947
948END
949    ;
950
951    printElementIncludes($F);
952    printConditionalElementIncludes($F, 0);
953
954    print F <<END
955
956#include "Document.h"
957#include "RuntimeEnabledFeatures.h"
958#include "Settings.h"
959#include <wtf/HashMap.h>
960#include <wtf/NeverDestroyed.h>
961
962namespace WebCore {
963
964using namespace $parameters{namespace}Names;
965
966typedef PassRefPtr<$parameters{namespace}Element> (*$parameters{namespace}ConstructorFunction)(const QualifiedName&, Document&$formElementArgumentForDeclaration, bool createdByParser);
967
968END
969    ;
970
971    my %tagConstructorMap = buildConstructorMap();
972
973    printConstructors($F, \%tagConstructorMap);
974
975    print F <<END
976static NEVER_INLINE void populate$parameters{namespace}FactoryMap(HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>& map)
977{
978    struct TableEntry {
979        const QualifiedName& name;
980        $parameters{namespace}ConstructorFunction function;
981    };
982
983    static const TableEntry table[] = {
984END
985    ;
986
987    printFunctionTable($F, \%tagConstructorMap);
988
989    print F <<END
990    };
991
992    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i)
993        map.add(table[i].name.localName().impl(), table[i].function);
994}
995
996PassRefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
997{
998END
999    ;
1000
1001    if ($parameters{namespace} ne "HTML" and $parameters{namespace} ne "SVG") {
1002        print F <<END
1003#if ENABLE(DASHBOARD_SUPPORT)
1004    Settings* settings = document.settings();
1005    if (settings && settings->usesDashboardBackwardCompatibilityMode())
1006        return 0;
1007#endif
1008END
1009        ;
1010    }
1011
1012    print F <<END
1013    static NeverDestroyed<HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>> functions;
1014    if (functions.get().isEmpty())
1015        populate$parameters{namespace}FactoryMap(functions);
1016    if ($parameters{namespace}ConstructorFunction function = functions.get().get(name.localName().impl())) {
1017END
1018    ;
1019
1020    if ($parameters{namespace} eq "HTML") {
1021        print F "        if (RefPtr<$parameters{namespace}Element> element = function(name, document, formElement, createdByParser))\n";
1022        print F "            return element.release();\n";
1023    } else {
1024        print F "        if (RefPtr<$parameters{namespace}Element> element = function(name, document, createdByParser))\n";
1025        print F "            return element.release();\n";
1026    }
1027
1028    print F "   }\n";
1029    print F "   return $parameters{fallbackInterfaceName}::create(name, document);\n";
1030
1031    print F <<END
1032}
1033
1034} // namespace WebCore
1035
1036END
1037    ;
1038
1039    print F "#endif\n" if $parameters{guardFactoryWith};
1040
1041    close F;
1042}
1043
1044sub printFactoryHeaderFile
1045{
1046    my $headerPath = shift;
1047    my $F;
1048    open F, ">$headerPath";
1049
1050    printLicenseHeader($F);
1051
1052    print F<<END
1053#ifndef $parameters{namespace}ElementFactory_h
1054#define $parameters{namespace}ElementFactory_h
1055
1056#include <wtf/Forward.h>
1057
1058namespace WebCore {
1059
1060    class Document;
1061    class HTMLFormElement;
1062    class QualifiedName;
1063
1064    class $parameters{namespace}Element;
1065
1066    class $parameters{namespace}ElementFactory {
1067    public:
1068END
1069;
1070
1071print F "        static PassRefPtr<$parameters{namespace}Element> createElement(const QualifiedName&, Document&";
1072print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
1073print F ", bool createdByParser = false);\n";
1074
1075printf F<<END
1076    };
1077}
1078
1079#endif // $parameters{namespace}ElementFactory_h
1080
1081END
1082;
1083
1084    close F;
1085}
1086
1087## Wrapper Factory routines
1088
1089sub usesDefaultJSWrapper
1090{
1091    my $name = shift;
1092
1093    # A tag reuses the default wrapper if its JSInterfaceName matches the default namespace Element.
1094    return $enabledTags{$name}{JSInterfaceName} eq $parameters{namespace} . "Element";
1095}
1096
1097sub printWrapperFunctions
1098{
1099    my $F = shift;
1100
1101    my %tagsSeen;
1102    for my $tagName (sort keys %enabledTags) {
1103        # Avoid defining the same wrapper method twice.
1104        my $JSInterfaceName = $enabledTags{$tagName}{JSInterfaceName};
1105        next if defined($tagsSeen{$JSInterfaceName}) || (usesDefaultJSWrapper($tagName) && ($parameters{fallbackJSInterfaceName} eq $parameters{namespace} . "Element"));
1106        $tagsSeen{$JSInterfaceName} = 1;
1107
1108        my $conditional = $enabledTags{$tagName}{conditional};
1109        if ($conditional) {
1110            my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")";
1111            print F "#if ${conditionalString}\n\n";
1112        }
1113
1114        if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) {
1115            print F <<END
1116static JSDOMWrapper* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element)
1117{
1118    if (element->isHTMLUnknownElement())
1119        return CREATE_DOM_WRAPPER(globalObject, $parameters{namespace}Element, element.get());
1120    return CREATE_DOM_WRAPPER(globalObject, ${JSInterfaceName}, element.get());
1121}
1122
1123END
1124            ;
1125        } elsif ($enabledTags{$tagName}{runtimeConditional}) {
1126            my $runtimeConditional = $enabledTags{$tagName}{runtimeConditional};
1127            print F <<END
1128static JSDOMWrapper* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element)
1129{
1130    if (!RuntimeEnabledFeatures::sharedFeatures().${runtimeConditional}Enabled()) {
1131        ASSERT(!element || element->is$parameters{fallbackInterfaceName}());
1132        return CREATE_DOM_WRAPPER(globalObject, $parameters{fallbackJSInterfaceName}, element.get());
1133    }
1134
1135    return CREATE_DOM_WRAPPER(globalObject, ${JSInterfaceName}, element.get());
1136}
1137END
1138    ;
1139        } else {
1140            print F <<END
1141static JSDOMWrapper* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element)
1142{
1143    return CREATE_DOM_WRAPPER(globalObject, ${JSInterfaceName}, element.get());
1144}
1145
1146END
1147    ;
1148        }
1149
1150        if ($conditional) {
1151            print F "#endif\n\n";
1152        }
1153    }
1154}
1155
1156sub printWrapperFactoryCppFile
1157{
1158    my $outputDir = shift;
1159    my $wrapperFactoryFileName = shift;
1160    my $F;
1161    open F, ">" . $outputDir . "/JS" . $wrapperFactoryFileName . ".cpp";
1162
1163    printLicenseHeader($F);
1164
1165    print F "#include \"config.h\"\n";
1166    print F "#include \"JS$parameters{namespace}ElementWrapperFactory.h\"\n\n";
1167
1168    print F "\n#if $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith};
1169
1170    printJSElementIncludes($F);
1171    printElementIncludes($F);
1172
1173    print F "\n#include \"$parameters{namespace}Names.h\"\n";
1174    print F <<END
1175
1176#include "Document.h"
1177#include "RuntimeEnabledFeatures.h"
1178#include "Settings.h"
1179#include <wtf/NeverDestroyed.h>
1180#include <wtf/StdLibExtras.h>
1181END
1182;
1183
1184    printConditionalElementIncludes($F, 1);
1185
1186    print F <<END
1187
1188using namespace JSC;
1189
1190namespace WebCore {
1191
1192using namespace $parameters{namespace}Names;
1193
1194typedef JSDOMWrapper* (*Create$parameters{namespace}ElementWrapperFunction)(JSDOMGlobalObject*, PassRefPtr<$parameters{namespace}Element>);
1195
1196END
1197;
1198
1199    printWrapperFunctions($F);
1200
1201print F <<END
1202
1203static NEVER_INLINE void populate$parameters{namespace}WrapperMap(HashMap<AtomicStringImpl*, Create$parameters{namespace}ElementWrapperFunction>& map)
1204{
1205    struct TableEntry {
1206        const QualifiedName& name;
1207        Create$parameters{namespace}ElementWrapperFunction function;
1208    };
1209
1210    static const TableEntry table[] = {
1211END
1212;
1213
1214    for my $tag (sort keys %enabledTags) {
1215        # Do not add the name to the map if it does not have a JS wrapper constructor or uses the default wrapper.
1216        next if (usesDefaultJSWrapper($tag, \%enabledTags) && ($parameters{fallbackJSInterfaceName} eq $parameters{namespace} . "Element"));
1217
1218        my $conditional = $enabledTags{$tag}{conditional};
1219        if ($conditional) {
1220            my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")";
1221            print F "#if ${conditionalString}\n";
1222        }
1223
1224        my $ucTag = $enabledTags{$tag}{JSInterfaceName};
1225
1226        # FIXME Remove unnecessary '&' from the following (print) line once we switch to a non-broken Visual Studio compiler.
1227        # https://bugs.webkit.org/show_bug.cgi?id=121235:
1228        print F "        { ${tag}Tag, &create${ucTag}Wrapper },\n";
1229
1230        if ($conditional) {
1231            print F "#endif\n";
1232        }
1233    }
1234
1235    print F <<END
1236    };
1237
1238    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i)
1239        map.add(table[i].name.localName().impl(), table[i].function);
1240}
1241
1242JSDOMWrapper* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element)
1243{
1244    static NeverDestroyed<HashMap<AtomicStringImpl*, Create$parameters{namespace}ElementWrapperFunction>> functions;
1245    if (functions.get().isEmpty())
1246        populate$parameters{namespace}WrapperMap(functions);
1247    if (auto function = functions.get().get(element->localName().impl()))
1248        return function(globalObject, element);
1249    return CREATE_DOM_WRAPPER(globalObject, $parameters{fallbackJSInterfaceName}, element.get());
1250}
1251
1252}
1253END
1254;
1255
1256    print F "\n#endif\n" if $parameters{guardFactoryWith};
1257
1258    close F;
1259}
1260
1261sub printWrapperFactoryHeaderFile
1262{
1263    my $outputDir = shift;
1264    my $wrapperFactoryFileName = shift;
1265    my $F;
1266    open F, ">" . $outputDir . "/JS" . $wrapperFactoryFileName . ".h";
1267
1268    printLicenseHeader($F);
1269
1270    print F "#ifndef JS$parameters{namespace}ElementWrapperFactory_h\n";
1271    print F "#define JS$parameters{namespace}ElementWrapperFactory_h\n\n";
1272
1273    print F "#if $parameters{guardFactoryWith}\n" if $parameters{guardFactoryWith};
1274
1275    print F <<END
1276#include <wtf/Forward.h>
1277
1278namespace WebCore {
1279
1280    class JSDOMWrapper;
1281    class JSDOMGlobalObject;
1282    class $parameters{namespace}Element;
1283
1284    JSDOMWrapper* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject*, PassRefPtr<$parameters{namespace}Element>);
1285
1286}
1287
1288END
1289    ;
1290
1291    print F "#endif // $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith};
1292
1293    print F "#endif // JS$parameters{namespace}ElementWrapperFactory_h\n";
1294
1295    close F;
1296}
1297