diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index 36b84c0fa479c..2df31ad963db0 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -1767,6 +1767,41 @@ RefPtr StyleComputer::font_matching_algorithm(FlyStr return {}; } +CSSPixels StyleComputer::default_user_font_size() const +{ + // FIXME: This value should be configurable by the user. + return 16; +} + +// https://w3c.github.io/csswg-drafts/css-fonts/#absolute-size-mapping +CSSPixelFraction StyleComputer::absolute_size_mapping(Keyword keyword) +{ + switch (keyword) { + case Keyword::XxSmall: + return CSSPixels(3) / 5; + case Keyword::XSmall: + return CSSPixels(3) / 4; + case Keyword::Small: + return CSSPixels(8) / 9; + case Keyword::Medium: + return 1; + case Keyword::Large: + return CSSPixels(6) / 5; + case Keyword::XLarge: + return CSSPixels(3) / 2; + case Keyword::XxLarge: + return 2; + case Keyword::XxxLarge: + return 3; + case Keyword::Smaller: + return CSSPixels(4) / 5; + case Keyword::Larger: + return CSSPixels(5) / 4; + default: + return 1; + } +} + RefPtr StyleComputer::compute_font_for_style_values(DOM::Element const* element, Optional pseudo_element, CSSStyleValue const& font_family, CSSStyleValue const& font_size, CSSStyleValue const& font_style, CSSStyleValue const& font_weight, CSSStyleValue const& font_stretch, int math_depth) const { auto* parent_element = element_to_inherit_style_from(element, pseudo_element); @@ -1776,8 +1811,7 @@ RefPtr StyleComputer::compute_font_for_style_values( auto weight = font_weight.to_font_weight(); bool bold = weight > Gfx::FontWeight::Regular; - // FIXME: Should be based on "user's default font size" - CSSPixels font_size_in_px = 16; + auto font_size_in_px = default_user_font_size(); Gfx::FontPixelMetrics font_pixel_metrics; if (parent_element && parent_element->computed_properties()) @@ -1800,34 +1834,6 @@ RefPtr StyleComputer::compute_font_for_style_values( Length::FontMetrics font_metrics { parent_font_size(), font_pixel_metrics }; if (font_size.is_keyword()) { - // https://w3c.github.io/csswg-drafts/css-fonts/#absolute-size-mapping - auto get_absolute_size_mapping = [](Keyword keyword) -> CSSPixelFraction { - switch (keyword) { - case Keyword::XxSmall: - return CSSPixels(3) / 5; - case Keyword::XSmall: - return CSSPixels(3) / 4; - case Keyword::Small: - return CSSPixels(8) / 9; - case Keyword::Medium: - return 1; - case Keyword::Large: - return CSSPixels(6) / 5; - case Keyword::XLarge: - return CSSPixels(3) / 2; - case Keyword::XxLarge: - return 2; - case Keyword::XxxLarge: - return 3; - case Keyword::Smaller: - return CSSPixels(4) / 5; - case Keyword::Larger: - return CSSPixels(5) / 4; - default: - return 1; - } - }; - auto const keyword = font_size.to_keyword(); if (keyword == Keyword::Math) { @@ -1880,7 +1886,7 @@ RefPtr StyleComputer::compute_font_for_style_values( font_size_in_px = CSSPixels::nearest_value_for(parent_element->computed_properties()->first_available_computed_font().pixel_metrics().size); } } - font_size_in_px *= get_absolute_size_mapping(keyword); + font_size_in_px *= absolute_size_mapping(keyword); } } else { Length::ResolutionContext const length_resolution_context { diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index c3566b3384d59..fd3e0502f1a4e 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -159,6 +159,8 @@ class StyleComputer { void load_fonts_from_sheet(CSSStyleSheet&); void unload_fonts_from_sheet(CSSStyleSheet&); + CSSPixels default_user_font_size() const; + static CSSPixelFraction absolute_size_mapping(Keyword); RefPtr compute_font_for_style_values(DOM::Element const* element, Optional pseudo_element, CSSStyleValue const& font_family, CSSStyleValue const& font_size, CSSStyleValue const& font_style, CSSStyleValue const& font_weight, CSSStyleValue const& font_stretch, int math_depth = 0) const; void set_viewport_rect(Badge, CSSPixelRect const& viewport_rect) { m_viewport_rect = viewport_rect; } diff --git a/Libraries/LibWeb/Editing/Commands.cpp b/Libraries/LibWeb/Editing/Commands.cpp index 21c95520a134d..04cc31e03b82a 100644 --- a/Libraries/LibWeb/Editing/Commands.cpp +++ b/Libraries/LibWeb/Editing/Commands.cpp @@ -798,6 +798,7 @@ static Array const commands { CommandDefinition { .command = CommandNames::delete_, .action = command_delete_action, + .preserves_overrides = true, }, // https://w3c.github.io/editing/docs/execCommand/#the-defaultparagraphseparator-command CommandDefinition { @@ -809,11 +810,13 @@ static Array const commands { CommandDefinition { .command = CommandNames::insertLineBreak, .action = command_insert_linebreak_action, + .preserves_overrides = true, }, // https://w3c.github.io/editing/docs/execCommand/#the-insertparagraph-command CommandDefinition { .command = CommandNames::insertParagraph, .action = command_insert_paragraph_action, + .preserves_overrides = true, }, // https://w3c.github.io/editing/docs/execCommand/#the-stylewithcss-command CommandDefinition { diff --git a/Libraries/LibWeb/Editing/Commands.h b/Libraries/LibWeb/Editing/Commands.h index 7ad268aaea5f5..ff3973b2b9c0f 100644 --- a/Libraries/LibWeb/Editing/Commands.h +++ b/Libraries/LibWeb/Editing/Commands.h @@ -19,6 +19,9 @@ struct CommandDefinition { Function value {}; Optional relevant_css_property {}; + // https://w3c.github.io/editing/docs/execCommand/#preserves-overrides + bool preserves_overrides { false }; + // https://w3c.github.io/editing/docs/execCommand/#inline-command-activated-values Vector inline_activated_values {}; }; diff --git a/Libraries/LibWeb/Editing/ExecCommand.cpp b/Libraries/LibWeb/Editing/ExecCommand.cpp index 57fd279481fc0..fa731e9364546 100644 --- a/Libraries/LibWeb/Editing/ExecCommand.cpp +++ b/Libraries/LibWeb/Editing/ExecCommand.cpp @@ -69,12 +69,22 @@ bool Document::exec_command(FlyString const& command, [[maybe_unused]] bool show // at the editing host that was actually affected. } + // https://w3c.github.io/editing/docs/execCommand/#preserves-overrides + // If a command preserves overrides, then before taking its action, the user agent must record current overrides. + auto overrides = Editing::record_current_overrides(*this); + // 5. Take the action for command, passing value to the instructions as an argument. auto optional_command = Editing::find_command_definition(command); VERIFY(optional_command.has_value()); auto const& command_definition = optional_command.release_value(); auto command_result = command_definition.action(*this, value); + // https://w3c.github.io/editing/docs/execCommand/#preserves-overrides + // After taking the action, if the active range is collapsed, it must restore states and values from the recorded + // list. + if (m_selection && m_selection->is_collapsed()) + Editing::restore_states_and_values(*this, overrides); + // 6. If the previous step returned false, return false. if (!command_result) return false; diff --git a/Libraries/LibWeb/Editing/Internal/Algorithms.cpp b/Libraries/LibWeb/Editing/Internal/Algorithms.cpp index 60d2cf9172d49..6040920e6e6d7 100644 --- a/Libraries/LibWeb/Editing/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/Editing/Internal/Algorithms.cpp @@ -4,7 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include +#include #include #include #include @@ -21,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -553,7 +556,7 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w end_block = {}; // 19. Record current states and values, and let overrides be the result. - auto overrides = record_current_states_and_values(*active_range()); + auto overrides = record_current_states_and_values(document); // 21. If start node and end node are the same, and start node is an editable Text node: if (start.node == end.node && is(*start.node) && start.node->is_editable()) { @@ -574,7 +577,7 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w } // 5. Restore states and values from overrides. - restore_states_and_values(*selection.range(), overrides); + restore_states_and_values(document, overrides); // 6. Abort these steps. return; @@ -658,7 +661,7 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w } // 3. Restore states and values from overrides. - restore_states_and_values(*selection.range(), overrides); + restore_states_and_values(document, overrides); // 4. Abort these steps. return; @@ -708,7 +711,7 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w end_block->remove(); // 4. Restore states and values from overrides. - restore_states_and_values(*active_range(), overrides); + restore_states_and_values(document, overrides); // 5. Abort these steps. return; @@ -717,7 +720,7 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w // 5. If end block's firstChild is not an inline node, restore states and values from record, then abort these // steps. if (!is_inline_node(*end_block->first_child())) { - restore_states_and_values(*active_range(), overrides); + restore_states_and_values(document, overrides); return; } @@ -880,7 +883,7 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w remove_extraneous_line_breaks_at_the_end_of_node(*start_block); // 41. Restore states and values from overrides. - restore_states_and_values(*active_range(), overrides); + restore_states_and_values(document, overrides); } // https://w3c.github.io/editing/docs/execCommand/#editing-host-of @@ -1908,6 +1911,37 @@ DOM::BoundaryPoint last_equivalent_point(DOM::BoundaryPoint boundary_point) return boundary_point; } +// https://w3c.github.io/editing/docs/execCommand/#legacy-font-size-for +String legacy_font_size(DOM::Document& document, int pixel_size) +{ + // 1. Let returned size be 1. + auto returned_size = 1; + + // 2. While returned size is less than 7: + while (returned_size < 7) { + // 1. Let lower bound be the resolved value of "font-size" in pixels of a font element whose size attribute is + // set to returned size. + auto lower_bound = font_size_to_pixel_size(document, MUST(String::formatted("{}", returned_size))).to_float(); + + // 2. Let upper bound be the resolved value of "font-size" in pixels of a font element whose size attribute is + // set to one plus returned size. + auto upper_bound = font_size_to_pixel_size(document, MUST(String::formatted("{}", returned_size + 1))).to_float(); + + // 3. Let average be the average of upper bound and lower bound. + auto average = (lower_bound + upper_bound) / 2; + + // 4. If pixel size is less than average, return the one-code unit string consisting of the digit returned size. + if (pixel_size < average) + return MUST(String::formatted("{}", returned_size)); + + // 5. Add one to returned size. + ++returned_size; + } + + // 3. Return "7". + return "7"_string; +} + // https://w3c.github.io/editing/docs/execCommand/#preserving-ranges void move_node_preserving_ranges(GC::Ref node, GC::Ref new_parent, u32 new_index) { @@ -2066,22 +2100,45 @@ Optional previous_equivalent_point(DOM::BoundaryPoint bounda return {}; } +// https://w3c.github.io/editing/docs/execCommand/#record-current-overrides +Vector record_current_overrides(DOM::Document const& document) +{ + // 1. Let overrides be a list of (string, string or boolean) ordered pairs, initially empty. + Vector overrides; + + // 2. If there is a value override for "createLink", add ("createLink", value override for "createLink") to + // overrides. + if (auto override = document.command_value_override(CommandNames::createLink); override.has_value()) + overrides.empend(CommandNames::createLink, override.release_value()); + + // 3. For each command in the list "bold", "italic", "strikethrough", "subscript", "superscript", "underline", in + // order: if there is a state override for command, add (command, command's state override) to overrides. + for (auto const& command : { CommandNames::bold, CommandNames::italic, CommandNames::strikethrough, + CommandNames::subscript, CommandNames::superscript, CommandNames::underline }) { + if (auto override = document.command_state_override(command); override.has_value()) + overrides.empend(command, override.release_value()); + } + + // 4. For each command in the list "fontName", "fontSize", "foreColor", "hiliteColor", in order: if there is a value + // override for command, add (command, command's value override) to overrides. + for (auto const& command : { CommandNames::fontName, CommandNames::fontSize, CommandNames::foreColor, + CommandNames::hiliteColor }) { + if (auto override = document.command_value_override(command); override.has_value()) + overrides.empend(command, override.release_value()); + } + + // 5. Return overrides. + return overrides; +} + // https://w3c.github.io/editing/docs/execCommand/#record-current-states-and-values -Vector record_current_states_and_values(GC::Ref active_range) +Vector record_current_states_and_values(DOM::Document const& document) { // 1. Let overrides be a list of (string, string or boolean) ordered pairs, initially empty. Vector overrides; // 2. Let node be the first formattable node effectively contained in the active range, or null if there is none. - GC::Ptr node; - auto common_ancestor = active_range->common_ancestor_container(); - common_ancestor->for_each_in_subtree([&](GC::Ref descendant) { - if (is_formattable_node(descendant) && is_effectively_contained_in_range(descendant, active_range)) { - node = descendant; - return TraversalDecision::Break; - } - return TraversalDecision::Continue; - }); + auto node = first_formattable_node_effectively_contained(*document.get_selection()); // 3. If node is null, return overrides. if (!node) @@ -2244,44 +2301,90 @@ void remove_node_preserving_its_descendants(GC::Ref node) } // https://w3c.github.io/editing/docs/execCommand/#restore-states-and-values -void restore_states_and_values(GC::Ref, Vector const& overrides) +void restore_states_and_values(DOM::Document& document, Vector const& overrides) { - // FIXME: 1. Let node be the first formattable node effectively contained in the active range, or null if there is none. - - // FIXME: 2. If node is not null, then for each (command, override) pair in overrides, in order: - { - // FIXME: 1. If override is a boolean, and queryCommandState(command) returns something different from override, take - // the action for command, with value equal to the empty string. - - // FIXME: 2. Otherwise, if override is a string, and command is neither "createLink" nor "fontSize", and - // queryCommandValue(command) returns something not equivalent to override, take the action for command, with - // value equal to override. - - // FIXME: 3. Otherwise, if override is a string; and command is "createLink"; and either there is a value override for - // "createLink" that is not equal to override, or there is no value override for "createLink" and node's - // effective command value for "createLink" is not equal to override: take the action for "createLink", with - // value equal to override. - - // FIXME: 4. Otherwise, if override is a string; and command is "fontSize"; and either there is a value override for - // "fontSize" that is not equal to override, or there is no value override for "fontSize" and node's - // effective command value for "fontSize" is not loosely equivalent to override: - { - // FIXME: 1. Convert override to an integer number of pixels, and set override to the legacy font size for the - // result. - - // FIXME: 2. Take the action for "fontSize", with value equal to override. - } + // 1. Let node be the first formattable node effectively contained in the active range, or null if there is none. + auto const& selection = *document.get_selection(); + auto node = first_formattable_node_effectively_contained(selection); + + // 2. If node is not null, + if (node) { + auto take_the_action_for_command = [&document](FlyString const& command, String const& value) { + auto const& command_definition = find_command_definition(command); + // FIXME: replace with VERIFY(command_definition.has_value()) as soon as all command definitions are in place. + if (command_definition.has_value()) + command_definition->action(document, value); + }; + + // then for each (command, override) pair in overrides, in order: + for (auto override : overrides) { + // 1. If override is a boolean, and queryCommandState(command) returns something different from override, + // take the action for command, with value equal to the empty string. + if (override.value.has() && document.query_command_state(override.command) != override.value.get()) { + take_the_action_for_command(override.command, {}); + } - // FIXME: 5. Otherwise, continue this loop from the beginning. + // 2. Otherwise, if override is a string, and command is neither "createLink" nor "fontSize", and + // queryCommandValue(command) returns something not equivalent to override, take the action for command, + // with value equal to override. + else if (override.value.has() && !override.command.is_one_of(CommandNames::createLink, CommandNames::fontSize) + && document.query_command_value(override.command) != override.value.get()) { + take_the_action_for_command(override.command, override.value.get()); + } + + // 3. Otherwise, if override is a string; and command is "createLink"; and either there is a value override + // for "createLink" that is not equal to override, or there is no value override for "createLink" and + // node's effective command value for "createLink" is not equal to override: take the action for + // "createLink", with value equal to override. + else if (auto value_override = document.command_value_override(CommandNames::createLink); + override.value.has() && override.command == CommandNames::createLink + && ((value_override.has_value() && value_override.value() != override.value.get()) + || (!value_override.has_value() + && effective_command_value(node, CommandNames::createLink) != override.value.get()))) { + take_the_action_for_command(CommandNames::createLink, override.value.get()); + } - // FIXME: 6. Set node to the first formattable node effectively contained in the active range, if there is one. + // 4. Otherwise, if override is a string; and command is "fontSize"; and either there is a value override + // for "fontSize" that is not equal to override, or there is no value override for "fontSize" and node's + // effective command value for "fontSize" is not loosely equivalent to override: + else if (auto value_override = document.command_value_override(CommandNames::fontSize); + override.value.has() && override.command == CommandNames::fontSize + && ((value_override.has_value() && value_override.value() != override.value.get()) + || (!value_override.has_value() + && !values_are_loosely_equivalent( + CommandNames::fontSize, + effective_command_value(node, CommandNames::fontSize), + override.value.get())))) { + // 1. Convert override to an integer number of pixels, and set override to the legacy font size for the + // result. + auto override_pixel_size = font_size_to_pixel_size(document, override.value.get()); + override.value = legacy_font_size(document, override_pixel_size.to_int()); + + // 2. Take the action for "fontSize", with value equal to override. + take_the_action_for_command(CommandNames::fontSize, override.value.get()); + } + + // 5. Otherwise, continue this loop from the beginning. + else { + continue; + } + + // 6. Set node to the first formattable node effectively contained in the active range, if there is one. + auto new_formattable_node = first_formattable_node_effectively_contained(selection); + if (new_formattable_node) + node = new_formattable_node; + } } // 3. Otherwise, for each (command, override) pair in overrides, in order: - for ([[maybe_unused]] auto const& override : overrides) { - // FIXME: 1. If override is a boolean, set the state override for command to override. - - // FIXME: 2. If override is a string, set the value override for command to override. + else { + for (auto const& override : overrides) { + // 1. If override is a boolean, set the state override for command to override. + // 2. If override is a string, set the value override for command to override. + override.value.visit( + [&](bool value) { document.set_command_state_override(override.command, value); }, + [&](String const& value) { document.set_command_value_override(override.command, value); }); + } } } @@ -2555,6 +2658,72 @@ void split_the_parent_of_nodes(Vector> const& nodes) remove_extraneous_line_breaks_at_the_end_of_node(*last_node->parent()); } +// https://w3c.github.io/editing/docs/execCommand/#equivalent-values +bool values_are_equivalent(FlyString const& command, Optional a, Optional b) +{ + // Two quantities are equivalent values for a command if either both are null, + if (!a.has_value() && !b.has_value()) + return true; + + // NOTE: Both need to be strings for all remaining conditions. + if (!a.has_value() || !b.has_value()) + return false; + + // or both are strings and the command defines equivalent values and they match the definition. + if (command.is_one_of(CommandNames::backColor, CommandNames::foreColor, CommandNames::hiliteColor)) { + // Either both strings are valid CSS colors and have the same red, green, blue, and alpha components, or neither + // string is a valid CSS color. + auto a_color = Color::from_string(a.value()); + auto b_color = Color::from_string(b.value()); + if (a_color.has_value()) + return a_color == b_color; + return !a_color.has_value() && !b_color.has_value(); + } + if (command == CommandNames::bold) { + // Either the two strings are equal, or one is "bold" and the other is "700", or one is "normal" and the other + // is "400". + if (a.value() == b.value()) + return true; + + auto either_is_bold = first_is_one_of("bold"sv, a.value(), b.value()); + auto either_is_700 = first_is_one_of("700"sv, a.value(), b.value()); + auto either_is_normal = first_is_one_of("normal"sv, a.value(), b.value()); + auto either_is_400 = first_is_one_of("400"sv, a.value(), b.value()); + + return (either_is_bold && either_is_700) || (either_is_normal && either_is_400); + } + + // or both are strings and they're equal and the command does not define any equivalent values, + return a.value() == b.value(); +} + +// https://w3c.github.io/editing/docs/execCommand/#loosely-equivalent-values +bool values_are_loosely_equivalent(FlyString const& command, Optional a, Optional b) +{ + // Two quantities are loosely equivalent values for a command if either they are equivalent values for the command, + if (values_are_equivalent(command, a, b)) + return true; + + // or if the command is the fontSize command; one of the quantities is one of "x-small", "small", "medium", "large", + // "x-large", "xx-large", or "xxx-large"; and the other quantity is the resolved value of "font-size" on a font + // element whose size attribute has the corresponding value set ("1" through "7" respectively). + if (command == CommandNames::fontSize && a.has_value() && b.has_value()) { + static constexpr Array named_quantities { "x-small"sv, "small"sv, "medium"sv, "large"sv, "x-large"sv, + "xx-large"sv, "xxx-large"sv }; + static constexpr Array size_quantities { "1"sv, "2"sv, "3"sv, "4"sv, "5"sv, "6"sv, "7"sv }; + static_assert(named_quantities.size() == size_quantities.size()); + + auto a_index = named_quantities.first_index_of(a.value()) + .value_or_lazy_evaluated_optional([&] { return size_quantities.first_index_of(a.value()); }); + auto b_index = named_quantities.first_index_of(b.value()) + .value_or_lazy_evaluated_optional([&] { return size_quantities.first_index_of(b.value()); }); + + return a_index.has_value() && a_index == b_index; + } + + return false; +} + // https://w3c.github.io/editing/docs/execCommand/#wrap GC::Ptr wrap( Vector> node_list, @@ -2736,6 +2905,45 @@ GC::Ptr wrap( return new_parent; } +GC::Ptr first_formattable_node_effectively_contained(Selection const& selection) +{ + auto active_range = selection.range(); + if (!active_range) + return {}; + + GC::Ptr node; + active_range->common_ancestor_container()->for_each_in_subtree([&](GC::Ref descendant) { + if (!is_effectively_contained_in_range(descendant, *active_range)) + return TraversalDecision::SkipChildrenAndContinue; + if (is_formattable_node(descendant)) { + node = descendant; + return TraversalDecision::Break; + } + return TraversalDecision::Continue; + }); + return node; +} + +CSSPixels font_size_to_pixel_size(DOM::Document& document, StringView font_size) +{ + auto style_computer = CSS::StyleComputer { document }; + auto pixel_size = style_computer.default_user_font_size(); + + // Try to map the font size directly to a keyword (e.g. medium or x-large) + auto keyword = CSS::keyword_from_string(font_size); + + // If that failed, try to interpret it as a legacy font size (e.g. 1 through 7) + if (!keyword.has_value()) + keyword = HTML::HTMLFontElement::parse_legacy_font_size(font_size); + + // If that also failed, give up + if (!keyword.has_value()) + return pixel_size; + + // Return scaled pixel size + return pixel_size * CSS::StyleComputer::absolute_size_mapping(keyword.release_value()); +} + bool has_visible_children(GC::Ref node) { bool has_visible_child = false; diff --git a/Libraries/LibWeb/Editing/Internal/Algorithms.h b/Libraries/LibWeb/Editing/Internal/Algorithms.h index eb9ab2338a830..e3e47a9a7a245 100644 --- a/Libraries/LibWeb/Editing/Internal/Algorithms.h +++ b/Libraries/LibWeb/Editing/Internal/Algorithms.h @@ -65,26 +65,32 @@ bool is_single_line_container(GC::Ref); bool is_visible_node(GC::Ref); bool is_whitespace_node(GC::Ref); DOM::BoundaryPoint last_equivalent_point(DOM::BoundaryPoint); +String legacy_font_size(DOM::Document&, int); void move_node_preserving_ranges(GC::Ref, GC::Ref new_parent, u32 new_index); Optional next_equivalent_point(DOM::BoundaryPoint); void normalize_sublists_in_node(GC::Ref); bool precedes_a_line_break(GC::Ref); Optional previous_equivalent_point(DOM::BoundaryPoint); -Vector record_current_states_and_values(GC::Ref); +Vector record_current_overrides(DOM::Document const&); +Vector record_current_states_and_values(DOM::Document const&); Vector record_the_values_of_nodes(Vector> const&); void remove_extraneous_line_breaks_at_the_end_of_node(GC::Ref); void remove_extraneous_line_breaks_before_node(GC::Ref); void remove_extraneous_line_breaks_from_a_node(GC::Ref); void remove_node_preserving_its_descendants(GC::Ref); -void restore_states_and_values(GC::Ref, Vector const&); +void restore_states_and_values(DOM::Document&, Vector const&); void restore_the_values_of_nodes(Vector const&); GC::Ref set_the_tag_name(GC::Ref, FlyString const&); Optional specified_command_value(GC::Ref, FlyString const& command); void split_the_parent_of_nodes(Vector> const&); +bool values_are_equivalent(FlyString const&, Optional, Optional); +bool values_are_loosely_equivalent(FlyString const&, Optional, Optional); GC::Ptr wrap(Vector>, Function)> sibling_criteria, Function()> new_parent_instructions); // Utility methods: +GC::Ptr first_formattable_node_effectively_contained(Selection const&); +CSSPixels font_size_to_pixel_size(DOM::Document&, StringView); bool has_visible_children(GC::Ref); bool is_heading(FlyString const&); Optional resolved_display(GC::Ref); diff --git a/Libraries/LibWeb/HTML/HTMLFontElement.cpp b/Libraries/LibWeb/HTML/HTMLFontElement.cpp index ccd091da95fe8..02351eec99f86 100644 --- a/Libraries/LibWeb/HTML/HTMLFontElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLFontElement.cpp @@ -26,7 +26,7 @@ enum class Mode { }; // https://html.spec.whatwg.org/multipage/rendering.html#rules-for-parsing-a-legacy-font-size -static Optional parse_legacy_font_size(StringView string) +Optional HTMLFontElement::parse_legacy_font_size(StringView string) { // 1. Let input be the attribute's value. // 2. Let position be a pointer into input, initially pointing at the start of the string. diff --git a/Libraries/LibWeb/HTML/HTMLFontElement.h b/Libraries/LibWeb/HTML/HTMLFontElement.h index 6dabdac03745c..794143117b3cc 100644 --- a/Libraries/LibWeb/HTML/HTMLFontElement.h +++ b/Libraries/LibWeb/HTML/HTMLFontElement.h @@ -19,6 +19,8 @@ class HTMLFontElement final : public HTMLElement { virtual void apply_presentational_hints(GC::Ref) const override; + static Optional parse_legacy_font_size(StringView); + private: HTMLFontElement(DOM::Document&, DOM::QualifiedName);