commit f4ee9ffe0839d5db2fed0a17a110d38c698fe07f
parent 454911cd834f085439203f6e66a285ceaeeeac40
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date: Fri, 3 Oct 2025 08:41:04 +0000
Bug 1787062 - Unify how <input> and <textarea> get initialized a bit. r=masayuki,smaug
Make the anonymous content creation synchronously so that we don't need
to update layout after-the-fact, and make editor creation lazy like we
do for <input>.
I think we should try to remove the trailing <br> for <textarea> after
this (just so that editor doesn't insert it). But this is green on try
and fixes the issue so worth landing incrementally.
Differential Revision: https://phabricator.services.mozilla.com/D267252
Diffstat:
2 files changed, 54 insertions(+), 11 deletions(-)
diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp
@@ -437,12 +437,7 @@ nsresult nsTextControlFrame::CreateAnonymousContent(
}
bool nsTextControlFrame::ShouldInitializeEagerly() const {
- // textareas are eagerly initialized.
- if (!IsSingleLineTextControl()) {
- return true;
- }
-
- // Also, input elements which have a cached selection should get eager
+ // Input elements which have a cached selection should get eager
// editor initialization.
TextControlElement* textControlElement = ControlElement();
if (textControlElement->HasCachedSelection()) {
@@ -459,6 +454,7 @@ bool nsTextControlFrame::ShouldInitializeEagerly() const {
// If text in the editor is being dragged, we need the editor to create
// new source node for the drag session (TextEditor creates the text node
// in the anonymous <div> element.
+ // XXX UpdateValueDisplay creates it now, is this needed?
if (nsCOMPtr<nsIDragSession> dragSession =
nsContentUtils::GetDragSession(PresContext())) {
if (dragSession->IsDraggingTextInTextControl()) {
@@ -955,7 +951,9 @@ nsresult nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
AttrModType aModType) {
if (aAttribute == nsGkAtoms::value && !mEditorHasBeenInitialized) {
- UpdateValueDisplay(true);
+ if (IsSingleLineTextControl()) {
+ UpdateValueDisplay(true);
+ }
return NS_OK;
}
@@ -1055,10 +1053,6 @@ void nsTextControlFrame::SetInitialChildList(ChildListID aListID,
nsresult nsTextControlFrame::UpdateValueDisplay(bool aNotify,
bool aBeforeEditorInit,
const nsAString* aValue) {
- if (!IsSingleLineTextControl()) { // textareas don't use this
- return NS_OK;
- }
-
MOZ_ASSERT(mRootNode, "Must have a div content\n");
MOZ_ASSERT(!mEditorHasBeenInitialized,
"Do not call this after editor has been initialized");
diff --git a/testing/web-platform/tests/html/rendering/widgets/textarea-scroll-restoration.html b/testing/web-platform/tests/html/rendering/widgets/textarea-scroll-restoration.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test textarea scroll restoration</title>
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element-2">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="container">
+ <textarea id="textarea" rows=3>
+ A
+ B
+ C
+ D
+ E
+ F
+ G
+ H
+ I
+ </textarea>
+</div>
+<script>
+
+function frame() {
+ return new Promise(r => {
+ requestAnimationFrame(() => requestAnimationFrame(r));
+ });
+}
+
+let container = document.getElementById("container");
+let textarea = document.getElementById("textarea");
+
+async function test_restoration() {
+ textarea.scrollTop = 10000;
+ await frame();
+ let top = textarea.scrollTop;
+ assert_not_equals(top, 0, "Should've scrolled down");
+ container.style.display = "inline-block";
+ assert_equals(textarea.scrollTop, top, "Should've preserved the scroll position");
+ container.style.display = "";
+}
+
+promise_test(test_restoration);
+promise_test(async function() {
+ textarea.focus();
+ await frame();
+ await test_restoration();
+}, "after focus");
+</script>