summaryrefslogtreecommitdiff
path: root/docs/architecture.md
blob: 63142feb786fc68e0fa066bc5116b68da6c4cc0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
LibNSLayout Architecture
========================

LibNSLayout is a library for performing layout on a Document Object Model
for HTML.  Its purpose is to allow client applications to provide DOM
information and convert that into a render list, which can be displayed
by the client.

Dependencies
------------

Clients of LibNSLayout must use the following additional libraries, because
their types are used in the LibNSLayout interface:

*   `LibDOM` is used to provide the DOM interface.
*   `LibCSS` is used to provide the CSS handling.
*   `LibWapcaplet` is used for interned strings.
*   `LibNSLog` for logging.

Interface
---------

The devision of responsibilities between LibNSLayout and its clients are
as follows:

### Client

*   Fetching the document to be displayed.
*   Creating a CSS selection context (with default user-agent, and user CSS).
*   Generating DOM.
*   Creating a LibNSLayout layout for the document, passing the DOM document,
    CSS selection context, appropriate CSS media descriptor, and scale.
*   Listening to DOM changes.
    *   Fetching resources needed by DOM.
        *   CSS (STYLE elements, and LINK elements):
            *   Parsing the CSS.
            *   Updating CSS selection context as stylesheets are fetched,
                and notifying LibNSLayout.
        *   JavaScript (SCRIPT elements, and LINK elements)
            *   Executing JavaScript.
        *   Favicons (LINK elements.)
        *   Images, Frames, Iframes.
    *   Notifying LibNSLayout of DOM changes.
*   Performing resource fetches on behalf of LibNSLayout.
    *   (Such as when LibNSLayout requires a background image or web font for
        an element due to CSS.)
*   Asking LibNSLayout to perform layout.
    *   Displaying the returned render list.
*   Asking LibNSLayout for layout info (e.g. due to JavaScript.)
*   Passing mouse actions to LibNSLayout.
*   Passing keyboard input to LibNSLayout.
*   Passing scale changes to LibNSLayout.
*   Performing measurement of text; given a string & style, calculating its
    width in pixels.

### LibNSLayout

*   Creates a layout object that's opaque to the client, and returns its
    handle.
*   Performs CSS selection as appropriate when DOM changes.
*   Asking client to fetch a resource that's needed for a computed style.
*   Asking client to measure text.
*   Performs line breaking.
*   Performs layout (if required) when asked by client and returns render list.
*   Performs layout (if required) when asked by client for layout info.

Details
-------

### Text layout

LibNSLayout is responsible for performing line-breaking.  It uses a third
party library ([libunibreak](https://github.com/adah1972/libunibreak)) to
provide the Unicode line breaking algorithm implementation.  LibNSLayout
will:

1.  Pass the text of a paragraph to the `libunibreak` and get back a list of
    possible break points.
2.  Ask the client to measure each non-breakable section.
3.  Try to fit as many non-breakable sections on a line as possible, given
    the available width.

Note that some breaks may not be permissible, since they will fall inside
inline elements which are styled to prevent wrap.  Also, to measure each
non-breakable section, there may be multiple calls to the client to measure
sub-sections of the non-breakable section according to different parts of the
text having different styles.  We can probably avoid this depending on which
CSS properties are different.  (e.g. `color` and `text-decoration` won't affect
text measurement.)

> **TODO**: How to do justified text?
> 
> Probably we can measure the minimum space width needed on a line,
> fit as much text as possible with that, and share any remaining space
> out over each break-point that is a space.

### Layout

Layout will be lazy, and only actually done when prompted by the client.
The client will be getting DOM modification events and informing LibNSLayout.
LibNSLayout will be notified when elements are added and removed from the
DOM, and when TEXT nodes are added/modified/removed.

> **TODO**: How lazy to be?
> 
> We could either:
> 
> 1.  Build/update layout tree as we get the updates (but not performing
>     layout until asked by the client).
> 2.  Queue DOM update events and elide updates that cancel themselves out.
>     For example if an element is added and immediately removed, there is
>     no point in doing anything.  (Or is there?  Consider starting fetches
>     as early as possible.)  We would then only update the layout tree when
>     the queue gets too long or when the client requires a layout.

If an element is added, we would perform CSS selection, and assuming its not
got `display: none` we'd add the elements entry in the layout tree.  When we
add the new entry to the layout tree, we'd mark its parents as needing layout.
We'd keep walking up the ancestor chain marking as needing layout until we hit
a layout entry who's width and height are fixed, meaning it can't affect its
parent layout.

When the client requires a layout, we would then walk the tree, ensuring every
layout node has a valid `x`, `y`, `width`, and `height`.

### Rendering

The client is responsible for rendering, and any compositing.  When the
client asks LibNSLayout to layout, LibNSLayout returns a render list.

> **TODO**: Details?
>
> 1.  Consider render list optimisation (e.g. NetSurf's Knockout rendering).
> 2.  What will the render list look like.

### Fetches

There are several kinds of fetches to consider:

*   Fetch resources directly required by the document.  (Client initiated.)
    *   CSS
    *   JavaScript
    *   Images
    *   Iframes
    *   etc...
*   Fetch resources required by element computed styles.  (LibNSLayout
    initiated.)
    *   Background images.
    *   Web fonts.

For a client initiated fetch of CSS, the selection context is updated when
we have fetched the style sheet, and LibNSLog is informed.

For a LibNSLayout initiated fetch, LibNSLayout creates a `nsl_resource_t`
and passes a pointer to the client, along with the URL to fetch.
The `nsl_resource_t` is opaque to the client.  When the resource is fetched,
the client calls a callback passing, the `nsl_resource_t`, and a pointer to
the client handle for the resource (`hlcache_handle`, in NetSurf).  Then
`LibNSLayout` can set the pointer to the client resource handle inside the
`nsl_resource_t`, and the client handle pointer will be used to ask the client
about e.g. intrinsic dimensions.  The pointer to the client handle will also
appear in the render list that is given to the client for display.

For fetches started by client (for example, replaced-element images) that
LibNSLayout needs to know about, the client starts the fetch when it gets
the DOM event about the newly added/modified IMG element, it then informs
LibNSLayout of the updated DOM node, passing the client handle for the
resource.  Since there is a non-null client resource handle, LibNSLayout
knows it's a replaced element, and can ask the client for intrinsic dimensions.

```c
nslayout_res nslayout_dom_event_add_element_replaced(
		nslayout *layout,
		dom_node *element,
		nsl_resource_t *replaced,
		nsl_resource_type_t type);
```

The `nsl_resource_type_t` would be a client defined enum allowing the client
have different handle types for different things, e.g. image resource handle,
and form button handle.  The type would be passed back with the resource.

The client is responsible for calling a function to tell LibNSLayout what
the intrinsic dimensions are for a resource, if applicable.  It can do this
once it has the information (e.g. once it's fetched and parsed the header of
an image).