Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ describe('flex-grow column chat layout (issue #520)', () => {

await snapshot();

const lastItem = list.querySelectorAll('.list-item')[13] as HTMLElement;
const listRect = list.getBoundingClientRect();
const lastRect = lastItem.getBoundingClientRect();
const itemStyle = getComputedStyle(lastItem);
const listStyle = getComputedStyle(list);
const bottomOverflowAtTop = lastRect.bottom - listRect.bottom;
const expectedScrollGap =
bottomOverflowAtTop +
parseFloat(itemStyle.marginBottom || '0') +
parseFloat(listStyle.paddingBottom || '0');

// Programmatic assertions to ensure expected layout behavior
const chatH = chat.offsetHeight;
const headerH = header.offsetHeight;
Expand All @@ -119,6 +130,9 @@ describe('flex-grow column chat layout (issue #520)', () => {
expect(firstItem.offsetHeight).toBe(secondItem.offsetHeight);
// Items should not be stretched to container height
expect(firstItem.offsetHeight).toBeLessThan(listH);
// Centered overflow should not leave extra blank scroll range after the
// last item's margin box and the container's bottom padding.
expect(Math.round(list.scrollHeight - list.clientHeight))
.toBe(Math.round(expectedScrollGap));
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('relayout-align', () => {
class: 'flexbox align-items-flex-start',
style: {
display: 'flex',
'-webkit-align-items': 'flex-start',
'align-items': 'flex-start',
height: '100px',
position: 'relative',
'box-sizing': 'border-box',
Expand All @@ -37,7 +37,6 @@ describe('relayout-align', () => {
'data-offset-y': '0',
class: 'align-self-auto',
style: {
'-webkit-align-self': 'auto',
'align-self': 'auto',
border: '5px solid green',
width: '50px',
Expand All @@ -49,7 +48,6 @@ describe('relayout-align', () => {
'data-offset-y': '0',
class: 'align-self-flex-start',
style: {
'-webkit-align-self': 'flex-start',
'align-self': 'flex-start',
border: '5px solid green',
width: '50px',
Expand All @@ -61,7 +59,6 @@ describe('relayout-align', () => {
'data-offset-y': '90',
class: 'align-self-flex-end',
style: {
'-webkit-align-self': 'flex-end',
'align-self': 'flex-end',
border: '5px solid green',
width: '50px',
Expand All @@ -73,7 +70,6 @@ describe('relayout-align', () => {
'data-offset-y': '45',
class: 'align-self-center',
style: {
'-webkit-align-self': 'center',
'align-self': 'center',
border: '5px solid green',
width: '50px',
Expand All @@ -85,7 +81,6 @@ describe('relayout-align', () => {
'data-offset-y': '0',
class: 'align-self-baseline',
style: {
'-webkit-align-self': 'baseline',
'align-self': 'baseline',
border: '5px solid green',
width: '50px',
Expand All @@ -97,7 +92,6 @@ describe('relayout-align', () => {
'data-offset-y': '0',
class: 'align-self-stretch',
style: {
'-webkit-align-self': 'stretch',
'align-self': 'stretch',
border: '5px solid green',
width: '50px',
Expand Down
1 change: 1 addition & 0 deletions webf/lib/foundation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export 'src/foundation/logger.dart';
export 'src/foundation/form_data/form_data.dart';
export 'src/foundation/loading_state_registry.dart';
export 'src/foundation/debug_flags.dart';
export 'src/foundation/perf_debug_identity.dart';
export 'src/foundation/positioned_layout_logging.dart';
export 'src/foundation/flex_layout_logging.dart';
export 'src/foundation/widget_logging.dart';
Expand Down
12 changes: 12 additions & 0 deletions webf/lib/src/css/border.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ mixin CSSBorderMixin on RenderStyle {
set borderTopWidth(CSSLengthValue? value) {
if (value == _borderTopWidth) return;
_borderTopWidth = value;
markNeedsIntrinsicMeasurement('borderWidth');
markNeedsLayout();
resetBoxDecoration();
}
Expand All @@ -170,6 +171,7 @@ mixin CSSBorderMixin on RenderStyle {
set borderRightWidth(CSSLengthValue? value) {
if (value == _borderRightWidth) return;
_borderRightWidth = value;
markNeedsIntrinsicMeasurement('borderWidth');
markNeedsLayout();
resetBoxDecoration();
}
Expand All @@ -186,6 +188,7 @@ mixin CSSBorderMixin on RenderStyle {
set borderBottomWidth(CSSLengthValue? value) {
if (value == _borderBottomWidth) return;
_borderBottomWidth = value;
markNeedsIntrinsicMeasurement('borderWidth');
markNeedsLayout();
resetBoxDecoration();
}
Expand All @@ -202,6 +205,7 @@ mixin CSSBorderMixin on RenderStyle {
set borderLeftWidth(CSSLengthValue? value) {
if (value == _borderLeftWidth) return;
_borderLeftWidth = value;
markNeedsIntrinsicMeasurement('borderWidth');
markNeedsLayout();
resetBoxDecoration();
}
Expand Down Expand Up @@ -266,6 +270,8 @@ mixin CSSBorderMixin on RenderStyle {
set borderTopStyle(CSSBorderStyleType? value) {
if (value == _borderTopStyle) return;
_borderTopStyle = value;
markNeedsIntrinsicMeasurement('borderStyle');
markNeedsLayout();
markNeedsPaint();
resetBoxDecoration();
}
Expand All @@ -277,6 +283,8 @@ mixin CSSBorderMixin on RenderStyle {
set borderRightStyle(CSSBorderStyleType? value) {
if (value == _borderRightStyle) return;
_borderRightStyle = value;
markNeedsIntrinsicMeasurement('borderStyle');
markNeedsLayout();
markNeedsPaint();
resetBoxDecoration();
}
Expand All @@ -288,6 +296,8 @@ mixin CSSBorderMixin on RenderStyle {
set borderBottomStyle(CSSBorderStyleType? value) {
if (value == _borderBottomStyle) return;
_borderBottomStyle = value;
markNeedsIntrinsicMeasurement('borderStyle');
markNeedsLayout();
markNeedsPaint();
resetBoxDecoration();
}
Expand All @@ -299,6 +309,8 @@ mixin CSSBorderMixin on RenderStyle {
set borderLeftStyle(CSSBorderStyleType? value) {
if (value == _borderLeftStyle) return;
_borderLeftStyle = value;
markNeedsIntrinsicMeasurement('borderStyle');
markNeedsLayout();
markNeedsPaint();
resetBoxDecoration();
}
Expand Down
1 change: 1 addition & 0 deletions webf/lib/src/css/display.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mixin CSSDisplayMixin on RenderStyle {
set display(CSSDisplay? value) {
if (_display != value) {
_display = value;
markNeedsIntrinsicMeasurement('display');
markNeedsLayout();
// CSS display affects accessibility visibility (e.g., display:none)
attachedRenderBoxModel?.markNeedsSemanticsUpdate();
Expand Down
5 changes: 5 additions & 0 deletions webf/lib/src/css/font_face.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart';
import 'package:webf/css.dart';
import 'package:webf/foundation.dart';
import 'package:webf/launcher.dart';
import 'package:webf/rendering.dart';
import 'dart:convert';
import 'package:webf/src/foundation/logger.dart';

Expand Down Expand Up @@ -72,6 +73,9 @@ class CSSFontFace {

static void _markRenderSubtreeNeedsLayout(RenderObject root) {
root.visitChildren(_markRenderSubtreeNeedsLayout);
if (root is RenderBoxModel) {
root.markNeedsIntrinsicMeasurementUpdate('fontFaceSubtree');
}
root.markNeedsLayout();
root.markNeedsPaint();
}
Expand Down Expand Up @@ -357,6 +361,7 @@ class CSSFontFace {
} finally {
// Remove from loading map when done
_loadingFonts.remove(descriptorKey);
renderStyle.markNeedsIntrinsicMeasurement('fontFaceLoad');
renderStyle.markNeedsLayout();
}
}
Expand Down
1 change: 1 addition & 0 deletions webf/lib/src/css/padding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ mixin CSSPaddingMixin on RenderStyle {
CSSLengthValue get paddingTop => _normalizePaddingLength(_paddingTop) ?? CSSLengthValue.zero;

void _markSelfAndParentNeedsLayout() {
markNeedsIntrinsicMeasurement('padding');
markNeedsLayout();
// Sizing may affect parent size, mark parent as needsLayout in case
// renderBoxModel has tight constraints which will prevent parent from marking.
Expand Down
8 changes: 8 additions & 0 deletions webf/lib/src/css/render_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,14 @@ abstract class RenderStyle extends DiagnosticableTree with Diagnosticable {
widgetRenderBox?.clearIntersectionChangeListeners();
}

@pragma('vm:prefer-inline')
void markNeedsIntrinsicMeasurement([String reason = 'renderStyle']) {
everyAttachedWidgetRenderBox((element, renderObject) {
renderObject.markNeedsIntrinsicMeasurementUpdate(reason);
return true;
});
}

@pragma('vm:prefer-inline')
void markNeedsLayout() {
everyAttachedWidgetRenderBox((element, renderObject) {
Expand Down
1 change: 1 addition & 0 deletions webf/lib/src/css/sizing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ mixin CSSSizingMixin on RenderStyle {

void _markSelfAndParentNeedsLayout() {
if (!hasRenderBox()) return;
markNeedsIntrinsicMeasurement('sizing');
markNeedsLayout();

// Sizing may affect parent size, mark parent as needsLayout in case
Expand Down
8 changes: 7 additions & 1 deletion webf/lib/src/css/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ mixin CSSTextMixin on RenderStyle {

void updateFontRelativeLength() {
if (_fontRelativeProperties.isEmpty) return;
markNeedsIntrinsicMeasurement('fontRelativeLength');
markNeedsLayout();
if (isSelfBoxModelSizeTight()) {
markParentNeedsLayout();
Expand All @@ -278,6 +279,7 @@ mixin CSSTextMixin on RenderStyle {

void updateRootFontRelativeLength() {
if (_rootFontRelativeProperties.isEmpty) return;
markNeedsIntrinsicMeasurement('rootFontRelativeLength');
markNeedsLayout();
if (isSelfBoxModelSizeTight()) {
markParentNeedsLayout();
Expand Down Expand Up @@ -597,6 +599,7 @@ mixin CSSTextMixin on RenderStyle {
// text and layout (line-height, white-space) changes.
void _markNestChildrenTextAndLayoutNeedsLayout(RenderStyle renderStyle, String styleProperty) {
if (renderStyle.isSelfRenderLayoutBox()) {
renderStyle.markNeedsIntrinsicMeasurement('textLayout:$styleProperty');
renderStyle.markNeedsLayout();

visitor(RenderObject child) {
Expand All @@ -620,6 +623,7 @@ mixin CSSTextMixin on RenderStyle {
void _markTextNeedsLayout() {
visitor(RenderObject child) {
if (child is RenderTextBox) {
child.renderStyle.markNeedsIntrinsicMeasurement('textDirect');
child.renderStyle.markNeedsLayout();
} else {
child.visitChildren(visitor);
Expand All @@ -635,7 +639,9 @@ mixin CSSTextMixin on RenderStyle {
void _markChildrenTextNeedsLayout(RenderStyle renderStyle, String styleProperty) {
visitor(dom.Node child) {
if (child is dom.TextNode) {
child.parentElement!.attachedRenderer?.markNeedsLayout();
final RenderStyle parentStyle = child.parentElement!.renderStyle;
parentStyle.markNeedsIntrinsicMeasurement('textInherited:$styleProperty');
parentStyle.markNeedsLayout();
}

if (child is dom.Element && child.style[styleProperty].isEmpty) {
Expand Down
14 changes: 14 additions & 0 deletions webf/lib/src/foundation/debug_flags.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ class DebugFlags {
static int cssGridProfilingMinMs = 2;

// Removed: Use FlexLog filters to enable flex logs.
static bool enableFlexFastPathProfiling =
const bool.fromEnvironment('WEBF_DEBUG_FLEX_FAST_PATH', defaultValue: false);
static int flexFastPathProfilingSummaryEvery =
const int.fromEnvironment('WEBF_DEBUG_FLEX_FAST_PATH_SUMMARY_EVERY', defaultValue: 50);
static int flexFastPathProfilingMaxDetailLogs =
const int.fromEnvironment('WEBF_DEBUG_FLEX_FAST_PATH_MAX_DETAIL_LOGS', defaultValue: 20);
static bool enableFlexAnonymousMetricsProfiling =
const bool.fromEnvironment('WEBF_DEBUG_FLEX_ANON_METRICS', defaultValue: false);
static int flexAnonymousMetricsProfilingSummaryEvery =
const int.fromEnvironment('WEBF_DEBUG_FLEX_ANON_METRICS_SUMMARY_EVERY', defaultValue: 50);
static int flexAnonymousMetricsProfilingMaxDetailLogs =
const int.fromEnvironment('WEBF_DEBUG_FLEX_ANON_METRICS_MAX_DETAIL_LOGS', defaultValue: 20);
static String flexAnonymousMetricsProfilingWatchedPathContains =
const String.fromEnvironment('WEBF_DEBUG_FLEX_ANON_METRICS_WATCH_PATH', defaultValue: '');

/// Debug flag to enable inline layout visualization.
/// When true, paints debug information for line boxes, margins, padding, etc.
Expand Down
Loading
Loading