1 #include "html.h"
2 #include "document.h"
3 #include "stylesheet.h"
4 #include "html_tag.h"
5 #include "el_text.h"
6 #include "el_para.h"
7 #include "el_space.h"
8 #include "el_body.h"
9 #include "el_image.h"
10 #include "el_table.h"
11 #include "el_td.h"
12 #include "el_link.h"
13 #include "el_title.h"
14 #include "el_style.h"
15 #include "el_script.h"
16 #include "el_comment.h"
17 #include "el_cdata.h"
18 #include "el_base.h"
19 #include "el_anchor.h"
20 #include "el_break.h"
21 #include "el_div.h"
22 #include "el_font.h"
23 #include "el_tr.h"
24 #include <math.h>
25 #include <stdio.h>
26 #include <algorithm>
27 #include "gumbo.h"
28 #include "utf8_strings.h"
29 
document(litehtml::document_container * objContainer,litehtml::context * ctx)30 litehtml::document::document(litehtml::document_container* objContainer, litehtml::context* ctx)
31 {
32 	m_container	= objContainer;
33 	m_context	= ctx;
34 }
35 
~document()36 litehtml::document::~document()
37 {
38 	m_over_element = 0;
39 	if(m_container)
40 	{
41 		for(fonts_map::iterator f = m_fonts.begin(); f != m_fonts.end(); f++)
42 		{
43 			m_container->delete_font(f->second.font);
44 		}
45 	}
46 }
47 
createFromString(const tchar_t * str,litehtml::document_container * objPainter,litehtml::context * ctx,litehtml::css * user_styles)48 litehtml::document::ptr litehtml::document::createFromString( const tchar_t* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles)
49 {
50 	return createFromUTF8(litehtml_to_utf8(str), objPainter, ctx, user_styles);
51 }
52 
createFromUTF8(const char * str,litehtml::document_container * objPainter,litehtml::context * ctx,litehtml::css * user_styles)53 litehtml::document::ptr litehtml::document::createFromUTF8(const char* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles)
54 {
55 	// parse document into GumboOutput
56 	GumboOutput* output = gumbo_parse((const char*) str);
57 
58 	// Create litehtml::document
59 	litehtml::document::ptr doc = std::make_shared<litehtml::document>(objPainter, ctx);
60 
61 	// Create litehtml::elements.
62 	elements_vector root_elements;
63 	doc->create_node(output->root, root_elements);
64 	if (!root_elements.empty())
65 	{
66 		doc->m_root = root_elements.back();
67 	}
68 	// Destroy GumboOutput
69 	gumbo_destroy_output(&kGumboDefaultOptions, output);
70 
71 	// Let's process created elements tree
72 	if (doc->m_root)
73 	{
74 		doc->container()->get_media_features(doc->m_media);
75 
76 		// apply master CSS
77 		doc->m_root->apply_stylesheet(ctx->master_css());
78 
79 		// parse elements attributes
80 		doc->m_root->parse_attributes();
81 
82 		// parse style sheets linked in document
83 		media_query_list::ptr media;
84 		for (css_text::vector::iterator css = doc->m_css.begin(); css != doc->m_css.end(); css++)
85 		{
86 			if (!css->media.empty())
87 			{
88 				media = media_query_list::create_from_string(css->media, doc);
89 			}
90 			else
91 			{
92 				media = 0;
93 			}
94 			doc->m_styles.parse_stylesheet(css->text.c_str(), css->baseurl.c_str(), doc, media);
95 		}
96 		// Sort css selectors using CSS rules.
97 		doc->m_styles.sort_selectors();
98 
99 		// get current media features
100 		if (!doc->m_media_lists.empty())
101 		{
102 			doc->update_media_lists(doc->m_media);
103 		}
104 
105 		// Apply parsed styles.
106 		doc->m_root->apply_stylesheet(doc->m_styles);
107 
108 		// Apply user styles if any
109 		if (user_styles)
110 		{
111 			doc->m_root->apply_stylesheet(*user_styles);
112 		}
113 
114 		// Parse applied styles in the elements
115 		doc->m_root->parse_styles();
116 
117 		// Now the m_tabular_elements is filled with tabular elements.
118 		// We have to check the tabular elements for missing table elements
119 		// and create the anonymous boxes in visual table layout
120 		doc->fix_tables_layout();
121 
122 		// Fanaly initialize elements
123 		doc->m_root->init();
124 	}
125 
126 	return doc;
127 }
128 
add_font(const tchar_t * name,int size,const tchar_t * weight,const tchar_t * style,const tchar_t * decoration,font_metrics * fm)129 litehtml::uint_ptr litehtml::document::add_font( const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm )
130 {
131 	uint_ptr ret = 0;
132 
133 	if( !name || (name && !t_strcasecmp(name, _t("inherit"))) )
134 	{
135 		name = m_container->get_default_font_name();
136 	}
137 
138 	if(!size)
139 	{
140 		size = container()->get_default_font_size();
141 	}
142 
143 	tchar_t strSize[20];
144 	t_itoa(size, strSize, 20, 10);
145 
146 	tstring key = name;
147 	key += _t(":");
148 	key += strSize;
149 	key += _t(":");
150 	key += weight;
151 	key += _t(":");
152 	key += style;
153 	key += _t(":");
154 	key += decoration;
155 
156 	if(m_fonts.find(key) == m_fonts.end())
157 	{
158 		font_style fs = (font_style) value_index(style, font_style_strings, fontStyleNormal);
159 		int	fw = value_index(weight, font_weight_strings, -1);
160 		if(fw >= 0)
161 		{
162 			switch(fw)
163 			{
164 			case litehtml::fontWeightBold:
165 				fw = 700;
166 				break;
167 			case litehtml::fontWeightBolder:
168 				fw = 600;
169 				break;
170 			case litehtml::fontWeightLighter:
171 				fw = 300;
172 				break;
173 			default:
174 				fw = 400;
175 				break;
176 			}
177 		} else
178 		{
179 			fw = t_atoi(weight);
180 			if(fw < 100)
181 			{
182 				fw = 400;
183 			}
184 		}
185 
186 		unsigned int decor = 0;
187 
188 		if(decoration)
189 		{
190 			std::vector<tstring> tokens;
191 			split_string(decoration, tokens, _t(" "));
192 			for(std::vector<tstring>::iterator i = tokens.begin(); i != tokens.end(); i++)
193 			{
194 				if(!t_strcasecmp(i->c_str(), _t("underline")))
195 				{
196 					decor |= font_decoration_underline;
197 				} else if(!t_strcasecmp(i->c_str(), _t("line-through")))
198 				{
199 					decor |= font_decoration_linethrough;
200 				} else if(!t_strcasecmp(i->c_str(), _t("overline")))
201 				{
202 					decor |= font_decoration_overline;
203 				}
204 			}
205 		}
206 
207 		font_item fi= {0};
208 
209 		fi.font = m_container->create_font(name, size, fw, fs, decor, &fi.metrics);
210 		m_fonts[key] = fi;
211 		ret = fi.font;
212 		if(fm)
213 		{
214 			*fm = fi.metrics;
215 		}
216 	}
217 	return ret;
218 }
219 
get_font(const tchar_t * name,int size,const tchar_t * weight,const tchar_t * style,const tchar_t * decoration,font_metrics * fm)220 litehtml::uint_ptr litehtml::document::get_font( const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm )
221 {
222 	if( !name || (name && !t_strcasecmp(name, _t("inherit"))) )
223 	{
224 		name = m_container->get_default_font_name();
225 	}
226 
227 	if(!size)
228 	{
229 		size = container()->get_default_font_size();
230 	}
231 
232 	tchar_t strSize[20];
233 	t_itoa(size, strSize, 20, 10);
234 
235 	tstring key = name;
236 	key += _t(":");
237 	key += strSize;
238 	key += _t(":");
239 	key += weight;
240 	key += _t(":");
241 	key += style;
242 	key += _t(":");
243 	key += decoration;
244 
245 	fonts_map::iterator el = m_fonts.find(key);
246 
247 	if(el != m_fonts.end())
248 	{
249 		if(fm)
250 		{
251 			*fm = el->second.metrics;
252 		}
253 		return el->second.font;
254 	}
255 	return add_font(name, size, weight, style, decoration, fm);
256 }
257 
render(int max_width,render_type rt)258 int litehtml::document::render( int max_width, render_type rt )
259 {
260 	int ret = 0;
261 	if(m_root)
262 	{
263 		if(rt == render_fixed_only)
264 		{
265 			m_fixed_boxes.clear();
266 			m_root->render_positioned(rt);
267 		} else
268 		{
269 			ret = m_root->render(0, 0, max_width);
270 			if(m_root->fetch_positioned())
271 			{
272 				m_fixed_boxes.clear();
273 				m_root->render_positioned(rt);
274 			}
275 			m_size.width	= 0;
276 			m_size.height	= 0;
277 			m_root->calc_document_size(m_size);
278 		}
279 	}
280 	return ret;
281 }
282 
draw(uint_ptr hdc,int x,int y,const position * clip)283 void litehtml::document::draw( uint_ptr hdc, int x, int y, const position* clip )
284 {
285 	if(m_root)
286 	{
287 		m_root->draw(hdc, x, y, clip);
288 		m_root->draw_stacking_context(hdc, x, y, clip, true);
289 	}
290 }
291 
cvt_units(const tchar_t * str,int fontSize,bool * is_percent) const292 int litehtml::document::cvt_units( const tchar_t* str, int fontSize, bool* is_percent/*= 0*/ ) const
293 {
294 	if(!str)	return 0;
295 
296 	css_length val;
297 	val.fromString(str);
298 	if(is_percent && val.units() == css_units_percentage && !val.is_predefined())
299 	{
300 		*is_percent = true;
301 	}
302 	return cvt_units(val, fontSize);
303 }
304 
cvt_units(css_length & val,int fontSize,int size) const305 int litehtml::document::cvt_units( css_length& val, int fontSize, int size ) const
306 {
307 	if(val.is_predefined())
308 	{
309 		return 0;
310 	}
311 	int ret = 0;
312 	switch(val.units())
313 	{
314 	case css_units_percentage:
315 		ret = val.calc_percent(size);
316 		break;
317 	case css_units_em:
318 		ret = round_f(val.val() * fontSize);
319 		val.set_value((float) ret, css_units_px);
320 		break;
321 	case css_units_pt:
322 		ret = m_container->pt_to_px((int) val.val());
323 		val.set_value((float) ret, css_units_px);
324 		break;
325 	case css_units_in:
326 		ret = m_container->pt_to_px((int) (val.val() * 72));
327 		val.set_value((float) ret, css_units_px);
328 		break;
329 	case css_units_cm:
330 		ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72));
331 		val.set_value((float) ret, css_units_px);
332 		break;
333 	case css_units_mm:
334 		ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10);
335 		val.set_value((float) ret, css_units_px);
336 		break;
337 	case css_units_vw:
338 		ret = (int)((double)m_media.width * (double)val.val() / 100.0);
339 		break;
340 	case css_units_vh:
341 		ret = (int)((double)m_media.height * (double)val.val() / 100.0);
342 		break;
343 	case css_units_vmin:
344 		ret = (int)((double)std::min(m_media.height, m_media.width) * (double)val.val() / 100.0);
345 		break;
346 	case css_units_vmax:
347 		ret = (int)((double)std::max(m_media.height, m_media.width) * (double)val.val() / 100.0);
348 		break;
349 	default:
350 		ret = (int) val.val();
351 		break;
352 	}
353 	return ret;
354 }
355 
width() const356 int litehtml::document::width() const
357 {
358 	return m_size.width;
359 }
360 
height() const361 int litehtml::document::height() const
362 {
363 	return m_size.height;
364 }
365 
add_stylesheet(const tchar_t * str,const tchar_t * baseurl,const tchar_t * media)366 void litehtml::document::add_stylesheet( const tchar_t* str, const tchar_t* baseurl, const tchar_t* media )
367 {
368 	if(str && str[0])
369 	{
370 		m_css.push_back(css_text(str, baseurl, media));
371 	}
372 }
373 
on_mouse_over(int x,int y,int client_x,int client_y,position::vector & redraw_boxes)374 bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes )
375 {
376 	if(!m_root)
377 	{
378 		return false;
379 	}
380 
381 	element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y);
382 
383 	bool state_was_changed = false;
384 
385 	if(over_el != m_over_element)
386 	{
387 		if(m_over_element)
388 		{
389 			if(m_over_element->on_mouse_leave())
390 			{
391 				state_was_changed = true;
392 			}
393 		}
394 		m_over_element = over_el;
395 	}
396 
397 	const tchar_t* cursor = 0;
398 
399 	if(m_over_element)
400 	{
401 		if(m_over_element->on_mouse_over())
402 		{
403 			state_was_changed = true;
404 		}
405 		cursor = m_over_element->get_cursor();
406 	}
407 
408 	m_container->set_cursor(cursor ? cursor : _t("auto"));
409 
410 	if(state_was_changed)
411 	{
412 		return m_root->find_styles_changes(redraw_boxes, 0, 0);
413 	}
414 	return false;
415 }
416 
on_mouse_leave(position::vector & redraw_boxes)417 bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes )
418 {
419 	if(!m_root)
420 	{
421 		return false;
422 	}
423 	if(m_over_element)
424 	{
425 		if(m_over_element->on_mouse_leave())
426 		{
427 			return m_root->find_styles_changes(redraw_boxes, 0, 0);
428 		}
429 	}
430 	return false;
431 }
432 
on_lbutton_down(int x,int y,int client_x,int client_y,position::vector & redraw_boxes)433 bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes )
434 {
435 	if(!m_root)
436 	{
437 		return false;
438 	}
439 
440 	element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y);
441 
442 	bool state_was_changed = false;
443 
444 	if(over_el != m_over_element)
445 	{
446 		if(m_over_element)
447 		{
448 			if(m_over_element->on_mouse_leave())
449 			{
450 				state_was_changed = true;
451 			}
452 		}
453 		m_over_element = over_el;
454 		if(m_over_element)
455 		{
456 			if(m_over_element->on_mouse_over())
457 			{
458 				state_was_changed = true;
459 			}
460 		}
461 	}
462 
463 	const tchar_t* cursor = 0;
464 
465 	if(m_over_element)
466 	{
467 		if(m_over_element->on_lbutton_down())
468 		{
469 			state_was_changed = true;
470 		}
471 		cursor = m_over_element->get_cursor();
472 	}
473 
474 	m_container->set_cursor(cursor ? cursor : _t("auto"));
475 
476 	if(state_was_changed)
477 	{
478 		return m_root->find_styles_changes(redraw_boxes, 0, 0);
479 	}
480 
481 	return false;
482 }
483 
on_lbutton_up(int x,int y,int client_x,int client_y,position::vector & redraw_boxes)484 bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y, position::vector& redraw_boxes )
485 {
486 	if(!m_root)
487 	{
488 		return false;
489 	}
490 	if(m_over_element)
491 	{
492 		if(m_over_element->on_lbutton_up())
493 		{
494 			return m_root->find_styles_changes(redraw_boxes, 0, 0);
495 		}
496 	}
497 	return false;
498 }
499 
create_element(const tchar_t * tag_name,const string_map & attributes)500 litehtml::element::ptr litehtml::document::create_element(const tchar_t* tag_name, const string_map& attributes)
501 {
502 	element::ptr newTag;
503 	document::ptr this_doc = shared_from_this();
504 	if(m_container)
505 	{
506 		newTag = m_container->create_element(tag_name, attributes, this_doc);
507 	}
508 	if(!newTag)
509 	{
510 		if(!t_strcmp(tag_name, _t("br")))
511 		{
512 			newTag = std::make_shared<litehtml::el_break>(this_doc);
513 		} else if(!t_strcmp(tag_name, _t("p")))
514 		{
515 			newTag = std::make_shared<litehtml::el_para>(this_doc);
516 		} else if(!t_strcmp(tag_name, _t("img")))
517 		{
518 			newTag = std::make_shared<litehtml::el_image>(this_doc);
519 		} else if(!t_strcmp(tag_name, _t("table")))
520 		{
521 			newTag = std::make_shared<litehtml::el_table>(this_doc);
522 		} else if(!t_strcmp(tag_name, _t("td")) || !t_strcmp(tag_name, _t("th")))
523 		{
524 			newTag = std::make_shared<litehtml::el_td>(this_doc);
525 		} else if(!t_strcmp(tag_name, _t("link")))
526 		{
527 			newTag = std::make_shared<litehtml::el_link>(this_doc);
528 		} else if(!t_strcmp(tag_name, _t("title")))
529 		{
530 			newTag = std::make_shared<litehtml::el_title>(this_doc);
531 		} else if(!t_strcmp(tag_name, _t("a")))
532 		{
533 			newTag = std::make_shared<litehtml::el_anchor>(this_doc);
534 		} else if(!t_strcmp(tag_name, _t("tr")))
535 		{
536 			newTag = std::make_shared<litehtml::el_tr>(this_doc);
537 		} else if(!t_strcmp(tag_name, _t("style")))
538 		{
539 			newTag = std::make_shared<litehtml::el_style>(this_doc);
540 		} else if(!t_strcmp(tag_name, _t("base")))
541 		{
542 			newTag = std::make_shared<litehtml::el_base>(this_doc);
543 		} else if(!t_strcmp(tag_name, _t("body")))
544 		{
545 			newTag = std::make_shared<litehtml::el_body>(this_doc);
546 		} else if(!t_strcmp(tag_name, _t("div")))
547 		{
548 			newTag = std::make_shared<litehtml::el_div>(this_doc);
549 		} else if(!t_strcmp(tag_name, _t("script")))
550 		{
551 			newTag = std::make_shared<litehtml::el_script>(this_doc);
552 		} else if(!t_strcmp(tag_name, _t("font")))
553 		{
554 			newTag = std::make_shared<litehtml::el_font>(this_doc);
555 		} else
556 		{
557 			newTag = std::make_shared<litehtml::html_tag>(this_doc);
558 		}
559 	}
560 
561 	if(newTag)
562 	{
563 		newTag->set_tagName(tag_name);
564 		for (string_map::const_iterator iter = attributes.begin(); iter != attributes.end(); iter++)
565 		{
566 			newTag->set_attr(iter->first.c_str(), iter->second.c_str());
567 		}
568 	}
569 
570 	return newTag;
571 }
572 
get_fixed_boxes(position::vector & fixed_boxes)573 void litehtml::document::get_fixed_boxes( position::vector& fixed_boxes )
574 {
575 	fixed_boxes = m_fixed_boxes;
576 }
577 
add_fixed_box(const position & pos)578 void litehtml::document::add_fixed_box( const position& pos )
579 {
580 	m_fixed_boxes.push_back(pos);
581 }
582 
media_changed()583 bool litehtml::document::media_changed()
584 {
585 	if(!m_media_lists.empty())
586 	{
587 		container()->get_media_features(m_media);
588 		if (update_media_lists(m_media))
589 		{
590 			m_root->refresh_styles();
591 			m_root->parse_styles();
592 			return true;
593 		}
594 	}
595 	return false;
596 }
597 
lang_changed()598 bool litehtml::document::lang_changed()
599 {
600 	if(!m_media_lists.empty())
601 	{
602 		tstring culture;
603 		container()->get_language(m_lang, culture);
604 		if(!culture.empty())
605 		{
606 			m_culture = m_lang + _t('-') + culture;
607 		}
608 		else
609 		{
610 			m_culture.clear();
611 		}
612 		m_root->refresh_styles();
613 		m_root->parse_styles();
614 		return true;
615 	}
616 	return false;
617 }
618 
update_media_lists(const media_features & features)619 bool litehtml::document::update_media_lists(const media_features& features)
620 {
621 	bool update_styles = false;
622 	for(media_query_list::vector::iterator iter = m_media_lists.begin(); iter != m_media_lists.end(); iter++)
623 	{
624 		if((*iter)->apply_media_features(features))
625 		{
626 			update_styles = true;
627 		}
628 	}
629 	return update_styles;
630 }
631 
add_media_list(media_query_list::ptr list)632 void litehtml::document::add_media_list( media_query_list::ptr list )
633 {
634 	if(list)
635 	{
636 		if(std::find(m_media_lists.begin(), m_media_lists.end(), list) == m_media_lists.end())
637 		{
638 			m_media_lists.push_back(list);
639 		}
640 	}
641 }
642 
create_node(GumboNode * node,elements_vector & elements)643 void litehtml::document::create_node(GumboNode* node, elements_vector& elements)
644 {
645 	switch (node->type)
646 	{
647 	case GUMBO_NODE_ELEMENT:
648 		{
649 			string_map attrs;
650 			GumboAttribute* attr;
651 			for (unsigned int i = 0; i < node->v.element.attributes.length; i++)
652 			{
653 				attr = (GumboAttribute*)node->v.element.attributes.data[i];
654 				attrs[tstring(litehtml_from_utf8(attr->name))] = litehtml_from_utf8(attr->value);
655 			}
656 
657 
658 			element::ptr ret;
659 			const char* tag = gumbo_normalized_tagname(node->v.element.tag);
660 			if (tag[0])
661 			{
662 				ret = create_element(litehtml_from_utf8(tag), attrs);
663 			}
664 			else
665 			{
666 				if (node->v.element.original_tag.data && node->v.element.original_tag.length)
667 				{
668 					std::string strA;
669 					gumbo_tag_from_original_text(&node->v.element.original_tag);
670 					strA.append(node->v.element.original_tag.data, node->v.element.original_tag.length);
671 					ret = create_element(litehtml_from_utf8(strA.c_str()), attrs);
672 				}
673 			}
674 			if (ret)
675 			{
676 				elements_vector child;
677 				for (unsigned int i = 0; i < node->v.element.children.length; i++)
678 				{
679 					child.clear();
680 					create_node(static_cast<GumboNode*> (node->v.element.children.data[i]), child);
681 					std::for_each(child.begin(), child.end(),
682 						[&ret](element::ptr& el)
683 						{
684 							ret->appendChild(el);
685 						}
686 					);
687 				}
688 				elements.push_back(ret);
689 			}
690 		}
691 		break;
692 	case GUMBO_NODE_TEXT:
693 		{
694 			std::wstring str;
695 			std::wstring str_in = (const wchar_t*) (utf8_to_wchar(node->v.text.text));
696 			ucode_t c;
697 			for (size_t i = 0; i < str_in.length(); i++)
698 			{
699 				c = (ucode_t) str_in[i];
700 				if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'))
701 				{
702 					if (!str.empty())
703 					{
704 						elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this()));
705 						str.clear();
706 					}
707 					str += c;
708 					elements.push_back(std::make_shared<el_space>(litehtml_from_wchar(str.c_str()), shared_from_this()));
709 					str.clear();
710 				}
711 				// CJK character range
712 				else if (c >= 0x4E00 && c <= 0x9FCC)
713 				{
714 					if (!str.empty())
715 					{
716 						elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this()));
717 						str.clear();
718 					}
719 					str += c;
720 					elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this()));
721 					str.clear();
722 				}
723 				else
724 				{
725 					str += c;
726 				}
727 			}
728 			if (!str.empty())
729 			{
730 				elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this()));
731 			}
732 		}
733 		break;
734 	case GUMBO_NODE_CDATA:
735 		{
736 			element::ptr ret = std::make_shared<el_cdata>(shared_from_this());
737 			ret->set_data(litehtml_from_utf8(node->v.text.text));
738 			elements.push_back(ret);
739 		}
740 		break;
741 	case GUMBO_NODE_COMMENT:
742 		{
743 			element::ptr ret = std::make_shared<el_comment>(shared_from_this());
744 			ret->set_data(litehtml_from_utf8(node->v.text.text));
745 			elements.push_back(ret);
746 		}
747 		break;
748 	case GUMBO_NODE_WHITESPACE:
749 		{
750 			tstring str = litehtml_from_utf8(node->v.text.text);
751 			for (size_t i = 0; i < str.length(); i++)
752 			{
753 				elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), shared_from_this()));
754 			}
755 		}
756 		break;
757 	default:
758 		break;
759 	}
760 }
761 
fix_tables_layout()762 void litehtml::document::fix_tables_layout()
763 {
764 	size_t i = 0;
765 	while (i < m_tabular_elements.size())
766 	{
767 		element::ptr el_ptr = m_tabular_elements[i];
768 
769 		switch (el_ptr->get_display())
770 		{
771 		case display_inline_table:
772 		case display_table:
773 			fix_table_children(el_ptr, display_table_row_group, _t("table-row-group"));
774 			break;
775 		case display_table_footer_group:
776 		case display_table_row_group:
777 		case display_table_header_group:
778 			fix_table_parent(el_ptr, display_table, _t("table"));
779 			fix_table_children(el_ptr, display_table_row, _t("table-row"));
780 			break;
781 		case display_table_row:
782 			fix_table_parent(el_ptr, display_table_row_group, _t("table-row-group"));
783 			fix_table_children(el_ptr, display_table_cell, _t("table-cell"));
784 			break;
785 		case display_table_cell:
786 			fix_table_parent(el_ptr, display_table_row, _t("table-row"));
787 			break;
788 		// TODO: make table layout fix for table-caption, table-column etc. elements
789 		case display_table_caption:
790 		case display_table_column:
791 		case display_table_column_group:
792 		default:
793 			break;
794 		}
795 		i++;
796 	}
797 }
798 
fix_table_children(element::ptr & el_ptr,style_display disp,const tchar_t * disp_str)799 void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str)
800 {
801 	elements_vector tmp;
802 	elements_vector::iterator first_iter = el_ptr->m_children.begin();
803 	elements_vector::iterator cur_iter = el_ptr->m_children.begin();
804 
805 	auto flush_elements = [&]()
806 	{
807 		element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this());
808 		style st;
809 		st.add_property(_t("display"), disp_str, 0, false);
810 		annon_tag->add_style(st);
811 		annon_tag->parent(el_ptr);
812 		annon_tag->parse_styles();
813 		std::for_each(tmp.begin(), tmp.end(),
814 			[&annon_tag](element::ptr& el)
815 			{
816 				annon_tag->appendChild(el);
817 			}
818 		);
819 		first_iter = el_ptr->m_children.insert(first_iter, annon_tag);
820 		cur_iter = first_iter + 1;
821 		while (cur_iter != el_ptr->m_children.end() && (*cur_iter)->parent() != el_ptr)
822 		{
823 			cur_iter = el_ptr->m_children.erase(cur_iter);
824 		}
825 		first_iter = cur_iter;
826 		tmp.clear();
827 	};
828 
829 	while (cur_iter != el_ptr->m_children.end())
830 	{
831 		if ((*cur_iter)->get_display() != disp)
832 		{
833 			if (!(*cur_iter)->is_white_space() || ((*cur_iter)->is_white_space() && !tmp.empty()))
834 			{
835 				if (tmp.empty())
836 				{
837 					first_iter = cur_iter;
838 				}
839 				tmp.push_back((*cur_iter));
840 			}
841 			cur_iter++;
842 		}
843 		else if (!tmp.empty())
844 		{
845 			flush_elements();
846 		}
847 		else
848 		{
849 			cur_iter++;
850 		}
851 	}
852 	if (!tmp.empty())
853 	{
854 		flush_elements();
855 	}
856 }
857 
fix_table_parent(element::ptr & el_ptr,style_display disp,const tchar_t * disp_str)858 void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str)
859 {
860 	element::ptr parent = el_ptr->parent();
861 
862 	if (parent->get_display() != disp)
863 	{
864 		elements_vector::iterator this_element = std::find_if(parent->m_children.begin(), parent->m_children.end(),
865 			[&](element::ptr& el)
866 			{
867 				if (el == el_ptr)
868 				{
869 					return true;
870 				}
871 				return false;
872 			}
873 		);
874 		if (this_element != parent->m_children.end())
875 		{
876 			style_display el_disp = el_ptr->get_display();
877 			elements_vector::iterator first = this_element;
878 			elements_vector::iterator last = this_element;
879 			elements_vector::iterator cur = this_element;
880 
881 			// find first element with same display
882 			while (true)
883 			{
884 				if (cur == parent->m_children.begin()) break;
885 				cur--;
886 				if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp)
887 				{
888 					first = cur;
889 				}
890 				else
891 				{
892 					break;
893 				}
894 			}
895 
896 			// find last element with same display
897 			cur = this_element;
898 			while (true)
899 			{
900 				cur++;
901 				if (cur == parent->m_children.end()) break;
902 
903 				if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp)
904 				{
905 					last = cur;
906 				}
907 				else
908 				{
909 					break;
910 				}
911 			}
912 
913 			// extract elements with the same display and wrap them with anonymous object
914 			element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this());
915 			style st;
916 			st.add_property(_t("display"), disp_str, 0, false);
917 			annon_tag->add_style(st);
918 			annon_tag->parent(parent);
919 			annon_tag->parse_styles();
920 			std::for_each(first, last + 1,
921 				[&annon_tag](element::ptr& el)
922 				{
923 					annon_tag->appendChild(el);
924 				}
925 			);
926 			first = parent->m_children.erase(first, last + 1);
927 			parent->m_children.insert(first, annon_tag);
928 		}
929 	}
930 }
931