Commit aa418844 authored by Adam Barth's avatar Adam Barth

Delete OldTextPainter

Also, delete all references to DOM APIs.
parent 739fda1a
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'dart:typed_data';
void drawText(ui.Canvas canvas, String lh) {
ui.Paint paint = new ui.Paint();
// offset down
canvas.translate(0.0, 100.0);
// set up the text
ui.Document document = new ui.Document();
ui.Text arabic = document.createText("مرحبا");
ui.Text english = document.createText(" Hello");
ui.Element block = document.createElement('div');
block.style['display'] = 'paragraph';
block.style['font-family'] = 'monospace';
block.style['font-size'] = '50px';
block.style['line-height'] = lh;
block.style['color'] = '#0000A0';
block.appendChild(arabic);
block.appendChild(english);
ui.LayoutRoot layoutRoot = new ui.LayoutRoot();
layoutRoot.rootElement = block;
layoutRoot.maxWidth = ui.view.width - 20.0; // you need to set a width for this to paint
layoutRoot.layout();
// draw a line at the text's baseline
ui.Path path = new ui.Path();
path.moveTo(0.0, 0.0);
path.lineTo(block.maxContentWidth, 0.0);
path.moveTo(0.0, block.alphabeticBaseline);
path.lineTo(block.maxContentWidth, block.alphabeticBaseline);
path.moveTo(0.0, block.height);
path.lineTo(block.maxContentWidth, block.height);
paint
..color = const ui.Color(0xFFFF9000)
..style = ui.PaintingStyle.stroke
..strokeWidth = 3.0;
canvas.drawPath(path, paint);
// paint the text
layoutRoot.paint(canvas);
}
ui.Picture paint(ui.Rect paintBounds) {
ui.PictureRecorder recorder = new ui.PictureRecorder();
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
ui.Paint paint = new ui.Paint()
..color = const ui.Color(0xFFFFFFFF)
..style = ui.PaintingStyle.fill;
canvas.drawRect(new ui.Rect.fromLTRB(0.0, 0.0, ui.view.width, ui.view.height), paint);
canvas.translate(10.0, 0.0);
drawText(canvas, '1.0');
drawText(canvas, 'lh');
return recorder.endRecording();
}
ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) {
final double devicePixelRatio = ui.view.devicePixelRatio;
ui.Rect sceneBounds = new ui.Rect.fromLTWH(0.0, 0.0, ui.view.width * devicePixelRatio, ui.view.height * devicePixelRatio);
Float64List deviceTransform = new Float64List(16)
..[0] = devicePixelRatio
..[5] = devicePixelRatio
..[10] = 1.0
..[15] = 1.0;
ui.SceneBuilder sceneBuilder = new ui.SceneBuilder(sceneBounds)
..pushTransform(deviceTransform)
..addPicture(ui.Offset.zero, picture, paintBounds)
..pop();
return sceneBuilder.build();
}
void beginFrame(double timeStamp) {
ui.Rect paintBounds = new ui.Rect.fromLTWH(0.0, 0.0, ui.view.width, ui.view.height);
ui.Picture picture = paint(paintBounds);
ui.Scene scene = composite(picture, paintBounds);
ui.view.scene = scene;
}
void main() {
ui.view.setFrameCallback(beginFrame);
ui.view.scheduleFrame();
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'dart:typed_data';
const kMaxIterations = 100;
int peakCount = 1000; // this is the number that must be reached for us to start reporting the peak number of nodes in the tree each frame
void report(String s) {
// uncomment this if you want to see what the mutations are
// print("$s\n");
}
ui.LayoutRoot layoutRoot = new ui.LayoutRoot();
ui.Document document = new ui.Document();
ui.Element root;
math.Random random = new math.Random();
bool pickThis(double odds) {
return random.nextDouble() < odds;
}
String generateCharacter(int min, int max) {
String result = new String.fromCharCode(random.nextInt(max)+min);
report("generated character: '$result'");
return result;
}
String colorToCSSString(ui.Color color) {
return 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})';
}
void mutate(ui.Canvas canvas) {
// mutate the DOM randomly
int iterationsLeft = kMaxIterations;
ui.Node node = root;
ui.Node other = null;
while (node != null && iterationsLeft > 0) {
iterationsLeft -= 1;
if (node is ui.Element && pickThis(0.4)) {
node = (node as ui.Element).firstChild;
} else if (node.nextSibling != null && pickThis(0.5)) {
node = node.nextSibling;
} else if (other == null && node != root && pickThis(0.1)) {
other = node;
node = root;
} else if (node != root && other != null && pickThis(0.1)) {
report("insertBefore()");
try {
node.insertBefore([other]);
} catch (_) {
}
break;
} else if (node != root && pickThis(0.001)) {
report("remove()");
node.remove();
} else if (node is ui.Element) {
if (pickThis(0.1)) {
report("appending a new text node (ASCII)");
node.appendChild(document.createText(generateCharacter(0x20, 0x7F)));
break;
} else if (pickThis(0.05)) {
report("appending a new text node (Latin1)");
node.appendChild(document.createText(generateCharacter(0x20, 0xFF)));
break;
} else if (pickThis(0.025)) {
report("appending a new text node (BMP)");
node.appendChild(document.createText(generateCharacter(0x20, 0xFFFF)));
break;
} else if (pickThis(0.0125)) {
report("appending a new text node (Unicode)");
node.appendChild(document.createText(generateCharacter(0x20, 0x10FFFF)));
break;
} else if (pickThis(0.1)) {
report("appending a new Element");
node.appendChild(document.createElement('t'));
break;
} else if (pickThis(0.1)) {
report("styling: color");
node.style['color'] = colorToCSSString(new ui.Color(random.nextInt(0xFFFFFFFF) | 0xC0808080));
break;
} else if (pickThis(0.1)) {
report("styling: font-size");
node.style['font-size'] = "${random.nextDouble() * 48.0}px";
break;
} else if (pickThis(0.2)) {
report("styling: font-weight");
node.style['font-weight'] = "${random.nextInt(8)+1}00";
break;
} else if (pickThis(0.1)) {
report("styling: line-height, dynamic");
node.style['line-height'] = "${random.nextDouble()*1.5}";
break;
} else if (pickThis(0.001)) {
report("styling: line-height, fixed");
node.style['line-height'] = "${random.nextDouble()*1.5}px";
break;
} else if (pickThis(0.025)) {
report("styling: font-style italic");
node.style['font-style'] = "italic";
break;
} else if (pickThis(0.05)) {
report("styling: font-style normal");
node.style['font-style'] = "normal";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration");
node.style['text-decoration-line'] = "none";
break;
} else if (pickThis(0.01)) {
report("styling: text-decoration");
node.style['text-decoration-line'] = "underline";
break;
} else if (pickThis(0.01)) {
report("styling: text-decoration");
node.style['text-decoration-line'] = "overline";
break;
} else if (pickThis(0.01)) {
report("styling: text-decoration");
node.style['text-decoration-line'] = "underline overline";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-style: inherit");
node.style['text-decoration-style'] = "inherit";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-style: solid");
node.style['text-decoration-style'] = "solid";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-style: double");
node.style['text-decoration-style'] = "double";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-style: dotted");
node.style['text-decoration-style'] = "dotted";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-style: dashed");
node.style['text-decoration-style'] = "dashed";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-style: wavy");
node.style['text-decoration-style'] = "wavy";
break;
} else if (pickThis(0.1)) {
report("styling: text-decoration-color");
node.style['text-decoration-color'] = colorToCSSString(new ui.Color(random.nextInt(0xFFFFFFFF)));
break;
}
} else {
assert(node is ui.Text);
final ui.Text text = node;
if (pickThis(0.1)) {
report("appending a new text node (ASCII)");
text.appendData(generateCharacter(0x20, 0x7F));
break;
} else if (pickThis(0.05)) {
report("appending a new text node (Latin1)");
text.appendData(generateCharacter(0x20, 0xFF));
break;
} else if (pickThis(0.025)) {
report("appending a new text node (BMP)");
text.appendData(generateCharacter(0x20, 0xFFFF));
break;
} else if (pickThis(0.0125)) {
report("appending a new text node (Unicode)");
text.appendData(generateCharacter(0x20, 0x10FFFF));
break;
} else if (text.length > 1 && pickThis(0.1)) {
report("deleting character from Text node");
text.deleteData(random.nextInt(text.length), 1);
break;
}
}
}
report("counting...");
node = root;
int count = 1;
while (node != null) {
if (node is ui.Element && node.firstChild != null) {
node = (node as ui.Element).firstChild;
count += 1;
} else {
while (node != null && node.nextSibling == null)
node = node.parentNode;
if (node != null && node.nextSibling != null) {
node = node.nextSibling;
count += 1;
}
}
}
report("node count: $count\r");
if (count > peakCount) {
peakCount = count;
print("peak node count so far: $count\r");
}
// draw the result
report("recording...");
layoutRoot.maxWidth = ui.view.width;
layoutRoot.layout();
layoutRoot.paint(canvas);
report("painting...");
}
ui.Picture paint(ui.Rect paintBounds) {
ui.PictureRecorder recorder = new ui.PictureRecorder();
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
mutate(canvas);
return recorder.endRecording();
}
ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) {
final double devicePixelRatio = ui.view.devicePixelRatio;
ui.Rect sceneBounds = new ui.Rect.fromLTWH(0.0, 0.0, ui.view.width * devicePixelRatio, ui.view.height * devicePixelRatio);
Float64List deviceTransform = new Float64List(16)
..[0] = devicePixelRatio
..[5] = devicePixelRatio
..[10] = 1.0
..[15] = 1.0;
ui.SceneBuilder sceneBuilder = new ui.SceneBuilder(sceneBounds)
..pushTransform(deviceTransform)
..addPicture(ui.Offset.zero, picture, paintBounds)
..pop();
return sceneBuilder.build();
}
void beginFrame(double timeStamp) {
ui.Rect paintBounds = new ui.Rect.fromLTWH(0.0, 0.0, ui.view.width, ui.view.height);
ui.Picture picture = paint(paintBounds);
ui.Scene scene = composite(picture, paintBounds);
ui.view.scene = scene;
ui.view.scheduleFrame();
}
void main() {
root = document.createElement('p');
root.style['display'] = 'paragraph';
root.style['color'] = '#FFFFFF';
layoutRoot.rootElement = root;
ui.view.setFrameCallback(beginFrame);
ui.view.scheduleFrame();
}
......@@ -7,7 +7,7 @@ import 'dart:ui' as ui;
import 'dart:typed_data';
double timeBase = null;
ui.LayoutRoot layoutRoot = new ui.LayoutRoot();
ui.Paragraph paragraph;
ui.Picture paint(ui.Rect paintBounds, double delta) {
ui.PictureRecorder recorder = new ui.PictureRecorder();
......@@ -19,11 +19,11 @@ ui.Picture paint(ui.Rect paintBounds, double delta) {
new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0));
double sin = math.sin(delta / 200);
layoutRoot.maxWidth = 150.0 + (50 * sin);
layoutRoot.layout();
paragraph.maxWidth = 150.0 + (50 * sin);
paragraph.layout();
canvas.translate(layoutRoot.maxWidth / -2.0, (layoutRoot.maxWidth / 2.0) - 125);
layoutRoot.paint(canvas);
canvas.translate(paragraph.maxWidth / -2.0, (paragraph.maxWidth / 2.0) - 125);
paragraph.paint(canvas, ui.Offset.zero);
return recorder.endRecording();
}
......@@ -55,18 +55,13 @@ void beginFrame(double timeStamp) {
}
void main() {
var document = new ui.Document();
var arabic = document.createText("هذا هو قليلا طويلة من النص الذي يجب التفاف .");
var more = document.createText(" و أكثر قليلا لجعله أطول. ");
var block = document.createElement('p');
block.style['display'] = 'paragraph';
block.style['direction'] = 'rtl';
block.style['unicode-bidi'] = 'plaintext';
block.style['color'] = 'black';
block.appendChild(arabic);
block.appendChild(more);
layoutRoot.rootElement = block;
// TODO(abarth): We're missing some bidi style information:
// block.style['direction'] = 'rtl';
// block.style['unicode-bidi'] = 'plaintext';
ui.ParagraphBuilder builder = new ui.ParagraphBuilder();
builder.addText("هذا هو قليلا طويلة من النص الذي يجب التفاف .");
builder.addText(" و أكثر قليلا لجعله أطول. ");
paragraph = builder.build(new ui.ParagraphStyle());
ui.view.setFrameCallback(beginFrame);
ui.view.scheduleFrame();
......
......@@ -10,12 +10,7 @@ import 'text_style.dart';
/// An immutable span of text
abstract class TextSpan {
// This class must be immutable, because we won't notice when it changes
ui.Node _toDOM(ui.Document owner);
String toString([String prefix = '']);
void _applyStyleToContainer(ui.Element container) {
}
void build(ui.ParagraphBuilder builder);
ui.ParagraphStyle get paragraphStyle => null;
}
......@@ -29,10 +24,6 @@ class PlainTextSpan extends TextSpan {
/// The text contained in the span
final String text;
ui.Node _toDOM(ui.Document owner) {
return owner.createText(text);
}
void build(ui.ParagraphBuilder builder) {
builder.addText(text);
}
......@@ -62,15 +53,6 @@ class StyledTextSpan extends TextSpan {
/// The children to which the style is applied
final List<TextSpan> children;
ui.Node _toDOM(ui.Document owner) {
ui.Element parent = owner.createElement('t');
style.applyToCSSStyle(parent.style);
for (TextSpan child in children) {
parent.appendChild(child._toDOM(owner));
}
return parent;
}
void build(ui.ParagraphBuilder builder) {
builder.pushStyle(style.textStyle);
for (TextSpan child in children)
......@@ -80,10 +62,6 @@ class StyledTextSpan extends TextSpan {
ui.ParagraphStyle get paragraphStyle => style.paragraphStyle;
void _applyStyleToContainer(ui.Element container) {
style.applyToContainerCSSStyle(container.style);
}
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
......@@ -119,181 +97,9 @@ class StyledTextSpan extends TextSpan {
}
}
const bool _kEnableNewTextPainter = true;
abstract class TextPainter {
factory TextPainter(TextSpan text) {
if (_kEnableNewTextPainter)
return new _NewTextPainter(text);
return new _OldTextPainter(text);
}
/// The (potentially styled) text to paint
TextSpan get text;
void set text(TextSpan value);
/// The minimum width at which to layout the text
double get minWidth;
void set minWidth(double value);
/// The maximum width at which to layout the text
double get maxWidth;
void set maxWidth(double value);
/// The minimum height at which to layout the text
double get minHeight;
void set minHeight(double value);
/// The maximum height at which to layout the text
double get maxHeight;
void set maxHeight(double value);
/// The width at which decreasing the width of the text would prevent it from
/// painting itself completely within its bounds
double get minContentWidth;
/// The width at which increasing the width of the text no longer decreases
/// the height
double get maxContentWidth;
/// The height required to paint the text completely within its bounds
double get height;
/// The distance from the top of the text to the first baseline of the given
/// type
double computeDistanceToActualBaseline(TextBaseline baseline);
/// Compute the visual position of the glyphs for painting the text
void layout();
/// Paint the text onto the given canvas at the given offset
void paint(ui.Canvas canvas, ui.Offset offset);
}
/// An object that paints a [TextSpan] into a canvas
class _OldTextPainter implements TextPainter {
_OldTextPainter(TextSpan text) {
_layoutRoot.rootElement = _document.createElement('p');
assert(text != null);
this.text = text;
}
ui.Paragraph _paragraph;
final ui.Document _document = new ui.Document();
final ui.LayoutRoot _layoutRoot = new ui.LayoutRoot();
bool _needsLayout = true;
TextSpan _text;
/// The (potentially styled) text to paint
TextSpan get text => _text;
void set text(TextSpan value) {
if (_text == value)
return;
_text = value;
_layoutRoot.rootElement.setChild(_text._toDOM(_document));
_layoutRoot.rootElement.removeAttribute('style');
_text._applyStyleToContainer(_layoutRoot.rootElement);
_needsLayout = true;
}
/// The minimum width at which to layout the text
double get minWidth => _layoutRoot.minWidth;
void set minWidth(value) {
if (_layoutRoot.minWidth == value)
return;
_layoutRoot.minWidth = value;
_needsLayout = true;
}
/// The maximum width at which to layout the text
double get maxWidth => _layoutRoot.maxWidth;
void set maxWidth(value) {
if (_layoutRoot.maxWidth == value)
return;
_layoutRoot.maxWidth = value;
_needsLayout = true;
}
/// The minimum height at which to layout the text
double get minHeight => _layoutRoot.minHeight;
void set minHeight(value) {
if (_layoutRoot.minHeight == value)
return;
_layoutRoot.minHeight = value;
_needsLayout = true;
}
/// The maximum height at which to layout the text
double get maxHeight => _layoutRoot.maxHeight;
void set maxHeight(value) {
if (_layoutRoot.maxHeight == value)
return;
_layoutRoot.maxHeight = value;
}
// Unfortunately, using full precision floating point here causes bad layouts
// because floating point math isn't associative. If we add and subtract
// padding, for example, we'll get different values when we estimate sizes and
// when we actually compute layout because the operations will end up associated
// differently. To work around this problem for now, we round fractional pixel
// values up to the nearest whole pixel value. The right long-term fix is to do
// layout using fixed precision arithmetic.
double _applyFloatingPointHack(double layoutValue) {
return layoutValue.ceilToDouble();
}
/// The width at which decreasing the width of the text would prevent it from painting itself completely within its bounds
double get minContentWidth {
assert(!_needsLayout);
return _applyFloatingPointHack(_layoutRoot.rootElement.minContentWidth);
}
/// The width at which increasing the width of the text no longer decreases the height
double get maxContentWidth {
assert(!_needsLayout);
return _applyFloatingPointHack(_layoutRoot.rootElement.maxContentWidth);
}
/// The height required to paint the text completely within its bounds
double get height {
assert(!_needsLayout);
return _applyFloatingPointHack(_layoutRoot.rootElement.height);
}
/// The distance from the top of the text to the first baseline of the given type
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(!_needsLayout);
ui.Element root = _layoutRoot.rootElement;
switch (baseline) {
case TextBaseline.alphabetic: return root.alphabeticBaseline;
case TextBaseline.ideographic: return root.ideographicBaseline;
}
}
/// Compute the visual position of the glyphs for painting the text
void layout() {
if (!_needsLayout)
return;
_layoutRoot.layout();
_needsLayout = false;
}
/// Paint the text onto the given canvas at the given offset
void paint(ui.Canvas canvas, ui.Offset offset) {
assert(!_needsLayout && "Please call layout() before paint() to position the text before painting it." is String);
// TODO(ianh): Make LayoutRoot support a paint offset so we don't
// need to translate for each span of text.
canvas.translate(offset.dx, offset.dy);
_layoutRoot.paint(canvas);
canvas.translate(-offset.dx, -offset.dy);
}
}
class _NewTextPainter implements TextPainter {
_NewTextPainter(TextSpan text) {
class TextPainter {
TextPainter(TextSpan text) {
this.text = text;
}
......
......@@ -119,38 +119,6 @@ class TextStyle {
);
}
static String _colorToCSSString(Color color) {
return 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})';
}
static String _fontFamilyToCSSString(String fontFamily) {
// TODO(hansmuller): escape the fontFamily string.
return fontFamily;
}
static String _decorationToCSSString(List<TextDecoration> decoration) {
assert(decoration != null);
const toCSS = const <TextDecoration, String>{
TextDecoration.none: 'none',
TextDecoration.underline: 'underline',
TextDecoration.overline: 'overline',
TextDecoration.lineThrough: 'line-through'
};
return decoration.map((TextDecoration d) => toCSS[d]).join(' ');
}
static String _decorationStyleToCSSString(TextDecorationStyle decorationStyle) {
assert(decorationStyle != null);
const toCSS = const <TextDecorationStyle, String>{
TextDecorationStyle.solid: 'solid',
TextDecorationStyle.double: 'double',
TextDecorationStyle.dotted: 'dotted',
TextDecorationStyle.dashed: 'dashed',
TextDecorationStyle.wavy: 'wavy'
};
return toCSS[decorationStyle];
}
ui.TextStyle get textStyle {
return new ui.TextStyle(
color: color,
......@@ -172,65 +140,6 @@ class TextStyle {
);
}
/// Program this text style into the engine
///
/// Note: This function will likely be removed when we refactor the interface
/// between the framework and the engine
void applyToCSSStyle(ui.CSSStyleDeclaration cssStyle) {
if (color != null) {
cssStyle['color'] = _colorToCSSString(color);
}
if (fontFamily != null) {
cssStyle['font-family'] = _fontFamilyToCSSString(fontFamily);
}
if (fontSize != null) {
cssStyle['font-size'] = '${fontSize}px';
}
if (fontWeight != null) {
cssStyle['font-weight'] = const {
FontWeight.w100: '100',
FontWeight.w200: '200',
FontWeight.w300: '300',
FontWeight.w400: '400',
FontWeight.w500: '500',
FontWeight.w600: '600',
FontWeight.w700: '700',
FontWeight.w800: '800',
FontWeight.w900: '900'
}[fontWeight];
}
if (fontStyle != null) {
cssStyle['font-style'] = const {
FontStyle.normal: 'normal',
FontStyle.italic: 'italic',
}[fontStyle];
}
if (decoration != null) {
cssStyle['text-decoration'] = _decorationToCSSString(decoration);
if (decorationColor != null)
cssStyle['text-decoration-color'] = _colorToCSSString(decorationColor);
if (decorationStyle != null)
cssStyle['text-decoration-style'] = _decorationStyleToCSSString(decorationStyle);
}
}
/// Program the container aspects of this text style into the engine
///
/// Note: This function will likely be removed when we refactor the interface
/// between the framework and the engine
void applyToContainerCSSStyle(ui.CSSStyleDeclaration cssStyle) {
if (textAlign != null) {
cssStyle['text-align'] = const {
TextAlign.left: 'left',
TextAlign.right: 'right',
TextAlign.center: 'center',
}[textAlign];
}
if (height != null) {
cssStyle['line-height'] = '$height';
}
}
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
......
import 'dart:ui';
import 'package:test/test.dart';
import 'dom_utils.dart';
void main() {
Document document = new Document();
test("should throw with invalid arguments", () {
var parent = document.createElement("div");
expect(() {
Function.apply(parent.appendChild, []);
}, throws);
expect(() {
parent.appendChild(null);
}, throws);
expect(() {
Function.apply(parent.appendChild, [{"tagName": "div"}]);
}, throws);
});
test("should insert children", () {
var parent = document.createElement("div");
var child1 = parent.appendChild(document.createElement("div"));
var child2 = parent.appendChild(document.createText(" text "));
var child3 = parent.appendChild(document.createText(" "));
var child4 = parent.appendChild(document.createElement("div"));
expect(child1.parentNode, equals(parent));
expect(child2.parentNode, equals(parent));
expect(child3.parentNode, equals(parent));
expect(child4.parentNode, equals(parent));
expect(childNodeCount(parent), equals(4));
expect(childElementCount(parent), equals(2));
});
test("should insert children with a fragment", () {
var fragment = document.createDocumentFragment();
var child1 = fragment.appendChild(document.createElement("div"));
var child2 = fragment.appendChild(document.createText(" text "));
var child3 = fragment.appendChild(document.createText(" "));
var child4 = fragment.appendChild(document.createElement("div"));
var parent = document.createElement("div");
parent.appendChild(fragment);
expect(child1.parentNode, equals(parent));
expect(child2.parentNode, equals(parent));
expect(child3.parentNode, equals(parent));
expect(child4.parentNode, equals(parent));
expect(childNodeCount(parent), equals(4));
expect(childElementCount(parent), equals(2));
});
// TODO(dart): These might be real bugs too.
// test("should throw when appending to a text", () {
// var parent = new Text();
// expect(() {
// parent.appendChild(document.createElement("div"));
// }, throws);
// });
}
import 'dart:ui';
import 'package:test/test.dart';
import 'dom_utils.dart';
void main() {
var doc;
setUp(() {
doc = new Document();
});
test("should allow replacing the document element", () {
var oldChild = doc.appendChild(doc.createElement("div"));
expect(childElementCount(doc), equals(1));
var newChild = doc.createElement("div");
oldChild.replaceWith([newChild]);
expect(childElementCount(doc), equals(1));
expect(newChild.parentNode, equals(doc));
expect(oldChild.parentNode, isNull);
});
test("should allow replacing a text child with an element", () {
var oldChild = doc.appendChild(doc.createText("text here"));
expect(childElementCount(doc), equals(0));
expect(childNodeCount(doc), equals(1));
var newChild = doc.createElement("div");
oldChild.replaceWith([newChild]);
expect(childElementCount(doc), equals(1));
expect(childNodeCount(doc), equals(1));
expect(newChild.parentNode, equals(doc));
expect(oldChild.parentNode, isNull);
});
test("should allow replacing the document element with text", () {
var oldChild = doc.appendChild(doc.createElement("div"));
expect(childElementCount(doc), equals(1));
var newChild = doc.createText(" text ");
oldChild.replaceWith([newChild]);
expect(childElementCount(doc), equals(0));
expect(childNodeCount(doc), equals(1));
expect(newChild.parentNode, equals(doc));
expect(oldChild.parentNode, isNull);
});
test("should allow inserting text with a fragment", () {
var fragment = doc.createDocumentFragment();
fragment.appendChild(doc.createText(" text "));
fragment.appendChild(doc.createText(" text "));
expect(childNodeCount(doc), equals(0));
doc.appendChild(fragment);
expect(childElementCount(doc), equals(0));
expect(childNodeCount(doc), equals(2));
});
test("should allow replacing the document element with a fragment", () {
var oldChild = doc.appendChild(doc.createElement("div"));
expect(childElementCount(doc), equals(1));
var fragment = doc.createDocumentFragment();
fragment.appendChild(doc.createText(" text "));
var newChild = fragment.appendChild(doc.createElement("div"));
fragment.appendChild(doc.createText(" "));
oldChild.replaceWith([fragment]);
expect(childElementCount(doc), equals(1));
expect(childNodeCount(doc), equals(3));
expect(newChild.parentNode, equals(doc));
expect(oldChild.parentNode, isNull);
});
test("should throw when inserting multiple elements", () {
doc.appendChild(doc.createElement("div"));
doc.appendChild(doc.createText(" text "));
expect(childElementCount(doc), equals(1));
doc.createElement("div");
});
test("should throw when inserting multiple elements with a fragment", () {
doc.appendChild(doc.createElement("div"));
expect(childElementCount(doc), equals(1));
var fragment = doc.createDocumentFragment();
fragment.appendChild(doc.createText(" text "));
fragment.appendChild(doc.createElement("div"));
fragment.appendChild(doc.createElement("div"));
fragment.appendChild(doc.createText(" "));
});
}
import 'dart:ui';
int childNodeCount(parent) {
int count = 0;
for (Node node = parent.firstChild; node != null; node = node.nextSibling)
++count;
return count;
}
int childElementCount(parent) {
int count = 0;
for (Element element = parent.firstElementChild; element != null; element = element.nextElementSibling)
++count;
return count;
}
import 'dart:ui';
import 'package:test/test.dart';
void main() {
test("getChildElements should only include immediate children", () {
var doc = new Document();
var parent = doc.createElement('parent');
var child1 = doc.createElement('child1');
var child2 = doc.createElement('child1');
var grandchild = doc.createElement('grandchild');
doc.appendChild(parent);
parent.appendChild(child1);
parent.appendChild(child2);
child1.appendChild(grandchild);
var children = parent.getChildElements();
expect(children.length, equals(2));
expect(children[0], equals(child1));
expect(children[1], equals(child2));
});
}
import 'dart:ui' as ui;
import 'package:test/test.dart';
void main() {
test('should be settable using "style" attribute', () {
ui.LayoutRoot layoutRoot = new ui.LayoutRoot();
var document = new ui.Document();
var foo = document.createElement('foo');
layoutRoot.rootElement = foo;
foo.setAttribute('style', 'color: red');
expect(foo.getAttribute('style'), equals('color: red'));
expect(foo.style["color"], equals('rgb(255, 0, 0)'));
});
test('should not crash when setting style to null', () {
ui.LayoutRoot layoutRoot = new ui.LayoutRoot();
var document = new ui.Document();
var foo = document.createElement('foo');
layoutRoot.rootElement = foo;
expect(foo.style['color'], isNull);
foo.style["color"] = null; // This used to crash.
expect(foo.style['color'], isNull);
foo.style["color"] = "blue";
expect(foo.style['color'], equals("rgb(0, 0, 255)"));
foo.style["color"] = null;
expect(foo.style['color'], isNull);
foo.style["color"] = "blue";
expect(foo.style['color'], equals("rgb(0, 0, 255)"));
foo.style.removeProperty("color");
expect(foo.style['color'], isNull);
layoutRoot.layout();
});
}
import 'dart:ui';
import 'package:test/test.dart';
void main() {
Document document = new Document();
test("should throw with invalid arguments", () {
Element parent = document.createElement("div");
Element child = document.createElement("div");
parent.appendChild(child);
// TODO(eseidel): This should throw!
// expect(() {
// parent.insertBefore([parent]);
// }, throws);
expect(() {
child.insertBefore(<Node>[parent]);
}, throws);
});
}
import 'dart:ui';
import 'package:test/test.dart';
void main() {
test("should return null for elements not a child of a scope", () {
var doc = new Document();
var element = doc.createElement("div");
expect(element.owner, isNull);
});
test("should return the document for elements in the document scope", () {
var doc = new Document();
var element = doc.createElement("div");
doc.appendChild(element);
expect(element.owner, equals(doc));
});
}
import 'dart:ui';
import 'package:test/test.dart';
import 'dom_utils.dart';
void main() {
Document document = new Document();
test("should replace elements", () {
Element parent = document.createElement("div");
Element oldChild = parent.appendChild(document.createElement("div"));
Element newChild = document.createElement("div");
oldChild.replaceWith(<Node>[newChild]);
expect(oldChild.parentNode, isNull);
expect(newChild.parentNode, equals(parent));
});
test("should replace text", () {
Element parent = document.createElement("div");
Node oldChild = parent.appendChild(document.createText(" it's a text "));
Element newChild = document.createElement("div");
oldChild.replaceWith(<Node>[newChild]);
expect(oldChild.parentNode, isNull);
expect(newChild.parentNode, equals(parent));
});
test("should replace children with a fragment", () {
DocumentFragment fragment = document.createDocumentFragment();
Element child1 = fragment.appendChild(document.createElement("div"));
Node child2 = fragment.appendChild(document.createText(" text "));
Node child3 = fragment.appendChild(document.createText(" "));
Element child4 = fragment.appendChild(document.createElement("div"));
Element parent = document.createElement("div");
Element oldChild = parent.appendChild(document.createElement("div"));
Element lastChild = parent.appendChild(document.createElement("div"));
oldChild.replaceWith(<Node>[fragment]);
expect(child1.parentNode, equals(parent));
expect(child2.parentNode, equals(parent));
expect(child3.parentNode, equals(parent));
expect(child4.parentNode, equals(parent));
expect(oldChild.parentNode, isNull);
expect(childNodeCount(parent), equals(5));
expect(childElementCount(parent), equals(3));
expect(parent.lastChild, equals(lastChild));
});
// test("should throw when appending to a text", () {
// var parent = new Text();
// expect(() {
// parent.replaceChild(document.createElement("div"), null);
// }, throws);
// });
}
import 'dart:ui' as ui;
import 'package:test/test.dart';
void main() {
test("createText(null) shouldn't crash", () {
var doc = new ui.Document();
doc.createText(null);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment