mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-28 11:49:44 +00:00
LibWeb: Implement the "fontSize" editing command
This commit is contained in:
parent
9366a50dd3
commit
1d2500e31f
Notes:
github-actions[bot]
2025-01-10 22:36:54 +00:00
Author: https://github.com/gmta
Commit: 1d2500e31f
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3216
5 changed files with 141 additions and 1 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include <LibWeb/HTML/HTMLImageElement.h>
|
#include <LibWeb/HTML/HTMLImageElement.h>
|
||||||
#include <LibWeb/HTML/HTMLLIElement.h>
|
#include <LibWeb/HTML/HTMLLIElement.h>
|
||||||
#include <LibWeb/HTML/HTMLTableElement.h>
|
#include <LibWeb/HTML/HTMLTableElement.h>
|
||||||
|
#include <LibWeb/HTML/Numbers.h>
|
||||||
#include <LibWeb/Layout/Node.h>
|
#include <LibWeb/Layout/Node.h>
|
||||||
#include <LibWeb/Namespace.h>
|
#include <LibWeb/Namespace.h>
|
||||||
|
|
||||||
|
@ -500,6 +501,98 @@ bool command_font_name_action(DOM::Document& document, String const& value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class FontSizeMode : u8 {
|
||||||
|
Absolute,
|
||||||
|
RelativePlus,
|
||||||
|
RelativeMinus,
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://w3c.github.io/editing/docs/execCommand/#the-fontsize-command
|
||||||
|
bool command_font_size_action(DOM::Document& document, String const& value)
|
||||||
|
{
|
||||||
|
// 1. Strip leading and trailing whitespace from value.
|
||||||
|
auto resulting_value = MUST(value.trim_ascii_whitespace());
|
||||||
|
|
||||||
|
// 2. If value is not a valid floating point number, and would not be a valid floating point number if a single
|
||||||
|
// leading "+" character were stripped, return false.
|
||||||
|
if (!HTML::is_valid_floating_point_number(resulting_value)) {
|
||||||
|
if (!resulting_value.starts_with_bytes("+"sv)
|
||||||
|
|| !HTML::is_valid_floating_point_number(MUST(resulting_value.substring_from_byte_offset(1))))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If the first character of value is "+", delete the character and let mode be "relative-plus".
|
||||||
|
auto mode = FontSizeMode::Absolute;
|
||||||
|
if (resulting_value.starts_with_bytes("+"sv)) {
|
||||||
|
resulting_value = MUST(resulting_value.substring_from_byte_offset(1));
|
||||||
|
mode = FontSizeMode::RelativePlus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Otherwise, if the first character of value is "-", delete the character and let mode be "relative-minus".
|
||||||
|
else if (resulting_value.starts_with_bytes("-"sv)) {
|
||||||
|
resulting_value = MUST(resulting_value.substring_from_byte_offset(1));
|
||||||
|
mode = FontSizeMode::RelativeMinus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Otherwise, let mode be "absolute".
|
||||||
|
// NOTE: This is the default set in step 3.
|
||||||
|
|
||||||
|
// 6. Apply the rules for parsing non-negative integers to value, and let number be the result.
|
||||||
|
i64 number = HTML::parse_non_negative_integer(resulting_value).release_value();
|
||||||
|
|
||||||
|
// 7. If mode is "relative-plus", add three to number.
|
||||||
|
if (mode == FontSizeMode::RelativePlus)
|
||||||
|
number += 3;
|
||||||
|
|
||||||
|
// 8. If mode is "relative-minus", negate number, then add three to it.
|
||||||
|
if (mode == FontSizeMode::RelativeMinus)
|
||||||
|
number = -number + 3;
|
||||||
|
|
||||||
|
// 9. If number is less than one, let number equal 1.
|
||||||
|
number = AK::max(number, 1);
|
||||||
|
|
||||||
|
// 10. If number is greater than seven, let number equal 7.
|
||||||
|
number = AK::min(number, 7);
|
||||||
|
|
||||||
|
// 11. Set value to the string here corresponding to number:
|
||||||
|
// 1: x-small
|
||||||
|
// 2: small
|
||||||
|
// 3: medium
|
||||||
|
// 4: large
|
||||||
|
// 5: x-large
|
||||||
|
// 6: xx-large
|
||||||
|
// 7: xxx-large
|
||||||
|
auto const& font_sizes = named_font_sizes();
|
||||||
|
resulting_value = MUST(String::from_utf8(font_sizes[number - 1]));
|
||||||
|
|
||||||
|
// 12. Set the selection's value to value.
|
||||||
|
set_the_selections_value(document, CommandNames::fontSize, resulting_value);
|
||||||
|
|
||||||
|
// 13. Return true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/editing/docs/execCommand/#the-fontsize-command
|
||||||
|
String command_font_size_value(DOM::Document const& document)
|
||||||
|
{
|
||||||
|
// 1. If the active range is null, return the empty string.
|
||||||
|
auto range = active_range(document);
|
||||||
|
if (!range)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 2. Let pixel size be the effective command value of the first formattable node that is effectively contained in
|
||||||
|
// the active range, or if there is no such node, the effective command value of the active range's start node,
|
||||||
|
// in either case interpreted as a number of pixels.
|
||||||
|
auto first_formattable_node = first_formattable_node_effectively_contained(range);
|
||||||
|
auto value = effective_command_value(
|
||||||
|
first_formattable_node ? first_formattable_node : *range->start_container(),
|
||||||
|
CommandNames::fontSize);
|
||||||
|
auto pixel_size = font_size_to_pixel_size(value.value_or({}));
|
||||||
|
|
||||||
|
// 3. Return the legacy font size for pixel size.
|
||||||
|
return legacy_font_size(pixel_size.to_int());
|
||||||
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/editing/docs/execCommand/#the-forwarddelete-command
|
// https://w3c.github.io/editing/docs/execCommand/#the-forwarddelete-command
|
||||||
bool command_forward_delete_action(DOM::Document& document, String const&)
|
bool command_forward_delete_action(DOM::Document& document, String const&)
|
||||||
{
|
{
|
||||||
|
@ -1129,6 +1222,13 @@ static Array const commands {
|
||||||
.action = command_font_name_action,
|
.action = command_font_name_action,
|
||||||
.relevant_css_property = CSS::PropertyID::FontFamily,
|
.relevant_css_property = CSS::PropertyID::FontFamily,
|
||||||
},
|
},
|
||||||
|
// https://w3c.github.io/editing/docs/execCommand/#the-fontsize-command
|
||||||
|
CommandDefinition {
|
||||||
|
.command = CommandNames::fontSize,
|
||||||
|
.action = command_font_size_action,
|
||||||
|
.value = command_font_size_value,
|
||||||
|
.relevant_css_property = CSS::PropertyID::FontSize,
|
||||||
|
},
|
||||||
// https://w3c.github.io/editing/docs/execCommand/#the-forwarddelete-command
|
// https://w3c.github.io/editing/docs/execCommand/#the-forwarddelete-command
|
||||||
CommandDefinition {
|
CommandDefinition {
|
||||||
.command = CommandNames::forwardDelete,
|
.command = CommandNames::forwardDelete,
|
||||||
|
|
|
@ -36,6 +36,8 @@ bool command_default_paragraph_separator_action(DOM::Document&, String const&);
|
||||||
String command_default_paragraph_separator_value(DOM::Document const&);
|
String command_default_paragraph_separator_value(DOM::Document const&);
|
||||||
bool command_delete_action(DOM::Document&, String const&);
|
bool command_delete_action(DOM::Document&, String const&);
|
||||||
bool command_font_name_action(DOM::Document&, String const&);
|
bool command_font_name_action(DOM::Document&, String const&);
|
||||||
|
bool command_font_size_action(DOM::Document&, String const&);
|
||||||
|
String command_font_size_value(DOM::Document const&);
|
||||||
bool command_forward_delete_action(DOM::Document&, String const&);
|
bool command_forward_delete_action(DOM::Document&, String const&);
|
||||||
bool command_insert_linebreak_action(DOM::Document&, String const&);
|
bool command_insert_linebreak_action(DOM::Document&, String const&);
|
||||||
bool command_insert_paragraph_action(DOM::Document&, String const&);
|
bool command_insert_paragraph_action(DOM::Document&, String const&);
|
||||||
|
|
|
@ -3961,7 +3961,12 @@ GC::Ptr<DOM::Node> first_formattable_node_effectively_contained(GC::Ptr<DOM::Ran
|
||||||
|
|
||||||
CSSPixels font_size_to_pixel_size(StringView font_size)
|
CSSPixels font_size_to_pixel_size(StringView font_size)
|
||||||
{
|
{
|
||||||
auto pixel_size = CSS::StyleComputer::default_user_font_size();
|
// If the font size ends in 'px', interpret the preceding as a number and return it.
|
||||||
|
if (font_size.length() >= 2 && font_size.substring_view(font_size.length() - 2).equals_ignoring_ascii_case("px"sv)) {
|
||||||
|
auto optional_number = font_size.substring_view(0, font_size.length() - 2).to_number<float>();
|
||||||
|
if (optional_number.has_value())
|
||||||
|
return CSSPixels::nearest_value_for(optional_number.value());
|
||||||
|
}
|
||||||
|
|
||||||
// Try to map the font size directly to a keyword (e.g. medium or x-large)
|
// Try to map the font size directly to a keyword (e.g. medium or x-large)
|
||||||
auto keyword = CSS::keyword_from_string(font_size);
|
auto keyword = CSS::keyword_from_string(font_size);
|
||||||
|
@ -3971,6 +3976,7 @@ CSSPixels font_size_to_pixel_size(StringView font_size)
|
||||||
keyword = HTML::HTMLFontElement::parse_legacy_font_size(font_size);
|
keyword = HTML::HTMLFontElement::parse_legacy_font_size(font_size);
|
||||||
|
|
||||||
// If that also failed, give up
|
// If that also failed, give up
|
||||||
|
auto pixel_size = CSS::StyleComputer::default_user_font_size();
|
||||||
if (!keyword.has_value())
|
if (!keyword.has_value())
|
||||||
return pixel_size;
|
return pixel_size;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
div: "foobar"
|
||||||
|
fontSize value: 3
|
||||||
|
div: "foo<font size="5" style="font-size: x-large;">bar</font>"
|
||||||
|
fontSize value: 5
|
||||||
|
div: "<font size="4" style="font-size: large;">foo</font><font style=""><font size="4" style="font-size: large;"><font size="4" style="font-size: large;">bar</font></font></font>"
|
||||||
|
fontSize value: 4
|
26
Tests/LibWeb/Text/input/Editing/execCommand-fontSize.html
Normal file
26
Tests/LibWeb/Text/input/Editing/execCommand-fontSize.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<div contenteditable="true">foobar</div>
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
const range = document.createRange();
|
||||||
|
getSelection().addRange(range);
|
||||||
|
|
||||||
|
const divElm = document.querySelector('div');
|
||||||
|
println(`div: "${divElm.innerHTML}"`);
|
||||||
|
println(`fontSize value: ${document.queryCommandValue('fontSize')}`);
|
||||||
|
|
||||||
|
// Set fontSize for 'bar'
|
||||||
|
range.setStart(divElm.childNodes[0], 3);
|
||||||
|
range.setEnd(divElm.childNodes[0], 6);
|
||||||
|
document.execCommand('fontSize', false, '5');
|
||||||
|
println(`div: "${divElm.innerHTML}"`);
|
||||||
|
println(`fontSize value: ${document.queryCommandValue('fontSize')}`);
|
||||||
|
|
||||||
|
// Set fontSize for 'foobar'
|
||||||
|
range.setStart(divElm.childNodes[0], 0);
|
||||||
|
range.setEnd(divElm.childNodes[1].childNodes[0], 3);
|
||||||
|
document.execCommand('fontSize', false, '4');
|
||||||
|
println(`div: "${divElm.innerHTML}"`);
|
||||||
|
println(`fontSize value: ${document.queryCommandValue('fontSize')}`);
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue