Let's build browser engine! in typescript vol11 Build Layout tree 
Tags: browser
Published: 2019-06-16 09:25:19

---
title: Let's build browser engine! in typescript vol11 Build Layout tree 
published: true
description: Understand browser step by step. Let's build browser engine! in typescript vol11 Build Layout tree.
tags: browser
---

I'm not sure, which method I have to keep node balance.

```typescript
test("LayoutBox#getInlineContainer anonymous block", () => {
  const layoutBoxAnonymousBlock = new LayoutBox(
    new Dimensions(
      new Rect(1, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0)
    ),
    new BoxType.AnonymousBlock(),
    []
  );
  expect(layoutBoxAnonymousBlock.getInlineContainer()).toEqual(layoutBoxAnonymousBlock);
});

test("LayoutBox#getInlineContainer inline node", () => {
  const layoutBoxInlineNode = new LayoutBox(
    new Dimensions(
      new Rect(1, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0)
    ),
    new BoxType.InlineNode(oneStyledNode),
    []
  );
  expect(layoutBoxInlineNode.getInlineContainer()).toEqual(layoutBoxInlineNode);
});

test("LayoutBox#getInlineContainer block node 1", () => {
  const layoutBoxAnonymousBlock = new LayoutBox(
    new Dimensions(
      new Rect(1, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0)
    ),
    new BoxType.AnonymousBlock(),
    []
  );
  const layoutBoxBlockNode = new LayoutBox(
    new Dimensions(
      new Rect(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0)
    ),
    new BoxType.BlockNode(oneStyledNode),
    [layoutBoxAnonymousBlock]
  );
  expect(layoutBoxBlockNode.getInlineContainer()).toEqual(layoutBoxAnonymousBlock);
});

test("LayoutBox#getInlineContainer block node 2", () => {
  const layoutBoxBlockNode = new LayoutBox(
    new Dimensions(
      new Rect(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0),
      new EdgeSizes(0, 0, 0, 0)
    ),
    new BoxType.BlockNode(oneStyledNode),
    []
  );
  expect(layoutBoxBlockNode.getInlineContainer()).toEqual(
    LayoutBox.Create(new BoxType.AnonymousBlock())
  );
});
```

```typescript
export class LayoutBox {
  (snip)

  // NOTE: This may modify this.children :trollface:
  getInlineContainer(): LayoutBox {
    switch (this.boxType.format) {
      case BoxType.Format.AnonymousBlock:
      case BoxType.Format.InlineNode:
        return this;
      case BoxType.Format.BlockNode:
        if (
          this.children.length === 0 ||
          this.children[this.children.length - 1].boxType.format !== BoxType.Format.AnonymousBlock
        ) {
          this.children.push(LayoutBox.Create(new BoxType.AnonymousBlock()));
        }
        return this.children[this.children.length - 1];
    }
  }
}
```

