├── Cargo.toml ├── README.md ├── bin └── main.rs ├── example ├── example1.css ├── example1.html ├── example2.css └── example2.html ├── shaders ├── solid.glslf └── solid.glslv └── src ├── command.rs ├── css.rs ├── css_parser.rs ├── dom.rs ├── html_parse.rs ├── layout.rs ├── lib.rs ├── render.rs └── style.rs /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "browser_engine" 3 | version = "0.1.0" 4 | authors = ["tensor-programming "] 5 | 6 | [dependencies] 7 | gfx = "0.14.0" 8 | gfx_text = "0.15.0" 9 | gfx_window_glutin = "0.14.0" 10 | glutin = "0.7.1" 11 | 12 | [[bin]] 13 | name = "main" 14 | path = "bin/main.rs" 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust_browser_6_final 2 | 3 | # In this video, we finish our browser engine. 4 | 5 | ### Check out the Youtube Tutorial for this [Rust Tutorial](https://youtu.be/xNu6U5XCMMQ). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. 6 | 7 | ### Check out our blog at [tensor-programming.com](http://tensor-programming.com/). 8 | 9 | ### Our [Twitter](https://twitter.com/TensorProgram), our [facebook](https://www.facebook.com/Tensor-Programming-1197847143611799/) and our [Steemit](https://steemit.com/@tensor). 10 | 11 | ### Donate if you like the content: 12 | ### Ada: DdzFFzCqrhsqPcLbpt3C9nkSW2HvMJJCER5c9ijxKwXDet3GT5KchnUp458zN9uVmCzRjzwyy8usFUEhwBQ63h2ZjvyAXHYnHRG8MZpv 13 | ### Eth: 0xD210Ea51F1615794A16080A108d2BC5471F60166 14 | ### Ltc: LXsKxF5JhmMtKgqfcUFdvcXVwiaSqxN9cP 15 | -------------------------------------------------------------------------------- /bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate browser_engine; 2 | use browser_engine::{command, css, css_parser, dom, html_parse, layout, render, style}; 3 | 4 | use std::env; 5 | use std::fs::File; 6 | use std::io::{BufReader, Read}; 7 | 8 | fn main() { 9 | let nodes = get_html(); 10 | for n in nodes.iter() { 11 | dom::pretty_print(n, 0); 12 | } 13 | 14 | let ref root_node = nodes[0]; 15 | 16 | let stylesheet = get_css(); 17 | println!("{:?}", stylesheet); 18 | 19 | let style_tree_root = style::StyledNode::new(&root_node, &stylesheet); 20 | style::pretty_print(&style_tree_root, 0); 21 | 22 | let mut viewport = layout::Dimensions::default(); 23 | viewport.content.width = 1024.0; 24 | viewport.content.height = 768.0; 25 | 26 | let layout_tree = layout::layout_tree(&style_tree_root, viewport); 27 | layout::pretty_print(&layout_tree, 0); 28 | 29 | let display_commands = command::build_display_commands(&layout_tree); 30 | render::render_loop(&display_commands); 31 | } 32 | 33 | fn get_html() -> Vec { 34 | let mut path = env::current_dir().unwrap(); 35 | path.push("example/example2.html"); 36 | 37 | let mut file_reader = match File::open(&path) { 38 | Ok(f) => BufReader::new(f), 39 | Err(e) => panic!("file: {}, error: {}", path.display(), e), 40 | }; 41 | 42 | let mut html_input = String::new(); 43 | file_reader.read_to_string(&mut html_input).unwrap(); 44 | 45 | let nodes = html_parse::HtmlParser::new(&html_input).parse_nodes(); 46 | nodes 47 | } 48 | 49 | fn get_css() -> css::Stylesheet { 50 | let mut path = env::current_dir().unwrap(); 51 | path.push("example/example2.css"); 52 | 53 | let mut file_reader = match File::open(&path) { 54 | Ok(f) => BufReader::new(f), 55 | Err(e) => panic!("file: {}, error: {}", path.display(), e), 56 | }; 57 | 58 | let mut css_input = String::new(); 59 | file_reader.read_to_string(&mut css_input).unwrap(); 60 | 61 | let stylesheet = css_parser::CssParser::new(&css_input).parse_stylesheet(); 62 | stylesheet 63 | } 64 | -------------------------------------------------------------------------------- /example/example1.css: -------------------------------------------------------------------------------- 1 | html { 2 | display: block; 3 | } 4 | 5 | head { 6 | display: none; 7 | } 8 | 9 | body { 10 | display: block; 11 | margin: 6px; 12 | } 13 | 14 | div { 15 | display: block; 16 | } 17 | 18 | .blue { 19 | background-color: blue; 20 | } 21 | 22 | .orange { 23 | background-color: orange; 24 | } 25 | 26 | .black { 27 | background-color: black; 28 | } 29 | 30 | .green { 31 | background-color: #2ebe1a; 32 | } 33 | 34 | .red { 35 | background-color: #d61717; 36 | } 37 | 38 | .orangered { 39 | background-color: orangered; 40 | } 41 | 42 | .bronze { 43 | background-color: #c26b13; 44 | } 45 | 46 | .box { 47 | display: inline-block; 48 | margin-right: 5px; 49 | margin-bottom: 5px; 50 | margin-left: 5px; 51 | margin-top: 5px; 52 | height: 40px; 53 | width: 30%; 54 | } -------------------------------------------------------------------------------- /example/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
This is a blue box
9 |
10 |
11 |
12 |
13 |
14 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /example/example2.css: -------------------------------------------------------------------------------- 1 | .box { 2 | display: inline-block; 3 | margin-right: 1px; 4 | margin-bottom: 1px; 5 | margin-left: 1px; 6 | margin-top: 1px; 7 | height: 30px; 8 | width: 10%; 9 | border-color: black; 10 | border-left-width: 2px; 11 | border-right-width: 2px; 12 | border-top-width: 2px; 13 | padding-top: -5px; 14 | padding-bottom: 10px; 15 | } 16 | 17 | .black { 18 | background-color: black; 19 | } 20 | 21 | .silver { 22 | background-color: silver; 23 | } 24 | 25 | .gray { 26 | background-color: gray; 27 | } 28 | 29 | .grey { 30 | background-color: grey; 31 | } 32 | 33 | .white { 34 | background-color: white; 35 | } 36 | 37 | .maroon { 38 | background-color: maroon; 39 | } 40 | 41 | .red { 42 | background-color: red; 43 | } 44 | 45 | .purple { 46 | background-color: purple; 47 | } 48 | 49 | .fuchsia { 50 | background-color: fuchsia; 51 | } 52 | 53 | .green { 54 | background-color: green; 55 | } 56 | 57 | .lime { 58 | background-color: lime; 59 | } 60 | 61 | .olive { 62 | background-color: olive; 63 | } 64 | 65 | .yellow { 66 | background-color: yellow; 67 | } 68 | 69 | .navy { 70 | background-color: navy; 71 | } 72 | 73 | .blue { 74 | background-color: blue; 75 | } 76 | 77 | .teal { 78 | background-color: teal; 79 | } 80 | 81 | .aqua { 82 | background-color: aqua; 83 | } 84 | 85 | .orange { 86 | background-color: orange; 87 | } 88 | 89 | .aliceblue { 90 | background-color: aliceblue; 91 | } 92 | 93 | .antiquewhite { 94 | background-color: antiquewhite; 95 | } 96 | 97 | .aquamarine { 98 | background-color: aquamarine; 99 | } 100 | 101 | .azure { 102 | background-color: azure; 103 | } 104 | 105 | .beige { 106 | background-color: beige; 107 | } 108 | 109 | .bisque { 110 | background-color: bisque; 111 | } 112 | 113 | .blanchedalmond { 114 | background-color: blanchedalmond; 115 | } 116 | 117 | .blueviolet { 118 | background-color: blueviolet; 119 | } 120 | 121 | .brown { 122 | background-color: brown; 123 | } 124 | 125 | .burlywood { 126 | background-color: burlywood; 127 | } 128 | 129 | .cadetblue { 130 | background-color: cadetblue; 131 | } 132 | 133 | .chartreuse { 134 | background-color: chartreuse; 135 | } 136 | 137 | .chocolate { 138 | background-color: chocolate; 139 | } 140 | 141 | .coral { 142 | background-color: coral; 143 | } 144 | 145 | .cornflowerblue { 146 | background-color: cornflowerblue; 147 | } 148 | 149 | .cornsilk { 150 | background-color: cornsilk; 151 | } 152 | 153 | .crimson { 154 | background-color: crimson; 155 | } 156 | 157 | .darkblue { 158 | background-color: darkblue; 159 | } 160 | 161 | .darkcyan { 162 | background-color: darkcyan; 163 | } 164 | 165 | .darkgoldenrod { 166 | background-color: darkgoldenrod; 167 | } 168 | 169 | .darkgray { 170 | background-color: darkgray; 171 | } 172 | 173 | .darkgrey { 174 | background-color: darkgrey; 175 | } 176 | 177 | .darkgreen { 178 | background-color: darkgreen; 179 | } 180 | 181 | .darkkhaki { 182 | background-color: darkkhaki; 183 | } 184 | 185 | .darkmagenta { 186 | background-color: darkmagenta; 187 | } 188 | 189 | .darkolivegreen { 190 | background-color: darkolivegreen; 191 | } 192 | 193 | .darkorange { 194 | background-color: darkorange; 195 | } 196 | 197 | .darkorchid { 198 | background-color: darkorchid; 199 | } 200 | 201 | .darkred { 202 | background-color: darkred; 203 | } 204 | 205 | .darksalmon { 206 | background-color: darksalmon; 207 | } 208 | 209 | .darkseagreen { 210 | background-color: darkseagreen; 211 | } 212 | 213 | .darkslateblue { 214 | background-color: darkslateblue; 215 | } 216 | 217 | .darkslategray { 218 | background-color: darkslategray; 219 | } 220 | 221 | .darkslategrey { 222 | background-color: darkslategrey; 223 | } 224 | 225 | .darkturquoise { 226 | background-color: darkturquoise; 227 | } 228 | 229 | .darkviolet { 230 | background-color: darkviolet; 231 | } 232 | 233 | .deeppink { 234 | background-color: deeppink; 235 | } 236 | 237 | .deepskyblue { 238 | background-color: deepskyblue; 239 | } 240 | 241 | .dimgray { 242 | background-color: dimgray; 243 | } 244 | 245 | .dimgrey { 246 | background-color: dimgrey; 247 | } 248 | 249 | .dodgerblue { 250 | background-color: dodgerblue; 251 | } 252 | 253 | .firebrick { 254 | background-color: firebrick; 255 | } 256 | 257 | .floralwhite { 258 | background-color: floralwhite; 259 | } 260 | 261 | .forestgreen { 262 | background-color: forestgreen; 263 | } 264 | 265 | .gainsboro { 266 | background-color: gainsboro; 267 | } 268 | 269 | .ghostwhite { 270 | background-color: ghostwhite; 271 | } 272 | 273 | .gold { 274 | background-color: gold; 275 | } 276 | 277 | .goldenrod { 278 | background-color: goldenrod; 279 | } 280 | 281 | .greenyellow { 282 | background-color: greenyellow; 283 | } 284 | 285 | .honeydew { 286 | background-color: honeydew; 287 | } 288 | 289 | .hotpink { 290 | background-color: hotpink; 291 | } 292 | 293 | .indianred { 294 | background-color: indianred; 295 | } 296 | 297 | .indigo { 298 | background-color: indigo; 299 | } 300 | 301 | .ivory { 302 | background-color: ivory; 303 | } 304 | 305 | .khaki { 306 | background-color: khaki; 307 | } 308 | 309 | .lavender { 310 | background-color: lavender; 311 | } 312 | 313 | .lavenderblush { 314 | background-color: lavenderblush; 315 | } 316 | 317 | .lawngreen { 318 | background-color: lawngreen; 319 | } 320 | 321 | .lemonchiffon { 322 | background-color: lemonchiffon; 323 | } 324 | 325 | .lightblue { 326 | background-color: lightblue; 327 | } 328 | 329 | .lightcoral { 330 | background-color: lightcoral; 331 | } 332 | 333 | .lightcyan { 334 | background-color: lightcyan; 335 | } 336 | 337 | .lightgoldenrodyellow { 338 | background-color: lightgoldenrodyellow; 339 | } 340 | 341 | .lightgray { 342 | background-color: lightgray; 343 | } 344 | 345 | .lightgrey { 346 | background-color: lightgrey; 347 | } 348 | 349 | .lightgreen { 350 | background-color: lightgreen; 351 | } 352 | 353 | .lightpink { 354 | background-color: lightpink; 355 | } 356 | 357 | .lightsalmon { 358 | background-color: lightsalmon; 359 | } 360 | 361 | .lightseagreen { 362 | background-color: lightseagreen; 363 | } 364 | 365 | .lightskyblue { 366 | background-color: lightskyblue; 367 | } 368 | 369 | .lightslategray { 370 | background-color: lightslategray; 371 | } 372 | 373 | .lightslategrey { 374 | background-color: lightslategrey; 375 | } 376 | 377 | .lightsteelblue { 378 | background-color: lightsteelblue; 379 | } 380 | 381 | .lightyellow { 382 | background-color: lightyellow; 383 | } 384 | 385 | .limegreen { 386 | background-color: limegreen; 387 | } 388 | 389 | .linen { 390 | background-color: linen; 391 | } 392 | 393 | .mediumaquamarine { 394 | background-color: mediumaquamarine; 395 | } 396 | 397 | .mediumblue { 398 | background-color: mediumblue; 399 | } 400 | 401 | .mediumorchid { 402 | background-color: mediumorchid; 403 | } 404 | 405 | .mediumpurple { 406 | background-color: mediumpurple; 407 | } 408 | 409 | .mediumseagreen { 410 | background-color: mediumseagreen; 411 | } 412 | 413 | .mediumslateblue { 414 | background-color: mediumslateblue; 415 | } 416 | 417 | .mediumspringgreen { 418 | background-color: mediumspringgreen; 419 | } 420 | 421 | .mediumturquoise { 422 | background-color: mediumturquoise; 423 | } 424 | 425 | .mediumvioletred { 426 | background-color: mediumvioletred; 427 | } 428 | 429 | .midnightblue { 430 | background-color: midnightblue; 431 | } 432 | 433 | .mintcream { 434 | background-color: mintcream; 435 | } 436 | 437 | .mistyrose { 438 | background-color: mistyrose; 439 | } 440 | 441 | .moccasin { 442 | background-color: moccasin; 443 | } 444 | 445 | .navajowhite { 446 | background-color: navajowhite; 447 | } 448 | 449 | .oldlace { 450 | background-color: oldlace; 451 | } 452 | 453 | .olivedrab { 454 | background-color: olivedrab; 455 | } 456 | 457 | .orangered { 458 | background-color: orangered; 459 | } 460 | 461 | .orchid { 462 | background-color: orchid; 463 | } 464 | 465 | .palegoldenrod { 466 | background-color: palegoldenrod; 467 | } 468 | 469 | .palegreen { 470 | background-color: palegreen; 471 | } 472 | 473 | .paleturquoise { 474 | background-color: paleturquoise; 475 | } 476 | 477 | .palevioletred { 478 | background-color: palevioletred; 479 | } 480 | 481 | .papayawhip { 482 | background-color: papayawhip; 483 | } 484 | 485 | .peachpuff { 486 | background-color: peachpuff; 487 | } 488 | 489 | .peru { 490 | background-color: peru; 491 | } 492 | 493 | .pink { 494 | background-color: pink; 495 | } 496 | 497 | .plum { 498 | background-color: plum; 499 | } 500 | 501 | .powderblue { 502 | background-color: powderblue; 503 | } 504 | 505 | .rosybrown { 506 | background-color: rosybrown; 507 | } 508 | 509 | .royalblue { 510 | background-color: royalblue; 511 | } 512 | 513 | .saddlebrown { 514 | background-color: saddlebrown; 515 | } 516 | 517 | .salmon { 518 | background-color: salmon; 519 | } 520 | 521 | .sandybrown { 522 | background-color: sandybrown; 523 | } 524 | 525 | .seagreen { 526 | background-color: seagreen; 527 | } 528 | 529 | .seashell { 530 | background-color: seashell; 531 | } 532 | 533 | .sienna { 534 | background-color: sienna; 535 | } 536 | 537 | .skyblue { 538 | background-color: skyblue; 539 | } 540 | 541 | .slateblue { 542 | background-color: slateblue; 543 | } 544 | 545 | .slategray { 546 | background-color: slategray; 547 | } 548 | 549 | .slategrey { 550 | background-color: slategrey; 551 | } 552 | 553 | .snow { 554 | background-color: snow; 555 | } 556 | 557 | .springgreen { 558 | background-color: springgreen; 559 | } 560 | 561 | .steelblue { 562 | background-color: steelblue; 563 | } 564 | 565 | .tan { 566 | background-color: tan; 567 | } 568 | 569 | .thistle { 570 | background-color: thistle; 571 | } 572 | 573 | .tomato { 574 | background-color: tomato; 575 | } 576 | 577 | .turquoise { 578 | background-color: turquoise; 579 | } 580 | 581 | .violet { 582 | background-color: violet; 583 | } 584 | 585 | .wheat { 586 | background-color: wheat; 587 | } 588 | 589 | .whitesmoke { 590 | background-color: whitesmoke; 591 | } 592 | 593 | .yellowgreen { 594 | background-color: yellowgreen; 595 | } 596 | 597 | .rebeccapurple { 598 | background-color: rebeccapurple; 599 | } -------------------------------------------------------------------------------- /example/example2.html: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /shaders/solid.glslf: -------------------------------------------------------------------------------- 1 | varying vec4 v_Color; 2 | 3 | void main() { 4 | gl_FragColor = v_Color; 5 | } -------------------------------------------------------------------------------- /shaders/solid.glslv: -------------------------------------------------------------------------------- 1 | attribute vec2 a_Pos; 2 | attribute vec3 a_Color; 3 | varying vec4 v_Color; 4 | 5 | void main() { 6 | v_Color = vec4(a_Color, 1.0); 7 | gl_Position = vec4(a_Pos, 0.0, 1.0); 8 | } -------------------------------------------------------------------------------- /src/command.rs: -------------------------------------------------------------------------------- 1 | use css::{Color, Value}; 2 | use layout::{LayoutBox, Rectangle}; 3 | use std::fmt; 4 | 5 | pub type DisplayList = Vec; 6 | 7 | pub enum DisplayCommand { 8 | SolidRectangle(Color, Rectangle), 9 | } 10 | 11 | pub fn build_display_commands(root: &LayoutBox) -> DisplayList { 12 | let mut commands = Vec::new(); 13 | 14 | render_layout_box(&mut commands, root); 15 | commands 16 | } 17 | 18 | fn render_layout_box(commands: &mut DisplayList, layout_box: &LayoutBox) { 19 | render_background(commands, layout_box); 20 | render_borders(commands, layout_box); 21 | 22 | for child in &layout_box.children { 23 | render_layout_box(commands, child); 24 | } 25 | } 26 | 27 | fn render_background(commands: &mut DisplayList, layout_box: &LayoutBox) { 28 | get_color(layout_box, "background-color").map(|color| { 29 | commands.push(DisplayCommand::SolidRectangle( 30 | color, 31 | layout_box.dimensions.border_box(), 32 | )) 33 | }); 34 | } 35 | 36 | fn get_color(layout_box: &LayoutBox, name: &str) -> Option { 37 | match layout_box.styled_node.value(name) { 38 | Some(v) => match **v { 39 | Value::Color(ref c) => return Some(c.clone()), 40 | _ => return None, 41 | }, 42 | None => return None, 43 | } 44 | } 45 | 46 | fn render_borders(commands: &mut DisplayList, layout_box: &LayoutBox) { 47 | let color = match get_color(layout_box, "border-color") { 48 | Some(color) => color, 49 | _ => return, 50 | }; 51 | 52 | let d = &layout_box.dimensions; 53 | let border_box = d.border_box(); 54 | 55 | commands.push(DisplayCommand::SolidRectangle( 56 | color.clone(), 57 | Rectangle { 58 | x: border_box.x, 59 | y: border_box.y, 60 | width: d.border.left, 61 | height: border_box.height, 62 | }, 63 | )); 64 | 65 | commands.push(DisplayCommand::SolidRectangle( 66 | color.clone(), 67 | Rectangle { 68 | x: border_box.x + border_box.width - d.border.right, 69 | y: border_box.y, 70 | width: d.border.right, 71 | height: border_box.height, 72 | }, 73 | )); 74 | 75 | commands.push(DisplayCommand::SolidRectangle( 76 | color.clone(), 77 | Rectangle { 78 | x: border_box.x, 79 | y: border_box.y, 80 | width: border_box.width, 81 | height: d.border.top, 82 | }, 83 | )); 84 | 85 | commands.push(DisplayCommand::SolidRectangle( 86 | color, 87 | Rectangle { 88 | x: border_box.x, 89 | y: border_box.y + border_box.height - d.border.bottom, 90 | width: border_box.width, 91 | height: d.border.bottom, 92 | }, 93 | )); 94 | } 95 | 96 | impl fmt::Debug for DisplayCommand { 97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 98 | match *self { 99 | DisplayCommand::SolidRectangle(ref c, ref r) => write!(f, "{:?} {:?}", c, r), 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/css.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::default::Default; 3 | 4 | #[derive(PartialEq)] 5 | pub struct Stylesheet { 6 | pub rules: Vec, 7 | } 8 | #[derive(PartialEq)] 9 | pub struct Rule { 10 | pub selectors: Vec, 11 | pub declarations: Vec, 12 | } 13 | #[derive(PartialEq, Eq)] 14 | pub struct Selector { 15 | pub simple: Vec, 16 | pub combinators: Vec, 17 | } 18 | 19 | #[derive(PartialEq, Eq)] 20 | pub struct SimpleSelector { 21 | pub tag_name: Option, 22 | pub id: Option, 23 | pub classes: Vec, 24 | } 25 | #[derive(PartialEq)] 26 | pub struct Declaration { 27 | pub property: String, 28 | pub value: Value, 29 | } 30 | #[derive(PartialEq)] 31 | pub enum Value { 32 | Color(Color), 33 | Length(f32, Unit), 34 | Other(String), 35 | } 36 | #[derive(PartialEq)] 37 | pub enum Unit { 38 | Em, 39 | Ex, 40 | Ch, 41 | Rem, 42 | Vh, 43 | Vw, 44 | Vmin, 45 | Vmax, 46 | Px, 47 | Mm, 48 | Q, 49 | Cm, 50 | In, 51 | Pt, 52 | Pc, 53 | Pct, 54 | } 55 | 56 | #[derive(PartialEq, Clone)] 57 | pub struct Color { 58 | pub r: f32, 59 | pub g: f32, 60 | pub b: f32, 61 | pub a: f32, 62 | } 63 | 64 | 65 | impl Stylesheet { 66 | pub fn new(rules: Vec) -> Stylesheet { 67 | Stylesheet { rules } 68 | } 69 | } 70 | impl Default for Stylesheet { 71 | fn default() -> Self { 72 | Stylesheet { rules: Vec::new() } 73 | } 74 | } 75 | impl fmt::Debug for Stylesheet { 76 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 77 | let mut rule_result = String::new(); 78 | for rule in &self.rules { 79 | if rule_result.len() > 0 { 80 | rule_result.push_str("\n\n"); 81 | } 82 | rule_result.push_str(&format!("{:?}", rule)); 83 | } 84 | write!(f, "{}", rule_result) 85 | } 86 | } 87 | 88 | impl Rule { 89 | pub fn new(selectors: Vec, declarations: Vec) -> Rule { 90 | Rule { 91 | selectors, 92 | declarations, 93 | } 94 | } 95 | } 96 | 97 | impl Default for Rule { 98 | fn default() -> Self { 99 | Rule { 100 | selectors: Vec::new(), 101 | declarations: Vec::new(), 102 | } 103 | } 104 | } 105 | 106 | impl fmt::Debug for Rule { 107 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 108 | let mut sel_result = String::new(); 109 | let mut decl_result = String::new(); 110 | let tab = " "; 111 | 112 | for selector in &self.selectors { 113 | if sel_result.len() > 0 { 114 | sel_result.push_str(", "); 115 | } 116 | sel_result.push_str(&format!("{:?}", selector)); 117 | } 118 | 119 | for declaration in &self.declarations { 120 | decl_result.push_str(tab); 121 | decl_result.push_str(&format!("{:?}", declaration)); 122 | decl_result.push('\n'); 123 | } 124 | 125 | write!(f, "{} {{\n{}}}", sel_result, decl_result) 126 | } 127 | } 128 | 129 | 130 | impl Selector { 131 | pub fn new(simple: Vec, combinators: Vec) -> Selector { 132 | Selector { 133 | simple, 134 | combinators, 135 | } 136 | } 137 | } 138 | impl Default for Selector { 139 | fn default() -> Self { 140 | Selector { 141 | simple: Vec::new(), 142 | combinators: Vec::new(), 143 | } 144 | } 145 | } 146 | impl fmt::Debug for Selector { 147 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 148 | let mut result = String::new(); 149 | 150 | for sel in &self.simple { 151 | if result.len() > 0 { 152 | result.push_str(", "); 153 | } 154 | result.push_str(&format!("{:?}", sel)); 155 | } 156 | 157 | write!(f, "{}", result) 158 | } 159 | } 160 | 161 | impl SimpleSelector { 162 | pub fn new( 163 | tag_name: Option, 164 | id: Option, 165 | classes: Vec, 166 | ) -> SimpleSelector { 167 | SimpleSelector { 168 | tag_name, 169 | id, 170 | classes, 171 | } 172 | } 173 | } 174 | 175 | impl Default for SimpleSelector { 176 | fn default() -> Self { 177 | SimpleSelector { 178 | tag_name: None, 179 | id: None, 180 | classes: Vec::new(), 181 | } 182 | } 183 | } 184 | 185 | impl fmt::Debug for SimpleSelector { 186 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 187 | let mut result = String::new(); 188 | 189 | match self.tag_name { 190 | Some(ref t) => result.push_str(t), 191 | None => {} 192 | } 193 | 194 | match self.id { 195 | Some(ref s) => { 196 | result.push('#'); 197 | result.push_str(s); 198 | } 199 | None => {} 200 | } 201 | 202 | for class in &self.classes { 203 | result.push('.'); 204 | result.push_str(class); 205 | } 206 | 207 | write!(f, "{}", result) 208 | } 209 | } 210 | 211 | impl Declaration { 212 | pub fn new(property: String, value: Value) -> Declaration { 213 | Declaration { property, value } 214 | } 215 | } 216 | 217 | impl Default for Declaration { 218 | fn default() -> Self { 219 | Declaration { 220 | property: String::from(""), 221 | value: Value::Other(String::from("")), 222 | } 223 | } 224 | } 225 | 226 | 227 | impl fmt::Debug for Declaration { 228 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 229 | write!(f, "{}: {:?}", self.property, self.value) 230 | } 231 | } 232 | 233 | impl fmt::Debug for Value { 234 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 235 | match *self { 236 | Value::Color(ref c) => write!(f, "{:?}", c), 237 | Value::Length(l, _) => write!(f, "{:?}", l), 238 | Value::Other(ref s) => write!(f, "{:?}", s), 239 | } 240 | } 241 | } 242 | 243 | impl Color { 244 | pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { 245 | Color { r, g, b, a } 246 | } 247 | } 248 | 249 | impl Default for Color { 250 | fn default() -> Self { 251 | Color::new(1.0, 1.0, 1.0, 1.0) 252 | } 253 | } 254 | 255 | impl fmt::Debug for Color { 256 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 257 | write!(f, "r: {} g: {} b: {} a: {}", self.r, self.g, self.b, self.a) 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/css_parser.rs: -------------------------------------------------------------------------------- 1 | use css::{Color, Declaration, Rule, Selector, SimpleSelector, Stylesheet, Unit, Value}; 2 | 3 | use std::iter::Peekable; 4 | use std::str::Chars; 5 | 6 | pub struct CssParser<'a> { 7 | chars: Peekable>, 8 | } 9 | 10 | impl<'a> CssParser<'a> { 11 | pub fn new(full_css: &str) -> CssParser { 12 | CssParser { 13 | chars: full_css.chars().peekable(), 14 | } 15 | } 16 | 17 | pub fn parse_stylesheet(&mut self) -> Stylesheet { 18 | let mut stylesheet = Stylesheet::default(); 19 | 20 | while self.chars.peek().is_some() { 21 | let selectors = self.parse_selectors(); 22 | let styles = self.parse_declarations(); 23 | let rule = Rule::new(selectors, styles); 24 | 25 | 26 | stylesheet.rules.push(rule); 27 | } 28 | 29 | stylesheet 30 | } 31 | 32 | fn parse_selectors(&mut self) -> Vec { 33 | let mut selectors = Vec::new(); 34 | 35 | while self.chars.peek().map_or(false, |c| *c != '{') { 36 | let selector = self.parse_selector(); 37 | 38 | if selector != Selector::default() { 39 | selectors.push(selector); 40 | } 41 | 42 | self.consume_while(char::is_whitespace); 43 | if self.chars.peek().map_or(false, |c| *c == ',') { 44 | self.chars.next(); 45 | } 46 | } 47 | 48 | self.chars.next(); 49 | selectors 50 | } 51 | 52 | fn parse_selector(&mut self) -> Selector { 53 | let mut sselector = SimpleSelector::default(); 54 | let mut selector = Selector::default(); 55 | 56 | self.consume_while(char::is_whitespace); 57 | 58 | sselector.tag_name = match self.chars.peek() { 59 | Some(&c) if is_valid_start_ident(c) => Some(self.parse_identifier()), 60 | _ => None, 61 | }; 62 | 63 | let mut multiple_ids = false; 64 | while self.chars 65 | .peek() 66 | .map_or(false, |c| *c != ',' && *c != '{' && !(*c).is_whitespace()) 67 | { 68 | match self.chars.peek() { 69 | Some(&c) if c == '#' => { 70 | self.chars.next(); 71 | if sselector.id.is_some() || multiple_ids { 72 | sselector.id = None; 73 | multiple_ids = true; 74 | self.parse_id(); 75 | } else { 76 | sselector.id = self.parse_id(); 77 | } 78 | } 79 | Some(&c) if c == '.' => { 80 | self.chars.next(); 81 | let class_name = self.parse_identifier(); 82 | 83 | if class_name != String::from("") { 84 | sselector.classes.push(class_name); 85 | } 86 | } 87 | _ => { 88 | self.consume_while(|c| c != ',' && c != '{'); 89 | } 90 | } 91 | } 92 | 93 | if sselector != SimpleSelector::default() { 94 | selector.simple.push(sselector); 95 | } 96 | 97 | selector 98 | } 99 | 100 | fn parse_identifier(&mut self) -> String { 101 | let mut ident = String::new(); 102 | 103 | match self.chars.peek() { 104 | Some(&c) => if is_valid_start_ident(c) { 105 | ident.push_str(&self.consume_while(is_valid_ident)) 106 | }, 107 | None => {} 108 | } 109 | 110 | ident.to_lowercase() 111 | } 112 | 113 | fn parse_id(&mut self) -> Option { 114 | match &self.parse_identifier()[..] { 115 | "" => None, 116 | s @ _ => Some(s.to_string()), 117 | } 118 | } 119 | 120 | fn parse_declarations(&mut self) -> Vec { 121 | let mut declarations = Vec::::new(); 122 | 123 | while self.chars.peek().map_or(false, |c| *c != '}') { 124 | self.consume_while(char::is_whitespace); 125 | 126 | let property = self.consume_while(|x| x != ':').to_lowercase(); 127 | 128 | self.chars.next(); 129 | self.consume_while(char::is_whitespace); 130 | 131 | let value = self.consume_while(|x| x != ';' && x != '\n' && x != '}') 132 | .to_lowercase(); 133 | 134 | let value_enum = match property.as_ref() { 135 | "background-color" | "border-color" | "color" => { 136 | Value::Color(translate_color(&value)) 137 | } 138 | "margin-right" | 139 | "margin-bottom" | 140 | "margin-left" | 141 | "margin-top" | 142 | "padding-right" | 143 | "padding-bottom" | 144 | "padding-left" | 145 | "padding-top" | 146 | "border-right-width" | 147 | "border-bottom-width" | 148 | "border-left-width" | 149 | "border-top-width" | 150 | "height" | 151 | "width" => translate_length(&value), 152 | _ => Value::Other(value), 153 | }; 154 | 155 | let declaration = Declaration::new(property, value_enum); 156 | 157 | if self.chars.peek().map_or(false, |c| *c == ';') { 158 | declarations.push(declaration); 159 | self.chars.next(); 160 | } else { 161 | self.consume_while(char::is_whitespace); 162 | if self.chars.peek().map_or(false, |c| *c == '}') { 163 | declarations.push(declaration); 164 | } 165 | } 166 | self.consume_while(char::is_whitespace); 167 | } 168 | 169 | self.chars.next(); 170 | declarations 171 | } 172 | 173 | 174 | fn consume_while(&mut self, condition: F) -> String 175 | where 176 | F: Fn(char) -> bool, 177 | { 178 | let mut result = String::new(); 179 | while self.chars.peek().map_or(false, |c| condition(*c)) { 180 | result.push(self.chars.next().unwrap()); 181 | } 182 | 183 | result 184 | } 185 | } 186 | 187 | fn translate_length(value: &str) -> Value { 188 | let mut num_str = String::new(); 189 | let mut unit = String::new(); 190 | let mut parsing_num = true; 191 | 192 | for c in value.chars() { 193 | if c.is_numeric() && parsing_num { 194 | num_str.push(c); 195 | } else { 196 | unit.push(c); 197 | parsing_num = false; 198 | } 199 | } 200 | 201 | let number = num_str.parse().unwrap_or(0.0); 202 | 203 | match unit.as_ref() { 204 | "em" => Value::Length(number, Unit::Em), 205 | "ex" => Value::Length(number, Unit::Ex), 206 | "ch" => Value::Length(number, Unit::Ch), 207 | "rem" => Value::Length(number, Unit::Rem), 208 | "vh" => Value::Length(number, Unit::Vh), 209 | "vw" => Value::Length(number, Unit::Vw), 210 | "vmin" => Value::Length(number, Unit::Vmin), 211 | "vmax" => Value::Length(number, Unit::Vmax), 212 | "px" | "" => Value::Length(number, Unit::Px), 213 | "mm" => Value::Length(number, Unit::Mm), 214 | "q" => Value::Length(number, Unit::Q), 215 | "cm" => Value::Length(number, Unit::Cm), 216 | "in" => Value::Length(number, Unit::In), 217 | "pt" => Value::Length(number, Unit::Pt), 218 | "pc" => Value::Length(number, Unit::Pc), 219 | "%" => Value::Length(number, Unit::Pct), 220 | _ => Value::Length(number, Unit::Px), 221 | } 222 | } 223 | 224 | 225 | fn translate_color(color: &str) -> Color { 226 | if color.starts_with("#") { 227 | if color.len() == 7 { 228 | let red = match u8::from_str_radix(&color[1..3], 16) { 229 | Ok(n) => n as f32 / 255.0, 230 | Err(_) => 0.0, 231 | }; 232 | let green = match u8::from_str_radix(&color[3..5], 16) { 233 | Ok(n) => n as f32 / 255.0, 234 | Err(_) => 0.0, 235 | }; 236 | let blue = match u8::from_str_radix(&color[5..7], 16) { 237 | Ok(n) => n as f32 / 255.0, 238 | Err(_) => 0.0, 239 | }; 240 | return Color::new(red, green, blue, 1.0); 241 | } else if color.len() == 4 { 242 | let red = match u8::from_str_radix(&color[1..2], 16) { 243 | Ok(n) => n as f32 / 15.0, 244 | Err(_) => 0.0, 245 | }; 246 | let green = match u8::from_str_radix(&color[2..3], 16) { 247 | Ok(n) => n as f32 / 15.0, 248 | Err(_) => 0.0, 249 | }; 250 | let blue = match u8::from_str_radix(&color[3..4], 16) { 251 | Ok(n) => n as f32 / 15.0, 252 | Err(_) => 0.0, 253 | }; 254 | return Color::new(red, green, blue, 1.0); 255 | } else { 256 | return Color::default(); 257 | } 258 | } else if color.starts_with("rgb") { 259 | return Color::default(); 260 | } else if color.starts_with("hsl") { 261 | return Color::default(); 262 | } else { 263 | return match color { 264 | "black" => Color::new(0.0, 0.0, 0.0, 1.0), 265 | "silver" => Color::new( 266 | 0.7529411764705882, 267 | 0.7529411764705882, 268 | 0.7529411764705882, 269 | 1.0, 270 | ), 271 | "gray" | "grey" => Color::new( 272 | 0.5019607843137255, 273 | 0.5019607843137255, 274 | 0.5019607843137255, 275 | 1.0, 276 | ), 277 | "white" => Color::new(1.0, 1.0, 1.0, 1.0), 278 | "maroon" => Color::new(0.5019607843137255, 0.0, 0.0, 1.0), 279 | "red" => Color::new(1.0, 0.0, 0.0, 1.0), 280 | "purple" => Color::new(0.5019607843137255, 0.0, 0.5019607843137255, 1.0), 281 | "fuchsia" => Color::new(1.0, 0.0, 1.0, 1.0), 282 | "green" => Color::new(0.0, 0.5019607843137255, 0.0, 1.0), 283 | "lime" => Color::new(0.0, 1.0, 0.0, 1.0), 284 | "olive" => Color::new(0.5019607843137255, 0.5019607843137255, 0.0, 1.0), 285 | "yellow" => Color::new(1.0, 1.0, 0.0, 1.0), 286 | "navy" => Color::new(0.0, 0.0, 0.5019607843137255, 1.0), 287 | "blue" => Color::new(0.0, 0.0, 1.0, 1.0), 288 | "teal" => Color::new(0.0, 0.5019607843137255, 0.5019607843137255, 1.0), 289 | "aqua" => Color::new(0.0, 1.0, 1.0, 1.0), 290 | "orange" => Color::new(1.0, 0.6470588235294118, 0.0, 1.0), 291 | "aliceblue" => Color::new(0.9411764705882353, 0.9725490196078431, 1.0, 1.0), 292 | "antiquewhite" => Color::new( 293 | 0.9803921568627451, 294 | 0.9215686274509803, 295 | 0.8431372549019608, 296 | 1.0, 297 | ), 298 | "aquamarine" => Color::new(0.4980392156862745, 1.0, 0.8313725490196079, 1.0), 299 | "azure" => Color::new(0.9411764705882353, 1.0, 1.0, 1.0), 300 | "beige" => Color::new( 301 | 0.9607843137254902, 302 | 0.9607843137254902, 303 | 0.8627450980392157, 304 | 1.0, 305 | ), 306 | "bisque" => Color::new(1.0, 0.8941176470588236, 0.7686274509803922, 1.0), 307 | "blanchedalmond" => Color::new(1.0, 0.9215686274509803, 0.803921568627451, 1.0), 308 | "blueviolet" => Color::new( 309 | 0.5411764705882353, 310 | 0.16862745098039217, 311 | 0.8862745098039215, 312 | 1.0, 313 | ), 314 | "brown" => Color::new( 315 | 0.6470588235294118, 316 | 0.16470588235294117, 317 | 0.16470588235294117, 318 | 1.0, 319 | ), 320 | "burlywood" => Color::new( 321 | 0.8705882352941177, 322 | 0.7215686274509804, 323 | 0.5294117647058824, 324 | 1.0, 325 | ), 326 | "cadetblue" => Color::new( 327 | 0.37254901960784315, 328 | 0.6196078431372549, 329 | 0.6274509803921569, 330 | 1.0, 331 | ), 332 | "chartreuse" => Color::new(0.4980392156862745, 1.0, 0.0, 1.0), 333 | "chocolate" => Color::new( 334 | 0.8235294117647058, 335 | 0.4117647058823529, 336 | 0.11764705882352941, 337 | 1.0, 338 | ), 339 | "coral" => Color::new(1.0, 0.4980392156862745, 0.3137254901960784, 1.0), 340 | "cornflowerblue" => Color::new( 341 | 0.39215686274509803, 342 | 0.5843137254901961, 343 | 0.9294117647058824, 344 | 1.0, 345 | ), 346 | "cornsilk" => Color::new(1.0, 0.9725490196078431, 0.8627450980392157, 1.0), 347 | "crimson" => Color::new( 348 | 0.8627450980392157, 349 | 0.0784313725490196, 350 | 0.23529411764705882, 351 | 1.0, 352 | ), 353 | "darkblue" => Color::new(0.0, 0.0, 0.5450980392156862, 1.0), 354 | "darkcyan" => Color::new(0.0, 0.5450980392156862, 0.5450980392156862, 1.0), 355 | "darkgoldenrod" => Color::new( 356 | 0.7215686274509804, 357 | 0.5254901960784314, 358 | 0.043137254901960784, 359 | 1.0, 360 | ), 361 | "darkgray" | "darkgrey" => Color::new( 362 | 0.6627450980392157, 363 | 0.6627450980392157, 364 | 0.6627450980392157, 365 | 1.0, 366 | ), 367 | "darkgreen" => Color::new(0.0, 0.39215686274509803, 0.0, 1.0), 368 | "darkkhaki" => Color::new( 369 | 0.7411764705882353, 370 | 0.7176470588235294, 371 | 0.4196078431372549, 372 | 1.0, 373 | ), 374 | "darkmagenta" => Color::new(0.5450980392156862, 0.0, 0.5450980392156862, 1.0), 375 | "darkolivegreen" => Color::new( 376 | 0.3333333333333333, 377 | 0.4196078431372549, 378 | 0.1843137254901961, 379 | 1.0, 380 | ), 381 | "darkorange" => Color::new(1.0, 0.5490196078431373, 0.0, 1.0), 382 | "darkorchid" => Color::new(0.6, 0.19607843137254902, 0.8, 1.0), 383 | "darkred" => Color::new(0.5450980392156862, 0.0, 0.0, 1.0), 384 | "darksalmon" => Color::new( 385 | 0.9137254901960784, 386 | 0.5882352941176471, 387 | 0.47843137254901963, 388 | 1.0, 389 | ), 390 | "darkseagreen" => Color::new( 391 | 0.5607843137254902, 392 | 0.7372549019607844, 393 | 0.5607843137254902, 394 | 1.0, 395 | ), 396 | "darkslateblue" => Color::new( 397 | 0.2823529411764706, 398 | 0.23921568627450981, 399 | 0.5450980392156862, 400 | 1.0, 401 | ), 402 | "darkslategray" | "darkslategrey" => Color::new( 403 | 0.1843137254901961, 404 | 0.30980392156862746, 405 | 0.30980392156862746, 406 | 1.0, 407 | ), 408 | "darkturquoise" => Color::new(0.0, 0.807843137254902, 0.8196078431372549, 1.0), 409 | "darkviolet" => Color::new(0.5803921568627451, 0.0, 0.8274509803921568, 1.0), 410 | "deeppink" => Color::new(1.0, 0.0784313725490196, 0.5764705882352941, 1.0), 411 | "deepskyblue" => Color::new(0.0, 0.7490196078431373, 1.0, 1.0), 412 | "dimgray" | "dimgrey" => Color::new( 413 | 0.4117647058823529, 414 | 0.4117647058823529, 415 | 0.4117647058823529, 416 | 1.0, 417 | ), 418 | "dodgerblue" => Color::new(0.11764705882352941, 0.5647058823529412, 1.0, 1.0), 419 | "firebrick" => Color::new( 420 | 0.6980392156862745, 421 | 0.13333333333333333, 422 | 0.13333333333333333, 423 | 1.0, 424 | ), 425 | "floralwhite" => Color::new(1.0, 0.9803921568627451, 0.9411764705882353, 1.0), 426 | "forestgreen" => Color::new( 427 | 0.13333333333333333, 428 | 0.5450980392156862, 429 | 0.13333333333333333, 430 | 1.0, 431 | ), 432 | "gainsboro" => Color::new( 433 | 0.8627450980392157, 434 | 0.8627450980392157, 435 | 0.8627450980392157, 436 | 1.0, 437 | ), 438 | "ghostwhite" => Color::new(0.9725490196078431, 0.9725490196078431, 1.0, 1.0), 439 | "gold" => Color::new(1.0, 0.8431372549019608, 0.0, 1.0), 440 | "goldenrod" => Color::new( 441 | 0.8549019607843137, 442 | 0.6470588235294118, 443 | 0.12549019607843137, 444 | 1.0, 445 | ), 446 | "greenyellow" => Color::new(0.6784313725490196, 1.0, 0.1843137254901961, 1.0), 447 | "honeydew" => Color::new(0.9411764705882353, 1.0, 0.9411764705882353, 1.0), 448 | "hotpink" => Color::new(1.0, 0.4117647058823529, 0.7058823529411765, 1.0), 449 | "indianred" => Color::new( 450 | 0.803921568627451, 451 | 0.3607843137254902, 452 | 0.3607843137254902, 453 | 1.0, 454 | ), 455 | "indigo" => Color::new(0.29411764705882354, 0.0, 0.5098039215686274, 1.0), 456 | "ivory" => Color::new(1.0, 1.0, 0.9411764705882353, 1.0), 457 | "khaki" => Color::new( 458 | 0.9411764705882353, 459 | 0.9019607843137255, 460 | 0.5490196078431373, 461 | 1.0, 462 | ), 463 | "lavender" => Color::new( 464 | 0.9019607843137255, 465 | 0.9019607843137255, 466 | 0.9803921568627451, 467 | 1.0, 468 | ), 469 | "lavenderblush" => Color::new(1.0, 0.9411764705882353, 0.9607843137254902, 1.0), 470 | "lawngreen" => Color::new(0.48627450980392156, 0.9882352941176471, 0.0, 1.0), 471 | "lemonchiffon" => Color::new(1.0, 0.9803921568627451, 0.803921568627451, 1.0), 472 | "lightblue" => Color::new( 473 | 0.6784313725490196, 474 | 0.8470588235294118, 475 | 0.9019607843137255, 476 | 1.0, 477 | ), 478 | "lightcoral" => Color::new( 479 | 0.9411764705882353, 480 | 0.5019607843137255, 481 | 0.5019607843137255, 482 | 1.0, 483 | ), 484 | "lightcyan" => Color::new(0.8784313725490196, 1.0, 1.0, 1.0), 485 | "lightgoldenrodyellow" => Color::new( 486 | 0.9803921568627451, 487 | 0.9803921568627451, 488 | 0.8235294117647058, 489 | 1.0, 490 | ), 491 | "lightgray" | "lightgrey" => Color::new( 492 | 0.8274509803921568, 493 | 0.8274509803921568, 494 | 0.8274509803921568, 495 | 1.0, 496 | ), 497 | "lightgreen" => Color::new( 498 | 0.5647058823529412, 499 | 0.9333333333333333, 500 | 0.5647058823529412, 501 | 1.0, 502 | ), 503 | "lightpink" => Color::new(1.0, 0.7137254901960784, 0.7568627450980392, 1.0), 504 | "lightsalmon" => Color::new(1.0, 0.6274509803921569, 0.47843137254901963, 1.0), 505 | "lightseagreen" => Color::new( 506 | 0.12549019607843137, 507 | 0.6980392156862745, 508 | 0.6666666666666666, 509 | 1.0, 510 | ), 511 | "lightskyblue" => Color::new( 512 | 0.5294117647058824, 513 | 0.807843137254902, 514 | 0.9803921568627451, 515 | 1.0, 516 | ), 517 | "lightslategray" | "lightslategrey" => { 518 | Color::new(0.4666666666666667, 0.5333333333333333, 0.6, 1.0) 519 | } 520 | "lightsteelblue" => Color::new( 521 | 0.6901960784313725, 522 | 0.7686274509803922, 523 | 0.8705882352941177, 524 | 1.0, 525 | ), 526 | "lightyellow" => Color::new(1.0, 1.0, 0.8784313725490196, 1.0), 527 | "limegreen" => Color::new( 528 | 0.19607843137254902, 529 | 0.803921568627451, 530 | 0.19607843137254902, 531 | 1.0, 532 | ), 533 | "linen" => Color::new( 534 | 0.9803921568627451, 535 | 0.9411764705882353, 536 | 0.9019607843137255, 537 | 1.0, 538 | ), 539 | "mediumaquamarine" => Color::new(0.4, 0.803921568627451, 0.6666666666666666, 1.0), 540 | "mediumblue" => Color::new(0.0, 0.0, 0.803921568627451, 1.0), 541 | "mediumorchid" => Color::new( 542 | 0.7294117647058823, 543 | 0.3333333333333333, 544 | 0.8274509803921568, 545 | 1.0, 546 | ), 547 | "mediumpurple" => Color::new( 548 | 0.5764705882352941, 549 | 0.4392156862745098, 550 | 0.8588235294117647, 551 | 1.0, 552 | ), 553 | "mediumseagreen" => Color::new( 554 | 0.23529411764705882, 555 | 0.7019607843137254, 556 | 0.44313725490196076, 557 | 1.0, 558 | ), 559 | "mediumslateblue" => Color::new( 560 | 0.4823529411764706, 561 | 0.40784313725490196, 562 | 0.9333333333333333, 563 | 1.0, 564 | ), 565 | "mediumspringgreen" => Color::new(0.0, 0.9803921568627451, 0.6039215686274509, 1.0), 566 | "mediumturquoise" => Color::new(0.2823529411764706, 0.8196078431372549, 0.8, 1.0), 567 | "mediumvioletred" => Color::new( 568 | 0.7803921568627451, 569 | 0.08235294117647059, 570 | 0.5215686274509804, 571 | 1.0, 572 | ), 573 | "midnightblue" => Color::new( 574 | 0.09803921568627451, 575 | 0.09803921568627451, 576 | 0.4392156862745098, 577 | 1.0, 578 | ), 579 | "mintcream" => Color::new(0.9607843137254902, 1.0, 0.9803921568627451, 1.0), 580 | "mistyrose" => Color::new(1.0, 0.8941176470588236, 0.8823529411764706, 1.0), 581 | "moccasin" => Color::new(1.0, 0.8941176470588236, 0.7098039215686275, 1.0), 582 | "navajowhite" => Color::new(1.0, 0.8705882352941177, 0.6784313725490196, 1.0), 583 | "oldlace" => Color::new( 584 | 0.9921568627450981, 585 | 0.9607843137254902, 586 | 0.9019607843137255, 587 | 1.0, 588 | ), 589 | "olivedrab" => Color::new( 590 | 0.4196078431372549, 591 | 0.5568627450980392, 592 | 0.13725490196078433, 593 | 1.0, 594 | ), 595 | "orangered" => Color::new(1.0, 0.27058823529411763, 0.0, 1.0), 596 | "orchid" => Color::new( 597 | 0.8549019607843137, 598 | 0.4392156862745098, 599 | 0.8392156862745098, 600 | 1.0, 601 | ), 602 | "palegoldenrod" => Color::new( 603 | 0.9333333333333333, 604 | 0.9098039215686274, 605 | 0.6666666666666666, 606 | 1.0, 607 | ), 608 | "palegreen" => Color::new(0.596078431372549, 0.984313725490196, 0.596078431372549, 1.0), 609 | "paleturquoise" => Color::new( 610 | 0.6862745098039216, 611 | 0.9333333333333333, 612 | 0.9333333333333333, 613 | 1.0, 614 | ), 615 | "palevioletred" => Color::new( 616 | 0.8588235294117647, 617 | 0.4392156862745098, 618 | 0.5764705882352941, 619 | 1.0, 620 | ), 621 | "papayawhip" => Color::new(1.0, 0.9372549019607843, 0.8352941176470589, 1.0), 622 | "peachpuff" => Color::new(1.0, 0.8549019607843137, 0.7254901960784313, 1.0), 623 | "peru" => Color::new( 624 | 0.803921568627451, 625 | 0.5215686274509804, 626 | 0.24705882352941178, 627 | 1.0, 628 | ), 629 | "pink" => Color::new(1.0, 0.7529411764705882, 0.796078431372549, 1.0), 630 | "plum" => Color::new( 631 | 0.8666666666666667, 632 | 0.6274509803921569, 633 | 0.8666666666666667, 634 | 1.0, 635 | ), 636 | "powderblue" => Color::new( 637 | 0.6901960784313725, 638 | 0.8784313725490196, 639 | 0.9019607843137255, 640 | 1.0, 641 | ), 642 | "rosybrown" => Color::new( 643 | 0.7372549019607844, 644 | 0.5607843137254902, 645 | 0.5607843137254902, 646 | 1.0, 647 | ), 648 | "royalblue" => Color::new( 649 | 0.2549019607843137, 650 | 0.4117647058823529, 651 | 0.8823529411764706, 652 | 1.0, 653 | ), 654 | "saddlebrown" => Color::new( 655 | 0.5450980392156862, 656 | 0.27058823529411763, 657 | 0.07450980392156863, 658 | 1.0, 659 | ), 660 | "salmon" => Color::new( 661 | 0.9803921568627451, 662 | 0.5019607843137255, 663 | 0.4470588235294118, 664 | 1.0, 665 | ), 666 | "sandybrown" => Color::new( 667 | 0.9568627450980393, 668 | 0.6431372549019608, 669 | 0.3764705882352941, 670 | 1.0, 671 | ), 672 | "seagreen" => Color::new( 673 | 0.1803921568627451, 674 | 0.5450980392156862, 675 | 0.3411764705882353, 676 | 1.0, 677 | ), 678 | "seashell" => Color::new(1.0, 0.9607843137254902, 0.9333333333333333, 1.0), 679 | "sienna" => Color::new( 680 | 0.6274509803921569, 681 | 0.3215686274509804, 682 | 0.17647058823529413, 683 | 1.0, 684 | ), 685 | "skyblue" => Color::new( 686 | 0.5294117647058824, 687 | 0.807843137254902, 688 | 0.9215686274509803, 689 | 1.0, 690 | ), 691 | "slateblue" => Color::new( 692 | 0.41568627450980394, 693 | 0.35294117647058826, 694 | 0.803921568627451, 695 | 1.0, 696 | ), 697 | "slategray" | "slategrey" => Color::new( 698 | 0.4392156862745098, 699 | 0.5019607843137255, 700 | 0.5647058823529412, 701 | 1.0, 702 | ), 703 | "snow" => Color::new(1.0, 0.9803921568627451, 0.9803921568627451, 1.0), 704 | "springgreen" => Color::new(0.0, 1.0, 0.4980392156862745, 1.0), 705 | "steelblue" => Color::new( 706 | 0.27450980392156865, 707 | 0.5098039215686274, 708 | 0.7058823529411765, 709 | 1.0, 710 | ), 711 | "tan" => Color::new( 712 | 0.8235294117647058, 713 | 0.7058823529411765, 714 | 0.5490196078431373, 715 | 1.0, 716 | ), 717 | "thistle" => Color::new( 718 | 0.8470588235294118, 719 | 0.7490196078431373, 720 | 0.8470588235294118, 721 | 1.0, 722 | ), 723 | "tomato" => Color::new(1.0, 0.38823529411764707, 0.2784313725490196, 1.0), 724 | "turquoise" => Color::new( 725 | 0.25098039215686274, 726 | 0.8784313725490196, 727 | 0.8156862745098039, 728 | 1.0, 729 | ), 730 | "violet" => Color::new( 731 | 0.9333333333333333, 732 | 0.5098039215686274, 733 | 0.9333333333333333, 734 | 1.0, 735 | ), 736 | "wheat" => Color::new( 737 | 0.9607843137254902, 738 | 0.8705882352941177, 739 | 0.7019607843137254, 740 | 1.0, 741 | ), 742 | "whitesmoke" => Color::new( 743 | 0.9607843137254902, 744 | 0.9607843137254902, 745 | 0.9607843137254902, 746 | 1.0, 747 | ), 748 | "yellowgreen" => Color::new( 749 | 0.6039215686274509, 750 | 0.803921568627451, 751 | 0.19607843137254902, 752 | 1.0, 753 | ), 754 | "rebeccapurple" => Color::new(0.4, 0.2, 0.6, 1.0), 755 | _ => Color::new(0.0, 0.0, 0.0, 1.0), 756 | }; 757 | } 758 | } 759 | 760 | fn is_valid_ident(c: char) -> bool { 761 | is_valid_start_ident(c) || c.is_digit(10) || c == '-' 762 | } 763 | 764 | fn is_valid_start_ident(c: char) -> bool { 765 | is_letter(c) || is_non_ascii(c) || c == '_' 766 | } 767 | 768 | fn is_letter(c: char) -> bool { 769 | is_upper_letter(c) || is_lower_letter(c) 770 | } 771 | 772 | fn is_upper_letter(c: char) -> bool { 773 | c >= 'A' && c <= 'Z' 774 | } 775 | 776 | fn is_lower_letter(c: char) -> bool { 777 | c >= 'a' && c <= 'z' 778 | } 779 | 780 | fn is_non_ascii(c: char) -> bool { 781 | c >= '\u{0080}' 782 | } 783 | -------------------------------------------------------------------------------- /src/dom.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use std::fmt; 3 | 4 | #[derive(PartialEq, Eq)] 5 | pub struct Node { 6 | pub children: Vec, 7 | pub node_type: NodeType, 8 | } 9 | 10 | #[derive(PartialEq, Eq, Clone)] 11 | pub enum NodeType { 12 | Text(String), 13 | Element(ElementData), 14 | Comment(String), 15 | } 16 | 17 | #[derive(PartialEq, Eq, Clone)] 18 | pub struct ElementData { 19 | pub tag_name: String, 20 | attributes: AttrMap, 21 | } 22 | 23 | impl ElementData { 24 | pub fn new(tag_name: String, attributes: AttrMap) -> ElementData { 25 | ElementData { 26 | tag_name, 27 | attributes, 28 | } 29 | } 30 | 31 | pub fn get_id(&self) -> Option<&String> { 32 | self.attributes.get("id") 33 | } 34 | 35 | pub fn get_classes(&self) -> HashSet<&str> { 36 | match self.attributes.get("class") { 37 | Some(s) => s.split(' ').collect(), 38 | None => HashSet::new(), 39 | } 40 | } 41 | } 42 | 43 | pub type AttrMap = HashMap; 44 | 45 | impl Node { 46 | pub fn new(node_type: NodeType, children: Vec) -> Node { 47 | Node { 48 | node_type, 49 | children, 50 | } 51 | } 52 | } 53 | 54 | impl fmt::Debug for Node { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | write!(f, "{:?}", self.node_type) 57 | } 58 | } 59 | 60 | impl fmt::Debug for NodeType { 61 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 62 | match *self { 63 | NodeType::Text(ref t) | NodeType::Comment(ref t) => write!(f, "{}", t), 64 | NodeType::Element(ref e) => write!(f, "{:?}", e), 65 | } 66 | } 67 | } 68 | 69 | impl fmt::Debug for ElementData { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | let mut attributes_string = String::new(); 72 | 73 | for (attr, value) in self.attributes.iter() { 74 | attributes_string.push_str(&format!(" {}=\"{}\"", attr, value)); 75 | } 76 | write!(f, "<{},{}>", self.tag_name, attributes_string) 77 | } 78 | } 79 | 80 | 81 | pub fn pretty_print(n: &Node, indent_size: usize) { 82 | let indent = (0..indent_size).map(|_| " ").collect::(); 83 | 84 | match n.node_type { 85 | NodeType::Element(ref e) => println!("{}{:?}", indent, e), 86 | NodeType::Text(ref t) => println!("{}{}", indent, t), 87 | NodeType::Comment(ref c) => println!("{}", indent, c), 88 | } 89 | 90 | for child in n.children.iter() { 91 | pretty_print(&child, indent_size + 2); 92 | } 93 | 94 | match n.node_type { 95 | NodeType::Element(ref e) => println!("{}<{}/>", indent, e.tag_name), 96 | _ => {} 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/html_parse.rs: -------------------------------------------------------------------------------- 1 | use dom::{AttrMap, ElementData, Node, NodeType}; 2 | 3 | use std::iter::Peekable; 4 | use std::str::Chars; 5 | 6 | 7 | pub struct HtmlParser<'a> { 8 | chars: Peekable>, 9 | node_q: Vec, 10 | } 11 | 12 | impl<'a> HtmlParser<'a> { 13 | pub fn new(full_html: &str) -> HtmlParser { 14 | HtmlParser { 15 | chars: full_html.chars().peekable(), 16 | node_q: Vec::new(), 17 | } 18 | } 19 | 20 | pub fn parse_nodes(&mut self) -> Vec { 21 | let mut nodes = Vec::new(); 22 | 23 | while self.chars.peek().is_some() { 24 | self.consume_while(char::is_whitespace); 25 | if self.chars.peek().map_or(false, |c| *c == '<') { 26 | self.chars.next(); 27 | if self.chars.peek().map_or(false, |c| *c == '/') { 28 | self.chars.next(); 29 | self.consume_while(char::is_whitespace); 30 | 31 | let close_tag_name = self.consume_while(is_valid_tag_name); 32 | 33 | self.consume_while(|x| x != '>'); 34 | self.chars.next(); 35 | 36 | self.node_q.push(close_tag_name); 37 | break; 38 | } else if self.chars.peek().map_or(false, |c| *c == '!') { 39 | self.chars.next(); 40 | nodes.push(self.parse_comment_node()); 41 | } else { 42 | let mut node = self.parse_node(); 43 | let insert_index = nodes.len(); 44 | 45 | match node.node_type { 46 | NodeType::Element(ref e) => if self.node_q.len() > 0 { 47 | let assumed_tag = self.node_q.remove(0); 48 | 49 | if e.tag_name != assumed_tag { 50 | nodes.append(&mut node.children); 51 | self.node_q.insert(0, assumed_tag); 52 | } 53 | }, 54 | _ => {} 55 | } 56 | 57 | nodes.insert(insert_index, node); 58 | } 59 | } else { 60 | nodes.push(self.parse_text_node()); 61 | } 62 | } 63 | nodes 64 | } 65 | 66 | fn parse_node(&mut self) -> Node { 67 | let tagname = self.consume_while(is_valid_tag_name); 68 | let attributes = self.parse_attributes(); 69 | 70 | let elem = ElementData::new(tagname, attributes); 71 | let children = self.parse_nodes(); 72 | Node::new(NodeType::Element(elem), children) 73 | } 74 | 75 | fn parse_text_node(&mut self) -> Node { 76 | let mut text_content = String::new(); 77 | 78 | while self.chars.peek().map_or(false, |c| *c != '<') { 79 | let whitespace = self.consume_while(char::is_whitespace); 80 | if whitespace.len() > 0 { 81 | text_content.push(' '); 82 | } 83 | let text_part = self.consume_while(|x| !x.is_whitespace() && x != '<'); 84 | text_content.push_str(&text_part); 85 | } 86 | Node::new(NodeType::Text(text_content), Vec::new()) 87 | } 88 | 89 | fn parse_comment_node(&mut self) -> Node { 90 | let mut comment_content = String::new(); 91 | 92 | if self.chars.peek().map_or(false, |c| *c == '-') { 93 | self.chars.next(); 94 | if self.chars.peek().map_or(false, |c| *c == '-') { 95 | self.chars.next(); 96 | } else { 97 | self.consume_while(|c| c != '>'); 98 | return Node::new(NodeType::Comment(comment_content), Vec::new()); 99 | } 100 | } else { 101 | self.consume_while(|c| c != '>'); 102 | return Node::new(NodeType::Comment(comment_content), Vec::new()); 103 | } 104 | 105 | if self.chars.peek().map_or(false, |c| *c == '>') { 106 | self.chars.next(); 107 | return Node::new(NodeType::Comment(comment_content), Vec::new()); 108 | } 109 | 110 | if self.chars.peek().map_or(false, |c| *c == '-') { 111 | self.chars.next(); 112 | if self.chars.peek().map_or(false, |c| *c == '>') { 113 | self.chars.next(); 114 | return Node::new(NodeType::Comment(comment_content), Vec::new()); 115 | } else { 116 | comment_content.push('-'); 117 | } 118 | } 119 | 120 | while self.chars.peek().is_some() { 121 | comment_content.push_str(&self.consume_while(|c| c != '<' && c != '-')); 122 | if self.chars.peek().map_or(false, |c| *c == '<') { 123 | self.chars.next(); 124 | if self.chars.peek().map_or(false, |c| *c == '!') { 125 | self.chars.next(); 126 | if self.chars.peek().map_or(false, |c| *c == '-') { 127 | self.chars.next(); 128 | if self.chars.peek().map_or(false, |c| *c == '-') { 129 | self.consume_while(|c| c != '>'); 130 | 131 | return Node::new(NodeType::Comment(String::from("")), Vec::new()); 132 | } else { 133 | comment_content.push_str("') { 144 | self.chars.next(); 145 | return Node::new( 146 | NodeType::Comment(String::from("")), 147 | Vec::new(), 148 | ); 149 | } else { 150 | comment_content.push_str("') { 170 | self.chars.next(); 171 | break; 172 | } else { 173 | comment_content.push_str("--"); 174 | } 175 | } else { 176 | comment_content.push('-'); 177 | } 178 | } 179 | } 180 | 181 | Node::new(NodeType::Comment(comment_content), Vec::new()) 182 | } 183 | 184 | fn parse_attributes(&mut self) -> AttrMap { 185 | let mut attributes = AttrMap::new(); 186 | 187 | while self.chars.peek().map_or(false, |c| *c != '>') { 188 | self.consume_while(char::is_whitespace); 189 | let name = self.consume_while(|c| is_valid_attr_name(c)).to_lowercase(); 190 | self.consume_while(char::is_whitespace); 191 | 192 | let value = if self.chars.peek().map_or(false, |c| *c == '=') { 193 | self.chars.next(); 194 | self.consume_while(char::is_whitespace); 195 | let s = self.parse_attr_value(); 196 | self.consume_while(|c| !c.is_whitespace() && c != '>'); 197 | self.consume_while(char::is_whitespace); 198 | s 199 | } else { 200 | "".to_string() 201 | }; 202 | attributes.insert(name, value); 203 | } 204 | self.chars.next(); 205 | 206 | attributes 207 | } 208 | 209 | fn parse_attr_value(&mut self) -> String { 210 | self.consume_while(char::is_whitespace); 211 | 212 | let result = match self.chars.peek() { 213 | Some(&c) if c == '"' || c == '\'' => { 214 | self.chars.next(); 215 | let ret = self.consume_while(|x| x != c); 216 | self.chars.next(); 217 | ret 218 | } 219 | _ => self.consume_while(is_valid_attr_value), 220 | }; 221 | 222 | result 223 | } 224 | 225 | 226 | fn consume_while(&mut self, condition: F) -> String 227 | where 228 | F: Fn(char) -> bool, 229 | { 230 | let mut result = String::new(); 231 | while self.chars.peek().map_or(false, |c| condition(*c)) { 232 | result.push(self.chars.next().unwrap()); 233 | } 234 | 235 | result 236 | } 237 | } 238 | 239 | fn is_valid_tag_name(ch: char) -> bool { 240 | ch.is_digit(36) 241 | } 242 | 243 | fn is_valid_attr_name(c: char) -> bool { 244 | !is_excluded_name(c) && !is_control(c) 245 | } 246 | 247 | fn is_control(ch: char) -> bool { 248 | match ch { 249 | '\u{007F}' => true, 250 | c if c >= '\u{0000}' && c <= '\u{001F}' => true, 251 | c if c >= '\u{0080}' && c <= '\u{009F}' => true, 252 | _ => false, 253 | } 254 | } 255 | 256 | fn is_excluded_name(c: char) -> bool { 257 | match c { 258 | ' ' | '"' | '\'' | '>' | '/' | '=' => true, 259 | _ => false, 260 | } 261 | } 262 | 263 | fn is_valid_attr_value(c: char) -> bool { 264 | match c { 265 | ' ' | '"' | '\'' | '=' | '<' | '>' | '`' => false, 266 | _ => true, 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/layout.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use css::{Unit, Value}; 4 | use style::{Display, StyledNode}; 5 | 6 | #[derive(Clone)] 7 | pub struct LayoutBox<'a> { 8 | pub dimensions: Dimensions, 9 | box_type: BoxType, 10 | pub styled_node: &'a StyledNode<'a>, 11 | pub children: Vec>, 12 | } 13 | #[derive(Clone, Copy, Default)] 14 | pub struct Dimensions { 15 | pub content: Rectangle, 16 | padding: EdgeSizes, 17 | pub border: EdgeSizes, 18 | margin: EdgeSizes, 19 | current: Rectangle, 20 | } 21 | 22 | #[derive(Clone, Copy, Default)] 23 | pub struct Rectangle { 24 | pub x: f32, 25 | pub y: f32, 26 | pub width: f32, 27 | pub height: f32, 28 | } 29 | 30 | #[derive(Clone, Copy, Default)] 31 | pub struct EdgeSizes { 32 | pub left: f32, 33 | pub right: f32, 34 | pub top: f32, 35 | pub bottom: f32, 36 | } 37 | 38 | #[derive(Clone)] 39 | pub enum BoxType { 40 | Block, 41 | Inline, 42 | InlineBlock, 43 | Anonymous, 44 | } 45 | 46 | 47 | impl<'a> LayoutBox<'a> { 48 | pub fn new(box_type: BoxType, styled_node: &'a StyledNode) -> LayoutBox<'a> { 49 | LayoutBox { 50 | box_type: box_type, 51 | styled_node: styled_node, 52 | dimensions: Default::default(), 53 | children: Vec::new(), 54 | } 55 | } 56 | 57 | 58 | fn layout(&mut self, b_box: Dimensions) { 59 | match self.box_type { 60 | BoxType::Block => self.layout_block(b_box), 61 | BoxType::Inline => self.layout_block(b_box), 62 | BoxType::InlineBlock => self.layout_inline_block(b_box), 63 | BoxType::Anonymous => {} 64 | } 65 | } 66 | 67 | fn layout_inline_block(&mut self, b_box: Dimensions) { 68 | self.calculate_inline_width(b_box); 69 | self.calculate_inline_position(b_box); 70 | self.layout_children(); 71 | self.calculate_height(); 72 | } 73 | 74 | fn calculate_inline_width(&mut self, b_box: Dimensions) { 75 | let s = self.styled_node; 76 | let d = &mut self.dimensions; 77 | 78 | d.content.width = get_absolute_num(s, b_box, "width").unwrap_or(0.0); 79 | d.margin.left = s.num_or("margin-left", 0.0); 80 | d.margin.right = s.num_or("margin-right", 0.0); 81 | d.padding.left = s.num_or("padding-left", 0.0); 82 | d.padding.right = s.num_or("padding-right", 0.0); 83 | d.border.left = s.num_or("border-left-width", 0.0); 84 | d.border.right = s.num_or("border-right-width", 0.0); 85 | } 86 | 87 | fn calculate_inline_position(&mut self, b_box: Dimensions) { 88 | let style = self.styled_node; 89 | let d = &mut self.dimensions; 90 | 91 | d.margin.top = style.num_or("margin-top", 0.0); 92 | d.margin.bottom = style.num_or("margin-bottom", 0.0); 93 | d.border.top = style.num_or("border-top-width", 0.0); 94 | d.border.bottom = style.num_or("border-bottom-width", 0.0); 95 | d.padding.top = style.num_or("padding-top", 0.0); 96 | d.padding.bottom = style.num_or("padding-bottom", 0.0); 97 | 98 | d.content.x = 99 | b_box.content.x + b_box.current.x + d.margin.left + d.border.left + d.padding.left; 100 | d.content.y = 101 | b_box.content.height + b_box.content.y + d.margin.top + d.border.top + d.padding.top; 102 | } 103 | 104 | fn layout_block(&mut self, b_box: Dimensions) { 105 | self.calculate_width(b_box); 106 | self.calculate_position(b_box); 107 | self.layout_children(); 108 | self.calculate_height(); 109 | } 110 | 111 | 112 | fn calculate_width(&mut self, b_box: Dimensions) { 113 | let style = self.styled_node; 114 | let d = &mut self.dimensions; 115 | 116 | let width = get_absolute_num(style, b_box, "width").unwrap_or(0.0); 117 | let margin_l = style.value("margin-left"); 118 | let margin_r = style.value("margin-right"); 119 | 120 | let margin_l_num = match margin_l { 121 | Some(m) => match **m { 122 | Value::Other(ref s) => s.parse().unwrap_or(0.0), 123 | _ => 0.0, 124 | }, 125 | None => 0.0, 126 | }; 127 | let margin_r_num = match margin_r { 128 | Some(m) => match **m { 129 | Value::Other(ref s) => s.parse().unwrap_or(0.0), 130 | _ => 0.0, 131 | }, 132 | None => 0.0, 133 | }; 134 | 135 | d.border.left = style.num_or("border-left-width", 0.0); 136 | d.border.right = style.num_or("border-right-width", 0.0); 137 | d.padding.left = style.num_or("padding-left", 0.0); 138 | d.padding.right = style.num_or("padding-right", 0.0); 139 | 140 | let total = width + margin_l_num + margin_r_num + d.border.left + d.border.right 141 | + d.padding.left + d.padding.right; 142 | 143 | let underflow = b_box.content.width - total; 144 | 145 | match (width, margin_l, margin_r) { 146 | (0.0, _, _) => { 147 | if underflow >= 0.0 { 148 | d.content.width = underflow; 149 | d.margin.right = margin_r_num; 150 | } else { 151 | d.margin.right = margin_r_num + underflow; 152 | d.content.width = width; 153 | } 154 | d.margin.left = margin_l_num; 155 | } 156 | (w, None, Some(_)) if w != 0.0 => { 157 | d.margin.left = underflow; 158 | d.margin.right = margin_r_num; 159 | d.content.width = w; 160 | } 161 | (w, Some(_), None) if w != 0.0 => { 162 | d.margin.right = underflow; 163 | d.margin.left = margin_l_num; 164 | d.content.width = w; 165 | } 166 | (w, None, None) if w != 0.0 => { 167 | d.margin.left = underflow / 2.0; 168 | d.margin.right = underflow / 2.0; 169 | d.content.width = w; 170 | } 171 | (_, _, _) => { 172 | d.margin.right = margin_r_num + underflow; 173 | d.margin.left = margin_l_num; 174 | d.content.width = width 175 | } 176 | } 177 | } 178 | 179 | fn calculate_position(&mut self, b_box: Dimensions) { 180 | let style = self.styled_node; 181 | let d = &mut self.dimensions; 182 | 183 | d.margin.top = style.num_or("margin-top", 0.0); 184 | d.margin.bottom = style.num_or("margin-bottom", 0.0); 185 | d.border.top = style.num_or("border-top-width", 0.0); 186 | d.border.bottom = style.num_or("border-bottom-width", 0.0); 187 | d.padding.top = style.num_or("padding-top", 0.0); 188 | d.padding.bottom = style.num_or("padding-bottom", 0.0); 189 | 190 | d.content.x = b_box.content.x + d.margin.left + d.border.left + d.padding.left; 191 | d.content.y = 192 | b_box.content.height + b_box.content.y + d.margin.top + d.border.top + d.padding.top; 193 | } 194 | 195 | fn calculate_height(&mut self) { 196 | self.styled_node.value("height").map_or((), |h| match **h { 197 | Value::Length(n, _) => self.dimensions.content.height = n, 198 | _ => {} 199 | }) 200 | } 201 | 202 | fn layout_children(&mut self) { 203 | let d = &mut self.dimensions; 204 | let mut max_child_height = 0.0; 205 | 206 | let mut prev_box_type = BoxType::Block; 207 | 208 | for child in &mut self.children { 209 | match prev_box_type { 210 | BoxType::InlineBlock => match child.box_type { 211 | BoxType::Block => { 212 | d.content.height += max_child_height; 213 | d.current.x = 0.0; 214 | } 215 | _ => {} 216 | }, 217 | _ => {} 218 | } 219 | 220 | child.layout(*d); 221 | let new_height = child.dimensions.margin_box().height; 222 | 223 | if new_height > max_child_height { 224 | max_child_height = new_height; 225 | } 226 | 227 | match child.box_type { 228 | BoxType::Block => d.content.height += child.dimensions.margin_box().height, 229 | BoxType::InlineBlock => { 230 | d.current.x += child.dimensions.margin_box().width; 231 | 232 | if d.current.x > d.content.width { 233 | d.content.height += max_child_height; 234 | d.current.x = 0.0; 235 | child.layout(*d); 236 | d.current.x += child.dimensions.margin_box().width; 237 | } 238 | } 239 | _ => {} 240 | } 241 | prev_box_type = child.box_type.clone(); 242 | } 243 | } 244 | } 245 | 246 | impl<'a> fmt::Debug for LayoutBox<'a> { 247 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 248 | write!(f, "type:\n {:?}\n{:?}\n", self.box_type, self.dimensions) 249 | } 250 | } 251 | 252 | impl Dimensions { 253 | fn padding_box(&self) -> Rectangle { 254 | self.content.expanded(self.padding) 255 | } 256 | 257 | pub fn border_box(&self) -> Rectangle { 258 | self.padding_box().expanded(self.border) 259 | } 260 | 261 | fn margin_box(&self) -> Rectangle { 262 | self.border_box().expanded(self.margin) 263 | } 264 | } 265 | 266 | impl fmt::Debug for Dimensions { 267 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 268 | write!( 269 | f, 270 | "content:\n {:?}\npadding:\n {:?}\nborder:\n {:?}\nmargin:\n {:?}", 271 | self.content, 272 | self.padding, 273 | self.border, 274 | self.margin 275 | ) 276 | } 277 | } 278 | 279 | impl Rectangle { 280 | fn expanded(&self, e: EdgeSizes) -> Rectangle { 281 | Rectangle { 282 | x: self.x - e.left, 283 | y: self.y - e.top, 284 | width: self.width + e.left + e.right, 285 | height: self.height + e.top + e.bottom, 286 | } 287 | } 288 | } 289 | 290 | impl fmt::Debug for Rectangle { 291 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 292 | write!( 293 | f, 294 | "x: {}, y: {}, w: {}, h: {}", 295 | self.x, 296 | self.y, 297 | self.width, 298 | self.height 299 | ) 300 | } 301 | } 302 | impl fmt::Debug for EdgeSizes { 303 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 304 | write!( 305 | f, 306 | "l: {} r: {} top: {} bot: {}", 307 | self.left, 308 | self.right, 309 | self.top, 310 | self.bottom 311 | ) 312 | } 313 | } 314 | 315 | impl fmt::Debug for BoxType { 316 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 317 | let display_type = match *self { 318 | BoxType::Block => "block", 319 | BoxType::Inline => "inline", 320 | BoxType::InlineBlock => "inline-block", 321 | BoxType::Anonymous => "anonymous", 322 | }; 323 | 324 | write!(f, "{}", display_type) 325 | } 326 | } 327 | 328 | fn get_absolute_num(s_node: &StyledNode, b_box: Dimensions, prop: &str) -> Option { 329 | match s_node.value(prop) { 330 | Some(ref v) => match ***v { 331 | Value::Length(l, ref u) => match *u { 332 | Unit::Px => Some(l), 333 | Unit::Pct => Some(l * b_box.content.width / 100.0), 334 | _ => panic!("Unimplemented css length unit"), 335 | }, 336 | _ => None, 337 | }, 338 | None => None, 339 | } 340 | } 341 | 342 | 343 | pub fn layout_tree<'a>( 344 | root: &'a StyledNode<'a>, 345 | mut containing_block: Dimensions, 346 | ) -> LayoutBox<'a> { 347 | containing_block.content.height = 0.0; 348 | 349 | let mut root_box = build_layout_tree(root); 350 | root_box.layout(containing_block); 351 | return root_box; 352 | } 353 | 354 | fn build_layout_tree<'a>(node: &'a StyledNode) -> LayoutBox<'a> { 355 | let mut layout_node = LayoutBox::new( 356 | match node.get_display() { 357 | Display::Block => BoxType::Block, 358 | Display::Inline => BoxType::Inline, 359 | Display::InlineBlock => BoxType::InlineBlock, 360 | Display::None => BoxType::Anonymous, 361 | }, 362 | node, 363 | ); 364 | 365 | for child in &node.children { 366 | match child.get_display() { 367 | Display::Block => layout_node.children.push(build_layout_tree(child)), 368 | Display::Inline => layout_node.children.push(build_layout_tree(child)), 369 | Display::InlineBlock => layout_node.children.push(build_layout_tree(child)), 370 | Display::None => {} 371 | } 372 | } 373 | layout_node 374 | } 375 | 376 | pub fn pretty_print<'a>(n: &'a LayoutBox, level: usize) { 377 | println!("{}{:?}\n", level, n); 378 | 379 | for child in n.children.iter() { 380 | pretty_print(&child, level + 1); 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gfx; 3 | extern crate gfx_text; 4 | extern crate gfx_window_glutin; 5 | extern crate glutin; 6 | 7 | pub mod render; 8 | pub mod command; 9 | pub mod dom; 10 | pub mod html_parse; 11 | pub mod css; 12 | pub mod css_parser; 13 | pub mod style; 14 | pub mod layout; 15 | -------------------------------------------------------------------------------- /src/render.rs: -------------------------------------------------------------------------------- 1 | use gfx; 2 | use gfx_text; 3 | use gfx_window_glutin; 4 | use glutin; 5 | 6 | use gfx::Factory; 7 | use gfx::traits::FactoryExt; 8 | use gfx::Device; 9 | 10 | use layout; 11 | use command::DisplayCommand; 12 | 13 | pub type DepthFormat = gfx::format::DepthStencil; 14 | pub type ColorFormat = gfx::format::Rgba8; 15 | 16 | const SCREEN_WIDTH: usize = 1024; 17 | const SCREEN_HEIGHT: usize = 768; 18 | const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0]; 19 | 20 | gfx_defines!{ 21 | vertex Vertex { 22 | pos: [f32; 2] = "a_Pos", 23 | color: [f32; 3] = "a_Color", 24 | } 25 | 26 | pipeline pipe { 27 | vbuf: gfx::VertexBuffer = (), 28 | out: gfx::RenderTarget = "Target0", 29 | } 30 | } 31 | 32 | #[derive(Copy, Clone)] 33 | struct RenderText<'a> { 34 | text: &'a str, 35 | position: [i32; 2], 36 | color: [f32; 4], 37 | } 38 | 39 | fn render_texts(command_list: &[DisplayCommand]) -> Vec { 40 | Vec::new() 41 | } 42 | 43 | fn render_commands(command_list: &[DisplayCommand]) -> (Vec, Vec) { 44 | let mut vertices = Vec::new(); 45 | let mut index_data = Vec::new(); 46 | let mut rect_num: u16 = 0; 47 | 48 | for command in command_list { 49 | match *command { 50 | DisplayCommand::SolidRectangle(ref color, ref rect) => { 51 | let c = [color.r, color.g, color.b]; 52 | 53 | let mut v = render_rectangle(&c, rect); 54 | vertices.append(&mut v); 55 | 56 | let index_base: u16 = rect_num * 4; 57 | index_data.append(&mut vec![ 58 | index_base, 59 | index_base + 1, 60 | index_base + 2, 61 | index_base + 2, 62 | index_base + 3, 63 | index_base, 64 | ]); 65 | rect_num += 1; 66 | } 67 | } 68 | } 69 | return (vertices, index_data); 70 | } 71 | 72 | fn render_rectangle(c: &[f32; 3], rect: &layout::Rectangle) -> Vec { 73 | let (x, y, h, w) = transform_rectangle(rect); 74 | let vertices = vec![ 75 | Vertex { 76 | pos: [x + w, y], 77 | color: *c, 78 | }, 79 | Vertex { 80 | pos: [x, y], 81 | color: *c, 82 | }, 83 | Vertex { 84 | pos: [x, y + h], 85 | color: *c, 86 | }, 87 | Vertex { 88 | pos: [x + w, y + h], 89 | color: *c, 90 | }, 91 | ]; 92 | 93 | vertices 94 | } 95 | 96 | fn transform_rectangle(rect: &layout::Rectangle) -> (f32, f32, f32, f32) { 97 | let w = rect.width / SCREEN_WIDTH as f32 * 2.0; 98 | let h = rect.height / SCREEN_HEIGHT as f32 * 2.0; 99 | let x = rect.x / SCREEN_WIDTH as f32 * 2.0 - 1.0; 100 | let y = -(rect.y / SCREEN_HEIGHT as f32 * 2.0 - 1.0 + h); 101 | 102 | (x, y, h, w) 103 | } 104 | 105 | 106 | pub fn render_loop(command_list: &[DisplayCommand]) { 107 | let builder = glutin::WindowBuilder::new() 108 | .with_title(String::from("Browser")) 109 | .with_dimensions(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32) 110 | .with_vsync(); 111 | 112 | let (window, mut device, mut factory, main_color, _main_depth) = 113 | gfx_window_glutin::init::(builder); 114 | 115 | let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into(); 116 | 117 | let pso = factory 118 | .create_pipeline_simple( 119 | include_bytes!("../shaders/solid.glslv"), 120 | include_bytes!("../shaders/solid.glslf"), 121 | pipe::new(), 122 | ) 123 | .unwrap(); 124 | 125 | let (vertices, index_data) = render_commands(command_list); 126 | let texts = render_texts(command_list); 127 | 128 | let (vertex_buffer, slice) = 129 | factory.create_vertex_buffer_with_slice(&vertices, &index_data[..]); 130 | 131 | let data = pipe::Data { 132 | vbuf: vertex_buffer, 133 | out: main_color, 134 | }; 135 | 136 | let mut test_renderer = gfx_text::new(factory).build().unwrap(); 137 | 138 | 'main: loop { 139 | for event in window.poll_events() { 140 | match event { 141 | glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) | 142 | glutin::Event::Closed => break 'main, 143 | _ => {} 144 | } 145 | } 146 | 147 | for text in &texts { 148 | test_renderer.add(text.text, text.position, text.color); 149 | } 150 | 151 | encoder.clear(&data.out, CLEAR_COLOR); 152 | 153 | encoder.draw(&slice, &pso, &data); 154 | test_renderer.draw(&mut encoder, &data.out); 155 | 156 | encoder.flush(&mut device); 157 | window.swap_buffers().unwrap(); 158 | device.cleanup(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/style.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::{fmt, str}; 3 | 4 | use dom::{ElementData, Node, NodeType}; 5 | use css::{Selector, Stylesheet, Value}; 6 | 7 | type PropertyMap<'a> = HashMap<&'a str, &'a Value>; 8 | 9 | pub struct StyledNode<'a> { 10 | node: &'a Node, 11 | styles: PropertyMap<'a>, 12 | pub children: Vec>, 13 | } 14 | 15 | pub enum Display { 16 | Block, 17 | Inline, 18 | InlineBlock, 19 | None, 20 | } 21 | 22 | impl<'a> StyledNode<'a> { 23 | pub fn new(node: &'a Node, stylesheet: &'a Stylesheet) -> StyledNode<'a> { 24 | let mut style_children = Vec::new(); 25 | 26 | for child in &node.children { 27 | match child.node_type { 28 | NodeType::Element(_) => style_children.push(StyledNode::new(&child, stylesheet)), 29 | _ => {} 30 | } 31 | } 32 | 33 | StyledNode { 34 | node, 35 | styles: match node.node_type { 36 | NodeType::Element(ref e) => StyledNode::get_styles(e, stylesheet), 37 | _ => PropertyMap::new(), 38 | }, 39 | children: style_children, 40 | } 41 | } 42 | 43 | fn get_styles(element: &'a ElementData, stylesheet: &'a Stylesheet) -> PropertyMap<'a> { 44 | let mut styles = PropertyMap::new(); 45 | 46 | for rule in &stylesheet.rules { 47 | for selector in &rule.selectors { 48 | if selector_matches(element, &selector) { 49 | for declar in &rule.declarations { 50 | styles.insert(&declar.property, &declar.value); 51 | } 52 | break; 53 | } 54 | } 55 | } 56 | styles 57 | } 58 | 59 | pub fn value(&self, name: &str) -> Option<&&Value> { 60 | self.styles.get(name) 61 | } 62 | 63 | pub fn get_display(&self) -> Display { 64 | match self.value("display") { 65 | Some(s) => match **s { 66 | Value::Other(ref v) => match v.as_ref() { 67 | "block" => Display::Block, 68 | "none" => Display::None, 69 | "inline-block" => Display::InlineBlock, 70 | _ => Display::Inline, 71 | }, 72 | _ => Display::Inline, 73 | }, 74 | None => Display::Inline, 75 | } 76 | } 77 | 78 | pub fn num_or(&self, name: &str, default: f32) -> f32 { 79 | match self.value(name) { 80 | Some(v) => match **v { 81 | Value::Length(n, _) => n, 82 | _ => default, 83 | }, 84 | None => default, 85 | } 86 | } 87 | } 88 | 89 | impl<'a> fmt::Debug for StyledNode<'a> { 90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 91 | write!(f, "{:?}: {:?}", self.node, self.styles) 92 | } 93 | } 94 | 95 | fn selector_matches(element: &ElementData, selector: &Selector) -> bool { 96 | for simple in &selector.simple { 97 | let mut selector_match = true; 98 | 99 | match simple.tag_name { 100 | Some(ref t) => if *t != element.tag_name { 101 | continue; 102 | }, 103 | None => {} 104 | }; 105 | 106 | match element.get_id() { 107 | Some(i) => match simple.id { 108 | Some(ref id) => if *i != *id { 109 | continue; 110 | }, 111 | None => {} 112 | }, 113 | None => match simple.id { 114 | Some(_) => { 115 | continue; 116 | } 117 | _ => {} 118 | }, 119 | } 120 | let element_classes = element.get_classes(); 121 | 122 | for class in &simple.classes { 123 | selector_match &= element_classes.contains::(class); 124 | } 125 | 126 | if selector_match { 127 | return true; 128 | } 129 | } 130 | false 131 | } 132 | 133 | pub fn pretty_print(node: &StyledNode, indent_size: usize) { 134 | let indent = (0..indent_size).map(|_| " ").collect::(); 135 | println!("{}{:?}", indent, node); 136 | 137 | for child in node.children.iter() { 138 | pretty_print(&child, indent_size + 2); 139 | } 140 | } 141 | --------------------------------------------------------------------------------