diff --git a/Documentation/CSSGeneratedFiles.md b/Documentation/CSSGeneratedFiles.md index 4db189975fc..73301015420 100644 --- a/Documentation/CSSGeneratedFiles.md +++ b/Documentation/CSSGeneratedFiles.md @@ -11,7 +11,7 @@ They are run automatically as part of the build, and most of the time you can ig ## Properties.json Each CSS property has an entry here, which describes what values it accepts, whether it's inherited, and similar data. -This generates `PropertyID.h` and `PropertyID.cpp`. +This generates `PropertyID.h`, `PropertyID.cpp`, `GeneratedCSSStyleProperties.h`, `GeneratedCSSStyleProperties.cpp` and `GeneratedCSSStyleProperties.idl`. Most of this data is found in the information box for that property in the relevant CSS spec. The file is organized as a single JSON object, with keys being property names, and the values being the data for that property. diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 52b5a58473f..5afa23adf24 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -832,6 +832,7 @@ set(GENERATED_SOURCES ARIA/AriaRoles.cpp CSS/DefaultStyleSheetSource.cpp CSS/Enums.cpp + CSS/GeneratedCSSStyleProperties.cpp CSS/Keyword.cpp CSS/MathFunctions.cpp CSS/MediaFeatureID.cpp diff --git a/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp b/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp index f617025e93b..3a4d546b9f0 100644 --- a/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp +++ b/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp @@ -314,6 +314,20 @@ String CSSStyleDeclaration::css_text() const return serialized(); } +// https://drafts.csswg.org/cssom/#dom-cssstyleproperties-cssfloat +String CSSStyleDeclaration::css_float() const +{ + // The cssFloat attribute, on getting, must return the result of invoking getPropertyValue() with float as argument. + return get_property_value("float"sv); +} + +WebIDL::ExceptionOr CSSStyleDeclaration::set_css_float(StringView value) +{ + // On setting, the attribute must invoke setProperty() with float as first argument, as second argument the given value, + // and no third argument. Any exceptions thrown must be re-thrown. + return set_property("float"sv, value, ""sv); +} + // https://www.w3.org/TR/cssom/#serialize-a-css-declaration static String serialize_a_css_declaration(CSS::PropertyID property, StringView value, Important important) { @@ -436,55 +450,6 @@ String PropertyOwningCSSStyleDeclaration::serialized() const return MUST(builder.to_string()); } -static CSS::PropertyID property_id_from_name(StringView name) -{ - // FIXME: Perhaps this should go in the code generator. - if (name == "cssFloat"sv) - return CSS::PropertyID::Float; - - if (auto property_id = CSS::property_id_from_camel_case_string(name); property_id.has_value()) - return property_id.value(); - - if (auto property_id = CSS::property_id_from_string(name); property_id.has_value()) - return property_id.value(); - - return CSS::PropertyID::Invalid; -} - -JS::ThrowCompletionOr CSSStyleDeclaration::internal_has_property(JS::PropertyKey const& name) const -{ - if (!name.is_string()) - return Base::internal_has_property(name); - return property_id_from_name(name.to_string()) != CSS::PropertyID::Invalid; -} - -JS::ThrowCompletionOr CSSStyleDeclaration::internal_get(JS::PropertyKey const& name, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const -{ - if (name.is_number()) - return { JS::PrimitiveString::create(vm(), item(name.as_number())) }; - if (!name.is_string()) - return Base::internal_get(name, receiver, cacheable_metadata, phase); - auto property_id = property_id_from_name(name.to_string()); - if (property_id == CSS::PropertyID::Invalid) - return Base::internal_get(name, receiver, cacheable_metadata, phase); - return JS::PrimitiveString::create(vm(), get_property_value(string_from_property_id(property_id))); -} - -JS::ThrowCompletionOr CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata) -{ - auto& vm = this->vm(); - if (!name.is_string()) - return Base::internal_set(name, value, receiver, cacheable_metadata); - auto property_id = property_id_from_name(name.to_string()); - if (property_id == CSS::PropertyID::Invalid) - return Base::internal_set(name, value, receiver, cacheable_metadata); - - auto css_text = value.is_null() ? String {} : TRY(value.to_string(vm)); - - TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return set_property(property_id, css_text); })); - return true; -} - WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_css_text(StringView css_text) { dbgln("(STUBBED) PropertyOwningCSSStyleDeclaration::set_css_text(css_text='{}')", css_text); diff --git a/Libraries/LibWeb/CSS/CSSStyleDeclaration.h b/Libraries/LibWeb/CSS/CSSStyleDeclaration.h index 10dd5141b2a..6f7e41f79c0 100644 --- a/Libraries/LibWeb/CSS/CSSStyleDeclaration.h +++ b/Libraries/LibWeb/CSS/CSSStyleDeclaration.h @@ -10,11 +10,14 @@ #include #include #include +#include #include namespace Web::CSS { -class CSSStyleDeclaration : public Bindings::PlatformObject { +class CSSStyleDeclaration + : public Bindings::PlatformObject + , public Bindings::GeneratedCSSStyleProperties { WEB_PLATFORM_OBJECT(CSSStyleDeclaration, Bindings::PlatformObject); JS_DECLARE_ALLOCATOR(CSSStyleDeclaration); @@ -39,16 +42,21 @@ public: String css_text() const; virtual WebIDL::ExceptionOr set_css_text(StringView) = 0; - virtual String serialized() const = 0; + String css_float() const; + WebIDL::ExceptionOr set_css_float(StringView); - virtual JS::ThrowCompletionOr internal_has_property(JS::PropertyKey const& name) const override; - virtual JS::ThrowCompletionOr internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) const override; - virtual JS::ThrowCompletionOr internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*) override; + virtual String serialized() const = 0; virtual JS::GCPtr parent_rule() const; protected: explicit CSSStyleDeclaration(JS::Realm&); + + virtual CSSStyleDeclaration& generated_style_properties_to_css_style_declaration() override { return *this; } + +private: + // ^PlatformObject + virtual Optional item_value(size_t index) const override; }; class PropertyOwningCSSStyleDeclaration : public CSSStyleDeclaration { diff --git a/Libraries/LibWeb/CSS/CSSStyleDeclaration.idl b/Libraries/LibWeb/CSS/CSSStyleDeclaration.idl index a13104d6d86..8918d8fe51e 100644 --- a/Libraries/LibWeb/CSS/CSSStyleDeclaration.idl +++ b/Libraries/LibWeb/CSS/CSSStyleDeclaration.idl @@ -1,3 +1,5 @@ +#import + // https://drafts.csswg.org/cssom/#cssstyledeclaration [Exposed=Window] interface CSSStyleDeclaration { @@ -15,8 +17,8 @@ interface CSSStyleDeclaration { readonly attribute CSSRule? parentRule; - // NOTE: cssFloat is implemented manually in the bindings code, along with the other property accessors. - // Hence, this does not need to be implemented. - // [CEReactions, LegacyNullToEmptyString] attribute CSSOMString cssFloat; + [CEReactions, LegacyNullToEmptyString] attribute CSSOMString cssFloat; }; + +CSSStyleDeclaration includes GeneratedCSSStyleProperties; diff --git a/Meta/CMake/libweb_generators.cmake b/Meta/CMake/libweb_generators.cmake index 76425440fc2..9075ad7947f 100644 --- a/Meta/CMake/libweb_generators.cmake +++ b/Meta/CMake/libweb_generators.cmake @@ -64,6 +64,16 @@ function (generate_css_implementation) arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/Keywords.json" ) + invoke_idl_generator( + "GeneratedCSSStyleProperties.cpp" + Lagom::GenerateCSSStyleProperties + "${LIBWEB_INPUT_FOLDER}/CSS/Properties.json" + "CSS/GeneratedCSSStyleProperties.h" + "CSS/GeneratedCSSStyleProperties.cpp" + "CSS/GeneratedCSSStyleProperties.idl" + arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/Properties.json" + ) + embed_as_string( "DefaultStyleSheetSource.cpp" "${LIBWEB_INPUT_FOLDER}/CSS/Default.css" @@ -98,6 +108,7 @@ function (generate_css_implementation) set(CSS_GENERATED_HEADERS "CSS/Enums.h" + "CSS/GeneratedCSSStyleProperties.h" "CSS/Keyword.h" "CSS/MathFunctions.h" "CSS/MediaFeatureID.h" @@ -164,7 +175,7 @@ function (generate_js_bindings target) add_custom_command( OUTPUT ${BINDINGS_SOURCES} COMMAND "$" -o "Bindings" --depfile "Bindings/${basename}.d" - ${depfile_prefix_arg} "${LIBWEB_INPUT_FOLDER}/${class}.idl" "${LIBWEB_INPUT_FOLDER}" + ${depfile_prefix_arg} "${LIBWEB_INPUT_FOLDER}/${class}.idl" "${LIBWEB_INPUT_FOLDER}" "${CMAKE_CURRENT_BINARY_DIR}" VERBATIM COMMENT "Generating Bindings for ${class}" DEPENDS Lagom::BindingsGenerator @@ -201,7 +212,7 @@ function (generate_js_bindings target) add_custom_command( OUTPUT ${exposed_interface_sources} COMMAND "${CMAKE_COMMAND}" -E make_directory "tmp" - COMMAND $ -o "${CMAKE_CURRENT_BINARY_DIR}/tmp" -b "${LIBWEB_INPUT_FOLDER}" ${LIBWEB_ALL_IDL_FILES} + COMMAND $ -o "${CMAKE_CURRENT_BINARY_DIR}/tmp" -b "${LIBWEB_INPUT_FOLDER}" -b "${CMAKE_CURRENT_BINARY_DIR}" ${LIBWEB_ALL_IDL_FILES} COMMAND "${CMAKE_COMMAND}" -E copy_if_different tmp/IntrinsicDefinitions.cpp "Bindings/IntrinsicDefinitions.cpp" COMMAND "${CMAKE_COMMAND}" -E copy_if_different tmp/DedicatedWorkerExposedInterfaces.h "Bindings/DedicatedWorkerExposedInterfaces.h" COMMAND "${CMAKE_COMMAND}" -E copy_if_different tmp/DedicatedWorkerExposedInterfaces.cpp "Bindings/DedicatedWorkerExposedInterfaces.cpp" diff --git a/Meta/CMake/utils.cmake b/Meta/CMake/utils.cmake index 079659b9d59..8407e82259d 100644 --- a/Meta/CMake/utils.cmake +++ b/Meta/CMake/utils.cmake @@ -184,6 +184,26 @@ function(invoke_generator name generator primary_source header implementation) set(CURRENT_LIB_GENERATED ${CURRENT_LIB_GENERATED} PARENT_SCOPE) endfunction() +function(invoke_idl_generator name generator primary_source header implementation idl) + cmake_parse_arguments(invoke_idl_generator "" "" "arguments;dependencies" ${ARGN}) + + add_custom_command( + OUTPUT "${header}" "${implementation}" "${idl}" + COMMAND $ -h "${header}.tmp" -c "${implementation}.tmp" -i "${idl}.tmp" ${invoke_idl_generator_arguments} + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${header}.tmp" "${header}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${implementation}.tmp" "${implementation}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${idl}.tmp" "${idl}" + COMMAND "${CMAKE_COMMAND}" -E remove "${header}.tmp" "${implementation}.tmp" "${idl}.tmp" + VERBATIM + DEPENDS ${generator} ${invoke_idl_generator_dependencies} "${primary_source}" + ) + + add_custom_target("generate_${name}" DEPENDS "${header}" "${implementation}" "${idl}") + add_dependencies(all_generated "generate_${name}") + list(APPEND CURRENT_LIB_GENERATED "${name}") + set(CURRENT_LIB_GENERATED ${CURRENT_LIB_GENERATED} PARENT_SCOPE) +endfunction() + function(download_file_multisource urls path) cmake_parse_arguments(DOWNLOAD "" "SHA256" "" ${ARGN}) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt index 8b7791b5bc2..3dc5a6c5816 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt @@ -6,6 +6,7 @@ lagom_tool(GenerateCSSMathFunctions SOURCES GenerateCSSMathFunctions.cpp lagom_tool(GenerateCSSMediaFeatureID SOURCES GenerateCSSMediaFeatureID.cpp LIBS LibMain) lagom_tool(GenerateCSSPropertyID SOURCES GenerateCSSPropertyID.cpp LIBS LibMain) lagom_tool(GenerateCSSPseudoClass SOURCES GenerateCSSPseudoClass.cpp LIBS LibMain) +lagom_tool(GenerateCSSStyleProperties SOURCES GenerateCSSStyleProperties.cpp LIBS LibMain) lagom_tool(GenerateCSSTransformFunctions SOURCES GenerateCSSTransformFunctions.cpp LIBS LibMain) lagom_tool(GenerateWindowOrWorkerInterfaces SOURCES GenerateWindowOrWorkerInterfaces.cpp LIBS LibMain LibIDL) lagom_tool(GenerateAriaRoles SOURCES GenerateAriaRoles.cpp LIBS LibMain) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSStyleProperties.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSStyleProperties.cpp new file mode 100644 index 00000000000..09e7784b1b6 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSStyleProperties.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2024, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "GeneratorUtil.h" +#include +#include +#include +#include + +ErrorOr generate_header_file(JsonObject& properties, Core::File& file); +ErrorOr generate_implementation_file(JsonObject& properties, Core::File& file); +ErrorOr generate_idl_file(JsonObject& properties, Core::File& file); + +ErrorOr serenity_main(Main::Arguments arguments) +{ + StringView generated_header_path; + StringView generated_implementation_path; + StringView generated_idl_path; + StringView properties_json_path; + + Core::ArgsParser args_parser; + args_parser.add_option(generated_header_path, "Path to the CSSStyleProperties header file to generate", "generated-header-path", 'h', "generated-header-path"); + args_parser.add_option(generated_implementation_path, "Path to the CSSStyleProperties implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path"); + args_parser.add_option(generated_idl_path, "Path to the CSSStyleProperties IDL file to generate", "generated-idl-path", 'i', "generated-idl-path"); + args_parser.add_option(properties_json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path"); + args_parser.parse(arguments); + + auto json = TRY(read_entire_file_as_json(properties_json_path)); + VERIFY(json.is_object()); + auto properties = json.as_object(); + + auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write)); + auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write)); + auto generated_idl_file = TRY(Core::File::open(generated_idl_path, Core::File::OpenMode::Write)); + + TRY(generate_header_file(properties, *generated_header_file)); + TRY(generate_implementation_file(properties, *generated_implementation_file)); + TRY(generate_idl_file(properties, *generated_idl_file)); + + return 0; +} + +static String get_snake_case_function_name_for_css_property_name(ByteString const& name) +{ + auto snake_case_name = snake_casify(name); + if (snake_case_name.starts_with('_')) + return MUST(snake_case_name.substring_from_byte_offset(1)); + + return snake_case_name; +} + +static String make_name_acceptable_cpp(String const& name) +{ + if (name.is_one_of("float")) { + StringBuilder builder; + builder.append(name); + builder.append('_'); + return MUST(builder.to_string()); + } + + return name; +} + +ErrorOr generate_header_file(JsonObject& properties, Core::File& file) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.append(R"~~~( +#pragma once + +#include +#include + +namespace Web::Bindings { + +class GeneratedCSSStyleProperties { +public: +)~~~"); + + properties.for_each_member([&](auto& name, auto&) { + auto declaration_generator = generator.fork(); + auto snake_case_name = get_snake_case_function_name_for_css_property_name(name); + declaration_generator.set("name:acceptable_cpp", make_name_acceptable_cpp(snake_case_name)); + + declaration_generator.append(R"~~~( + WebIDL::ExceptionOr set_@name:acceptable_cpp@(StringView value); + String @name:acceptable_cpp@() const; +)~~~"); + }); + + generator.append(R"~~~( +protected: + GeneratedCSSStyleProperties() = default; + virtual ~GeneratedCSSStyleProperties() = default; + + virtual CSS::CSSStyleDeclaration& generated_style_properties_to_css_style_declaration() = 0; + CSS::CSSStyleDeclaration const& generated_style_properties_to_css_style_declaration() const { return const_cast(*this).generated_style_properties_to_css_style_declaration(); } +}; // class GeneratedCSSStyleProperties + +} // namespace Web::Bindings +)~~~"); + + TRY(file.write_until_depleted(generator.as_string_view().bytes())); + return {}; +} + +ErrorOr generate_implementation_file(JsonObject& properties, Core::File& file) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.append(R"~~~( +#include +#include +#include + +namespace Web::Bindings { +)~~~"); + + properties.for_each_member([&](auto& name, auto&) { + auto definition_generator = generator.fork(); + definition_generator.set("name", name); + + auto snake_case_name = get_snake_case_function_name_for_css_property_name(name); + definition_generator.set("name:acceptable_cpp", make_name_acceptable_cpp(snake_case_name)); + + definition_generator.append(R"~~~( +WebIDL::ExceptionOr GeneratedCSSStyleProperties::set_@name:acceptable_cpp@(StringView value) +{ + return generated_style_properties_to_css_style_declaration().set_property("@name@"sv, value, ""sv); +} + +String GeneratedCSSStyleProperties::@name:acceptable_cpp@() const +{ + return generated_style_properties_to_css_style_declaration().get_property_value("@name@"sv); +} +)~~~"); + }); + + generator.append(R"~~~( +} // namespace Web::Bindings +)~~~"); + + TRY(file.write_until_depleted(generator.as_string_view().bytes())); + return {}; +} + +ErrorOr generate_idl_file(JsonObject& properties, Core::File& file) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.append(R"~~~( +interface mixin GeneratedCSSStyleProperties { +)~~~"); + + properties.for_each_member([&](auto& name, auto&) { + auto member_generator = generator.fork(); + + member_generator.set("name", name); + + auto snake_case_name = get_snake_case_function_name_for_css_property_name(name); + member_generator.set("name:snakecase", snake_case_name); + member_generator.set("name:acceptable_cpp", make_name_acceptable_cpp(snake_case_name)); + + // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-camel-cased-attribute + // For each CSS property property that is a supported CSS property, the following partial interface applies + // where camel-cased attribute is obtained by running the CSS property to IDL attribute algorithm for property. + // partial interface CSSStyleProperties { + // [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString _camel_cased_attribute; + // }; + member_generator.set("name:camelcase", css_property_to_idl_attribute(name)); + + member_generator.append(R"~~~( + [CEReactions, LegacyNullToEmptyString, AttributeCallbackName=@name:snakecase@_regular, ImplementedAs=@name:acceptable_cpp@] attribute CSSOMString @name:camelcase@; +)~~~"); + + // For each CSS property property that is a supported CSS property and that begins with the string -webkit-, + // the following partial interface applies where webkit-cased attribute is obtained by running the CSS property + // to IDL attribute algorithm for property, with the lowercase first flag set. + if (name.starts_with("-webkit-"sv)) { + member_generator.set("name:webkit", css_property_to_idl_attribute(name, /* lowercase_first= */ true)); + member_generator.append(R"~~~( + [CEReactions, LegacyNullToEmptyString, AttributeCallbackName=@name:snakecase@_webkit, ImplementedAs=@name:acceptable_cpp@] attribute CSSOMString @name:webkit@; +)~~~"); + } + + // For each CSS property property that is a supported CSS property, except for properties that have no + // "-" (U+002D) in the property name, the following partial interface applies where dashed attribute is + // property. + // partial interface CSSStyleProperties { + // [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString _dashed_attribute; + // }; + if (name.contains('-')) { + member_generator.append(R"~~~( + [CEReactions, LegacyNullToEmptyString, AttributeCallbackName=@name:snakecase@_dashed, ImplementedAs=@name:acceptable_cpp@] attribute CSSOMString @name@; +)~~~"); + } + }); + + generator.append(R"~~~( +}; +)~~~"); + + TRY(file.write_until_depleted(generator.as_string_view().bytes())); + return {}; +} diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GeneratorUtil.h b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GeneratorUtil.h index 3db5a746108..8a521828260 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GeneratorUtil.h +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GeneratorUtil.h @@ -64,3 +64,42 @@ inline ErrorOr read_entire_file_as_json(StringView filename) TRY(file->read_until_filled(json_data.bytes())); return JsonValue::from_string(json_data); } + +// https://drafts.csswg.org/cssom/#css-property-to-idl-attribute +inline String css_property_to_idl_attribute(StringView property_name, bool lowercase_first = false) +{ + // The CSS property to IDL attribute algorithm for property, optionally with a lowercase first flag set, is as follows: + // 1. Let output be the empty string. + StringBuilder output; + + // 2. Let uppercase next be unset. + bool uppercase_next = false; + + // 3. If the lowercase first flag is set, remove the first character from property. + StringView actual_property_name; + if (lowercase_first) { + actual_property_name = property_name.substring_view(1); + } else { + actual_property_name = property_name; + } + + // 4. For each character c in property: + for (auto c : actual_property_name) { + // 1. If c is "-" (U+002D), let uppercase next be set. + if (c == '-') { + uppercase_next = true; + } + // 2. Otherwise, if uppercase next is set, let uppercase next be unset and append c converted to ASCII uppercase to output. + else if (uppercase_next) { + uppercase_next = false; + output.append(to_ascii_uppercase(c)); + } + // 3. Otherwise, append c to output. + else { + output.append(c); + } + } + + // 5. Return output. + return MUST(output.to_string()); +} diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt new file mode 100644 index 00000000000..74c231cd7bf --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt @@ -0,0 +1,573 @@ +All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle: +'cssText': '' +'length': '201' +'parentRule': 'null' +'cssFloat': 'none' +'WebkitAlignContent': 'normal' +'webkitAlignContent': 'normal' +'-webkit-align-content': 'normal' +'WebkitAlignItems': 'normal' +'webkitAlignItems': 'normal' +'-webkit-align-items': 'normal' +'WebkitAlignSelf': 'auto' +'webkitAlignSelf': 'auto' +'-webkit-align-self': 'auto' +'WebkitAnimation': 'none auto ease 1 normal running 0s none' +'webkitAnimation': 'none auto ease 1 normal running 0s none' +'-webkit-animation': 'none auto ease 1 normal running 0s none' +'WebkitAnimationDelay': '0s' +'webkitAnimationDelay': '0s' +'-webkit-animation-delay': '0s' +'WebkitAnimationDirection': 'normal' +'webkitAnimationDirection': 'normal' +'-webkit-animation-direction': 'normal' +'WebkitAnimationDuration': 'auto' +'webkitAnimationDuration': 'auto' +'-webkit-animation-duration': 'auto' +'WebkitAnimationFillMode': 'none' +'webkitAnimationFillMode': 'none' +'-webkit-animation-fill-mode': 'none' +'WebkitAnimationIterationCount': '1' +'webkitAnimationIterationCount': '1' +'-webkit-animation-iteration-count': '1' +'WebkitAnimationName': 'none' +'webkitAnimationName': 'none' +'-webkit-animation-name': 'none' +'WebkitAnimationPlayState': 'running' +'webkitAnimationPlayState': 'running' +'-webkit-animation-play-state': 'running' +'WebkitAnimationTimingFunction': 'ease' +'webkitAnimationTimingFunction': 'ease' +'-webkit-animation-timing-function': 'ease' +'WebkitAppearance': 'auto' +'webkitAppearance': 'auto' +'-webkit-appearance': 'auto' +'WebkitBackgroundClip': 'border-box' +'webkitBackgroundClip': 'border-box' +'-webkit-background-clip': 'border-box' +'WebkitBackgroundOrigin': 'padding-box' +'webkitBackgroundOrigin': 'padding-box' +'-webkit-background-origin': 'padding-box' +'WebkitBackgroundSize': 'auto auto' +'webkitBackgroundSize': 'auto auto' +'-webkit-background-size': 'auto auto' +'WebkitBorderBottomLeftRadius': '0px' +'webkitBorderBottomLeftRadius': '0px' +'-webkit-border-bottom-left-radius': '0px' +'WebkitBorderBottomRightRadius': '0px' +'webkitBorderBottomRightRadius': '0px' +'-webkit-border-bottom-right-radius': '0px' +'WebkitBorderRadius': '0px 0px 0px 0px' +'webkitBorderRadius': '0px 0px 0px 0px' +'-webkit-border-radius': '0px 0px 0px 0px' +'WebkitBorderTopLeftRadius': '0px' +'webkitBorderTopLeftRadius': '0px' +'-webkit-border-top-left-radius': '0px' +'WebkitBorderTopRightRadius': '0px' +'webkitBorderTopRightRadius': '0px' +'-webkit-border-top-right-radius': '0px' +'WebkitBoxShadow': 'none' +'webkitBoxShadow': 'none' +'-webkit-box-shadow': 'none' +'WebkitBoxSizing': 'content-box' +'webkitBoxSizing': 'content-box' +'-webkit-box-sizing': 'content-box' +'WebkitFlex': '0 1 auto' +'webkitFlex': '0 1 auto' +'-webkit-flex': '0 1 auto' +'WebkitFlexBasis': 'auto' +'webkitFlexBasis': 'auto' +'-webkit-flex-basis': 'auto' +'WebkitFlexDirection': 'row' +'webkitFlexDirection': 'row' +'-webkit-flex-direction': 'row' +'WebkitFlexFlow': 'row nowrap' +'webkitFlexFlow': 'row nowrap' +'-webkit-flex-flow': 'row nowrap' +'WebkitFlexGrow': '0' +'webkitFlexGrow': '0' +'-webkit-flex-grow': '0' +'WebkitFlexShrink': '1' +'webkitFlexShrink': '1' +'-webkit-flex-shrink': '1' +'WebkitFlexWrap': 'nowrap' +'webkitFlexWrap': 'nowrap' +'-webkit-flex-wrap': 'nowrap' +'WebkitJustifyContent': 'normal' +'webkitJustifyContent': 'normal' +'-webkit-justify-content': 'normal' +'WebkitMask': 'none' +'webkitMask': 'none' +'-webkit-mask': 'none' +'WebkitOrder': '0' +'webkitOrder': '0' +'-webkit-order': '0' +'WebkitTextFillColor': 'rgb(0, 0, 0)' +'webkitTextFillColor': 'rgb(0, 0, 0)' +'-webkit-text-fill-color': 'rgb(0, 0, 0)' +'WebkitTransform': 'none' +'webkitTransform': 'none' +'-webkit-transform': 'none' +'WebkitTransformOrigin': '50% 50%' +'webkitTransformOrigin': '50% 50%' +'-webkit-transform-origin': '50% 50%' +'WebkitTransition': 'all 0s ease 0s' +'webkitTransition': 'all 0s ease 0s' +'-webkit-transition': 'all 0s ease 0s' +'WebkitTransitionDelay': '0s' +'webkitTransitionDelay': '0s' +'-webkit-transition-delay': '0s' +'WebkitTransitionDuration': '0s' +'webkitTransitionDuration': '0s' +'-webkit-transition-duration': '0s' +'WebkitTransitionProperty': 'all' +'webkitTransitionProperty': 'all' +'-webkit-transition-property': 'all' +'WebkitTransitionTimingFunction': 'ease' +'webkitTransitionTimingFunction': 'ease' +'-webkit-transition-timing-function': 'ease' +'accentColor': 'auto' +'accent-color': 'auto' +'alignContent': 'normal' +'align-content': 'normal' +'alignItems': 'normal' +'align-items': 'normal' +'alignSelf': 'auto' +'align-self': 'auto' +'animation': 'none auto ease 1 normal running 0s none' +'animationDelay': '0s' +'animation-delay': '0s' +'animationDirection': 'normal' +'animation-direction': 'normal' +'animationDuration': 'auto' +'animation-duration': 'auto' +'animationFillMode': 'none' +'animation-fill-mode': 'none' +'animationIterationCount': '1' +'animation-iteration-count': '1' +'animationName': 'none' +'animation-name': 'none' +'animationPlayState': 'running' +'animation-play-state': 'running' +'animationTimingFunction': 'ease' +'animation-timing-function': 'ease' +'appearance': 'auto' +'aspectRatio': 'auto' +'aspect-ratio': 'auto' +'backdropFilter': 'none' +'backdrop-filter': 'none' +'background': 'scroll border-box rgba(0, 0, 0, 0) none padding-box left 0% top 0% repeat repeat auto auto' +'backgroundAttachment': 'scroll' +'background-attachment': 'scroll' +'backgroundClip': 'border-box' +'background-clip': 'border-box' +'backgroundColor': 'rgba(0, 0, 0, 0)' +'background-color': 'rgba(0, 0, 0, 0)' +'backgroundImage': 'none' +'background-image': 'none' +'backgroundOrigin': 'padding-box' +'background-origin': 'padding-box' +'backgroundPosition': 'left 0% top 0%' +'background-position': 'left 0% top 0%' +'backgroundPositionX': 'left 0%' +'background-position-x': 'left 0%' +'backgroundPositionY': 'top 0%' +'background-position-y': 'top 0%' +'backgroundRepeat': 'repeat repeat' +'background-repeat': 'repeat repeat' +'backgroundSize': 'auto auto' +'background-size': 'auto auto' +'border': 'medium none rgb(0, 0, 0)' +'borderBottom': 'medium none rgb(0, 0, 0)' +'border-bottom': 'medium none rgb(0, 0, 0)' +'borderBottomColor': 'rgb(0, 0, 0)' +'border-bottom-color': 'rgb(0, 0, 0)' +'borderBottomLeftRadius': '0px' +'border-bottom-left-radius': '0px' +'borderBottomRightRadius': '0px' +'border-bottom-right-radius': '0px' +'borderBottomStyle': 'none' +'border-bottom-style': 'none' +'borderBottomWidth': 'medium' +'border-bottom-width': 'medium' +'borderCollapse': 'separate' +'border-collapse': 'separate' +'borderColor': 'rgb(0, 0, 0) rgb(0, 0, 0) rgb(0, 0, 0) rgb(0, 0, 0)' +'border-color': 'rgb(0, 0, 0) rgb(0, 0, 0) rgb(0, 0, 0) rgb(0, 0, 0)' +'borderLeft': 'medium none rgb(0, 0, 0)' +'border-left': 'medium none rgb(0, 0, 0)' +'borderLeftColor': 'rgb(0, 0, 0)' +'border-left-color': 'rgb(0, 0, 0)' +'borderLeftStyle': 'none' +'border-left-style': 'none' +'borderLeftWidth': 'medium' +'border-left-width': 'medium' +'borderRadius': '0px 0px 0px 0px' +'border-radius': '0px 0px 0px 0px' +'borderRight': 'medium none rgb(0, 0, 0)' +'border-right': 'medium none rgb(0, 0, 0)' +'borderRightColor': 'rgb(0, 0, 0)' +'border-right-color': 'rgb(0, 0, 0)' +'borderRightStyle': 'none' +'border-right-style': 'none' +'borderRightWidth': 'medium' +'border-right-width': 'medium' +'borderSpacing': '0px' +'border-spacing': '0px' +'borderStyle': 'none none none none' +'border-style': 'none none none none' +'borderTop': 'medium none rgb(0, 0, 0)' +'border-top': 'medium none rgb(0, 0, 0)' +'borderTopColor': 'rgb(0, 0, 0)' +'border-top-color': 'rgb(0, 0, 0)' +'borderTopLeftRadius': '0px' +'border-top-left-radius': '0px' +'borderTopRightRadius': '0px' +'border-top-right-radius': '0px' +'borderTopStyle': 'none' +'border-top-style': 'none' +'borderTopWidth': 'medium' +'border-top-width': 'medium' +'borderWidth': 'medium medium medium medium' +'border-width': 'medium medium medium medium' +'bottom': 'auto' +'boxShadow': 'none' +'box-shadow': 'none' +'boxSizing': 'content-box' +'box-sizing': 'content-box' +'captionSide': 'top' +'caption-side': 'top' +'clear': 'none' +'clip': 'auto' +'clipPath': 'none' +'clip-path': 'none' +'clipRule': 'nonzero' +'clip-rule': 'nonzero' +'color': 'rgb(0, 0, 0)' +'columnCount': 'auto' +'column-count': 'auto' +'columnGap': 'normal' +'column-gap': 'normal' +'columnSpan': 'none' +'column-span': 'none' +'columnWidth': 'auto' +'column-width': 'auto' +'columns': 'auto auto' +'content': 'normal' +'contentVisibility': 'visible' +'content-visibility': 'visible' +'counterIncrement': 'none' +'counter-increment': 'none' +'counterReset': 'none' +'counter-reset': 'none' +'counterSet': 'none' +'counter-set': 'none' +'cursor': 'auto' +'cx': '0px' +'cy': '0px' +'direction': 'ltr' +'display': 'block' +'fill': 'rgb(0, 0, 0)' +'fillOpacity': '1' +'fill-opacity': '1' +'fillRule': 'nonzero' +'fill-rule': 'nonzero' +'filter': 'none' +'flex': '0 1 auto' +'flexBasis': 'auto' +'flex-basis': 'auto' +'flexDirection': 'row' +'flex-direction': 'row' +'flexFlow': 'row nowrap' +'flex-flow': 'row nowrap' +'flexGrow': '0' +'flex-grow': '0' +'flexShrink': '1' +'flex-shrink': '1' +'flexWrap': 'nowrap' +'flex-wrap': 'nowrap' +'float': 'none' +'font': 'serif 16px normal normal normal 400 normal' +'fontFamily': 'serif' +'font-family': 'serif' +'fontFeatureSettings': 'normal' +'font-feature-settings': 'normal' +'fontLanguageOverride': 'normal' +'font-language-override': 'normal' +'fontSize': '16px' +'font-size': '16px' +'fontStretch': 'normal' +'font-stretch': 'normal' +'fontStyle': 'normal' +'font-style': 'normal' +'fontVariant': 'normal' +'font-variant': 'normal' +'fontVariationSettings': 'normal' +'font-variation-settings': 'normal' +'fontWeight': '400' +'font-weight': '400' +'fontWidth': 'normal' +'font-width': 'normal' +'gap': 'normal normal' +'grid': 'none auto auto' +'gridArea': 'auto auto auto auto' +'grid-area': 'auto auto auto auto' +'gridAutoColumns': 'auto' +'grid-auto-columns': 'auto' +'gridAutoFlow': 'row' +'grid-auto-flow': 'row' +'gridAutoRows': 'auto' +'grid-auto-rows': 'auto' +'gridColumn': 'auto auto' +'grid-column': 'auto auto' +'gridColumnEnd': 'auto' +'grid-column-end': 'auto' +'gridColumnGap': 'normal' +'grid-column-gap': 'normal' +'gridColumnStart': 'auto' +'grid-column-start': 'auto' +'gridGap': 'normal normal' +'grid-gap': 'normal normal' +'gridRow': 'auto auto' +'grid-row': 'auto auto' +'gridRowEnd': 'auto' +'grid-row-end': 'auto' +'gridRowGap': 'normal' +'grid-row-gap': 'normal' +'gridRowStart': 'auto' +'grid-row-start': 'auto' +'gridTemplate': 'none auto auto' +'grid-template': 'none auto auto' +'gridTemplateAreas': 'none' +'grid-template-areas': 'none' +'gridTemplateColumns': 'auto' +'grid-template-columns': 'auto' +'gridTemplateRows': 'auto' +'grid-template-rows': 'auto' +'height': '0px' +'imageRendering': 'auto' +'image-rendering': 'auto' +'inlineSize': 'auto' +'inline-size': 'auto' +'inset': 'auto auto auto auto' +'insetBlock': 'auto auto auto auto' +'inset-block': 'auto auto auto auto' +'insetBlockEnd': 'auto' +'inset-block-end': 'auto' +'insetBlockStart': 'auto' +'inset-block-start': 'auto' +'insetInline': 'auto auto auto auto' +'inset-inline': 'auto auto auto auto' +'insetInlineEnd': 'auto' +'inset-inline-end': 'auto' +'insetInlineStart': 'auto' +'inset-inline-start': 'auto' +'justifyContent': 'normal' +'justify-content': 'normal' +'justifyItems': 'legacy' +'justify-items': 'legacy' +'justifySelf': 'auto' +'justify-self': 'auto' +'left': 'auto' +'letterSpacing': 'normal' +'letter-spacing': 'normal' +'lineHeight': 'normal' +'line-height': 'normal' +'listStyle': 'disc outside none' +'list-style': 'disc outside none' +'listStyleImage': 'none' +'list-style-image': 'none' +'listStylePosition': 'outside' +'list-style-position': 'outside' +'listStyleType': 'disc' +'list-style-type': 'disc' +'margin': '8px 8px 8px 8px' +'marginBlock': '8px 8px 8px 8px' +'margin-block': '8px 8px 8px 8px' +'marginBlockEnd': '8px' +'margin-block-end': '8px' +'marginBlockStart': '8px' +'margin-block-start': '8px' +'marginBottom': '8px' +'margin-bottom': '8px' +'marginInline': '8px 8px 8px 8px' +'margin-inline': '8px 8px 8px 8px' +'marginInlineEnd': '8px' +'margin-inline-end': '8px' +'marginInlineStart': '8px' +'margin-inline-start': '8px' +'marginLeft': '8px' +'margin-left': '8px' +'marginRight': '8px' +'margin-right': '8px' +'marginTop': '8px' +'margin-top': '8px' +'mask': 'none' +'maskType': 'luminance' +'mask-type': 'luminance' +'mathDepth': '0' +'math-depth': '0' +'mathShift': 'normal' +'math-shift': 'normal' +'mathStyle': 'normal' +'math-style': 'normal' +'maxHeight': 'none' +'max-height': 'none' +'maxInlineSize': 'none' +'max-inline-size': 'none' +'maxWidth': 'none' +'max-width': 'none' +'minHeight': 'auto' +'min-height': 'auto' +'minInlineSize': '0px' +'min-inline-size': '0px' +'minWidth': 'auto' +'min-width': 'auto' +'objectFit': 'fill' +'object-fit': 'fill' +'objectPosition': 'left 50% top 50%' +'object-position': 'left 50% top 50%' +'opacity': '1' +'order': '0' +'outline': 'rgb(0, 0, 0) none medium' +'outlineColor': 'rgb(0, 0, 0)' +'outline-color': 'rgb(0, 0, 0)' +'outlineOffset': '0px' +'outline-offset': '0px' +'outlineStyle': 'none' +'outline-style': 'none' +'outlineWidth': 'medium' +'outline-width': 'medium' +'overflow': 'visible visible' +'overflowX': 'visible' +'overflow-x': 'visible' +'overflowY': 'visible' +'overflow-y': 'visible' +'padding': '0px 0px 0px 0px' +'paddingBlock': '0px 0px 0px 0px' +'padding-block': '0px 0px 0px 0px' +'paddingBlockEnd': '0px' +'padding-block-end': '0px' +'paddingBlockStart': '0px' +'padding-block-start': '0px' +'paddingBottom': '0px' +'padding-bottom': '0px' +'paddingInline': '0px 0px 0px 0px' +'padding-inline': '0px 0px 0px 0px' +'paddingInlineEnd': '0px' +'padding-inline-end': '0px' +'paddingInlineStart': '0px' +'padding-inline-start': '0px' +'paddingLeft': '0px' +'padding-left': '0px' +'paddingRight': '0px' +'padding-right': '0px' +'paddingTop': '0px' +'padding-top': '0px' +'placeContent': 'normal normal' +'place-content': 'normal normal' +'placeItems': 'normal legacy' +'place-items': 'normal legacy' +'placeSelf': 'auto auto' +'place-self': 'auto auto' +'pointerEvents': 'auto' +'pointer-events': 'auto' +'position': 'static' +'quotes': 'auto' +'r': '0px' +'right': 'auto' +'rotate': 'none' +'rowGap': 'normal' +'row-gap': 'normal' +'rx': 'auto' +'ry': 'auto' +'scrollbarGutter': 'auto' +'scrollbar-gutter': 'auto' +'scrollbarWidth': 'auto' +'scrollbar-width': 'auto' +'stopColor': 'rgb(0, 0, 0)' +'stop-color': 'rgb(0, 0, 0)' +'stopOpacity': '1' +'stop-opacity': '1' +'stroke': 'none' +'strokeLinecap': 'butt' +'stroke-linecap': 'butt' +'strokeLinejoin': 'miter' +'stroke-linejoin': 'miter' +'strokeMiterlimit': '4' +'stroke-miterlimit': '4' +'strokeOpacity': '1' +'stroke-opacity': '1' +'strokeWidth': '1px' +'stroke-width': '1px' +'tabSize': '8' +'tab-size': '8' +'tableLayout': 'auto' +'table-layout': 'auto' +'textAlign': 'start' +'text-align': 'start' +'textAnchor': 'start' +'text-anchor': 'start' +'textDecoration': 'rgb(0, 0, 0) none solid auto' +'text-decoration': 'rgb(0, 0, 0) none solid auto' +'textDecorationColor': 'rgb(0, 0, 0)' +'text-decoration-color': 'rgb(0, 0, 0)' +'textDecorationLine': 'none' +'text-decoration-line': 'none' +'textDecorationStyle': 'solid' +'text-decoration-style': 'solid' +'textDecorationThickness': 'auto' +'text-decoration-thickness': 'auto' +'textIndent': '0px' +'text-indent': '0px' +'textJustify': 'auto' +'text-justify': 'auto' +'textOverflow': 'clip' +'text-overflow': 'clip' +'textShadow': 'none' +'text-shadow': 'none' +'textTransform': 'none' +'text-transform': 'none' +'top': 'auto' +'transform': 'none' +'transformBox': 'view-box' +'transform-box': 'view-box' +'transformOrigin': '50% 50%' +'transform-origin': '50% 50%' +'transition': 'all 0s ease 0s' +'transitionDelay': '0s' +'transition-delay': '0s' +'transitionDuration': '0s' +'transition-duration': '0s' +'transitionProperty': 'all' +'transition-property': 'all' +'transitionTimingFunction': 'ease' +'transition-timing-function': 'ease' +'unicodeBidi': 'normal' +'unicode-bidi': 'normal' +'userSelect': 'auto' +'user-select': 'auto' +'verticalAlign': 'baseline' +'vertical-align': 'baseline' +'visibility': 'visible' +'whiteSpace': 'normal' +'white-space': 'normal' +'width': '284px' +'wordBreak': 'normal' +'word-break': 'normal' +'wordSpacing': 'normal' +'word-spacing': 'normal' +'wordWrap': 'normal' +'word-wrap': 'normal' +'writingMode': 'horizontal-tb' +'writing-mode': 'horizontal-tb' +'x': '0px' +'y': '0px' +'zIndex': 'auto' +'z-index': 'auto' +'getPropertyPriority': 'function getPropertyPriority() { [native code] }' +'getPropertyValue': 'function getPropertyValue() { [native code] }' +'removeProperty': 'function removeProperty() { [native code] }' +'item': 'function item() { [native code] }' +'setProperty': 'function setProperty() { [native code] }' +'constructor': 'function CSSStyleDeclaration() { [native code] }' diff --git a/Tests/LibWeb/Text/input/css/CSSStyleDeclaration-all-supported-properties-and-default-values.html b/Tests/LibWeb/Text/input/css/CSSStyleDeclaration-all-supported-properties-and-default-values.html new file mode 100644 index 00000000000..a499a8c48ea --- /dev/null +++ b/Tests/LibWeb/Text/input/css/CSSStyleDeclaration-all-supported-properties-and-default-values.html @@ -0,0 +1,18 @@ + + +