```typescript
test("buildLayoutTree 1", () => {
  const displayNone = new StyledNode(
    elem("no mean", new Map([["id", "target"]]), []),
    new Map([["display", new CssValue.Keyword("none")]]),
    []
  );
  expect(() => {
    buildLayoutTree(displayNone);
  }).toThrow();
});

test("buildLayoutTree 2", () => {
  const displayBlock = new StyledNode(
    elem("no mean", new Map([["id", "target"]]), []),
    new Map([["display", new CssValue.Keyword("block")]]),
    []
  );
  expect(buildLayoutTree(displayBlock)).toEqual(
    LayoutBox.Create(new BoxType.BlockNode(displayBlock))
  );
});

test("buildLayoutTree 3", () => {
  const displayInline = new StyledNode(
    elem("no mean", new Map([["id", "target"]]), []),
    new Map([["display", new CssValue.Keyword("no mean")]]),
    []
  );
  expect(buildLayoutTree(displayInline)).toEqual(
    LayoutBox.Create(new BoxType.InlineNode(displayInline))
  );
});

test("buildLayoutTree 4", () => {
  const childDisplayBlock = new StyledNode(
    elem("no mean", new Map([["id", "target2"]]), []),
    new Map([["display", new CssValue.Keyword("block")]]),
    []
  );
  const displayBlock = new StyledNode(
    elem("no mean", new Map([["id", "target"]]), []),
    new Map([["display", new CssValue.Keyword("block")]]),
    [childDisplayBlock]
  );
  expect(buildLayoutTree(displayBlock)).toEqual(
    new LayoutBox(
      new Dimensions(
        new Rect(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0)
      ),
      new BoxType.BlockNode(displayBlock),
      [LayoutBox.Create(new BoxType.BlockNode(childDisplayBlock))]
    )
  );
});

test("buildLayoutTree 5", () => {
  const childDisplayInline = new StyledNode(
    elem("no mean", new Map([["id", "target2"]]), []),
    new Map([["display", new CssValue.Keyword("no mean")]]),
    []
  );
  const displayBlock = new StyledNode(
    elem("no mean", new Map([["id", "target"]]), []),
    new Map([["display", new CssValue.Keyword("block")]]),
    [childDisplayInline]
  );
  expect(buildLayoutTree(displayBlock)).toEqual(
    new LayoutBox(
      new Dimensions(
        new Rect(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0)
      ),
      new BoxType.BlockNode(displayBlock),
      [
        new LayoutBox(
          new Dimensions(
            new Rect(0, 0, 0, 0),
            new EdgeSizes(0, 0, 0, 0),
            new EdgeSizes(0, 0, 0, 0),
            new EdgeSizes(0, 0, 0, 0)
          ),
          new BoxType.AnonymousBlock(),
          [LayoutBox.Create(new BoxType.InlineNode(childDisplayInline))]
        )
      ]
    )
  );
});

test("buildLayoutTree 6", () => {
  const childDisplayNone = new StyledNode(
    elem("no mean", new Map([["id", "target2"]]), []),
    new Map([["display", new CssValue.Keyword("none")]]),
    []
  );
  const displayBlock = new StyledNode(
    elem("no mean", new Map([["id", "target"]]), []),
    new Map([["display", new CssValue.Keyword("block")]]),
    [childDisplayNone]
  );
  expect(buildLayoutTree(displayBlock)).toEqual(
    new LayoutBox(
      new Dimensions(
        new Rect(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0),
        new EdgeSizes(0, 0, 0, 0)
      ),
      new BoxType.BlockNode(displayBlock),
      []
    )
  );
});
```

```typescript

export function buildLayoutTree(styleNode: StyledNode): LayoutBox {
  let root: LayoutBox;
  switch (styleNode.display()) {
    case Display.None: // NOTE: I'm not sure "Uncovered Line"
      throw Error("Root node has display: none.");
    case Display.Block:
      root = LayoutBox.Create(new BoxType.BlockNode(styleNode));
      break;
    case Display.Inline:
      root = LayoutBox.Create(new BoxType.InlineNode(styleNode));
      break;
    default:
      /* istanbul ignore next */
      throw Error("never");
  }

  for (const child of styleNode.children) {
    switch (child.display()) {
      case Display.Inline:
        root.getInlineContainer().children.push(buildLayoutTree(child));
        break;
      case Display.Block:
        root.children.push(buildLayoutTree(child));
        break;
      case Display.None:
        // do nothing
        break;
    }
  }
  return root;
}
```

## references

- [Let's build a browser engine! Part 1: Getting started](https://limpet.net/mbrubeck/2014/08/08/toy-layout-engine-1.html)
- [mbrubeck/robinson](https://github.com/mbrubeck/robinson)
- [sanemat/js-toy-engine](https://github.com/sanemat/js-toy-engine)
- [sanemat/ts-toy-engine](https://github.com/sanemat/ts-toy-engine)

## series

- [Let's build browser engine! in typescript vol0 Toy browser engine](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol0-toy-browser-engine-egm)
- [Let's build browser engine! in typescript vol1 Canvas with Color](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol1-canvas-with-color-2nd7)
- [Let's build browser engine! in typescript vol2 Display Command](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol2-display-command-3466)
- [Let's build browser engine! in typescript vol3 Layout Box, Dimensions](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol3-layout-box-dimensions-4613)
- [Let's build browser engine! in typescript vol4 Layout Tree to Display List](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol4-layout-tree-to-display-list-c54)
- [Let's build browser engine! in typescript vol5 DOM Shortcut](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol5-dom-shortcut-2l81)
- [Let's build browser engine! in typescript vol6 CSS Shortcut](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol6-css-shortcut-1958)
- [Let's build browser engine! in typescript vol7 Selector matching](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol7-selector-matching-1faf)
- [Let's build browser engine! in typescript vol8 Specificity](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol8-specificity-1c4c)
- [Let's build browser engine! in typescript vol9 DOM and Rules to Style tree](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol9-dom-and-rules-to-style-tree-18kp)
- [Let's build browser engine! in typescript vol10 style display](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol10-style-display-45ii)
- [Let's build browser engine! in typescript vol11 Build Layout tree](https://dev.to/sanemat/let-s-build-browser-engine-in-typescript-vol11-build-layout-tree-3gom)