Fix body height invalidation issue killing performance.

This commit is contained in:
Martín Lucas Golini
2026-07-03 13:27:27 -03:00
parent fcef1c2e2c
commit f3e5bee80a
3 changed files with 72 additions and 12 deletions

View File

@@ -291,27 +291,33 @@ Float UIHTMLBody::getLocalMinHeight() const {
return convertLengthAsDp( mMinHeightLocal, parentHeight );
}
void UIHTMLBody::setDocumentContentMinHeight( const Float& height ) {
bool UIHTMLBody::setDocumentContentMinHeight( const Float& height ) {
if ( mDocumentContentMinHeight == height )
return;
return false;
mDocumentContentMinHeight = height;
updateDocumentMinHeight();
return updateDocumentMinHeight();
}
void UIHTMLBody::updateDocumentMinHeight() {
bool UIHTMLBody::updateDocumentMinHeight() {
const Float oldMinHeight = getCurMinSize().getHeight();
Float minHeight =
std::max( { getLocalMinHeight(), mDocumentViewportMinHeight, mDocumentContentMinHeight } );
setMinHeight( minHeight );
bool minHeightChanged = minHeight != oldMinHeight;
bool sizeForced = false;
if ( minHeight < oldMinHeight &&
getPixelsSize().getHeight() > PixelDensity::dpToPx( minHeight ) )
getPixelsSize().getHeight() > PixelDensity::dpToPx( minHeight ) ) {
// Lowering min-height does not shrink the current box by itself. Reapply size through
// min/max fitting so the body can settle at the new floor.
setPixelsSize( { getPixelsSize().getWidth(), 0 } );
sizeForced = true;
}
return minHeightChanged || sizeForced;
}
void UIHTMLBody::updateDocumentContentMinHeightFromChildren() {
bool UIHTMLBody::updateDocumentContentMinHeightFromChildren() {
Float maxH = 0;
Node* child = mChild;
@@ -331,7 +337,7 @@ void UIHTMLBody::updateDocumentContentMinHeightFromChildren() {
child = child->getNextNode();
}
setDocumentContentMinHeight( maxH > 0 ? std::trunc( PixelDensity::pxToDp( maxH ) ) : 0 );
return setDocumentContentMinHeight( maxH > 0 ? std::trunc( PixelDensity::pxToDp( maxH ) ) : 0 );
}
Uint32 UIHTMLBody::onMessage( const NodeMessage* Msg ) {
@@ -340,10 +346,10 @@ Uint32 UIHTMLBody::onMessage( const NodeMessage* Msg ) {
if ( Msg->getMsg() == NodeMessage::LayoutAttributeChange && Msg->getSender() != this &&
!mSettingBodyHeight ) {
mSettingBodyHeight = true;
updateDocumentContentMinHeightFromChildren();
bool documentMinHeightChanged = updateDocumentContentMinHeightFromChildren();
mSettingBodyHeight = false;
if ( getParent() && getParent()->isType( UI_TYPE_HTML_HTML ) )
if ( documentMinHeightChanged && getParent() && getParent()->isType( UI_TYPE_HTML_HTML ) )
getParent()->asType<UIHTMLHtml>()->setLayoutDirty( LayoutInvalidation::Document );
}

View File

@@ -4173,6 +4173,60 @@ UTEST( UIHTML, BodyDocumentContentMinHeightCanShrink ) {
Engine::destroySingleton();
}
UTEST( UIHTML, BodyNoOpContentHeightChangeDoesNotDirtyHtml ) {
auto win = Engine::instance()->createWindow(
WindowSettings( 800, 600, "body no-op content height change", WindowStyle::Default,
WindowBackend::Default, 32, {}, 1, false, true ),
ContextSettings( false, 0, 0, GLv_default, true, false ) );
UISceneNode* sceneNode = init_test_inline_block();
UIWebView* webView = UIWebView::New();
webView->setParent( sceneNode->getRoot() );
webView->setPixelsSize( win->getWidth(), win->getHeight() );
webView->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
std::string html = R"html(
<!doctype html>
<html>
<body style="margin: 0;">
<div id="spacer" style="display: block; width: 100px; height: 900px;"></div>
</body>
</html>
)html";
sceneNode->setURI( "file://body-no-op-content-height-change.html" );
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ),
webView->getDocumentContainer(),
String::hash( "body-no-op-content-height-change" ) );
webView->refreshDocumentLayout();
win->getInput()->update();
SceneManager::instance()->update();
sceneNode->updateDirtyLayouts();
win->getInput()->update();
SceneManager::instance()->update();
sceneNode->updateDirtyLayouts();
auto* htmlNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_HTML )->asType<UILayout>();
auto* body = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY )->asType<UIWidget>();
auto* spacer = sceneNode->getRoot()->find( "spacer" )->asType<UIWidget>();
ASSERT_TRUE( htmlNode != nullptr );
ASSERT_TRUE( body != nullptr );
ASSERT_TRUE( spacer != nullptr );
ASSERT_GT( body->getPixelsSize().getHeight(), 850.f );
ASSERT_FALSE( htmlNode->isLayoutDirty() );
spacer->setPixelsSize(
{ spacer->getPixelsSize().getWidth() + 25.f, spacer->getPixelsSize().getHeight() } );
EXPECT_FALSE( htmlNode->isLayoutDirty() );
win->getInput()->update();
SceneManager::instance()->update();
sceneNode->updateDirtyLayouts();
Engine::destroySingleton();
}
UTEST( UIHTML, DeferredCSSKeepsTableHeightStableAfterViewportResize ) {
std::shared_ptr<ThreadPool> threadPool(
ThreadPool::createShared( eemax<int>( 4, Sys::getCPUCount() ) ) );