Files
bun.sh/src/css/properties/margin_padding.zig
taylor.fish 437e15bae5 Replace catch bun.outOfMemory() with safer alternatives (#22141)
Replace `catch bun.outOfMemory()`, which can accidentally catch
non-OOM-related errors, with either `bun.handleOom` or a manual `catch
|err| switch (err)`.

(For internal tracking: fixes STAB-1070)

---------

Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2025-08-26 12:50:25 -07:00

853 lines
36 KiB
Zig

pub const css = @import("../css_parser.zig");
const Property = css.Property;
const PropertyIdTag = css.PropertyIdTag;
const PropertyCategory = css.logical.PropertyCategory;
const LengthPercentageOrAuto = css.css_values.length.LengthPercentageOrAuto;
/// A value for the [inset](https://drafts.csswg.org/css-logical/#propdef-inset) shorthand property.
pub const Inset = struct {
top: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.inset);
const css_impl = css.DefineRectShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.top = css.PropertyIdTag.top,
.right = css.PropertyIdTag.right,
.bottom = css.PropertyIdTag.bottom,
.left = css.PropertyIdTag.left,
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [inset-block](https://drafts.csswg.org/css-logical/#propdef-inset-block) shorthand property.
pub const InsetBlock = struct {
/// The block start value.
block_start: LengthPercentageOrAuto,
/// The block end value.
block_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"inset-block");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.block_start = css.PropertyIdTag.@"inset-block-start",
.block_end = css.PropertyIdTag.@"inset-block-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [inset-inline](https://drafts.csswg.org/css-logical/#propdef-inset-inline) shorthand property.
pub const InsetInline = struct {
/// The inline start value.
inline_start: LengthPercentageOrAuto,
/// The inline end value.
inline_end: LengthPercentageOrAuto,
pub const PropertyFieldMap = .{
.inline_start = css.PropertyIdTag.@"inset-inline-start",
.inline_end = css.PropertyIdTag.@"inset-inline-end",
};
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"inset-inline");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [margin-block](https://drafts.csswg.org/css-logical/#propdef-margin-block) shorthand property.
pub const MarginBlock = struct {
/// The block start value.
block_start: LengthPercentageOrAuto,
/// The block end value.
block_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"margin-block");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.block_start = css.PropertyIdTag.@"margin-block-start",
.block_end = css.PropertyIdTag.@"margin-block-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [margin-inline](https://drafts.csswg.org/css-logical/#propdef-margin-inline) shorthand property.
pub const MarginInline = struct {
/// The inline start value.
inline_start: LengthPercentageOrAuto,
/// The inline end value.
inline_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"margin-inline");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.inline_start = css.PropertyIdTag.@"margin-inline-start",
.inline_end = css.PropertyIdTag.@"margin-inline-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [margin](https://drafts.csswg.org/css-box-4/#propdef-margin) shorthand property.
pub const Margin = struct {
top: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.margin);
const css_impl = css.DefineRectShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.top = css.PropertyIdTag.@"margin-top",
.right = css.PropertyIdTag.@"margin-right",
.bottom = css.PropertyIdTag.@"margin-bottom",
.left = css.PropertyIdTag.@"margin-left",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [padding-block](https://drafts.csswg.org/css-logical/#propdef-padding-block) shorthand property.
pub const PaddingBlock = struct {
/// The block start value.
block_start: LengthPercentageOrAuto,
/// The block end value.
block_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"padding-block");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.block_start = css.PropertyIdTag.@"padding-block-start",
.block_end = css.PropertyIdTag.@"padding-block-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [padding-inline](https://drafts.csswg.org/css-logical/#propdef-padding-inline) shorthand property.
pub const PaddingInline = struct {
/// The inline start value.
inline_start: LengthPercentageOrAuto,
/// The inline end value.
inline_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"padding-inline");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.inline_start = css.PropertyIdTag.@"padding-inline-start",
.inline_end = css.PropertyIdTag.@"padding-inline-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [padding](https://drafts.csswg.org/css-box-4/#propdef-padding) shorthand property.
pub const Padding = struct {
top: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.padding);
const css_impl = css.DefineRectShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.top = css.PropertyIdTag.@"padding-top",
.right = css.PropertyIdTag.@"padding-right",
.bottom = css.PropertyIdTag.@"padding-bottom",
.left = css.PropertyIdTag.@"padding-left",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [scroll-margin-block](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-block) shorthand property.
pub const ScrollMarginBlock = struct {
/// The block start value.
block_start: LengthPercentageOrAuto,
/// The block end value.
block_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin-block");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.block_start = css.PropertyIdTag.@"scroll-margin-block-start",
.block_end = css.PropertyIdTag.@"scroll-margin-block-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [scroll-margin-inline](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-inline) shorthand property.
pub const ScrollMarginInline = struct {
/// The inline start value.
inline_start: LengthPercentageOrAuto,
/// The inline end value.
inline_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin-inline");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.inline_start = css.PropertyIdTag.@"scroll-margin-inline-start",
.inline_end = css.PropertyIdTag.@"scroll-margin-inline-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [scroll-margin](https://drafts.csswg.org/css-scroll-snap/#scroll-margin) shorthand property.
pub const ScrollMargin = struct {
top: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin");
const css_impl = css.DefineRectShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.top = css.PropertyIdTag.@"scroll-margin-top",
.right = css.PropertyIdTag.@"scroll-margin-right",
.bottom = css.PropertyIdTag.@"scroll-margin-bottom",
.left = css.PropertyIdTag.@"scroll-margin-left",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [scroll-padding-block](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-block) shorthand property.
pub const ScrollPaddingBlock = struct {
/// The block start value.
block_start: LengthPercentageOrAuto,
/// The block end value.
block_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding-block");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.block_start = css.PropertyIdTag.@"scroll-padding-block-start",
.block_end = css.PropertyIdTag.@"scroll-padding-block-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [scroll-padding-inline](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-inline) shorthand property.
pub const ScrollPaddingInline = struct {
/// The inline start value.
inline_start: LengthPercentageOrAuto,
/// The inline end value.
inline_end: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding-inline");
const css_impl = css.DefineSizeShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.inline_start = css.PropertyIdTag.@"scroll-padding-inline-start",
.inline_end = css.PropertyIdTag.@"scroll-padding-inline-end",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
/// A value for the [scroll-padding](https://drafts.csswg.org/css-scroll-snap/#scroll-padding) shorthand property.
pub const ScrollPadding = struct {
top: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
// TODO: bring this back
// (old using name space) css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding");
const css_impl = css.DefineRectShorthand(@This(), LengthPercentageOrAuto);
pub const toCss = css_impl.toCss;
pub const parse = css_impl.parse;
pub const PropertyFieldMap = .{
.top = css.PropertyIdTag.@"scroll-padding-top",
.right = css.PropertyIdTag.@"scroll-padding-right",
.bottom = css.PropertyIdTag.@"scroll-padding-bottom",
.left = css.PropertyIdTag.@"scroll-padding-left",
};
pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() {
return css.implementDeepClone(@This(), this, allocator);
}
pub fn eql(lhs: *const @This(), rhs: *const @This()) bool {
return css.implementEql(@This(), lhs, rhs);
}
};
pub const MarginHandler = NewSizeHandler(
PropertyIdTag.@"margin-top",
PropertyIdTag.@"margin-bottom",
PropertyIdTag.@"margin-left",
PropertyIdTag.@"margin-right",
PropertyIdTag.@"margin-block-start",
PropertyIdTag.@"margin-block-end",
PropertyIdTag.@"margin-inline-start",
PropertyIdTag.@"margin-inline-end",
PropertyIdTag.margin,
PropertyIdTag.@"margin-block",
PropertyIdTag.@"margin-inline",
PropertyCategory.physical,
.{
.feature = css.Feature.logical_margin,
.shorthand_feature = css.Feature.logical_margin_shorthand,
},
);
pub const PaddingHandler = NewSizeHandler(
PropertyIdTag.@"padding-top",
PropertyIdTag.@"padding-bottom",
PropertyIdTag.@"padding-left",
PropertyIdTag.@"padding-right",
PropertyIdTag.@"padding-block-start",
PropertyIdTag.@"padding-block-end",
PropertyIdTag.@"padding-inline-start",
PropertyIdTag.@"padding-inline-end",
PropertyIdTag.padding,
PropertyIdTag.@"padding-block",
PropertyIdTag.@"padding-inline",
PropertyCategory.physical,
.{
.feature = css.Feature.logical_padding,
.shorthand_feature = css.Feature.logical_padding_shorthand,
},
);
pub const ScrollMarginHandler = NewSizeHandler(
PropertyIdTag.@"scroll-margin-top",
PropertyIdTag.@"scroll-margin-bottom",
PropertyIdTag.@"scroll-margin-left",
PropertyIdTag.@"scroll-margin-right",
PropertyIdTag.@"scroll-margin-block-start",
PropertyIdTag.@"scroll-margin-block-end",
PropertyIdTag.@"scroll-margin-inline-start",
PropertyIdTag.@"scroll-margin-inline-end",
PropertyIdTag.@"scroll-margin",
PropertyIdTag.@"scroll-margin-block",
PropertyIdTag.@"scroll-margin-inline",
PropertyCategory.physical,
null,
);
pub const InsetHandler = NewSizeHandler(
PropertyIdTag.top,
PropertyIdTag.bottom,
PropertyIdTag.left,
PropertyIdTag.right,
PropertyIdTag.@"inset-block-start",
PropertyIdTag.@"inset-block-end",
PropertyIdTag.@"inset-inline-start",
PropertyIdTag.@"inset-inline-end",
PropertyIdTag.inset,
PropertyIdTag.@"inset-block",
PropertyIdTag.@"inset-inline",
PropertyCategory.physical,
.{
.feature = css.Feature.logical_inset,
.shorthand_feature = css.Feature.logical_inset,
},
);
pub fn NewSizeHandler(
comptime top_prop: css.PropertyIdTag,
comptime bottom_prop: css.PropertyIdTag,
comptime left_prop: css.PropertyIdTag,
comptime right_prop: css.PropertyIdTag,
comptime block_start_prop: css.PropertyIdTag,
comptime block_end_prop: css.PropertyIdTag,
comptime inline_start_prop: css.PropertyIdTag,
comptime inline_end_prop: css.PropertyIdTag,
comptime shorthand_prop: css.PropertyIdTag,
comptime block_shorthand: css.PropertyIdTag,
comptime inline_shorthand: css.PropertyIdTag,
comptime shorthand_category: css.logical.PropertyCategory,
comptime shorthand_extra: ?struct { feature: css.compat.Feature, shorthand_feature: css.compat.Feature },
) type {
return struct {
top: ?LengthPercentageOrAuto = null,
bottom: ?LengthPercentageOrAuto = null,
left: ?LengthPercentageOrAuto = null,
right: ?LengthPercentageOrAuto = null,
block_start: ?Property = null,
block_end: ?Property = null,
inline_start: ?Property = null,
inline_end: ?Property = null,
has_any: bool = false,
category: css.logical.PropertyCategory = css.logical.PropertyCategory.default(),
pub fn handleProperty(
this: *@This(),
property: *const Property,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) bool {
switch (@as(PropertyIdTag, property.*)) {
top_prop => this.propertyHelper("top", top_prop.valueType(), &@field(property, @tagName(top_prop)), PropertyCategory.physical, dest, context),
bottom_prop => this.propertyHelper("bottom", bottom_prop.valueType(), &@field(property, @tagName(bottom_prop)), PropertyCategory.physical, dest, context),
left_prop => this.propertyHelper("left", left_prop.valueType(), &@field(property, @tagName(left_prop)), PropertyCategory.physical, dest, context),
right_prop => this.propertyHelper("right", right_prop.valueType(), &@field(property, @tagName(right_prop)), PropertyCategory.physical, dest, context),
block_start_prop => {
this.flushHelper("block_start", block_start_prop.valueType(), &@field(property, @tagName(block_start_prop)), PropertyCategory.logical, dest, context);
this.logicalPropertyHelper("block_start", property.deepClone(context.allocator), dest, context);
},
block_end_prop => {
this.flushHelper("block_end", block_end_prop.valueType(), &@field(property, @tagName(block_end_prop)), PropertyCategory.logical, dest, context);
this.logicalPropertyHelper("block_end", property.deepClone(context.allocator), dest, context);
},
inline_start_prop => {
this.flushHelper("inline_start", inline_start_prop.valueType(), &@field(property, @tagName(inline_start_prop)), PropertyCategory.logical, dest, context);
this.logicalPropertyHelper("inline_start", property.deepClone(context.allocator), dest, context);
},
inline_end_prop => {
this.flushHelper("inline_end", inline_end_prop.valueType(), &@field(property, @tagName(inline_end_prop)), PropertyCategory.logical, dest, context);
this.logicalPropertyHelper("inline_end", property.deepClone(context.allocator), dest, context);
},
block_shorthand => {
const val = &@field(property, @tagName(block_shorthand));
this.flushHelper("block_start", block_start_prop.valueType(), &val.block_start, .logical, dest, context);
this.flushHelper("block_end", block_end_prop.valueType(), &val.block_end, .logical, dest, context);
this.logicalPropertyHelper("block_start", @unionInit(Property, @tagName(block_start_prop), val.block_start.deepClone(context.allocator)), dest, context);
this.logicalPropertyHelper("block_end", @unionInit(Property, @tagName(block_end_prop), val.block_end.deepClone(context.allocator)), dest, context);
},
inline_shorthand => {
const val = &@field(property, @tagName(inline_shorthand));
this.flushHelper("inline_start", inline_start_prop.valueType(), &val.inline_start, .logical, dest, context);
this.flushHelper("inline_end", inline_end_prop.valueType(), &val.inline_end, .logical, dest, context);
this.logicalPropertyHelper("inline_start", @unionInit(Property, @tagName(inline_start_prop), val.inline_start.deepClone(context.allocator)), dest, context);
this.logicalPropertyHelper("inline_end", @unionInit(Property, @tagName(inline_end_prop), val.inline_end.deepClone(context.allocator)), dest, context);
},
shorthand_prop => {
const val = &@field(property, @tagName(shorthand_prop));
this.flushHelper("top", top_prop.valueType(), &val.top, shorthand_category, dest, context);
this.flushHelper("right", right_prop.valueType(), &val.right, shorthand_category, dest, context);
this.flushHelper("bottom", bottom_prop.valueType(), &val.bottom, shorthand_category, dest, context);
this.flushHelper("left", left_prop.valueType(), &val.left, shorthand_category, dest, context);
this.top = val.top.deepClone(context.allocator);
this.right = val.right.deepClone(context.allocator);
this.bottom = val.bottom.deepClone(context.allocator);
this.left = val.left.deepClone(context.allocator);
this.block_start = null;
this.block_end = null;
this.inline_start = null;
this.inline_end = null;
this.has_any = true;
},
css.PropertyIdTag.unparsed => {
switch (property.unparsed.property_id) {
top_prop, bottom_prop, left_prop, right_prop, block_start_prop, block_end_prop, inline_start_prop, inline_end_prop, block_shorthand, inline_shorthand, shorthand_prop => {
// Even if we weren't able to parse the value (e.g. due to var() references),
// we can still add vendor prefixes to the property itself.
switch (property.unparsed.property_id) {
block_start_prop => this.logicalPropertyHelper("block_start", property.deepClone(context.allocator), dest, context),
block_end_prop => this.logicalPropertyHelper("block_end", property.deepClone(context.allocator), dest, context),
inline_start_prop => this.logicalPropertyHelper("inline_start", property.deepClone(context.allocator), dest, context),
inline_end_prop => this.logicalPropertyHelper("inline_end", property.deepClone(context.allocator), dest, context),
else => {
this.flush(dest, context);
dest.append(context.allocator, property.deepClone(context.allocator)) catch unreachable;
},
}
},
else => return false,
}
},
else => return false,
}
return true;
}
pub fn finalize(this: *@This(), dest: *css.DeclarationList, context: *css.PropertyHandlerContext) void {
this.flush(dest, context);
}
fn flushHelper(
this: *@This(),
comptime field: []const u8,
comptime T: type,
val: *const T,
comptime category: PropertyCategory,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) void {
// If the category changes betweet logical and physical,
// or if the value contains syntax that isn't supported across all targets,
// preserve the previous value as a fallback.
if (category != this.category or (@field(this, field) != null and context.targets.browsers != null and !val.isCompatible(context.targets.browsers.?))) {
this.flush(dest, context);
}
}
fn propertyHelper(
this: *@This(),
comptime field: []const u8,
comptime T: type,
val: *const T,
comptime category: PropertyCategory,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) void {
this.flushHelper(field, T, val, category, dest, context);
@field(this, field) = val.deepClone(context.allocator);
this.category = category;
this.has_any = true;
}
fn logicalPropertyHelper(
this: *@This(),
comptime field: []const u8,
val: css.Property,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) void {
// Assume unparsed properties might contain unsupported syntax that we must preserve as a fallback.
if (this.category != PropertyCategory.logical or (@field(this, field) != null and val == .unparsed)) {
this.flush(dest, context);
}
if (@field(this, field)) |*p| p.deinit(context.allocator);
@field(this, field) = val;
this.category = PropertyCategory.logical;
this.has_any = true;
}
fn flush(this: *@This(), dest: *css.DeclarationList, context: *css.PropertyHandlerContext) void {
if (!this.has_any) return;
this.has_any = false;
const top = bun.take(&this.top);
const bottom = bun.take(&this.bottom);
const left = bun.take(&this.left);
const right = bun.take(&this.right);
const logical_supported = if (comptime shorthand_extra != null) !context.shouldCompileLogical(shorthand_extra.?.feature) else true;
if ((shorthand_category != .logical or logical_supported) and top != null and bottom != null and left != null and right != null) {
dest.append(
context.allocator,
@unionInit(
Property,
@tagName(shorthand_prop),
.{
.top = top.?,
.bottom = bottom.?,
.left = left.?,
.right = right.?,
},
),
) catch |err| bun.handleOom(err);
} else {
if (top) |t| {
dest.append(
context.allocator,
@unionInit(Property, @tagName(top_prop), t),
) catch |err| bun.handleOom(err);
}
if (bottom) |b| {
dest.append(
context.allocator,
@unionInit(Property, @tagName(bottom_prop), b),
) catch |err| bun.handleOom(err);
}
if (left) |b| {
dest.append(
context.allocator,
@unionInit(Property, @tagName(left_prop), b),
) catch |err| bun.handleOom(err);
}
if (right) |b| {
dest.append(
context.allocator,
@unionInit(Property, @tagName(right_prop), b),
) catch |err| bun.handleOom(err);
}
}
var block_start = bun.take(&this.block_start);
var block_end = bun.take(&this.block_end);
var inline_start = bun.take(&this.inline_start);
var inline_end = bun.take(&this.inline_end);
if (logical_supported) {
this.logicalSideHelper(&block_start, &block_end, "block_start", "block_end", block_shorthand, block_start_prop, block_end_prop, logical_supported, dest, context);
} else {
this.prop(&block_start, block_start_prop, top_prop, dest, context);
this.prop(&block_end, block_end_prop, bottom_prop, dest, context);
}
if (logical_supported) {
this.logicalSideHelper(&inline_start, &inline_end, "inline_start", "inline_end", inline_shorthand, inline_start_prop, inline_end_prop, logical_supported, dest, context);
} else if (inline_start != null or inline_end != null) {
if (inline_start != null and inline_start.? == @field(Property, @tagName(inline_start_prop)) and inline_end != null and inline_end.? == @field(Property, @tagName(inline_end_prop)) and
@field(inline_start.?, @tagName(inline_start_prop)).eql(&@field(inline_end.?, @tagName(inline_end_prop))))
{
this.prop(&inline_start, inline_start_prop, left_prop, dest, context);
this.prop(&inline_end, inline_end_prop, right_prop, dest, context);
} else {
this.logicalPropHelper(&inline_start, inline_start_prop, left_prop, right_prop, dest, context);
this.logicalPropHelper(&inline_end, inline_end_prop, right_prop, left_prop, dest, context);
}
}
}
inline fn logicalPropHelper(
this: *@This(),
val: *?Property,
comptime logical: css.PropertyIdTag,
comptime ltr: css.PropertyIdTag,
comptime rtl: css.PropertyIdTag,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) void {
_ = this; // autofix
_ = dest; // autofix
if (val.*) |*_v| {
if (@as(css.PropertyIdTag, _v.*) == logical) {
const v = &@field(_v, @tagName(logical));
context.addLogicalRule(
context.allocator,
@unionInit(Property, @tagName(ltr), v.deepClone(context.allocator)),
@unionInit(Property, @tagName(rtl), v.deepClone(context.allocator)),
);
} else if (_v.* == .unparsed) {
const v = &_v.unparsed;
context.addLogicalRule(
context.allocator,
Property{
.unparsed = v.withPropertyId(context.allocator, ltr),
},
Property{
.unparsed = v.withPropertyId(context.allocator, rtl),
},
);
}
}
}
inline fn logicalSideHelper(
this: *@This(),
start: *?Property,
end: *?Property,
comptime start_name: []const u8,
comptime end_name: []const u8,
comptime shorthand_property: css.PropertyIdTag,
comptime start_prop: css.PropertyIdTag,
comptime end_prop: css.PropertyIdTag,
logical_supported: bool,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) void {
_ = this; // autofix
const shorthand_supported = logical_supported and if (comptime shorthand_extra != null) !context.shouldCompileLogical(shorthand_extra.?.shorthand_feature) else true;
if (start.* != null and @as(PropertyIdTag, start.*.?) == start_prop and
end.* != null and @as(PropertyIdTag, end.*.?) == end_prop and
shorthand_supported)
{
const ValueType = shorthand_property.valueType();
var value: ValueType = undefined;
@field(value, start_name) = @field(start.*.?, @tagName(start_prop)).deepClone(context.allocator);
@field(value, end_name) = @field(end.*.?, @tagName(end_prop)).deepClone(context.allocator);
if (std.meta.fields(ValueType).len != 2) {
@compileError(@typeName(ValueType) ++ " has more than two fields. This could cause undefined memory.");
}
dest.append(context.allocator, @unionInit(
Property,
@tagName(shorthand_property),
value,
)) catch |err| bun.handleOom(err);
} else {
if (start.* != null) {
bun.handleOom(dest.append(context.allocator, start.*.?));
}
if (end.* != null) {
bun.handleOom(dest.append(context.allocator, end.*.?));
}
}
}
inline fn prop(
this: *@This(),
val: *?Property,
comptime logical: css.PropertyIdTag,
comptime physical: css.PropertyIdTag,
dest: *css.DeclarationList,
context: *css.PropertyHandlerContext,
) void {
_ = this; // autofix
if (val.*) |*v| {
if (@as(css.PropertyIdTag, v.*) == logical) {
dest.append(
context.allocator,
@unionInit(
Property,
@tagName(physical),
@field(v, @tagName(logical)),
),
) catch |err| bun.handleOom(err);
} else if (v.* == .unparsed) {
dest.append(
context.allocator,
Property{
.unparsed = v.unparsed.withPropertyId(context.allocator, physical),
},
) catch |err| bun.handleOom(err);
}
}
}
};
}
const bun = @import("bun");
const std = @import("std");
const Allocator = std.mem.Allocator;