Browser Engine

All this time I have been thinking how browsers can be so bloated. I tried multiple alternatives, Firefox, Chrome, Midori, Palemoon, Luakit, Surf, Netsurf, Dillo, etc. Some of them had less resource consumption but were still heavy, others were lighter but missed support for a big part of the web.

With this in mind, I started a simple experiment to understand a bit better how a browser works. After searching for a bit I found a nice tutorial for the base of a browser engine by a mozilla engineer, Matt Brubeck.

At the same time I was reading and checking the Clean Architecture of Uncle Bob, I didn't implement it per se, but at least tried to achieve some of the isolation of the main business logic he proposes.

With all of this in mind I started by checking the browser engine main parts.

Main Architecture

Here we can see a diagram of the pipeline created with the modules I grabbed from the tutorial page:

With the main modules of the engine defined I started the implementation.

Implementation

Communication and Configuration

Since I wanted to enforce a bigger level of isolation between each of the logic elements, I started by defining a common way of communication between the multiple parts. I'm used to GNU/Linux and the command line so I selected that each module will output text in json format and pass it to the next module using the pipe mechanism. Something like:


    parsers | style | box_layot | block_layout | painting | render

I will use the lua programming language to implement the code and since lua has a structure called table that can represent arrays and dictionaries with any types, I can make a generic converter of a table to json which takes care of all the needed formatting of output and input for the communication. Something like:

Since pipes will be used to pass the results of one module to the other, our delivery mechanism will be the stdin/stdout.

We may want to pass some configuration to the logic, to that we will use command line parameters has the configuration provider

To join all this with the main logic and abstract the logic from the rest we implemented operations which realize the following steps:

Here we can see a operation implementation that will activate the html parser while reading regular text from stdin and sending json formatted text to stdout. In this case I provided the input and output configuration with two tables, but I could use the command line configuration provider to fetch them, or get optional configurations and use this as defaults.


local Operation = require "operation.operation"
local Parser = require "html.parser"

local input_config = {
    {type = "std"}
}

local output_config = {
    {type = "std", format = "json"}
}

local parse_html = function(self, input_data)
    local parser = Parser.new(input_data)
    return parser:parse()
end

local operation = Operation.new(input_config, output_config, parse_html)
operation:run()

Main Implementation

Here we describe what each module does:

After the painting module generates a list of drawing command I processed them using a simple implementation with Love2D. Here we can see a simple html and css file and the interface rendered using love2D.

This was the point I wanted to achieve in order to understand a bit better how browsers work. There is still tons of stuff missing so if you are interested I invite you to continue my experimentation :) .

Repository

If you want to check the code go to the repository