[web] Fix framework material text field tests (#25367)

diff --git a/lib/web_ui/lib/src/engine/text/layout_service.dart b/lib/web_ui/lib/src/engine/text/layout_service.dart
index 52e7aa0..2ef23dc 100644
--- a/lib/web_ui/lib/src/engine/text/layout_service.dart
+++ b/lib/web_ui/lib/src/engine/text/layout_service.dart
@@ -298,19 +298,6 @@
     // it possible to do hit testing. Once we find the box, we look inside that
     // box to find where exactly the `offset` is located.
 
-    // [offset] is above all the lines.
-    if (offset.dy < 0) {
-      return ui.TextPosition(offset: 0, affinity: ui.TextAffinity.downstream);
-    }
-
-    // [offset] is below all the lines.
-    if (offset.dy >= paragraph.height) {
-      return ui.TextPosition(
-        offset: paragraph.toPlainText().length,
-        affinity: ui.TextAffinity.upstream,
-      );
-    }
-
     final EngineLineMetrics line = _findLineForY(offset.dy);
     // [offset] is to the left of the line.
     if (offset.dx <= line.left) {
@@ -773,13 +760,6 @@
 
   /// Extends the line by setting a [newEnd].
   void extendTo(LineBreakResult newEnd) {
-    // If the current end of the line is a hard break, the line shouldn't be
-    // extended any further.
-    assert(
-      isEmpty || !end.isHard || _isLastBoxAPlaceholder,
-      'Cannot extend a line that ends with a hard break.',
-    );
-
     ascent = math.max(ascent, spanometer.ascent);
     descent = math.max(descent, spanometer.descent);
 
diff --git a/lib/web_ui/test/text/canvas_paragraph_test.dart b/lib/web_ui/test/text/canvas_paragraph_test.dart
index d1f5d2b..8687fa9 100644
--- a/lib/web_ui/test/text/canvas_paragraph_test.dart
+++ b/lib/web_ui/test/text/canvas_paragraph_test.dart
@@ -320,7 +320,7 @@
       })
         ..layout(constrain(double.infinity));
 
-      // Above the line.
+      // Above the line, at the beginning.
       expect(
         paragraph.getPositionForOffset(ui.Offset(0, -5)),
         pos(0, ui.TextAffinity.downstream),
@@ -335,9 +335,9 @@
         paragraph.getPositionForOffset(ui.Offset(0, 5)),
         pos(0, ui.TextAffinity.downstream),
       );
-      // Below the line.
+      // Below the line, at the end.
       expect(
-        paragraph.getPositionForOffset(ui.Offset(0, 12)),
+        paragraph.getPositionForOffset(ui.Offset(130, 12)),
         pos(13, ui.TextAffinity.upstream),
       );
       // At the end of the line.
@@ -350,6 +350,16 @@
         paragraph.getPositionForOffset(ui.Offset(74, 5)),
         pos(7, ui.TextAffinity.downstream),
       );
+      // On the left half of "p" in "ipsum" (above the line).
+      expect(
+        paragraph.getPositionForOffset(ui.Offset(74, -5)),
+        pos(7, ui.TextAffinity.downstream),
+      );
+      // On the left half of "p" in "ipsum" (below the line).
+      expect(
+        paragraph.getPositionForOffset(ui.Offset(74, 15)),
+        pos(7, ui.TextAffinity.downstream),
+      );
       // On the right half of "p" in "ipsum".
       expect(
         paragraph.getPositionForOffset(ui.Offset(76, 5)),
@@ -378,7 +388,7 @@
       //   "ipsum "
       //   "dolor sit"
 
-      // Above the first line.
+      // Above the first line, at the beginning.
       expect(
         paragraph.getPositionForOffset(ui.Offset(0, -5)),
         pos(0, ui.TextAffinity.downstream),
@@ -398,6 +408,11 @@
         paragraph.getPositionForOffset(ui.Offset(60, 5)),
         pos(6, ui.TextAffinity.upstream),
       );
+      // At the end of the first line (above the line).
+      expect(
+        paragraph.getPositionForOffset(ui.Offset(60, -5)),
+        pos(6, ui.TextAffinity.upstream),
+      );
       // After the end of the first line to the right.
       expect(
         paragraph.getPositionForOffset(ui.Offset(70, 5)),
@@ -430,9 +445,9 @@
         pos(12, ui.TextAffinity.upstream),
       );
 
-      // Below the third line "dolor sit".
+      // Below the third line "dolor sit", at the end.
       expect(
-        paragraph.getPositionForOffset(ui.Offset(0, 40)),
+        paragraph.getPositionForOffset(ui.Offset(90, 40)),
         pos(21, ui.TextAffinity.upstream),
       );
       // At the end of the third line.
@@ -473,7 +488,7 @@
       //   "ipsum "
       //   "dolor sit"
 
-      // Above the first line.
+      // Above the first line, at the beginning.
       expect(
         paragraph.getPositionForOffset(ui.Offset(0, -5)),
         pos(0, ui.TextAffinity.downstream),
@@ -520,9 +535,9 @@
         pos(12, ui.TextAffinity.upstream),
       );
 
-      // Below the third line "dolor sit".
+      // Below the third line "dolor sit", at the end.
       expect(
-        paragraph.getPositionForOffset(ui.Offset(0, 40)),
+        paragraph.getPositionForOffset(ui.Offset(90, 40)),
         pos(21, ui.TextAffinity.upstream),
       );
       // At the end of the third line.
diff --git a/lib/web_ui/test/text/layout_service_rich_test.dart b/lib/web_ui/test/text/layout_service_rich_test.dart
index 8ed1609..3bcd3e6 100644
--- a/lib/web_ui/test/text/layout_service_rich_test.dart
+++ b/lib/web_ui/test/text/layout_service_rich_test.dart
@@ -42,6 +42,21 @@
 void testMain() async {
   await ui.webOnlyInitializeTestDomRenderer();
 
+  test('does not crash on empty spans', () {
+    final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
+      builder.pushStyle(EngineTextStyle.only(color: blue));
+      builder.addText('');
+
+      builder.pushStyle(EngineTextStyle.only(color: red));
+      builder.addText('Lorem ipsum');
+
+      builder.pushStyle(EngineTextStyle.only(color: green));
+      builder.addText('');
+    });
+
+    expect(() => paragraph.layout(constrain(double.infinity)), returnsNormally);
+  });
+
   test('measures spans in the same line correctly', () {
     final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
       builder.pushStyle(EngineTextStyle.only(fontSize: 12.0));