1 #include "html.h"
2 #include "html_tag.h"
3 #include "document.h"
4 #include "iterators.h"
5 #include "stylesheet.h"
6 #include "table.h"
7 #include <algorithm>
8 #include <locale>
9 #include "el_before_after.h"
10 #include "num_cvt.h"
11 
html_tag(const std::shared_ptr<litehtml::document> & doc)12 litehtml::html_tag::html_tag(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc)
13 {
14 	m_box_sizing			= box_sizing_content_box;
15 	m_z_index				= 0;
16 	m_overflow				= overflow_visible;
17 	m_box					= 0;
18 	m_text_align			= text_align_left;
19 	m_el_position			= element_position_static;
20 	m_display				= display_inline;
21 	m_vertical_align		= va_baseline;
22 	m_list_style_type		= list_style_type_none;
23 	m_list_style_position	= list_style_position_outside;
24 	m_float					= float_none;
25 	m_clear					= clear_none;
26 	m_font					= 0;
27 	m_font_size				= 0;
28 	m_white_space			= white_space_normal;
29 	m_lh_predefined			= false;
30 	m_line_height			= 0;
31 	m_visibility			= visibility_visible;
32 	m_border_spacing_x		= 0;
33 	m_border_spacing_y		= 0;
34 	m_border_collapse		= border_collapse_separate;
35 }
36 
~html_tag()37 litehtml::html_tag::~html_tag()
38 {
39 
40 }
41 
appendChild(const element::ptr & el)42 bool litehtml::html_tag::appendChild(const element::ptr &el)
43 {
44 	if(el)
45 	{
46 		el->parent(shared_from_this());
47 		m_children.push_back(el);
48 		return true;
49 	}
50 	return false;
51 }
52 
removeChild(const element::ptr & el)53 bool litehtml::html_tag::removeChild(const element::ptr &el)
54 {
55 	if(el && el->parent() == shared_from_this())
56 	{
57 		el->parent(nullptr);
58 		m_children.erase(std::remove(m_children.begin(), m_children.end(), el), m_children.end());
59 		return true;
60 	}
61 	return false;
62 }
63 
clearRecursive()64 void litehtml::html_tag::clearRecursive()
65 {
66 	for(auto& el : m_children)
67 	{
68 		el->clearRecursive();
69 		el->parent(nullptr);
70 	}
71 	m_children.clear();
72 }
73 
74 
get_tagName() const75 const litehtml::tchar_t* litehtml::html_tag::get_tagName() const
76 {
77 	return m_tag.c_str();
78 }
79 
set_attr(const tchar_t * name,const tchar_t * val)80 void litehtml::html_tag::set_attr( const tchar_t* name, const tchar_t* val )
81 {
82 	if(name && val)
83 	{
84 		tstring s_val = name;
85 		for(size_t i = 0; i < s_val.length(); i++)
86 		{
87 			s_val[i] = std::tolower(s_val[i], std::locale::classic());
88 		}
89 		m_attrs[s_val] = val;
90 
91 		if( t_strcasecmp( name, _t("class") ) == 0 )
92 		{
93 			m_class_values.resize( 0 );
94 			split_string( val, m_class_values, _t(" ") );
95 		}
96 	}
97 }
98 
get_attr(const tchar_t * name,const tchar_t * def) const99 const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, const tchar_t* def ) const
100 {
101 	string_map::const_iterator attr = m_attrs.find(name);
102 	if(attr != m_attrs.end())
103 	{
104 		return attr->second.c_str();
105 	}
106 	return def;
107 }
108 
select_all(const tstring & selector)109 litehtml::elements_vector litehtml::html_tag::select_all( const tstring& selector )
110 {
111 	css_selector sel(media_query_list::ptr(0));
112 	sel.parse(selector);
113 
114 	return select_all(sel);
115 }
116 
select_all(const css_selector & selector)117 litehtml::elements_vector litehtml::html_tag::select_all( const css_selector& selector )
118 {
119 	litehtml::elements_vector res;
120 	select_all(selector, res);
121 	return res;
122 }
123 
select_all(const css_selector & selector,elements_vector & res)124 void litehtml::html_tag::select_all(const css_selector& selector, elements_vector& res)
125 {
126 	if(select(selector))
127 	{
128 		res.push_back(shared_from_this());
129 	}
130 
131 	for(auto& el : m_children)
132 	{
133 		el->select_all(selector, res);
134 	}
135 }
136 
137 
select_one(const tstring & selector)138 litehtml::element::ptr litehtml::html_tag::select_one( const tstring& selector )
139 {
140 	css_selector sel(media_query_list::ptr(0));
141 	sel.parse(selector);
142 
143 	return select_one(sel);
144 }
145 
select_one(const css_selector & selector)146 litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selector )
147 {
148 	if(select(selector))
149 	{
150 		return shared_from_this();
151 	}
152 
153 	for(auto& el : m_children)
154 	{
155 		element::ptr res = el->select_one(selector);
156 		if(res)
157 		{
158 			return res;
159 		}
160 	}
161 	return 0;
162 }
163 
apply_stylesheet(const litehtml::css & stylesheet)164 void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet )
165 {
166 	remove_before_after();
167 
168 	for(const auto& sel : stylesheet.selectors())
169 	{
170 		int apply = select(*sel, false);
171 
172 		if(apply != select_no_match)
173 		{
174 			used_selector::ptr us = std::unique_ptr<used_selector>(new used_selector(sel, false));
175 
176 			if(sel->is_media_valid())
177 			{
178 				if(apply & select_match_pseudo_class)
179 				{
180 					if(select(*sel, true))
181 					{
182 						if(apply & select_match_with_after)
183 						{
184 							element::ptr el = get_element_after();
185 							if(el)
186 							{
187 								el->add_style(*sel->m_style);
188 							}
189 						} else if(apply & select_match_with_before)
190 						{
191 							element::ptr el = get_element_before();
192 							if(el)
193 							{
194 								el->add_style(*sel->m_style);
195 							}
196 						}
197 						else
198 						{
199 							add_style(*sel->m_style);
200 							us->m_used = true;
201 						}
202 					}
203 				} else if(apply & select_match_with_after)
204 				{
205 					element::ptr el = get_element_after();
206 					if(el)
207 					{
208 						el->add_style(*sel->m_style);
209 					}
210 				} else if(apply & select_match_with_before)
211 				{
212 					element::ptr el = get_element_before();
213 					if(el)
214 					{
215 						el->add_style(*sel->m_style);
216 					}
217 				} else
218 				{
219 					add_style(*sel->m_style);
220 					us->m_used = true;
221 				}
222 			}
223 			m_used_styles.push_back(std::move(us));
224 		}
225 	}
226 
227 	for(auto& el : m_children)
228 	{
229 		if(el->get_display() != display_inline_text)
230 		{
231 			el->apply_stylesheet(stylesheet);
232 		}
233 	}
234 }
235 
get_content_size(size & sz,int max_width)236 void litehtml::html_tag::get_content_size( size& sz, int max_width )
237 {
238 	sz.height	= 0;
239 	if(m_display == display_block)
240 	{
241 		sz.width	= max_width;
242 	} else
243 	{
244 		sz.width	= 0;
245 	}
246 }
247 
draw(uint_ptr hdc,int x,int y,const position * clip)248 void litehtml::html_tag::draw( uint_ptr hdc, int x, int y, const position* clip )
249 {
250 	position pos = m_pos;
251 	pos.x	+= x;
252 	pos.y	+= y;
253 
254 	draw_background(hdc, x, y, clip);
255 
256 	if(m_display == display_list_item && m_list_style_type != list_style_type_none)
257 	{
258 		if(m_overflow > overflow_visible)
259 		{
260 			position border_box = pos;
261 			border_box += m_padding;
262 			border_box += m_borders;
263 
264 			border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
265 
266 			bdr_radius -= m_borders;
267 			bdr_radius -= m_padding;
268 
269 			get_document()->container()->set_clip(pos, bdr_radius, true, true);
270 		}
271 
272 		draw_list_marker(hdc, pos);
273 
274 		if(m_overflow > overflow_visible)
275 		{
276 			get_document()->container()->del_clip();
277 		}
278 	}
279 }
280 
get_font(font_metrics * fm)281 litehtml::uint_ptr litehtml::html_tag::get_font(font_metrics* fm)
282 {
283 	if(fm)
284 	{
285 		*fm = m_font_metrics;
286 	}
287 	return m_font;
288 }
289 
get_style_property(const tchar_t * name,bool inherited,const tchar_t * def)290 const litehtml::tchar_t* litehtml::html_tag::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ )
291 {
292 	const tchar_t* ret = m_style.get_property(name);
293 	element::ptr el_parent = parent();
294 	if (el_parent)
295 	{
296 		if ( ( ret && !t_strcasecmp(ret, _t("inherit")) ) || (!ret && inherited) )
297 		{
298 			ret = el_parent->get_style_property(name, inherited, def);
299 		}
300 	}
301 
302 	if(!ret)
303 	{
304 		ret = def;
305 	}
306 
307 	return ret;
308 }
309 
parse_styles(bool is_reparse)310 void litehtml::html_tag::parse_styles(bool is_reparse)
311 {
312 	const tchar_t* style = get_attr(_t("style"));
313 
314 	if(style)
315 	{
316 		m_style.add(style, NULL);
317 	}
318 
319 	init_font();
320 	document::ptr doc = get_document();
321 
322 	m_el_position	= (element_position)	value_index(get_style_property(_t("position"),		false,	_t("static")),		element_position_strings,	element_position_fixed);
323 	m_text_align	= (text_align)			value_index(get_style_property(_t("text-align"),		true,	_t("left")),		text_align_strings,			text_align_left);
324 	m_overflow		= (overflow)			value_index(get_style_property(_t("overflow"),		false,	_t("visible")),		overflow_strings,			overflow_visible);
325 	m_white_space	= (white_space)			value_index(get_style_property(_t("white-space"),	true,	_t("normal")),		white_space_strings,		white_space_normal);
326 	m_display		= (style_display)		value_index(get_style_property(_t("display"),		false,	_t("inline")),		style_display_strings,		display_inline);
327 	m_visibility	= (visibility)			value_index(get_style_property(_t("visibility"),	true,	_t("visible")),		visibility_strings,			visibility_visible);
328 	m_box_sizing	= (box_sizing)			value_index(get_style_property(_t("box-sizing"),		false,	_t("content-box")),	box_sizing_strings,			box_sizing_content_box);
329 
330 	if(m_el_position != element_position_static)
331 	{
332 		const tchar_t* val = get_style_property(_t("z-index"), false, 0);
333 		if(val)
334 		{
335 			m_z_index = t_atoi(val);
336 		}
337 	}
338 
339 	const tchar_t* va	= get_style_property(_t("vertical-align"), true,	_t("baseline"));
340 	m_vertical_align = (vertical_align) value_index(va, vertical_align_strings, va_baseline);
341 
342 	const tchar_t* fl	= get_style_property(_t("float"), false,	_t("none"));
343 	m_float = (element_float) value_index(fl, element_float_strings, float_none);
344 
345 	m_clear = (element_clear) value_index(get_style_property(_t("clear"), false, _t("none")), element_clear_strings, clear_none);
346 
347 	if (m_float != float_none)
348 	{
349 		// reset display in to block for floating elements
350 		if (m_display != display_none)
351 		{
352 			m_display = display_block;
353 		}
354 	}
355 	else if (m_display == display_table ||
356 		m_display == display_inline_table ||
357 		m_display == display_table_caption ||
358 		m_display == display_table_cell ||
359 		m_display == display_table_column ||
360 		m_display == display_table_column_group ||
361 		m_display == display_table_footer_group ||
362 		m_display == display_table_header_group ||
363 		m_display == display_table_row ||
364 		m_display == display_table_row_group)
365 	{
366 		doc->add_tabular(shared_from_this());
367 	}
368 	// fix inline boxes with absolute/fixed positions
369 	else if (m_display != display_none && is_inline_box())
370 	{
371 		if (m_el_position == element_position_absolute || m_el_position == element_position_fixed)
372 		{
373 			m_display = display_block;
374 		}
375 	}
376 
377 	m_css_text_indent.fromString(	get_style_property(_t("text-indent"),	true,	_t("0")),	_t("0"));
378 
379 	m_css_width.fromString(			get_style_property(_t("width"),			false,	_t("auto")), _t("auto"));
380 	m_css_height.fromString(		get_style_property(_t("height"),		false,	_t("auto")), _t("auto"));
381 
382 	doc->cvt_units(m_css_width, m_font_size);
383 	doc->cvt_units(m_css_height, m_font_size);
384 
385 	m_css_min_width.fromString(		get_style_property(_t("min-width"),		false,	_t("0")));
386 	m_css_min_height.fromString(	get_style_property(_t("min-height"),		false,	_t("0")));
387 
388 	m_css_max_width.fromString(		get_style_property(_t("max-width"),		false,	_t("none")),	_t("none"));
389 	m_css_max_height.fromString(	get_style_property(_t("max-height"),		false,	_t("none")),	_t("none"));
390 
391 	doc->cvt_units(m_css_min_width, m_font_size);
392 	doc->cvt_units(m_css_min_height, m_font_size);
393 
394 	m_css_offsets.left.fromString(		get_style_property(_t("left"),				false,	_t("auto")), _t("auto"));
395 	m_css_offsets.right.fromString(		get_style_property(_t("right"),				false,	_t("auto")), _t("auto"));
396 	m_css_offsets.top.fromString(		get_style_property(_t("top"),				false,	_t("auto")), _t("auto"));
397 	m_css_offsets.bottom.fromString(	get_style_property(_t("bottom"),			false,	_t("auto")), _t("auto"));
398 
399 	doc->cvt_units(m_css_offsets.left, m_font_size);
400 	doc->cvt_units(m_css_offsets.right, m_font_size);
401 	doc->cvt_units(m_css_offsets.top,		m_font_size);
402 	doc->cvt_units(m_css_offsets.bottom,	m_font_size);
403 
404 	m_css_margins.left.fromString(		get_style_property(_t("margin-left"),		false,	_t("0")), _t("auto"));
405 	m_css_margins.right.fromString(		get_style_property(_t("margin-right"),		false,	_t("0")), _t("auto"));
406 	m_css_margins.top.fromString(		get_style_property(_t("margin-top"),			false,	_t("0")), _t("auto"));
407 	m_css_margins.bottom.fromString(	get_style_property(_t("margin-bottom"),		false,	_t("0")), _t("auto"));
408 
409 	m_css_padding.left.fromString(		get_style_property(_t("padding-left"),		false,	_t("0")), _t(""));
410 	m_css_padding.right.fromString(		get_style_property(_t("padding-right"),		false,	_t("0")), _t(""));
411 	m_css_padding.top.fromString(		get_style_property(_t("padding-top"),		false,	_t("0")), _t(""));
412 	m_css_padding.bottom.fromString(	get_style_property(_t("padding-bottom"),		false,	_t("0")), _t(""));
413 
414 	m_css_borders.left.width.fromString(	get_style_property(_t("border-left-width"),		false,	_t("medium")), border_width_strings);
415 	m_css_borders.right.width.fromString(	get_style_property(_t("border-right-width"),		false,	_t("medium")), border_width_strings);
416 	m_css_borders.top.width.fromString(		get_style_property(_t("border-top-width"),		false,	_t("medium")), border_width_strings);
417 	m_css_borders.bottom.width.fromString(	get_style_property(_t("border-bottom-width"),	false,	_t("medium")), border_width_strings);
418 
419 	m_css_borders.left.color = web_color::from_string(get_style_property(_t("border-left-color"),	false,	_t("")), doc->container());
420 	m_css_borders.left.style = (border_style) value_index(get_style_property(_t("border-left-style"), false, _t("none")), border_style_strings, border_style_none);
421 
422     m_css_borders.right.color = web_color::from_string(get_style_property(_t("border-right-color"), false, _t("")), doc->container());
423 	m_css_borders.right.style = (border_style) value_index(get_style_property(_t("border-right-style"), false, _t("none")), border_style_strings, border_style_none);
424 
425     m_css_borders.top.color = web_color::from_string(get_style_property(_t("border-top-color"), false, _t("")), doc->container());
426 	m_css_borders.top.style = (border_style) value_index(get_style_property(_t("border-top-style"), false, _t("none")), border_style_strings, border_style_none);
427 
428     m_css_borders.bottom.color = web_color::from_string(get_style_property(_t("border-bottom-color"), false, _t("")), doc->container());
429 	m_css_borders.bottom.style = (border_style) value_index(get_style_property(_t("border-bottom-style"), false, _t("none")), border_style_strings, border_style_none);
430 
431 	m_css_borders.radius.top_left_x.fromString(get_style_property(_t("border-top-left-radius-x"), false, _t("0")));
432 	m_css_borders.radius.top_left_y.fromString(get_style_property(_t("border-top-left-radius-y"), false, _t("0")));
433 
434 	m_css_borders.radius.top_right_x.fromString(get_style_property(_t("border-top-right-radius-x"), false, _t("0")));
435 	m_css_borders.radius.top_right_y.fromString(get_style_property(_t("border-top-right-radius-y"), false, _t("0")));
436 
437 	m_css_borders.radius.bottom_right_x.fromString(get_style_property(_t("border-bottom-right-radius-x"), false, _t("0")));
438 	m_css_borders.radius.bottom_right_y.fromString(get_style_property(_t("border-bottom-right-radius-y"), false, _t("0")));
439 
440 	m_css_borders.radius.bottom_left_x.fromString(get_style_property(_t("border-bottom-left-radius-x"), false, _t("0")));
441 	m_css_borders.radius.bottom_left_y.fromString(get_style_property(_t("border-bottom-left-radius-y"), false, _t("0")));
442 
443 	doc->cvt_units(m_css_borders.radius.bottom_left_x,			m_font_size);
444 	doc->cvt_units(m_css_borders.radius.bottom_left_y,			m_font_size);
445 	doc->cvt_units(m_css_borders.radius.bottom_right_x,			m_font_size);
446 	doc->cvt_units(m_css_borders.radius.bottom_right_y,			m_font_size);
447 	doc->cvt_units(m_css_borders.radius.top_left_x,				m_font_size);
448 	doc->cvt_units(m_css_borders.radius.top_left_y,				m_font_size);
449 	doc->cvt_units(m_css_borders.radius.top_right_x,				m_font_size);
450 	doc->cvt_units(m_css_borders.radius.top_right_y,				m_font_size);
451 
452 	doc->cvt_units(m_css_text_indent,								m_font_size);
453 
454 	m_margins.left		= doc->cvt_units(m_css_margins.left,		m_font_size);
455 	m_margins.right		= doc->cvt_units(m_css_margins.right,		m_font_size);
456 	m_margins.top		= doc->cvt_units(m_css_margins.top,		m_font_size);
457 	m_margins.bottom	= doc->cvt_units(m_css_margins.bottom,	m_font_size);
458 
459 	m_padding.left		= doc->cvt_units(m_css_padding.left,		m_font_size);
460 	m_padding.right		= doc->cvt_units(m_css_padding.right,		m_font_size);
461 	m_padding.top		= doc->cvt_units(m_css_padding.top,		m_font_size);
462 	m_padding.bottom	= doc->cvt_units(m_css_padding.bottom,	m_font_size);
463 
464 	m_borders.left		= doc->cvt_units(m_css_borders.left.width,	m_font_size);
465 	m_borders.right		= doc->cvt_units(m_css_borders.right.width,	m_font_size);
466 	m_borders.top		= doc->cvt_units(m_css_borders.top.width,		m_font_size);
467 	m_borders.bottom	= doc->cvt_units(m_css_borders.bottom.width,	m_font_size);
468 
469 	css_length line_height;
470 	line_height.fromString(get_style_property(_t("line-height"),	true,	_t("normal")), _t("normal"));
471 	if(line_height.is_predefined())
472 	{
473 		m_line_height = m_font_metrics.height;
474 		m_lh_predefined = true;
475 	} else if(line_height.units() == css_units_none)
476 	{
477 		m_line_height = (int) (line_height.val() * m_font_size);
478 		m_lh_predefined = false;
479 	} else
480 	{
481 		m_line_height =  doc->cvt_units(line_height,	m_font_size, m_font_size);
482 		m_lh_predefined = false;
483 	}
484 
485 
486 	if(m_display == display_list_item)
487 	{
488 		const tchar_t* list_type = get_style_property(_t("list-style-type"), true, _t("disc"));
489 		m_list_style_type = (list_style_type) value_index(list_type, list_style_type_strings, list_style_type_disc);
490 
491 		const tchar_t* list_pos = get_style_property(_t("list-style-position"), true, _t("outside"));
492 		m_list_style_position = (list_style_position) value_index(list_pos, list_style_position_strings, list_style_position_outside);
493 
494 		const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
495 		if(list_image && list_image[0])
496 		{
497 			tstring url;
498 			css::parse_css_url(list_image, url);
499 
500 			const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
501 			doc->container()->load_image(url.c_str(), list_image_baseurl, true);
502 		}
503 
504 	}
505 
506 	parse_background();
507 
508 	if(!is_reparse)
509 	{
510 		for(auto& el : m_children)
511 		{
512 			el->parse_styles();
513 		}
514 	}
515 }
516 
render(int x,int y,int max_width,bool second_pass)517 int litehtml::html_tag::render( int x, int y, int max_width, bool second_pass )
518 {
519 	if (m_display == display_table || m_display == display_inline_table)
520 	{
521 		return render_table(x, y, max_width, second_pass);
522 	}
523 
524 	return render_box(x, y, max_width, second_pass);
525 }
526 
is_white_space() const527 bool litehtml::html_tag::is_white_space() const
528 {
529 	return false;
530 }
531 
get_font_size() const532 int litehtml::html_tag::get_font_size() const
533 {
534 	return m_font_size;
535 }
536 
get_base_line()537 int litehtml::html_tag::get_base_line()
538 {
539 	if(is_replaced())
540 	{
541 		return 0;
542 	}
543 	int bl = 0;
544 	if(!m_boxes.empty())
545 	{
546 		bl = m_boxes.back()->baseline() + content_margins_bottom();
547 	}
548 	return bl;
549 }
550 
init()551 void litehtml::html_tag::init()
552 {
553 	if (m_display == display_table || m_display == display_inline_table)
554 	{
555 		if (m_grid)
556 		{
557 			m_grid->clear();
558 		}
559 		else
560 		{
561 			m_grid = std::unique_ptr<table_grid>(new table_grid());
562 		}
563 
564 		go_inside_table 		table_selector;
565 		table_rows_selector		row_selector;
566 		table_cells_selector	cell_selector;
567 
568 		elements_iterator row_iter(shared_from_this(), &table_selector, &row_selector);
569 
570 		element::ptr row = row_iter.next(false);
571 		while (row)
572 		{
573 			m_grid->begin_row(row);
574 
575 			elements_iterator cell_iter(row, &table_selector, &cell_selector);
576 			element::ptr cell = cell_iter.next();
577 			while (cell)
578 			{
579 				m_grid->add_cell(cell);
580 
581 				cell = cell_iter.next(false);
582 			}
583 			row = row_iter.next(false);
584 		}
585 
586 		m_grid->finish();
587 	}
588 
589 	for (auto& el : m_children)
590 	{
591 		el->init();
592 	}
593 }
594 
select(const css_selector & selector,bool apply_pseudo)595 int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo)
596 {
597 	int right_res = select(selector.m_right, apply_pseudo);
598 	if(right_res == select_no_match)
599 	{
600 		return select_no_match;
601 	}
602 	element::ptr el_parent = parent();
603 	if(selector.m_left)
604 	{
605 		if (!el_parent)
606 		{
607 			return select_no_match;
608 		}
609 		switch(selector.m_combinator)
610 		{
611 		case combinator_descendant:
612 			{
613 				bool is_pseudo = false;
614 				element::ptr res = find_ancestor(*selector.m_left, apply_pseudo, &is_pseudo);
615 				if(!res)
616 				{
617 					return select_no_match;
618 				} else
619 				{
620 					if(is_pseudo)
621 					{
622 						right_res |= select_match_pseudo_class;
623 					}
624 				}
625 			}
626 			break;
627 		case combinator_child:
628 			{
629 				int res = el_parent->select(*selector.m_left, apply_pseudo);
630 				if(res == select_no_match)
631 				{
632 					return select_no_match;
633 				} else
634 				{
635 					if(right_res != select_match_pseudo_class)
636 					{
637 						right_res |= res;
638 					}
639 				}
640 			}
641 			break;
642 		case combinator_adjacent_sibling:
643 			{
644 				bool is_pseudo = false;
645 				element::ptr res = el_parent->find_adjacent_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
646 				if(!res)
647 				{
648 					return select_no_match;
649 				} else
650 				{
651 					if(is_pseudo)
652 					{
653 						right_res |= select_match_pseudo_class;
654 					}
655 				}
656 			}
657 			break;
658 		case combinator_general_sibling:
659 			{
660 				bool is_pseudo = false;
661 				element::ptr res =  el_parent->find_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
662 				if(!res)
663 				{
664 					return select_no_match;
665 				} else
666 				{
667 					if(is_pseudo)
668 					{
669 						right_res |= select_match_pseudo_class;
670 					}
671 				}
672 			}
673 			break;
674 		default:
675 			right_res = select_no_match;
676 		}
677 	}
678 	return right_res;
679 }
680 
select(const css_element_selector & selector,bool apply_pseudo)681 int litehtml::html_tag::select(const css_element_selector& selector, bool apply_pseudo)
682 {
683 	if(!selector.m_tag.empty() && selector.m_tag != _t("*"))
684 	{
685 		if(selector.m_tag != m_tag)
686 		{
687 			return select_no_match;
688 		}
689 	}
690 
691 	int res = select_match;
692 	element::ptr el_parent = parent();
693 
694 	for(css_attribute_selector::vector::const_iterator i = selector.m_attrs.begin(); i != selector.m_attrs.end(); i++)
695 	{
696 		const tchar_t* attr_value = get_attr(i->attribute.c_str());
697 		switch(i->condition)
698 		{
699 		case select_exists:
700 			if(!attr_value)
701 			{
702 				return select_no_match;
703 			}
704 			break;
705 		case select_equal:
706 			if(!attr_value)
707 			{
708 				return select_no_match;
709 			} else
710 			{
711 				if(i->attribute == _t("class"))
712 				{
713 					const string_vector & tokens1 = m_class_values;
714 					const string_vector & tokens2 = i->class_val;
715 					bool found = true;
716 					for(string_vector::const_iterator str1 = tokens2.begin(); str1 != tokens2.end() && found; str1++)
717 					{
718 						bool f = false;
719 						for(string_vector::const_iterator str2 = tokens1.begin(); str2 != tokens1.end() && !f; str2++)
720 						{
721 							if( !t_strcasecmp(str1->c_str(), str2->c_str()) )
722 							{
723 								f = true;
724 							}
725 						}
726 						if(!f)
727 						{
728 							found = false;
729 						}
730 					}
731 					if(!found)
732 					{
733 						return select_no_match;
734 					}
735 				} else
736 				{
737 					if( t_strcasecmp(i->val.c_str(), attr_value) )
738 					{
739 						return select_no_match;
740 					}
741 				}
742 			}
743 			break;
744 		case select_contain_str:
745 			if(!attr_value)
746 			{
747 				return select_no_match;
748 			} else if(!t_strstr(attr_value, i->val.c_str()))
749 			{
750 				return select_no_match;
751 			}
752 			break;
753 		case select_start_str:
754 			if(!attr_value)
755 			{
756 				return select_no_match;
757 			} else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
758 			{
759 				return select_no_match;
760 			}
761 			break;
762 		case select_end_str:
763 			if(!attr_value)
764 			{
765 				return select_no_match;
766 			} else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
767 			{
768 				const tchar_t* s = attr_value + t_strlen(attr_value) - i->val.length() - 1;
769 				if(s < attr_value)
770 				{
771 					return select_no_match;
772 				}
773 				if(i->val != s)
774 				{
775 					return select_no_match;
776 				}
777 			}
778 			break;
779 		case select_pseudo_element:
780 			if(i->val == _t("after"))
781 			{
782 				res |= select_match_with_after;
783 			} else if(i->val == _t("before"))
784 			{
785 				res |= select_match_with_before;
786 			} else
787 			{
788 				return select_no_match;
789 			}
790 			break;
791 		case select_pseudo_class:
792 			if(apply_pseudo)
793 			{
794 				if (!el_parent) return select_no_match;
795 
796 				tstring selector_param;
797 				tstring	selector_name;
798 
799 				tstring::size_type begin	= i->val.find_first_of(_t('('));
800 				tstring::size_type end		= (begin == tstring::npos) ? tstring::npos : find_close_bracket(i->val, begin);
801 				if(begin != tstring::npos && end != tstring::npos)
802 				{
803 					selector_param = i->val.substr(begin + 1, end - begin - 1);
804 				}
805 				if(begin != tstring::npos)
806 				{
807 					selector_name = i->val.substr(0, begin);
808 					litehtml::trim(selector_name);
809 				} else
810 				{
811 					selector_name = i->val;
812 				}
813 
814 				int selector = value_index(selector_name.c_str(), pseudo_class_strings);
815 
816 				switch(selector)
817 				{
818 				case pseudo_class_only_child:
819 					if (!el_parent->is_only_child(shared_from_this(), false))
820 					{
821 						return select_no_match;
822 					}
823 					break;
824 				case pseudo_class_only_of_type:
825 					if (!el_parent->is_only_child(shared_from_this(), true))
826 					{
827 						return select_no_match;
828 					}
829 					break;
830 				case pseudo_class_first_child:
831 					if (!el_parent->is_nth_child(shared_from_this(), 0, 1, false))
832 					{
833 						return select_no_match;
834 					}
835 					break;
836 				case pseudo_class_first_of_type:
837 					if (!el_parent->is_nth_child(shared_from_this(), 0, 1, true))
838 					{
839 						return select_no_match;
840 					}
841 					break;
842 				case pseudo_class_last_child:
843 					if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, false))
844 					{
845 						return select_no_match;
846 					}
847 					break;
848 				case pseudo_class_last_of_type:
849 					if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, true))
850 					{
851 						return select_no_match;
852 					}
853 					break;
854 				case pseudo_class_nth_child:
855 				case pseudo_class_nth_of_type:
856 				case pseudo_class_nth_last_child:
857 				case pseudo_class_nth_last_of_type:
858 					{
859 						if(selector_param.empty()) return select_no_match;
860 
861 						int num = 0;
862 						int off = 0;
863 
864 						parse_nth_child_params(selector_param, num, off);
865 						if(!num && !off) return select_no_match;
866 						switch(selector)
867 						{
868 						case pseudo_class_nth_child:
869 							if (!el_parent->is_nth_child(shared_from_this(), num, off, false))
870 							{
871 								return select_no_match;
872 							}
873 							break;
874 						case pseudo_class_nth_of_type:
875 							if (!el_parent->is_nth_child(shared_from_this(), num, off, true))
876 							{
877 								return select_no_match;
878 							}
879 							break;
880 						case pseudo_class_nth_last_child:
881 							if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false))
882 							{
883 								return select_no_match;
884 							}
885 							break;
886 						case pseudo_class_nth_last_of_type:
887 							if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true))
888 							{
889 								return select_no_match;
890 							}
891 							break;
892 						}
893 
894 					}
895 					break;
896 				case pseudo_class_not:
897 					{
898 						css_element_selector sel;
899 						sel.parse(selector_param);
900 						if(select(sel, apply_pseudo))
901 						{
902 							return select_no_match;
903 						}
904 					}
905 					break;
906 				case pseudo_class_lang:
907 					{
908 						trim( selector_param );
909 
910 						if( !get_document()->match_lang( selector_param ) )
911 						{
912 							return select_no_match;
913 						}
914 					}
915 					break;
916 				default:
917 					if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), i->val) == m_pseudo_classes.end())
918 					{
919 						return select_no_match;
920 					}
921 					break;
922 				}
923 			} else
924 			{
925 				res |= select_match_pseudo_class;
926 			}
927 			break;
928 		}
929 	}
930 	return res;
931 }
932 
find_ancestor(const css_selector & selector,bool apply_pseudo,bool * is_pseudo)933 litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo)
934 {
935 	element::ptr el_parent = parent();
936 	if (!el_parent)
937 	{
938 		return nullptr;
939 	}
940 	int res = el_parent->select(selector, apply_pseudo);
941 	if(res != select_no_match)
942 	{
943 		if(is_pseudo)
944 		{
945 			if(res & select_match_pseudo_class)
946 			{
947 				*is_pseudo = true;
948 			} else
949 			{
950 				*is_pseudo = false;
951 			}
952 		}
953 		return el_parent;
954 	}
955 	return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo);
956 }
957 
get_floats_height(element_float el_float) const958 int litehtml::html_tag::get_floats_height(element_float el_float) const
959 {
960 	if(is_floats_holder())
961 	{
962 		int h = 0;
963 
964 		bool process = false;
965 
966 		for(const auto& fb : m_floats_left)
967 		{
968 			process = false;
969 			switch(el_float)
970 			{
971 			case float_none:
972 				process = true;
973 				break;
974 			case float_left:
975 				if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
976 				{
977 					process = true;
978 				}
979 				break;
980 			case float_right:
981 				if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
982 				{
983 					process = true;
984 				}
985 				break;
986 			}
987 			if(process)
988 			{
989 				if(el_float == float_none)
990 				{
991 					h = std::max(h, fb.pos.bottom());
992 				} else
993 				{
994 					h = std::max(h, fb.pos.top());
995 				}
996 			}
997 		}
998 
999 
1000 		for(const auto fb : m_floats_right)
1001 		{
1002 			process = false;
1003 			switch(el_float)
1004 			{
1005 			case float_none:
1006 				process = true;
1007 				break;
1008 			case float_left:
1009 				if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
1010 				{
1011 					process = true;
1012 				}
1013 				break;
1014 			case float_right:
1015 				if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
1016 				{
1017 					process = true;
1018 				}
1019 				break;
1020 			}
1021 			if(process)
1022 			{
1023 				if(el_float == float_none)
1024 				{
1025 					h = std::max(h, fb.pos.bottom());
1026 				} else
1027 				{
1028 					h = std::max(h, fb.pos.top());
1029 				}
1030 			}
1031 		}
1032 
1033 		return h;
1034 	}
1035 	element::ptr el_parent = parent();
1036 	if (el_parent)
1037 	{
1038 		int h = el_parent->get_floats_height(el_float);
1039 		return h - m_pos.y;
1040 	}
1041 	return 0;
1042 }
1043 
get_left_floats_height() const1044 int litehtml::html_tag::get_left_floats_height() const
1045 {
1046 	if(is_floats_holder())
1047 	{
1048 		int h = 0;
1049 		if(!m_floats_left.empty())
1050 		{
1051 			for (const auto& fb : m_floats_left)
1052 			{
1053 				h = std::max(h, fb.pos.bottom());
1054 			}
1055 		}
1056 		return h;
1057 	}
1058 	element::ptr el_parent = parent();
1059 	if (el_parent)
1060 	{
1061 		int h = el_parent->get_left_floats_height();
1062 		return h - m_pos.y;
1063 	}
1064 	return 0;
1065 }
1066 
get_right_floats_height() const1067 int litehtml::html_tag::get_right_floats_height() const
1068 {
1069 	if(is_floats_holder())
1070 	{
1071 		int h = 0;
1072 		if(!m_floats_right.empty())
1073 		{
1074 			for(const auto& fb : m_floats_right)
1075 			{
1076 				h = std::max(h, fb.pos.bottom());
1077 			}
1078 		}
1079 		return h;
1080 	}
1081 	element::ptr el_parent = parent();
1082 	if (el_parent)
1083 	{
1084 		int h = el_parent->get_right_floats_height();
1085 		return h - m_pos.y;
1086 	}
1087 	return 0;
1088 }
1089 
get_line_left(int y)1090 int litehtml::html_tag::get_line_left( int y )
1091 {
1092 	if(is_floats_holder())
1093 	{
1094 		if(m_cahe_line_left.is_valid && m_cahe_line_left.hash == y)
1095 		{
1096 			return m_cahe_line_left.val;
1097 		}
1098 
1099 		int w = 0;
1100 		for(const auto& fb : m_floats_left)
1101 		{
1102 			if (y >= fb.pos.top() && y < fb.pos.bottom())
1103 			{
1104 				w = std::max(w, fb.pos.right());
1105 				if (w < fb.pos.right())
1106 				{
1107 					break;
1108 				}
1109 			}
1110 		}
1111 		m_cahe_line_left.set_value(y, w);
1112 		return w;
1113 	}
1114 	element::ptr el_parent = parent();
1115 	if (el_parent)
1116 	{
1117 		int w = el_parent->get_line_left(y + m_pos.y);
1118 		if (w < 0)
1119 		{
1120 			w = 0;
1121 		}
1122 		return w - (w ? m_pos.x : 0);
1123 	}
1124 	return 0;
1125 }
1126 
get_line_right(int y,int def_right)1127 int litehtml::html_tag::get_line_right( int y, int def_right )
1128 {
1129 	if(is_floats_holder())
1130 	{
1131 		if(m_cahe_line_right.is_valid && m_cahe_line_right.hash == y)
1132 		{
1133 			if(m_cahe_line_right.is_default)
1134 			{
1135 				return def_right;
1136 			} else
1137 			{
1138 				return std::min(m_cahe_line_right.val, def_right);
1139 			}
1140 		}
1141 
1142 		int w = def_right;
1143 		m_cahe_line_right.is_default = true;
1144 		for(const auto& fb : m_floats_right)
1145 		{
1146 			if(y >= fb.pos.top() && y < fb.pos.bottom())
1147 			{
1148 				w = std::min(w, fb.pos.left());
1149 				m_cahe_line_right.is_default = false;
1150 				if(w > fb.pos.left())
1151 				{
1152 					break;
1153 				}
1154 			}
1155 		}
1156 		m_cahe_line_right.set_value(y, w);
1157 		return w;
1158 	}
1159 	element::ptr el_parent = parent();
1160 	if (el_parent)
1161 	{
1162 		int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x);
1163 		return w - m_pos.x;
1164 	}
1165 	return 0;
1166 }
1167 
1168 
get_line_left_right(int y,int def_right,int & ln_left,int & ln_right)1169 void litehtml::html_tag::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right )
1170 {
1171 	if(is_floats_holder())
1172 	{
1173 		ln_left		= get_line_left(y);
1174 		ln_right	= get_line_right(y, def_right);
1175 	} else
1176 	{
1177 		element::ptr el_parent = parent();
1178 		if (el_parent)
1179 		{
1180 			el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right);
1181 		}
1182 		ln_right -= m_pos.x;
1183 
1184 		if(ln_left < 0)
1185 		{
1186 			ln_left = 0;
1187 		} else if (ln_left > 0)
1188 		{
1189 			ln_left -= m_pos.x;
1190 			if (ln_left < 0)
1191 			{
1192 				ln_left = 0;
1193 			}
1194         }
1195 	}
1196 }
1197 
fix_line_width(int max_width,element_float flt)1198 int litehtml::html_tag::fix_line_width( int max_width, element_float flt )
1199 {
1200 	int ret_width = 0;
1201 	if(!m_boxes.empty())
1202 	{
1203 		elements_vector els;
1204 		m_boxes.back()->get_elements(els);
1205 		bool was_cleared = false;
1206 		if(!els.empty() && els.front()->get_clear() != clear_none)
1207 		{
1208 			if(els.front()->get_clear() == clear_both)
1209 			{
1210 				was_cleared = true;
1211 			} else
1212 			{
1213 				if(	(flt == float_left	&& els.front()->get_clear() == clear_left) ||
1214 					(flt == float_right	&& els.front()->get_clear() == clear_right) )
1215 				{
1216 					was_cleared = true;
1217 				}
1218 			}
1219 		}
1220 
1221 		if(!was_cleared)
1222 		{
1223 			m_boxes.pop_back();
1224 
1225 			for(elements_vector::iterator i = els.begin(); i != els.end(); i++)
1226 			{
1227 				int rw = place_element((*i), max_width);
1228 				if(rw > ret_width)
1229 				{
1230 					ret_width = rw;
1231 				}
1232 			}
1233 		} else
1234 		{
1235 			int line_top = 0;
1236 			if(m_boxes.back()->get_type() == box_line)
1237 			{
1238 				line_top = m_boxes.back()->top();
1239 			} else
1240 			{
1241 				line_top = m_boxes.back()->bottom();
1242 			}
1243 
1244 			int line_left	= 0;
1245 			int line_right	= max_width;
1246 			get_line_left_right(line_top, max_width, line_left, line_right);
1247 
1248 			if(m_boxes.back()->get_type() == box_line)
1249 			{
1250 				if(m_boxes.size() == 1 && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
1251 				{
1252 					int sz_font = get_font_size();
1253 					line_left += sz_font;
1254 				}
1255 
1256 				if(m_css_text_indent.val() != 0)
1257 				{
1258 					bool line_box_found = false;
1259 					for(box::vector::iterator iter = m_boxes.begin(); iter < m_boxes.end(); iter++)
1260 					{
1261 						if((*iter)->get_type() == box_line)
1262 						{
1263 							line_box_found = true;
1264 							break;
1265 						}
1266 					}
1267 					if(!line_box_found)
1268 					{
1269 						line_left += m_css_text_indent.calc_percent(max_width);
1270 					}
1271 				}
1272 
1273 			}
1274 
1275 			elements_vector els;
1276 			m_boxes.back()->new_width(line_left, line_right, els);
1277 			for(auto& el : els)
1278 			{
1279 				int rw = place_element(el, max_width);
1280 				if(rw > ret_width)
1281 				{
1282 					ret_width = rw;
1283 				}
1284 			}
1285 		}
1286 	}
1287 
1288 	return ret_width;
1289 }
1290 
add_float(const element::ptr & el,int x,int y)1291 void litehtml::html_tag::add_float(const element::ptr &el, int x, int y)
1292 {
1293 	if(is_floats_holder())
1294 	{
1295 		floated_box fb;
1296 		fb.pos.x		= el->left() + x;
1297 		fb.pos.y		= el->top()  + y;
1298 		fb.pos.width	= el->width();
1299 		fb.pos.height	= el->height();
1300 		fb.float_side	= el->get_float();
1301 		fb.clear_floats	= el->get_clear();
1302 		fb.el			= el;
1303 
1304 		if(fb.float_side == float_left)
1305 		{
1306 			if(m_floats_left.empty())
1307 			{
1308 				m_floats_left.push_back(fb);
1309 			} else
1310 			{
1311 				bool inserted = false;
1312 				for(floated_box::vector::iterator i = m_floats_left.begin(); i != m_floats_left.end(); i++)
1313 				{
1314 					if(fb.pos.right() > i->pos.right())
1315 					{
1316 						m_floats_left.insert(i, std::move(fb));
1317 						inserted = true;
1318 						break;
1319 					}
1320 				}
1321 				if(!inserted)
1322 				{
1323 					m_floats_left.push_back(std::move(fb));
1324 				}
1325 			}
1326 			m_cahe_line_left.invalidate();
1327 		} else if(fb.float_side == float_right)
1328 		{
1329 			if(m_floats_right.empty())
1330 			{
1331 				m_floats_right.push_back(std::move(fb));
1332 			} else
1333 			{
1334 				bool inserted = false;
1335 				for(floated_box::vector::iterator i = m_floats_right.begin(); i != m_floats_right.end(); i++)
1336 				{
1337 					if(fb.pos.left() < i->pos.left())
1338 					{
1339 						m_floats_right.insert(i, std::move(fb));
1340 						inserted = true;
1341 						break;
1342 					}
1343 				}
1344 				if(!inserted)
1345 				{
1346 					m_floats_right.push_back(fb);
1347 				}
1348 			}
1349 			m_cahe_line_right.invalidate();
1350 		}
1351 	} else
1352 	{
1353 		element::ptr el_parent = parent();
1354 		if (el_parent)
1355 		{
1356 			el_parent->add_float(el, x + m_pos.x, y + m_pos.y);
1357 		}
1358 	}
1359 }
1360 
find_next_line_top(int top,int width,int def_right)1361 int litehtml::html_tag::find_next_line_top( int top, int width, int def_right )
1362 {
1363 	if(is_floats_holder())
1364 	{
1365 		int new_top = top;
1366 		int_vector points;
1367 
1368 		for(const auto& fb : m_floats_left)
1369 		{
1370 			if(fb.pos.top() >= top)
1371 			{
1372 				if(find(points.begin(), points.end(), fb.pos.top()) == points.end())
1373 				{
1374 					points.push_back(fb.pos.top());
1375 				}
1376 			}
1377 			if (fb.pos.bottom() >= top)
1378 			{
1379 				if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
1380 				{
1381 					points.push_back(fb.pos.bottom());
1382 				}
1383 			}
1384 		}
1385 
1386 		for (const auto& fb : m_floats_right)
1387 		{
1388 			if (fb.pos.top() >= top)
1389 			{
1390 				if (find(points.begin(), points.end(), fb.pos.top()) == points.end())
1391 				{
1392 					points.push_back(fb.pos.top());
1393 				}
1394 			}
1395 			if (fb.pos.bottom() >= top)
1396 			{
1397 				if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
1398 				{
1399 					points.push_back(fb.pos.bottom());
1400 				}
1401 			}
1402 		}
1403 
1404 		if(!points.empty())
1405 		{
1406 			sort(points.begin(), points.end(), std::less<int>( ));
1407 			new_top = points.back();
1408 
1409 			for(auto pt : points)
1410 			{
1411 				int pos_left	= 0;
1412 				int pos_right	= def_right;
1413 				get_line_left_right(pt, def_right, pos_left, pos_right);
1414 
1415 				if(pos_right - pos_left >= width)
1416 				{
1417 					new_top = pt;
1418 					break;
1419 				}
1420 			}
1421 		}
1422 		return new_top;
1423 	}
1424 	element::ptr el_parent = parent();
1425 	if (el_parent)
1426 	{
1427 		int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x);
1428 		return new_top - m_pos.y;
1429 	}
1430 	return 0;
1431 }
1432 
parse_background()1433 void litehtml::html_tag::parse_background()
1434 {
1435 	// parse background-color
1436 	m_bg.m_color		= get_color(_t("background-color"), false, web_color(0, 0, 0, 0));
1437 
1438 	// parse background-position
1439 	const tchar_t* str = get_style_property(_t("background-position"), false, _t("0% 0%"));
1440 	if(str)
1441 	{
1442 		string_vector res;
1443 		split_string(str, res, _t(" \t"));
1444 		if(res.size() > 0)
1445 		{
1446 			if(res.size() == 1)
1447 			{
1448 				if( value_in_list(res[0].c_str(), _t("left;right;center")) )
1449 				{
1450 					m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1451 					m_bg.m_position.y.set_value(50, css_units_percentage);
1452 				} else if( value_in_list(res[0].c_str(), _t("top;bottom;center")) )
1453 				{
1454 					m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1455 					m_bg.m_position.x.set_value(50, css_units_percentage);
1456 				} else
1457 				{
1458 					m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1459 					m_bg.m_position.y.set_value(50, css_units_percentage);
1460 				}
1461 			} else
1462 			{
1463 				if(value_in_list(res[0].c_str(), _t("left;right")))
1464 				{
1465 					m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1466 					m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1467 				} else if(value_in_list(res[0].c_str(), _t("top;bottom")))
1468 				{
1469 					m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
1470 					m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1471 				} else if(value_in_list(res[1].c_str(), _t("left;right")))
1472 				{
1473 					m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
1474 					m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1475 				}else if(value_in_list(res[1].c_str(), _t("top;bottom")))
1476 				{
1477 					m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1478 					m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1479 				} else
1480 				{
1481 					m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1482 					m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1483 				}
1484 			}
1485 
1486 			if(m_bg.m_position.x.is_predefined())
1487 			{
1488 				switch(m_bg.m_position.x.predef())
1489 				{
1490 				case 0:
1491 					m_bg.m_position.x.set_value(0, css_units_percentage);
1492 					break;
1493 				case 1:
1494 					m_bg.m_position.x.set_value(100, css_units_percentage);
1495 					break;
1496 				case 2:
1497 					m_bg.m_position.x.set_value(50, css_units_percentage);
1498 					break;
1499 				}
1500 			}
1501 			if(m_bg.m_position.y.is_predefined())
1502 			{
1503 				switch(m_bg.m_position.y.predef())
1504 				{
1505 				case 0:
1506 					m_bg.m_position.y.set_value(0, css_units_percentage);
1507 					break;
1508 				case 1:
1509 					m_bg.m_position.y.set_value(100, css_units_percentage);
1510 					break;
1511 				case 2:
1512 					m_bg.m_position.y.set_value(50, css_units_percentage);
1513 					break;
1514 				}
1515 			}
1516 		} else
1517 		{
1518 			m_bg.m_position.x.set_value(0, css_units_percentage);
1519 			m_bg.m_position.y.set_value(0, css_units_percentage);
1520 		}
1521 	} else
1522 	{
1523 		m_bg.m_position.y.set_value(0, css_units_percentage);
1524 		m_bg.m_position.x.set_value(0, css_units_percentage);
1525 	}
1526 
1527 	str = get_style_property(_t("background-size"), false, _t("auto"));
1528 	if(str)
1529 	{
1530 		string_vector res;
1531 		split_string(str, res, _t(" \t"));
1532 		if(!res.empty())
1533 		{
1534 			m_bg.m_position.width.fromString(res[0], background_size_strings);
1535 			if(res.size() > 1)
1536 			{
1537 				m_bg.m_position.height.fromString(res[1], background_size_strings);
1538 			} else
1539 			{
1540 				m_bg.m_position.height.predef(background_size_auto);
1541 			}
1542 		} else
1543 		{
1544 			m_bg.m_position.width.predef(background_size_auto);
1545 			m_bg.m_position.height.predef(background_size_auto);
1546 		}
1547 	}
1548 
1549 	document::ptr doc = get_document();
1550 
1551 	doc->cvt_units(m_bg.m_position.x,		m_font_size);
1552 	doc->cvt_units(m_bg.m_position.y,		m_font_size);
1553 	doc->cvt_units(m_bg.m_position.width,	m_font_size);
1554 	doc->cvt_units(m_bg.m_position.height,	m_font_size);
1555 
1556 	// parse background_attachment
1557 	m_bg.m_attachment = (background_attachment) value_index(
1558 		get_style_property(_t("background-attachment"), false, _t("scroll")),
1559 		background_attachment_strings,
1560 		background_attachment_scroll);
1561 
1562 	// parse background_attachment
1563 	m_bg.m_repeat = (background_repeat) value_index(
1564 		get_style_property(_t("background-repeat"), false, _t("repeat")),
1565 		background_repeat_strings,
1566 		background_repeat_repeat);
1567 
1568 	// parse background_clip
1569 	m_bg.m_clip = (background_box) value_index(
1570 		get_style_property(_t("background-clip"), false, _t("border-box")),
1571 		background_box_strings,
1572 		background_box_border);
1573 
1574 	// parse background_origin
1575 	m_bg.m_origin = (background_box) value_index(
1576 		get_style_property(_t("background-origin"), false, _t("padding-box")),
1577 		background_box_strings,
1578 		background_box_content);
1579 
1580 	// parse background-image
1581 	css::parse_css_url(get_style_property(_t("background-image"), false, _t("")), m_bg.m_image);
1582 	m_bg.m_baseurl = get_style_property(_t("background-image-baseurl"), false, _t(""));
1583 
1584 	if(!m_bg.m_image.empty())
1585 	{
1586 		doc->container()->load_image(m_bg.m_image.c_str(), m_bg.m_baseurl.empty() ? 0 : m_bg.m_baseurl.c_str(), true);
1587 	}
1588 }
1589 
add_positioned(const element::ptr & el)1590 void litehtml::html_tag::add_positioned(const element::ptr &el)
1591 {
1592 	if (m_el_position != element_position_static || (!have_parent()))
1593 	{
1594 		m_positioned.push_back(el);
1595 	} else
1596 	{
1597 		element::ptr el_parent = parent();
1598 		if (el_parent)
1599 		{
1600 			el_parent->add_positioned(el);
1601 		}
1602 	}
1603 }
1604 
calc_outlines(int parent_width)1605 void litehtml::html_tag::calc_outlines( int parent_width )
1606 {
1607 	m_padding.left	= m_css_padding.left.calc_percent(parent_width);
1608 	m_padding.right	= m_css_padding.right.calc_percent(parent_width);
1609 
1610 	m_borders.left	= m_css_borders.left.width.calc_percent(parent_width);
1611 	m_borders.right	= m_css_borders.right.width.calc_percent(parent_width);
1612 
1613 	m_margins.left	= m_css_margins.left.calc_percent(parent_width);
1614 	m_margins.right	= m_css_margins.right.calc_percent(parent_width);
1615 
1616 	m_margins.top		= m_css_margins.top.calc_percent(parent_width);
1617 	m_margins.bottom	= m_css_margins.bottom.calc_percent(parent_width);
1618 
1619 	m_padding.top		= m_css_padding.top.calc_percent(parent_width);
1620 	m_padding.bottom	= m_css_padding.bottom.calc_percent(parent_width);
1621 }
1622 
calc_auto_margins(int parent_width)1623 void litehtml::html_tag::calc_auto_margins(int parent_width)
1624 {
1625 	if (get_element_position() != element_position_absolute && (m_display == display_block || m_display == display_table))
1626 	{
1627 		if (m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
1628 		{
1629 			int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right;
1630 			if (el_width <= parent_width)
1631 			{
1632 				m_margins.left = (parent_width - el_width) / 2;
1633 				m_margins.right = (parent_width - el_width) - m_margins.left;
1634 			}
1635 			else
1636 			{
1637 				m_margins.left = 0;
1638 				m_margins.right = 0;
1639 			}
1640 		}
1641 		else if (m_css_margins.left.is_predefined() && !m_css_margins.right.is_predefined())
1642 		{
1643 			int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right;
1644 			m_margins.left = parent_width - el_width;
1645 			if (m_margins.left < 0) m_margins.left = 0;
1646 		}
1647 		else if (!m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
1648 		{
1649 			int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left;
1650 			m_margins.right = parent_width - el_width;
1651 			if (m_margins.right < 0) m_margins.right = 0;
1652 		}
1653 	}
1654 }
1655 
parse_attributes()1656 void litehtml::html_tag::parse_attributes()
1657 {
1658 	for(auto& el : m_children)
1659 	{
1660 		el->parse_attributes();
1661 	}
1662 }
1663 
get_text(tstring & text)1664 void litehtml::html_tag::get_text( tstring& text )
1665 {
1666 	for (auto& el : m_children)
1667 	{
1668 		el->get_text(text);
1669 	}
1670 }
1671 
is_body() const1672 bool litehtml::html_tag::is_body()  const
1673 {
1674 	return false;
1675 }
1676 
set_data(const tchar_t * data)1677 void litehtml::html_tag::set_data( const tchar_t* data )
1678 {
1679 
1680 }
1681 
get_inline_boxes(position::vector & boxes)1682 void litehtml::html_tag::get_inline_boxes( position::vector& boxes )
1683 {
1684 	litehtml::box* old_box = 0;
1685 	position pos;
1686 	for(auto& el : m_children)
1687 	{
1688 		if(!el->skip())
1689 		{
1690 			if(el->m_box)
1691 			{
1692 				if(el->m_box != old_box)
1693 				{
1694 					if(old_box)
1695 					{
1696 						if(boxes.empty())
1697 						{
1698 							pos.x		-= m_padding.left + m_borders.left;
1699 							pos.width	+= m_padding.left + m_borders.left;
1700 						}
1701 						boxes.push_back(pos);
1702 					}
1703 					old_box		= el->m_box;
1704 					pos.x		= el->left() + el->margin_left();
1705 					pos.y		= el->top() - m_padding.top - m_borders.top;
1706 					pos.width	= 0;
1707 					pos.height	= 0;
1708 				}
1709 				pos.width	= el->right() - pos.x - el->margin_right() - el->margin_left();
1710 				pos.height	= std::max(pos.height, el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom);
1711 			} else if(el->get_display() == display_inline)
1712 			{
1713 				position::vector sub_boxes;
1714 				el->get_inline_boxes(sub_boxes);
1715 				if(!sub_boxes.empty())
1716 				{
1717 					sub_boxes.rbegin()->width += el->margin_right();
1718 					if(boxes.empty())
1719 					{
1720 						if(m_padding.left + m_borders.left > 0)
1721 						{
1722 							position padding_box = (*sub_boxes.begin());
1723 							padding_box.x		-= m_padding.left + m_borders.left + el->margin_left();
1724 							padding_box.width	= m_padding.left + m_borders.left + el->margin_left();
1725 							boxes.push_back(padding_box);
1726 						}
1727 					}
1728 
1729 					sub_boxes.rbegin()->width += el->margin_right();
1730 
1731 					boxes.insert(boxes.end(), sub_boxes.begin(), sub_boxes.end());
1732 				}
1733 			}
1734 		}
1735 	}
1736 	if(pos.width || pos.height)
1737 	{
1738 		if(boxes.empty())
1739 		{
1740 			pos.x		-= m_padding.left + m_borders.left;
1741 			pos.width	+= m_padding.left + m_borders.left;
1742 		}
1743 		boxes.push_back(pos);
1744 	}
1745 	if(!boxes.empty())
1746 	{
1747 		if(m_padding.right + m_borders.right > 0)
1748 		{
1749 			boxes.back().width += m_padding.right + m_borders.right;
1750 		}
1751 	}
1752 }
1753 
on_mouse_over()1754 bool litehtml::html_tag::on_mouse_over()
1755 {
1756 	bool ret = false;
1757 
1758 	element::ptr el = shared_from_this();
1759 	while(el)
1760 	{
1761 		if(el->set_pseudo_class(_t("hover"), true))
1762 		{
1763 			ret = true;
1764 		}
1765 		el = el->parent();
1766 	}
1767 
1768 	return ret;
1769 }
1770 
find_styles_changes(position::vector & redraw_boxes,int x,int y)1771 bool litehtml::html_tag::find_styles_changes( position::vector& redraw_boxes, int x, int y )
1772 {
1773 	if(m_display == display_inline_text)
1774 	{
1775 		return false;
1776 	}
1777 
1778 	bool ret = false;
1779 	bool apply = false;
1780 	for (used_selector::vector::iterator iter = m_used_styles.begin(); iter != m_used_styles.end() && !apply; iter++)
1781 	{
1782 		if((*iter)->m_selector->is_media_valid())
1783 		{
1784 			int res = select(*((*iter)->m_selector), true);
1785 			if( (res == select_no_match && (*iter)->m_used) || (res == select_match && !(*iter)->m_used) )
1786 			{
1787 				apply = true;
1788 			}
1789 		}
1790 	}
1791 
1792 	if(apply)
1793 	{
1794 		if(m_display == display_inline ||  m_display == display_table_row)
1795 		{
1796 			position::vector boxes;
1797 			get_inline_boxes(boxes);
1798 			for(position::vector::iterator pos = boxes.begin(); pos != boxes.end(); pos++)
1799 			{
1800 				pos->x	+= x;
1801 				pos->y	+= y;
1802 				redraw_boxes.push_back(*pos);
1803 			}
1804 		} else
1805 		{
1806 			position pos = m_pos;
1807 			if(m_el_position != element_position_fixed)
1808 			{
1809 				pos.x += x;
1810 				pos.y += y;
1811 			}
1812 			pos += m_padding;
1813 			pos += m_borders;
1814 			redraw_boxes.push_back(pos);
1815 		}
1816 
1817 		ret = true;
1818 		refresh_styles();
1819 		parse_styles();
1820 	}
1821 	for (auto& el : m_children)
1822 	{
1823 		if(!el->skip())
1824 		{
1825 			if(m_el_position != element_position_fixed)
1826 			{
1827 				if(el->find_styles_changes(redraw_boxes, x + m_pos.x, y + m_pos.y))
1828 				{
1829 					ret = true;
1830 				}
1831 			} else
1832 			{
1833 				if(el->find_styles_changes(redraw_boxes, m_pos.x, m_pos.y))
1834 				{
1835 					ret = true;
1836 				}
1837 			}
1838 		}
1839 	}
1840 	return ret;
1841 }
1842 
on_mouse_leave()1843 bool litehtml::html_tag::on_mouse_leave()
1844 {
1845 	bool ret = false;
1846 
1847 	element::ptr el = shared_from_this();
1848 	while(el)
1849 	{
1850 		if(el->set_pseudo_class(_t("hover"), false))
1851 		{
1852 			ret = true;
1853 		}
1854 		if(el->set_pseudo_class(_t("active"), false))
1855 		{
1856 			ret = true;
1857 		}
1858 		el = el->parent();
1859 	}
1860 
1861 	return ret;
1862 }
1863 
on_lbutton_down()1864 bool litehtml::html_tag::on_lbutton_down()
1865 {
1866     bool ret = false;
1867 
1868 	element::ptr el = shared_from_this();
1869     while (el)
1870     {
1871         if (el->set_pseudo_class(_t("active"), true))
1872         {
1873             ret = true;
1874         }
1875         el = el->parent();
1876     }
1877 
1878     return ret;
1879 }
1880 
on_lbutton_up()1881 bool litehtml::html_tag::on_lbutton_up()
1882 {
1883 	bool ret = false;
1884 
1885 	element::ptr el = shared_from_this();
1886     while (el)
1887     {
1888         if (el->set_pseudo_class(_t("active"), false))
1889         {
1890             ret = true;
1891         }
1892         el = el->parent();
1893     }
1894 
1895     on_click();
1896 
1897 	return ret;
1898 }
1899 
on_click()1900 void litehtml::html_tag::on_click()
1901 {
1902 	if (have_parent())
1903 	{
1904 		element::ptr el_parent = parent();
1905 		if (el_parent)
1906 		{
1907 			el_parent->on_click();
1908 		}
1909 	}
1910 }
1911 
get_cursor()1912 const litehtml::tchar_t* litehtml::html_tag::get_cursor()
1913 {
1914 	return get_style_property(_t("cursor"), true, 0);
1915 }
1916 
1917 static const int font_size_table[8][7] =
1918 {
1919 	{ 9,    9,     9,     9,    11,    14,    18},
1920 	{ 9,    9,     9,    10,    12,    15,    20},
1921 	{ 9,    9,     9,    11,    13,    17,    22},
1922 	{ 9,    9,    10,    12,    14,    18,    24},
1923 	{ 9,    9,    10,    13,    16,    20,    26},
1924 	{ 9,    9,    11,    14,    17,    21,    28},
1925 	{ 9,   10,    12,    15,    17,    23,    30},
1926 	{ 9,   10,    13,    16,    18,    24,    32}
1927 };
1928 
1929 
init_font()1930 void litehtml::html_tag::init_font()
1931 {
1932 	// initialize font size
1933 	const tchar_t* str = get_style_property(_t("font-size"), false, 0);
1934 
1935 	int parent_sz = 0;
1936 	int doc_font_size = get_document()->container()->get_default_font_size();
1937 	element::ptr el_parent = parent();
1938 	if (el_parent)
1939 	{
1940 		parent_sz = el_parent->get_font_size();
1941 	} else
1942 	{
1943 		parent_sz = doc_font_size;
1944 	}
1945 
1946 
1947 	if(!str)
1948 	{
1949 		m_font_size = parent_sz;
1950 	} else
1951 	{
1952 		m_font_size = parent_sz;
1953 
1954 		css_length sz;
1955 		sz.fromString(str, font_size_strings);
1956 		if(sz.is_predefined())
1957 		{
1958 			int idx_in_table = doc_font_size - 9;
1959 			if(idx_in_table >= 0 && idx_in_table <= 7)
1960 			{
1961 				if(sz.predef() >= fontSize_xx_small && sz.predef() <= fontSize_xx_large)
1962 				{
1963 					m_font_size = font_size_table[idx_in_table][sz.predef()];
1964 				} else
1965 				{
1966 					m_font_size = doc_font_size;
1967 				}
1968 			} else
1969 			{
1970 				switch(sz.predef())
1971 				{
1972 				case fontSize_xx_small:
1973 					m_font_size = doc_font_size * 3 / 5;
1974 					break;
1975 				case fontSize_x_small:
1976 					m_font_size = doc_font_size * 3 / 4;
1977 					break;
1978 				case fontSize_small:
1979 					m_font_size = doc_font_size * 8 / 9;
1980 					break;
1981 				case fontSize_large:
1982 					m_font_size = doc_font_size * 6 / 5;
1983 					break;
1984 				case fontSize_x_large:
1985 					m_font_size = doc_font_size * 3 / 2;
1986 					break;
1987 				case fontSize_xx_large:
1988 					m_font_size = doc_font_size * 2;
1989 					break;
1990 				default:
1991 					m_font_size = doc_font_size;
1992 					break;
1993 				}
1994 			}
1995 		} else
1996 		{
1997 			if(sz.units() == css_units_percentage)
1998 			{
1999 				m_font_size = sz.calc_percent(parent_sz);
2000 			} else if(sz.units() == css_units_none)
2001 			{
2002 				m_font_size = parent_sz;
2003 			} else
2004 			{
2005 				m_font_size = get_document()->cvt_units(sz, parent_sz);
2006 			}
2007 		}
2008 	}
2009 
2010 	// initialize font
2011 	const tchar_t* name			= get_style_property(_t("font-family"),		true,	_t("inherit"));
2012 	const tchar_t* weight		= get_style_property(_t("font-weight"),		true,	_t("normal"));
2013 	const tchar_t* style		= get_style_property(_t("font-style"),		true,	_t("normal"));
2014 	const tchar_t* decoration	= get_style_property(_t("text-decoration"),	true,	_t("none"));
2015 
2016 	m_font = get_document()->get_font(name, m_font_size, weight, style, decoration, &m_font_metrics);
2017 }
2018 
is_break() const2019 bool litehtml::html_tag::is_break() const
2020 {
2021 	return false;
2022 }
2023 
set_tagName(const tchar_t * tag)2024 void litehtml::html_tag::set_tagName( const tchar_t* tag )
2025 {
2026 	tstring s_val = tag;
2027 	for(size_t i = 0; i < s_val.length(); i++)
2028 	{
2029 		s_val[i] = std::tolower(s_val[i], std::locale::classic());
2030 	}
2031 	m_tag = s_val;
2032 }
2033 
draw_background(uint_ptr hdc,int x,int y,const position * clip)2034 void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const position* clip )
2035 {
2036 	position pos = m_pos;
2037 	pos.x	+= x;
2038 	pos.y	+= y;
2039 
2040 	position el_pos = pos;
2041 	el_pos += m_padding;
2042 	el_pos += m_borders;
2043 
2044 	if(m_display != display_inline && m_display != display_table_row)
2045 	{
2046 		if(el_pos.does_intersect(clip))
2047 		{
2048 			const background* bg = get_background();
2049 			if(bg)
2050 			{
2051 				background_paint bg_paint;
2052 				init_background_paint(pos, bg_paint, bg);
2053 
2054 				get_document()->container()->draw_background(hdc, bg_paint);
2055 			}
2056 			position border_box = pos;
2057 			border_box += m_padding;
2058 			border_box += m_borders;
2059 
2060 			borders bdr = m_css_borders;
2061 			bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
2062 
2063 			get_document()->container()->draw_borders(hdc, bdr, border_box, have_parent() ? false : true);
2064 		}
2065 	} else
2066 	{
2067 		const background* bg = get_background();
2068 
2069 		position::vector boxes;
2070 		get_inline_boxes(boxes);
2071 
2072 		background_paint bg_paint;
2073 		position content_box;
2074 
2075 		for(position::vector::iterator box = boxes.begin(); box != boxes.end(); box++)
2076 		{
2077 			box->x	+= x;
2078 			box->y	+= y;
2079 
2080 			if(box->does_intersect(clip))
2081 			{
2082 				content_box = *box;
2083 				content_box -= m_borders;
2084 				content_box -= m_padding;
2085 
2086 				if(bg)
2087 				{
2088 					init_background_paint(content_box, bg_paint, bg);
2089 				}
2090 
2091 				css_borders bdr;
2092 
2093 				// set left borders radius for the first box
2094 				if(box == boxes.begin())
2095 				{
2096 					bdr.radius.bottom_left_x	= m_css_borders.radius.bottom_left_x;
2097 					bdr.radius.bottom_left_y	= m_css_borders.radius.bottom_left_y;
2098 					bdr.radius.top_left_x		= m_css_borders.radius.top_left_x;
2099 					bdr.radius.top_left_y		= m_css_borders.radius.top_left_y;
2100 				}
2101 
2102 				// set right borders radius for the last box
2103 				if(box == boxes.end() - 1)
2104 				{
2105 					bdr.radius.bottom_right_x	= m_css_borders.radius.bottom_right_x;
2106 					bdr.radius.bottom_right_y	= m_css_borders.radius.bottom_right_y;
2107 					bdr.radius.top_right_x		= m_css_borders.radius.top_right_x;
2108 					bdr.radius.top_right_y		= m_css_borders.radius.top_right_y;
2109 				}
2110 
2111 
2112 				bdr.top		= m_css_borders.top;
2113 				bdr.bottom	= m_css_borders.bottom;
2114 				if(box == boxes.begin())
2115 				{
2116 					bdr.left	= m_css_borders.left;
2117 				}
2118 				if(box == boxes.end() - 1)
2119 				{
2120 					bdr.right	= m_css_borders.right;
2121 				}
2122 
2123 
2124 				if(bg)
2125 				{
2126 					bg_paint.border_radius = bdr.radius.calc_percents(bg_paint.border_box.width, bg_paint.border_box.width);
2127 					get_document()->container()->draw_background(hdc, bg_paint);
2128 				}
2129 				borders b = bdr;
2130 				b.radius = bdr.radius.calc_percents(box->width, box->height);
2131 				get_document()->container()->draw_borders(hdc, b, *box, false);
2132 			}
2133 		}
2134 	}
2135 }
2136 
render_inline(const element::ptr & container,int max_width)2137 int litehtml::html_tag::render_inline(const element::ptr &container, int max_width)
2138 {
2139 	int ret_width = 0;
2140 	int rw = 0;
2141 
2142 	white_space ws = get_white_space();
2143 	bool skip_spaces = false;
2144 	if (ws == white_space_normal ||
2145 		ws == white_space_nowrap ||
2146 		ws == white_space_pre_line)
2147 	{
2148 		skip_spaces = true;
2149 	}
2150 	bool was_space = false;
2151 
2152 	for (auto& el : m_children)
2153 	{
2154 		// skip spaces to make rendering a bit faster
2155 		if (skip_spaces)
2156 		{
2157 			if (el->is_white_space())
2158 			{
2159 				if (was_space)
2160 				{
2161 					el->skip(true);
2162 					continue;
2163 				}
2164 				else
2165 				{
2166 					was_space = true;
2167 				}
2168 			}
2169 			else
2170 			{
2171 				was_space = false;
2172 			}
2173 		}
2174 
2175 		rw = container->place_element( el, max_width );
2176 		if(rw > ret_width)
2177 		{
2178 			ret_width = rw;
2179 		}
2180 	}
2181 	return ret_width;
2182 }
2183 
place_element(const element::ptr & el,int max_width)2184 int litehtml::html_tag::place_element(const element::ptr &el, int max_width)
2185 {
2186 	if(el->get_display() == display_none) return 0;
2187 
2188 	if(el->get_display() == display_inline)
2189 	{
2190 		return el->render_inline(shared_from_this(), max_width);
2191 	}
2192 
2193 	element_position el_position = el->get_element_position();
2194 
2195 	if(el_position == element_position_absolute || el_position == element_position_fixed)
2196 	{
2197 		int line_top = 0;
2198 		if(!m_boxes.empty())
2199 		{
2200 			if(m_boxes.back()->get_type() == box_line)
2201 			{
2202 				line_top = m_boxes.back()->top();
2203 				if(!m_boxes.back()->is_empty())
2204 				{
2205 					line_top += line_height();
2206 				}
2207 			} else
2208 			{
2209 				line_top = m_boxes.back()->bottom();
2210 			}
2211 		}
2212 
2213 		el->render(0, line_top, max_width);
2214 		el->m_pos.x	+= el->content_margins_left();
2215 		el->m_pos.y	+= el->content_margins_top();
2216 
2217 		return 0;
2218 	}
2219 
2220 	int ret_width = 0;
2221 
2222 	switch(el->get_float())
2223 	{
2224 	case float_left:
2225 		{
2226 			int line_top = 0;
2227 			if(!m_boxes.empty())
2228 			{
2229 				if(m_boxes.back()->get_type() == box_line)
2230 				{
2231 					line_top = m_boxes.back()->top();
2232 				} else
2233 				{
2234 					line_top = m_boxes.back()->bottom();
2235 				}
2236 			}
2237 			line_top		= get_cleared_top(el, line_top);
2238 			int line_left	= 0;
2239 			int line_right	= max_width;
2240 			get_line_left_right(line_top, max_width, line_left, line_right);
2241 
2242 			el->render(line_left, line_top, line_right);
2243 			if(el->right() > line_right)
2244 			{
2245 				int new_top = find_next_line_top(el->top(), el->width(), max_width);
2246 				el->m_pos.x = get_line_left(new_top) + el->content_margins_left();
2247 				el->m_pos.y = new_top + el->content_margins_top();
2248 			}
2249 			add_float(el, 0, 0);
2250 			ret_width = fix_line_width(max_width, float_left);
2251 			if(!ret_width)
2252 			{
2253 				ret_width = el->right();
2254 			}
2255 		}
2256 		break;
2257 	case float_right:
2258 		{
2259 			int line_top = 0;
2260 			if(!m_boxes.empty())
2261 			{
2262 				if(m_boxes.back()->get_type() == box_line)
2263 				{
2264 					line_top = m_boxes.back()->top();
2265 				} else
2266 				{
2267 					line_top = m_boxes.back()->bottom();
2268 				}
2269 			}
2270 			line_top		= get_cleared_top(el, line_top);
2271 			int line_left	= 0;
2272 			int line_right	= max_width;
2273 			get_line_left_right(line_top, max_width, line_left, line_right);
2274 
2275 			el->render(0, line_top, line_right);
2276 
2277 			if(line_left + el->width() > line_right)
2278 			{
2279 				int new_top = find_next_line_top(el->top(), el->width(), max_width);
2280 				el->m_pos.x = get_line_right(new_top, max_width) - el->width() + el->content_margins_left();
2281 				el->m_pos.y = new_top + el->content_margins_top();
2282 			} else
2283 			{
2284 				el->m_pos.x = line_right - el->width() + el->content_margins_left();
2285 			}
2286 			add_float(el, 0, 0);
2287 			ret_width = fix_line_width(max_width, float_right);
2288 
2289 			if(!ret_width)
2290 			{
2291 				line_left	= 0;
2292 				line_right	= max_width;
2293 				get_line_left_right(line_top, max_width, line_left, line_right);
2294 
2295 				ret_width = ret_width + (max_width - line_right);
2296 			}
2297 		}
2298 		break;
2299 	default:
2300 		{
2301 			line_context line_ctx;
2302 			line_ctx.top = 0;
2303 			if (!m_boxes.empty())
2304 			{
2305 				line_ctx.top = m_boxes.back()->top();
2306 			}
2307 			line_ctx.left = 0;
2308 			line_ctx.right = max_width;
2309 			line_ctx.fix_top();
2310 			get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2311 
2312 			switch(el->get_display())
2313 			{
2314 			case display_inline_block:
2315 			case display_inline_table:
2316 				ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.right);
2317 				break;
2318 			case display_block:
2319 				if(el->is_replaced() || el->is_floats_holder())
2320 				{
2321 					element::ptr el_parent = el->parent();
2322 					el->m_pos.width = el->get_css_width().calc_percent(line_ctx.right - line_ctx.left);
2323 					el->m_pos.height = el->get_css_height().calc_percent(el_parent ? el_parent->m_pos.height : 0);
2324 				}
2325 				el->calc_outlines(line_ctx.right - line_ctx.left);
2326 				break;
2327 			case display_inline_text:
2328 				{
2329 					litehtml::size sz;
2330 					el->get_content_size(sz, line_ctx.right);
2331 					el->m_pos = sz;
2332 				}
2333 				break;
2334 			default:
2335 				ret_width = 0;
2336 				break;
2337 			}
2338 
2339 			bool add_box = true;
2340 			if(!m_boxes.empty())
2341 			{
2342 				if(m_boxes.back()->can_hold(el, m_white_space))
2343 				{
2344 					add_box = false;
2345 				}
2346 			}
2347 			if(add_box)
2348 			{
2349 				new_box(el, max_width, line_ctx);
2350 			} else if(!m_boxes.empty())
2351 			{
2352 				line_ctx.top = m_boxes.back()->top();
2353 			}
2354 
2355 			if (line_ctx.top != line_ctx.calculatedTop)
2356 			{
2357 				line_ctx.left = 0;
2358 				line_ctx.right = max_width;
2359 				line_ctx.fix_top();
2360 				get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2361 			}
2362 
2363 			if(!el->is_inline_box())
2364 			{
2365 				if(m_boxes.size() == 1)
2366 				{
2367 					if(collapse_top_margin())
2368 					{
2369 						int shift = el->margin_top();
2370 						if(shift >= 0)
2371 						{
2372 							line_ctx.top -= shift;
2373 							m_boxes.back()->y_shift(-shift);
2374 						}
2375 					}
2376 				} else
2377 				{
2378 					int shift = 0;
2379 					int prev_margin = m_boxes[m_boxes.size() - 2]->bottom_margin();
2380 
2381 					if(prev_margin > el->margin_top())
2382 					{
2383 						shift = el->margin_top();
2384 					} else
2385 					{
2386 						shift = prev_margin;
2387 					}
2388 					if(shift >= 0)
2389 					{
2390 						line_ctx.top -= shift;
2391 						m_boxes.back()->y_shift(-shift);
2392 					}
2393 				}
2394 			}
2395 
2396 			switch(el->get_display())
2397 			{
2398 			case display_table:
2399 			case display_list_item:
2400 				ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width());
2401 				break;
2402 			case display_block:
2403 			case display_table_cell:
2404 			case display_table_caption:
2405 			case display_table_row:
2406 				if(el->is_replaced() || el->is_floats_holder())
2407 				{
2408 					ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()) + line_ctx.left + (max_width - line_ctx.right);
2409 				} else
2410 				{
2411 					ret_width = el->render(0, line_ctx.top, max_width);
2412 				}
2413 				break;
2414 			default:
2415 				ret_width = 0;
2416 				break;
2417 			}
2418 
2419 			m_boxes.back()->add_element(el);
2420 
2421 			if(el->is_inline_box() && !el->skip())
2422 			{
2423 				ret_width = el->right() + (max_width - line_ctx.right);
2424 			}
2425 		}
2426 		break;
2427 	}
2428 
2429 	return ret_width;
2430 }
2431 
set_pseudo_class(const tchar_t * pclass,bool add)2432 bool litehtml::html_tag::set_pseudo_class( const tchar_t* pclass, bool add )
2433 {
2434 	bool ret = false;
2435 	if(add)
2436 	{
2437 		if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass) == m_pseudo_classes.end())
2438 		{
2439 			m_pseudo_classes.push_back(pclass);
2440 			ret = true;
2441 		}
2442 	} else
2443 	{
2444 		string_vector::iterator pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass);
2445 		if(pi != m_pseudo_classes.end())
2446 		{
2447 			m_pseudo_classes.erase(pi);
2448 			ret = true;
2449 		}
2450 	}
2451 	return ret;
2452 }
2453 
set_class(const tchar_t * pclass,bool add)2454 bool litehtml::html_tag::set_class( const tchar_t* pclass, bool add )
2455 {
2456 	string_vector classes;
2457 	bool changed = false;
2458 
2459 	split_string( pclass, classes, _t(" ") );
2460 
2461 	if(add)
2462 	{
2463 		for( auto & _class : classes  )
2464 		{
2465 			if(std::find(m_class_values.begin(), m_class_values.end(), _class) == m_class_values.end())
2466 			{
2467 				m_class_values.push_back( std::move( _class ) );
2468 				changed = true;
2469 			}
2470 		}
2471 	} else
2472 	{
2473 		for( const auto & _class : classes )
2474 		{
2475 			auto end = std::remove(m_class_values.begin(), m_class_values.end(), _class);
2476 
2477 			if(end != m_class_values.end())
2478 			{
2479 				m_class_values.erase(end, m_class_values.end());
2480 				changed = true;
2481 			}
2482 		}
2483 	}
2484 
2485 	if( changed )
2486 	{
2487 		tstring class_string;
2488 		join_string(class_string, m_class_values, _t(" "));
2489 		set_attr(_t("class"), class_string.c_str());
2490 
2491 		return true;
2492 	}
2493 	else
2494 	{
2495 		return false;
2496 	}
2497 
2498 }
2499 
line_height() const2500 int litehtml::html_tag::line_height() const
2501 {
2502 	return m_line_height;
2503 }
2504 
is_replaced() const2505 bool litehtml::html_tag::is_replaced() const
2506 {
2507 	return false;
2508 }
2509 
finish_last_box(bool end_of_render)2510 int litehtml::html_tag::finish_last_box(bool end_of_render)
2511 {
2512 	int line_top = 0;
2513 
2514 	if(!m_boxes.empty())
2515 	{
2516 		m_boxes.back()->finish(end_of_render);
2517 
2518 		if(m_boxes.back()->is_empty())
2519 		{
2520 			line_top = m_boxes.back()->top();
2521 			m_boxes.pop_back();
2522 		}
2523 
2524 		if(!m_boxes.empty())
2525 		{
2526 			line_top = m_boxes.back()->bottom();
2527 		}
2528 	}
2529 	return line_top;
2530 }
2531 
new_box(const element::ptr & el,int max_width,line_context & line_ctx)2532 int litehtml::html_tag::new_box(const element::ptr &el, int max_width, line_context& line_ctx)
2533 {
2534 	line_ctx.top = get_cleared_top(el, finish_last_box());
2535 
2536 	line_ctx.left = 0;
2537 	line_ctx.right = max_width;
2538 	line_ctx.fix_top();
2539 	get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2540 
2541 	if(el->is_inline_box() || el->is_floats_holder())
2542 	{
2543 		if (el->width() > line_ctx.right - line_ctx.left)
2544 		{
2545 			line_ctx.top = find_next_line_top(line_ctx.top, el->width(), max_width);
2546 			line_ctx.left = 0;
2547 			line_ctx.right = max_width;
2548 			line_ctx.fix_top();
2549 			get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2550 		}
2551 	}
2552 
2553 	int first_line_margin = 0;
2554 	if(m_boxes.empty() && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
2555 	{
2556 		int sz_font = get_font_size();
2557 		first_line_margin = sz_font;
2558 	}
2559 
2560 	if(el->is_inline_box())
2561 	{
2562 		int text_indent = 0;
2563 		if(m_css_text_indent.val() != 0)
2564 		{
2565 			bool line_box_found = false;
2566 			for(box::vector::iterator iter = m_boxes.begin(); iter != m_boxes.end(); iter++)
2567 			{
2568 				if((*iter)->get_type() == box_line)
2569 				{
2570 					line_box_found = true;
2571 					break;
2572 				}
2573 			}
2574 			if(!line_box_found)
2575 			{
2576 				text_indent = m_css_text_indent.calc_percent(max_width);
2577 			}
2578 		}
2579 
2580 		font_metrics fm;
2581 		get_font(&fm);
2582 		m_boxes.emplace_back(std::unique_ptr<line_box>(new line_box(line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, line_height(), fm, m_text_align)));
2583 	} else
2584 	{
2585 		m_boxes.emplace_back(std::unique_ptr<block_box>(new block_box(line_ctx.top, line_ctx.left, line_ctx.right)));
2586 	}
2587 
2588 	return line_ctx.top;
2589 }
2590 
get_cleared_top(const element::ptr & el,int line_top) const2591 int litehtml::html_tag::get_cleared_top(const element::ptr &el, int line_top) const
2592 {
2593 	switch(el->get_clear())
2594 	{
2595 	case clear_left:
2596 		{
2597 			int fh = get_left_floats_height();
2598 			if(fh && fh > line_top)
2599 			{
2600 				line_top = fh;
2601 			}
2602 		}
2603 		break;
2604 	case clear_right:
2605 		{
2606 			int fh = get_right_floats_height();
2607 			if(fh && fh > line_top)
2608 			{
2609 				line_top = fh;
2610 			}
2611 		}
2612 		break;
2613 	case clear_both:
2614 		{
2615 			int fh = get_floats_height();
2616 			if(fh && fh > line_top)
2617 			{
2618 				line_top = fh;
2619 			}
2620 		}
2621 		break;
2622 	default:
2623 		if(el->get_float() != float_none)
2624 		{
2625 			int fh = get_floats_height(el->get_float());
2626 			if(fh && fh > line_top)
2627 			{
2628 				line_top = fh;
2629 			}
2630 		}
2631 		break;
2632 	}
2633 	return line_top;
2634 }
2635 
get_display() const2636 litehtml::style_display litehtml::html_tag::get_display() const
2637 {
2638 	return m_display;
2639 }
2640 
get_float() const2641 litehtml::element_float litehtml::html_tag::get_float() const
2642 {
2643 	return m_float;
2644 }
2645 
is_floats_holder() const2646 bool litehtml::html_tag::is_floats_holder() const
2647 {
2648 	if(	m_display == display_inline_block ||
2649 		m_display == display_table_cell ||
2650 		!have_parent() ||
2651 		is_body() ||
2652 		m_float != float_none ||
2653 		m_el_position == element_position_absolute ||
2654 		m_el_position == element_position_fixed ||
2655 		m_overflow > overflow_visible)
2656 	{
2657 		return true;
2658 	}
2659 	return false;
2660 }
2661 
is_first_child_inline(const element::ptr & el) const2662 bool litehtml::html_tag::is_first_child_inline(const element::ptr& el) const
2663 {
2664 	if(!m_children.empty())
2665 	{
2666 		for (const auto& this_el : m_children)
2667 		{
2668 			if (!this_el->is_white_space())
2669 			{
2670 				if (el == this_el)
2671 				{
2672 					return true;
2673 				}
2674 				if (this_el->get_display() == display_inline)
2675 				{
2676 					if (this_el->have_inline_child())
2677 					{
2678 						return false;
2679 					}
2680 				} else
2681 				{
2682 					return false;
2683 				}
2684 			}
2685 		}
2686 	}
2687 	return false;
2688 }
2689 
is_last_child_inline(const element::ptr & el)2690 bool litehtml::html_tag::is_last_child_inline(const element::ptr& el)
2691 {
2692 	if(!m_children.empty())
2693 	{
2694 		for (auto this_el = m_children.rbegin(); this_el < m_children.rend(); ++this_el)
2695 		{
2696 			if (!(*this_el)->is_white_space())
2697 			{
2698 				if (el == (*this_el))
2699 				{
2700 					return true;
2701 				}
2702 				if ((*this_el)->get_display() == display_inline)
2703 				{
2704 					if ((*this_el)->have_inline_child())
2705 					{
2706 						return false;
2707 					}
2708 				} else
2709 				{
2710 					return false;
2711 				}
2712 			}
2713 		}
2714 	}
2715 	return false;
2716 }
2717 
get_white_space() const2718 litehtml::white_space litehtml::html_tag::get_white_space() const
2719 {
2720 	return m_white_space;
2721 }
2722 
get_vertical_align() const2723 litehtml::vertical_align litehtml::html_tag::get_vertical_align() const
2724 {
2725 	return m_vertical_align;
2726 }
2727 
get_css_left() const2728 litehtml::css_length litehtml::html_tag::get_css_left() const
2729 {
2730 	return m_css_offsets.left;
2731 }
2732 
get_css_right() const2733 litehtml::css_length litehtml::html_tag::get_css_right() const
2734 {
2735 	return m_css_offsets.right;
2736 }
2737 
get_css_top() const2738 litehtml::css_length litehtml::html_tag::get_css_top() const
2739 {
2740 	return m_css_offsets.top;
2741 }
2742 
get_css_bottom() const2743 litehtml::css_length litehtml::html_tag::get_css_bottom() const
2744 {
2745 	return m_css_offsets.bottom;
2746 }
2747 
2748 
get_css_offsets() const2749 litehtml::css_offsets litehtml::html_tag::get_css_offsets() const
2750 {
2751 	return m_css_offsets;
2752 }
2753 
get_clear() const2754 litehtml::element_clear litehtml::html_tag::get_clear() const
2755 {
2756 	return m_clear;
2757 }
2758 
get_css_width() const2759 litehtml::css_length litehtml::html_tag::get_css_width() const
2760 {
2761 	return m_css_width;
2762 }
2763 
get_css_height() const2764 litehtml::css_length litehtml::html_tag::get_css_height() const
2765 {
2766 	return m_css_height;
2767 }
2768 
get_children_count() const2769 size_t litehtml::html_tag::get_children_count() const
2770 {
2771 	return m_children.size();
2772 }
2773 
get_child(int idx) const2774 litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const
2775 {
2776 	return m_children[idx];
2777 }
2778 
set_css_width(css_length & w)2779 void litehtml::html_tag::set_css_width( css_length& w )
2780 {
2781 	m_css_width = w;
2782 }
2783 
apply_vertical_align()2784 void litehtml::html_tag::apply_vertical_align()
2785 {
2786 	if(!m_boxes.empty())
2787 	{
2788 		int add = 0;
2789 		int content_height	= m_boxes.back()->bottom();
2790 
2791 		if(m_pos.height > content_height)
2792 		{
2793 			switch(m_vertical_align)
2794 			{
2795 			case va_middle:
2796 				add = (m_pos.height - content_height) / 2;
2797 				break;
2798 			case va_bottom:
2799 				add = m_pos.height - content_height;
2800 				break;
2801 			default:
2802 				add = 0;
2803 				break;
2804 			}
2805 		}
2806 
2807 		if(add)
2808 		{
2809 			for(size_t i = 0; i < m_boxes.size(); i++)
2810 			{
2811 				m_boxes[i]->y_shift(add);
2812 			}
2813 		}
2814 	}
2815 }
2816 
get_element_position(css_offsets * offsets) const2817 litehtml::element_position litehtml::html_tag::get_element_position(css_offsets* offsets) const
2818 {
2819 	if(offsets && m_el_position != element_position_static)
2820 	{
2821 		*offsets = m_css_offsets;
2822 	}
2823 	return m_el_position;
2824 }
2825 
init_background_paint(position pos,background_paint & bg_paint,const background * bg)2826 void litehtml::html_tag::init_background_paint(position pos, background_paint &bg_paint, const background* bg)
2827 {
2828 	if(!bg) return;
2829 
2830 	bg_paint = *bg;
2831 	position content_box	= pos;
2832 	position padding_box	= pos;
2833 	padding_box += m_padding;
2834 	position border_box		= padding_box;
2835 	border_box += m_borders;
2836 
2837 	switch(bg->m_clip)
2838 	{
2839 	case litehtml::background_box_padding:
2840 		bg_paint.clip_box = padding_box;
2841 		break;
2842 	case litehtml::background_box_content:
2843 		bg_paint.clip_box = content_box;
2844 		break;
2845 	default:
2846 		bg_paint.clip_box = border_box;
2847 		break;
2848 	}
2849 
2850 	switch(bg->m_origin)
2851 	{
2852 	case litehtml::background_box_border:
2853 		bg_paint.origin_box = border_box;
2854 		break;
2855 	case litehtml::background_box_content:
2856 		bg_paint.origin_box = content_box;
2857 		break;
2858 	default:
2859 		bg_paint.origin_box = padding_box;
2860 		break;
2861 	}
2862 
2863 	if(!bg_paint.image.empty())
2864 	{
2865 		get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size);
2866 		if(bg_paint.image_size.width && bg_paint.image_size.height)
2867 		{
2868 			litehtml::size img_new_sz = bg_paint.image_size;
2869 			double img_ar_width		= (double) bg_paint.image_size.width / (double) bg_paint.image_size.height;
2870 			double img_ar_height	= (double) bg_paint.image_size.height / (double) bg_paint.image_size.width;
2871 
2872 
2873 			if(bg->m_position.width.is_predefined())
2874 			{
2875 				switch(bg->m_position.width.predef())
2876 				{
2877 				case litehtml::background_size_contain:
2878 					if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height )
2879 					{
2880 						img_new_sz.width = bg_paint.origin_box.width;
2881 						img_new_sz.height	= (int) ((double) bg_paint.origin_box.width * img_ar_height);
2882 					} else
2883 					{
2884 						img_new_sz.height = bg_paint.origin_box.height;
2885 						img_new_sz.width	= (int) ((double) bg_paint.origin_box.height * img_ar_width);
2886 					}
2887 					break;
2888 				case litehtml::background_size_cover:
2889 					if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height )
2890 					{
2891 						img_new_sz.width = bg_paint.origin_box.width;
2892 						img_new_sz.height	= (int) ((double) bg_paint.origin_box.width * img_ar_height);
2893 					} else
2894 					{
2895 						img_new_sz.height = bg_paint.origin_box.height;
2896 						img_new_sz.width	= (int) ((double) bg_paint.origin_box.height * img_ar_width);
2897 					}
2898 					break;
2899 					break;
2900 				case litehtml::background_size_auto:
2901 					if(!bg->m_position.height.is_predefined())
2902 					{
2903 						img_new_sz.height	= bg->m_position.height.calc_percent(bg_paint.origin_box.height);
2904 						img_new_sz.width	= (int) ((double) img_new_sz.height * img_ar_width);
2905 					}
2906 					break;
2907 				}
2908 			} else
2909 			{
2910 				img_new_sz.width = bg->m_position.width.calc_percent(bg_paint.origin_box.width);
2911 				if(bg->m_position.height.is_predefined())
2912 				{
2913 					img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height);
2914 				} else
2915 				{
2916 					img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
2917 				}
2918 			}
2919 
2920 			bg_paint.image_size = img_new_sz;
2921 			bg_paint.position_x = bg_paint.origin_box.x + (int) bg->m_position.x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width);
2922 			bg_paint.position_y = bg_paint.origin_box.y + (int) bg->m_position.y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height);
2923 		}
2924 
2925 	}
2926 	bg_paint.border_radius	= m_css_borders.radius.calc_percents(border_box.width, border_box.height);;
2927 	bg_paint.border_box		= border_box;
2928 	bg_paint.is_root		= have_parent() ? false : true;
2929 }
2930 
get_visibility() const2931 litehtml::visibility litehtml::html_tag::get_visibility() const
2932 {
2933 	return m_visibility;
2934 }
2935 
draw_list_marker(uint_ptr hdc,const position & pos)2936 void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position &pos )
2937 {
2938 	list_marker lm;
2939 
2940 	const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
2941 	size img_size;
2942 	if(list_image)
2943 	{
2944 		css::parse_css_url(list_image, lm.image);
2945 		lm.baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
2946 		get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size);
2947 	} else
2948 	{
2949 		lm.baseurl = 0;
2950 	}
2951 
2952 	int ln_height	= line_height();
2953 	int sz_font		= get_font_size();
2954 	lm.pos.x		= pos.x;
2955 	lm.pos.width = sz_font - sz_font * 2 / 3;
2956 	lm.color = get_color(_t("color"), true, web_color(0, 0, 0));
2957 	lm.marker_type = m_list_style_type;
2958 	lm.font = get_font();
2959 
2960 	if (m_list_style_type >= list_style_type_armenian)
2961 	{
2962 		lm.pos.y = pos.y;
2963 		lm.pos.height = pos.height;
2964 		lm.index = get_attr(_t("list_index"), _t(""))[0];
2965 	}
2966 	else
2967 	{
2968 		lm.pos.height = sz_font - sz_font * 2 / 3;
2969 		lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2;
2970 		lm.index = -1;
2971 	}
2972 
2973 	if(img_size.width && img_size.height)
2974 	{
2975 		if(lm.pos.y + img_size.height > pos.y + pos.height)
2976 		{
2977 			lm.pos.y = pos.y + pos.height - img_size.height;
2978 		}
2979 		if(img_size.width > lm.pos.width)
2980 		{
2981 			lm.pos.x -= img_size.width - lm.pos.width;
2982 		}
2983 
2984 		lm.pos.width	= img_size.width;
2985 		lm.pos.height	= img_size.height;
2986 	}
2987 
2988 	if (m_list_style_position == list_style_position_outside)
2989 	{
2990 		if (m_list_style_type >= list_style_type_armenian)
2991 		{
2992 			auto tw_space = get_document()->container()->text_width(_t(" "), lm.font);
2993 			lm.pos.x = pos.x - tw_space * 2;
2994 			lm.pos.width = tw_space;
2995 		}
2996 		else
2997 		{
2998 			lm.pos.x -= sz_font;
2999 		}
3000 	}
3001 
3002 	if (m_list_style_type >= list_style_type_armenian)
3003 	{
3004 		auto marker_text = get_list_marker_text(lm.index);
3005 		lm.pos.height = ln_height;
3006 		if (marker_text.empty())
3007 		{
3008 			get_document()->container()->draw_list_marker(hdc, lm);
3009 		}
3010 		else
3011 		{
3012 			marker_text += _t(".");
3013 			auto tw = get_document()->container()->text_width(marker_text.c_str(), lm.font);
3014 			auto text_pos = lm.pos;
3015 			text_pos.move_to(text_pos.right() - tw, text_pos.y);
3016 			text_pos.width = tw;
3017 			get_document()->container()->draw_text(hdc, marker_text.c_str(), lm.font, lm.color, text_pos);
3018 		}
3019 	}
3020 	else
3021 	{
3022 		get_document()->container()->draw_list_marker(hdc, lm);
3023 	}
3024 }
3025 
get_list_marker_text(int index)3026 litehtml::tstring litehtml::html_tag::get_list_marker_text(int index)
3027 {
3028 	switch (m_list_style_type)
3029 	{
3030 	case litehtml::list_style_type_decimal:
3031 		return t_to_string(index);
3032 	case litehtml::list_style_type_decimal_leading_zero:
3033 		{
3034 			auto txt = t_to_string(index);
3035 			if (txt.length() == 1)
3036 			{
3037 				txt = _t("0") + txt;
3038 			}
3039 			return txt;
3040 		}
3041 	case litehtml::list_style_type_lower_latin:
3042 	case litehtml::list_style_type_lower_alpha:
3043 		return num_cvt::to_latin_lower(index);
3044 	case litehtml::list_style_type_lower_greek:
3045 		return num_cvt::to_greek_lower(index);
3046 	case litehtml::list_style_type_upper_alpha:
3047 	case litehtml::list_style_type_upper_latin:
3048 		return num_cvt::to_latin_upper(index);
3049 	case litehtml::list_style_type_lower_roman:
3050 		return num_cvt::to_roman_lower(index);
3051 	case litehtml::list_style_type_upper_roman:
3052 		return num_cvt::to_roman_upper(index);
3053 	case litehtml::list_style_type_armenian:
3054 		break;
3055 	case litehtml::list_style_type_georgian:
3056 		break;
3057 	case litehtml::list_style_type_hebrew:
3058 		break;
3059 	case litehtml::list_style_type_hiragana:
3060 		break;
3061 	case litehtml::list_style_type_hiragana_iroha:
3062 		break;
3063 	case litehtml::list_style_type_katakana:
3064 		break;
3065 	case litehtml::list_style_type_katakana_iroha:
3066 		break;
3067 	}
3068 	return _t("");
3069 }
3070 
draw_children(uint_ptr hdc,int x,int y,const position * clip,draw_flag flag,int zindex)3071 void litehtml::html_tag::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex )
3072 {
3073 	if (m_display == display_table || m_display == display_inline_table)
3074 	{
3075 		draw_children_table(hdc, x, y, clip, flag, zindex);
3076 	}
3077 	else
3078 	{
3079 		draw_children_box(hdc, x, y, clip, flag, zindex);
3080 	}
3081 }
3082 
fetch_positioned()3083 bool litehtml::html_tag::fetch_positioned()
3084 {
3085 	bool ret = false;
3086 
3087 	m_positioned.clear();
3088 
3089 	litehtml::element_position el_pos;
3090 
3091 	for(auto& el : m_children)
3092 	{
3093 		el_pos = el->get_element_position();
3094 		if (el_pos != element_position_static)
3095 		{
3096 			add_positioned(el);
3097 		}
3098 		if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed))
3099 		{
3100 			ret = true;
3101 		}
3102 		if(el->fetch_positioned())
3103 		{
3104 			ret = true;
3105 		}
3106 	}
3107 	return ret;
3108 }
3109 
get_zindex() const3110 int litehtml::html_tag::get_zindex() const
3111 {
3112 	return m_z_index;
3113 }
3114 
render_positioned(render_type rt)3115 void litehtml::html_tag::render_positioned(render_type rt)
3116 {
3117 	position wnd_position;
3118 	get_document()->container()->get_client_rect(wnd_position);
3119 
3120 	element_position el_position;
3121 	bool process;
3122 	for (auto& el : m_positioned)
3123 	{
3124 		el_position = el->get_element_position();
3125 
3126 		process = false;
3127 		if(el->get_display() != display_none)
3128 		{
3129 			if(el_position == element_position_absolute)
3130 			{
3131 				if(rt != render_fixed_only)
3132 				{
3133 					process = true;
3134 				}
3135 			} else if(el_position == element_position_fixed)
3136 			{
3137 				if(rt != render_no_fixed)
3138 				{
3139 					process = true;
3140 				}
3141 			}
3142 		}
3143 
3144 		if(process)
3145 		{
3146 			int parent_height	= 0;
3147 			int parent_width	= 0;
3148 			int client_x		= 0;
3149 			int client_y		= 0;
3150 			if(el_position == element_position_fixed)
3151 			{
3152 				parent_height	= wnd_position.height;
3153 				parent_width	= wnd_position.width;
3154 				client_x		= wnd_position.left();
3155 				client_y		= wnd_position.top();
3156 			} else
3157 			{
3158 				element::ptr el_parent = el->parent();
3159 				if(el_parent)
3160 				{
3161 					parent_height	= el_parent->height();
3162 					parent_width	= el_parent->width();
3163 				}
3164 			}
3165 
3166 			css_length	css_left	= el->get_css_left();
3167 			css_length	css_right	= el->get_css_right();
3168 			css_length	css_top		= el->get_css_top();
3169 			css_length	css_bottom	= el->get_css_bottom();
3170 
3171 			bool need_render = false;
3172 
3173 			css_length el_w = el->get_css_width();
3174 			css_length el_h = el->get_css_height();
3175 
3176             int new_width = -1;
3177             int new_height = -1;
3178 			if(el_w.units() == css_units_percentage && parent_width)
3179 			{
3180                 new_width = el_w.calc_percent(parent_width);
3181                 if(el->m_pos.width != new_width)
3182 				{
3183 					need_render = true;
3184                     el->m_pos.width = new_width;
3185 				}
3186 			}
3187 
3188 			if(el_h.units() == css_units_percentage && parent_height)
3189 			{
3190                 new_height = el_h.calc_percent(parent_height);
3191                 if(el->m_pos.height != new_height)
3192 				{
3193 					need_render = true;
3194                     el->m_pos.height = new_height;
3195 				}
3196 			}
3197 
3198 			bool cvt_x = false;
3199 			bool cvt_y = false;
3200 
3201 			if(el_position == element_position_fixed)
3202 			{
3203 				if(!css_left.is_predefined() || !css_right.is_predefined())
3204 				{
3205 					if(!css_left.is_predefined() && css_right.is_predefined())
3206 					{
3207 						el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
3208 					} else if(css_left.is_predefined() && !css_right.is_predefined())
3209 					{
3210 						el->m_pos.x = parent_width - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
3211 					} else
3212 					{
3213 						el->m_pos.x		= css_left.calc_percent(parent_width) + el->content_margins_left();
3214 						el->m_pos.width	= parent_width - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
3215 						need_render = true;
3216 					}
3217 				}
3218 
3219 				if(!css_top.is_predefined() || !css_bottom.is_predefined())
3220 				{
3221 					if(!css_top.is_predefined() && css_bottom.is_predefined())
3222 					{
3223 						el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
3224 					} else if(css_top.is_predefined() && !css_bottom.is_predefined())
3225 					{
3226 						el->m_pos.y = parent_height - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
3227 					} else
3228 					{
3229 						el->m_pos.y			= css_top.calc_percent(parent_height) + el->content_margins_top();
3230 						el->m_pos.height	= parent_height - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
3231 						need_render = true;
3232 					}
3233 				}
3234 			} else
3235 			{
3236 				if(!css_left.is_predefined() || !css_right.is_predefined())
3237 				{
3238 					if(!css_left.is_predefined() && css_right.is_predefined())
3239 					{
3240 						el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
3241 					} else if(css_left.is_predefined() && !css_right.is_predefined())
3242 					{
3243 						el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
3244 					} else
3245 					{
3246 						el->m_pos.x		= css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
3247 						el->m_pos.width	= m_pos.width + m_padding.left + m_padding.right - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
3248                         if (new_width != -1)
3249                         {
3250                             el->m_pos.x += (el->m_pos.width - new_width) / 2;
3251                             el->m_pos.width = new_width;
3252                         }
3253                         need_render = true;
3254 					}
3255 					cvt_x = true;
3256 				}
3257 
3258 				if(!css_top.is_predefined() || !css_bottom.is_predefined())
3259 				{
3260 					if(!css_top.is_predefined() && css_bottom.is_predefined())
3261 					{
3262 						el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
3263 					} else if(css_top.is_predefined() && !css_bottom.is_predefined())
3264 					{
3265 						el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
3266 					} else
3267 					{
3268 						el->m_pos.y			= css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
3269 						el->m_pos.height	= m_pos.height + m_padding.top + m_padding.bottom - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
3270                         if (new_height != -1)
3271                         {
3272                             el->m_pos.y += (el->m_pos.height - new_height) / 2;
3273                             el->m_pos.height = new_height;
3274                         }
3275                         need_render = true;
3276 					}
3277 					cvt_y = true;
3278 				}
3279 			}
3280 
3281 			if(cvt_x || cvt_y)
3282 			{
3283 				int offset_x = 0;
3284 				int offset_y = 0;
3285 				element::ptr cur_el = el->parent();
3286 				element::ptr this_el = shared_from_this();
3287 				while(cur_el && cur_el != this_el)
3288 				{
3289 					offset_x += cur_el->m_pos.x;
3290 					offset_y += cur_el->m_pos.y;
3291 					cur_el = cur_el->parent();
3292 				}
3293 				if(cvt_x)	el->m_pos.x -= offset_x;
3294 				if(cvt_y)	el->m_pos.y -= offset_y;
3295 			}
3296 
3297 			if(need_render)
3298 			{
3299 				position pos = el->m_pos;
3300 				el->render(el->left(), el->top(), el->width(), true);
3301 				el->m_pos = pos;
3302 			}
3303 
3304 			if(el_position == element_position_fixed)
3305 			{
3306 				position fixed_pos;
3307 				el->get_redraw_box(fixed_pos);
3308 				get_document()->add_fixed_box(fixed_pos);
3309 			}
3310 		}
3311 
3312 		el->render_positioned();
3313 	}
3314 
3315 	if(!m_positioned.empty())
3316 	{
3317 		std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const litehtml::element::ptr& _Left, const litehtml::element::ptr& _Right)
3318 		{
3319 			return (_Left->get_zindex() < _Right->get_zindex());
3320 		});
3321 	}
3322 }
3323 
draw_stacking_context(uint_ptr hdc,int x,int y,const position * clip,bool with_positioned)3324 void litehtml::html_tag::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned )
3325 {
3326 	if(!is_visible()) return;
3327 
3328 	std::map<int, bool> zindexes;
3329 	if(with_positioned)
3330 	{
3331 		for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
3332 		{
3333 			zindexes[(*i)->get_zindex()];
3334 		}
3335 
3336 		for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3337 		{
3338 			if(idx->first < 0)
3339 			{
3340 				draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3341 			}
3342 		}
3343 	}
3344 	draw_children(hdc, x, y, clip, draw_block, 0);
3345 	draw_children(hdc, x, y, clip, draw_floats, 0);
3346 	draw_children(hdc, x, y, clip, draw_inlines, 0);
3347 	if(with_positioned)
3348 	{
3349 		for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3350 		{
3351 			if(idx->first == 0)
3352 			{
3353 				draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3354 			}
3355 		}
3356 
3357 		for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3358 		{
3359 			if(idx->first > 0)
3360 			{
3361 				draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3362 			}
3363 		}
3364 	}
3365 }
3366 
get_overflow() const3367 litehtml::overflow litehtml::html_tag::get_overflow() const
3368 {
3369 	return m_overflow;
3370 }
3371 
is_nth_child(const element::ptr & el,int num,int off,bool of_type) const3372 bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const
3373 {
3374 	int idx = 1;
3375 	for(const auto& child : m_children)
3376 	{
3377 		if(child->get_display() != display_inline_text)
3378 		{
3379 			if( (!of_type) || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
3380 			{
3381 				if(el == child)
3382 				{
3383 					if(num != 0)
3384 					{
3385 						if((idx - off) >= 0 && (idx - off) % num == 0)
3386 						{
3387 							return true;
3388 						}
3389 
3390 					} else if(idx == off)
3391 					{
3392 						return true;
3393 					}
3394 					return false;
3395 				}
3396 				idx++;
3397 			}
3398 			if(el == child) break;
3399 		}
3400 	}
3401 	return false;
3402 }
3403 
is_nth_last_child(const element::ptr & el,int num,int off,bool of_type) const3404 bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const
3405 {
3406 	int idx = 1;
3407 	for(elements_vector::const_reverse_iterator child = m_children.rbegin(); child != m_children.rend(); child++)
3408 	{
3409 		if((*child)->get_display() != display_inline_text)
3410 		{
3411 			if( !of_type || (of_type && !t_strcmp(el->get_tagName(), (*child)->get_tagName())) )
3412 			{
3413 				if(el == (*child))
3414 				{
3415 					if(num != 0)
3416 					{
3417 						if((idx - off) >= 0 && (idx - off) % num == 0)
3418 						{
3419 							return true;
3420 						}
3421 
3422 					} else if(idx == off)
3423 					{
3424 						return true;
3425 					}
3426 					return false;
3427 				}
3428 				idx++;
3429 			}
3430 			if(el == (*child)) break;
3431 		}
3432 	}
3433 	return false;
3434 }
3435 
parse_nth_child_params(tstring param,int & num,int & off)3436 void litehtml::html_tag::parse_nth_child_params( tstring param, int &num, int &off )
3437 {
3438 	if(param == _t("odd"))
3439 	{
3440 		num = 2;
3441 		off = 1;
3442 	} else if(param == _t("even"))
3443 	{
3444 		num = 2;
3445 		off = 0;
3446 	} else
3447 	{
3448 		string_vector tokens;
3449 		split_string(param, tokens, _t(" n"), _t("n"));
3450 
3451 		tstring s_num;
3452 		tstring s_off;
3453 
3454 		tstring s_int;
3455 		for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++)
3456 		{
3457 			if((*tok) == _t("n"))
3458 			{
3459 				s_num = s_int;
3460 				s_int.clear();
3461 			} else
3462 			{
3463 				s_int += (*tok);
3464 			}
3465 		}
3466 		s_off = s_int;
3467 
3468 		num = t_atoi(s_num.c_str());
3469 		off = t_atoi(s_off.c_str());
3470 	}
3471 }
3472 
calc_document_size(litehtml::size & sz,int x,int y)3473 void litehtml::html_tag::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ )
3474 {
3475 	if(is_visible() && m_el_position != element_position_fixed)
3476 	{
3477 		element::calc_document_size(sz, x, y);
3478 
3479 		if(m_overflow == overflow_visible)
3480 		{
3481 			for(auto& el : m_children)
3482 			{
3483 				el->calc_document_size(sz, x + m_pos.x, y + m_pos.y);
3484 			}
3485 		}
3486 
3487 		// root element (<html>) must to cover entire window
3488 		if(!have_parent())
3489 		{
3490 			position client_pos;
3491 			get_document()->container()->get_client_rect(client_pos);
3492 			m_pos.height = std::max(sz.height, client_pos.height) - content_margins_top() - content_margins_bottom();
3493 			m_pos.width	 = std::max(sz.width, client_pos.width) - content_margins_left() - content_margins_right();
3494 		}
3495 	}
3496 }
3497 
3498 
get_redraw_box(litehtml::position & pos,int x,int y)3499 void litehtml::html_tag::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/)
3500 {
3501 	if(is_visible())
3502 	{
3503 		element::get_redraw_box(pos, x, y);
3504 
3505 		if(m_overflow == overflow_visible)
3506 		{
3507 			for(auto& el : m_children)
3508 			{
3509 				if(el->get_element_position() != element_position_fixed)
3510 				{
3511 					el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y);
3512 				}
3513 			}
3514 		}
3515 	}
3516 }
3517 
find_adjacent_sibling(const element::ptr & el,const css_selector & selector,bool apply_pseudo,bool * is_pseudo)3518 litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ )
3519 {
3520 	element::ptr ret;
3521 	for(auto& e : m_children)
3522 	{
3523 		if(e->get_display() != display_inline_text)
3524 		{
3525 			if(e == el)
3526 			{
3527 				if(ret)
3528 				{
3529 					int res = ret->select(selector, apply_pseudo);
3530 					if(res != select_no_match)
3531 					{
3532 						if(is_pseudo)
3533 						{
3534 							if(res & select_match_pseudo_class)
3535 							{
3536 								*is_pseudo = true;
3537 							} else
3538 							{
3539 								*is_pseudo = false;
3540 							}
3541 						}
3542 						return ret;
3543 					}
3544 				}
3545 				return 0;
3546 			} else
3547 			{
3548 				ret = e;
3549 			}
3550 		}
3551 	}
3552 	return 0;
3553 }
3554 
find_sibling(const element::ptr & el,const css_selector & selector,bool apply_pseudo,bool * is_pseudo)3555 litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/)
3556 {
3557 	element::ptr ret = 0;
3558 	for(auto& e : m_children)
3559 	{
3560 		if(e->get_display() != display_inline_text)
3561 		{
3562 			if(e == el)
3563 			{
3564 				return ret;
3565 			} else if(!ret)
3566 			{
3567 				int res = e->select(selector, apply_pseudo);
3568 				if(res != select_no_match)
3569 				{
3570 					if(is_pseudo)
3571 					{
3572 						if(res & select_match_pseudo_class)
3573 						{
3574 							*is_pseudo = true;
3575 						} else
3576 						{
3577 							*is_pseudo = false;
3578 						}
3579 					}
3580 					ret = e;
3581 				}
3582 			}
3583 		}
3584 	}
3585 	return 0;
3586 }
3587 
is_only_child(const element::ptr & el,bool of_type) const3588 bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const
3589 {
3590 	int child_count = 0;
3591 	for(const auto& child : m_children)
3592 	{
3593 		if(child->get_display() != display_inline_text)
3594 		{
3595 			if( !of_type || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
3596 			{
3597 				child_count++;
3598 			}
3599 			if(child_count > 1) break;
3600 		}
3601 	}
3602 	if(child_count > 1)
3603 	{
3604 		return false;
3605 	}
3606 	return true;
3607 }
3608 
update_floats(int dy,const element::ptr & parent)3609 void litehtml::html_tag::update_floats(int dy, const element::ptr &parent)
3610 {
3611 	if(is_floats_holder())
3612 	{
3613 		bool reset_cache = false;
3614 		for(floated_box::vector::reverse_iterator fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++)
3615 		{
3616 			if(fb->el->is_ancestor(parent))
3617 			{
3618 				reset_cache	= true;
3619 				fb->pos.y	+= dy;
3620 			}
3621 		}
3622 		if(reset_cache)
3623 		{
3624 			m_cahe_line_left.invalidate();
3625 		}
3626 		reset_cache = false;
3627 		for(floated_box::vector::reverse_iterator fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++)
3628 		{
3629 			if(fb->el->is_ancestor(parent))
3630 			{
3631 				reset_cache	= true;
3632 				fb->pos.y	+= dy;
3633 			}
3634 		}
3635 		if(reset_cache)
3636 		{
3637 			m_cahe_line_right.invalidate();
3638 		}
3639 	} else
3640 	{
3641 		element::ptr el_parent = this->parent();
3642 		if (el_parent)
3643 		{
3644 			el_parent->update_floats(dy, parent);
3645 		}
3646 	}
3647 }
3648 
remove_before_after()3649 void litehtml::html_tag::remove_before_after()
3650 {
3651 	if(!m_children.empty())
3652 	{
3653 		if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
3654 		{
3655 			m_children.erase(m_children.begin());
3656 		}
3657 	}
3658 	if(!m_children.empty())
3659 	{
3660 		if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
3661 		{
3662 			m_children.erase(m_children.end() - 1);
3663 		}
3664 	}
3665 }
3666 
get_element_before()3667 litehtml::element::ptr litehtml::html_tag::get_element_before()
3668 {
3669 	if(!m_children.empty())
3670 	{
3671 		if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
3672 		{
3673 			return m_children.front();
3674 		}
3675 	}
3676 	element::ptr el = std::make_shared<el_before>(get_document());
3677 	el->parent(shared_from_this());
3678 	m_children.insert(m_children.begin(), el);
3679 	return el;
3680 }
3681 
get_element_after()3682 litehtml::element::ptr litehtml::html_tag::get_element_after()
3683 {
3684 	if(!m_children.empty())
3685 	{
3686 		if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
3687 		{
3688 			return m_children.back();
3689 		}
3690 	}
3691 	element::ptr el = std::make_shared<el_after>(get_document());
3692 	appendChild(el);
3693 	return el;
3694 }
3695 
add_style(const litehtml::style & st)3696 void litehtml::html_tag::add_style( const litehtml::style& st )
3697 {
3698 	m_style.combine(st);
3699 }
3700 
have_inline_child() const3701 bool litehtml::html_tag::have_inline_child() const
3702 {
3703 	if(!m_children.empty())
3704 	{
3705 		for(const auto& el : m_children)
3706 		{
3707 			if(!el->is_white_space())
3708 			{
3709 				return true;
3710 			}
3711 		}
3712 	}
3713 	return false;
3714 }
3715 
refresh_styles()3716 void litehtml::html_tag::refresh_styles()
3717 {
3718 	remove_before_after();
3719 
3720 	for (auto& el : m_children)
3721 	{
3722 		if(el->get_display() != display_inline_text)
3723 		{
3724 			el->refresh_styles();
3725 		}
3726 	}
3727 
3728 	m_style.clear();
3729 
3730 	for (auto& usel : m_used_styles)
3731 	{
3732 		usel->m_used = false;
3733 
3734 		if(usel->m_selector->is_media_valid())
3735 		{
3736 			int apply = select(*usel->m_selector, false);
3737 
3738 			if(apply != select_no_match)
3739 			{
3740 				if(apply & select_match_pseudo_class)
3741 				{
3742 					if(select(*usel->m_selector, true))
3743 					{
3744 						if(apply & select_match_with_after)
3745 						{
3746 							element::ptr el = get_element_after();
3747 							if(el)
3748 							{
3749 								el->add_style(*usel->m_selector->m_style);
3750 							}
3751 						} else if(apply & select_match_with_before)
3752 						{
3753 							element::ptr el = get_element_before();
3754 							if(el)
3755 							{
3756 								el->add_style(*usel->m_selector->m_style);
3757 							}
3758 						}
3759 						else
3760 						{
3761 							add_style(*usel->m_selector->m_style);
3762 							usel->m_used = true;
3763 						}
3764 					}
3765 				} else if(apply & select_match_with_after)
3766 				{
3767 					element::ptr el = get_element_after();
3768 					if(el)
3769 					{
3770 						el->add_style(*usel->m_selector->m_style);
3771 					}
3772 				} else if(apply & select_match_with_before)
3773 				{
3774 					element::ptr el = get_element_before();
3775 					if(el)
3776 					{
3777 						el->add_style(*usel->m_selector->m_style);
3778 					}
3779 				} else
3780 				{
3781 					add_style(*usel->m_selector->m_style);
3782 					usel->m_used = true;
3783 				}
3784 			}
3785 		}
3786 	}
3787 }
3788 
get_child_by_point(int x,int y,int client_x,int client_y,draw_flag flag,int zindex)3789 litehtml::element::ptr litehtml::html_tag::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex)
3790 {
3791 	element::ptr ret = 0;
3792 
3793 	if(m_overflow > overflow_visible)
3794 	{
3795 		if(!m_pos.is_point_inside(x, y))
3796 		{
3797 			return ret;
3798 		}
3799 	}
3800 
3801 	position pos = m_pos;
3802 	pos.x	= x - pos.x;
3803 	pos.y	= y - pos.y;
3804 
3805 	for(elements_vector::reverse_iterator i = m_children.rbegin(); i != m_children.rend() && !ret; i++)
3806 	{
3807 		element::ptr el = (*i);
3808 
3809 		if(el->is_visible() && el->get_display() != display_inline_text)
3810 		{
3811 			switch(flag)
3812 			{
3813 			case draw_positioned:
3814 				if(el->is_positioned() && el->get_zindex() == zindex)
3815 				{
3816 					if(el->get_element_position() == element_position_fixed)
3817 					{
3818 						ret = el->get_element_by_point(client_x, client_y, client_x, client_y);
3819 						if(!ret && (*i)->is_point_inside(client_x, client_y))
3820 						{
3821 							ret = (*i);
3822 						}
3823 					} else
3824 					{
3825 						ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3826 						if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3827 						{
3828 							ret = (*i);
3829 						}
3830 					}
3831 					el = 0;
3832 				}
3833 				break;
3834 			case draw_block:
3835 				if(!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
3836 				{
3837 					if(el->is_point_inside(pos.x, pos.y))
3838 					{
3839 						ret = el;
3840 					}
3841 				}
3842 				break;
3843 			case draw_floats:
3844 				if(el->get_float() != float_none && !el->is_positioned())
3845 				{
3846 					ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3847 
3848 					if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3849 					{
3850 						ret = (*i);
3851 					}
3852 					el = 0;
3853 				}
3854 				break;
3855 			case draw_inlines:
3856 				if(el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
3857 				{
3858 					if(el->get_display() == display_inline_block)
3859 					{
3860 						ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3861 						el = 0;
3862 					}
3863 					if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3864 					{
3865 						ret = (*i);
3866 					}
3867 				}
3868 				break;
3869 			default:
3870 				break;
3871 			}
3872 
3873 			if(el && !el->is_positioned())
3874 			{
3875 				if(flag == draw_positioned)
3876 				{
3877 					element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
3878 					if(child)
3879 					{
3880 						ret = child;
3881 					}
3882 				} else
3883 				{
3884 					if(	el->get_float() == float_none &&
3885 						el->get_display() != display_inline_block)
3886 					{
3887 						element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
3888 						if(child)
3889 						{
3890 							ret = child;
3891 						}
3892 					}
3893 				}
3894 			}
3895 		}
3896 	}
3897 
3898 	return ret;
3899 }
3900 
get_element_by_point(int x,int y,int client_x,int client_y)3901 litehtml::element::ptr litehtml::html_tag::get_element_by_point(int x, int y, int client_x, int client_y)
3902 {
3903 	if(!is_visible()) return 0;
3904 
3905 	element::ptr ret;
3906 
3907 	std::map<int, bool> zindexes;
3908 
3909 	for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
3910 	{
3911 		zindexes[(*i)->get_zindex()];
3912 	}
3913 
3914 	for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3915 	{
3916 		if(idx->first > 0)
3917 		{
3918 			ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3919 		}
3920 	}
3921 	if(ret) return ret;
3922 
3923 	for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3924 	{
3925 		if(idx->first == 0)
3926 		{
3927 			ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3928 		}
3929 	}
3930 	if(ret) return ret;
3931 
3932 	ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0);
3933 	if(ret) return ret;
3934 
3935 	ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0);
3936 	if(ret) return ret;
3937 
3938 	ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0);
3939 	if(ret) return ret;
3940 
3941 
3942 	for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3943 	{
3944 		if(idx->first < 0)
3945 		{
3946 			ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3947 		}
3948 	}
3949 	if(ret) return ret;
3950 
3951 	if(m_el_position == element_position_fixed)
3952 	{
3953 		if(is_point_inside(client_x, client_y))
3954 		{
3955 			ret = shared_from_this();
3956 		}
3957 	} else
3958 	{
3959 		if(is_point_inside(x, y))
3960 		{
3961 			ret = shared_from_this();
3962 		}
3963 	}
3964 
3965 	return ret;
3966 }
3967 
get_background(bool own_only)3968 const litehtml::background* litehtml::html_tag::get_background(bool own_only)
3969 {
3970 	if(own_only)
3971 	{
3972 		// return own background with check for empty one
3973 		if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
3974 		{
3975 			return 0;
3976 		}
3977 		return &m_bg;
3978 	}
3979 
3980 	if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
3981 	{
3982 		// if this is root element (<html>) try to get background from body
3983 		if (!have_parent())
3984 		{
3985 			for (const auto& el : m_children)
3986 			{
3987 				if( el->is_body() )
3988 				{
3989 					// return own body background
3990 					return el->get_background(true);
3991 				}
3992 			}
3993 		}
3994 		return 0;
3995 	}
3996 
3997 	if(is_body())
3998 	{
3999 		element::ptr el_parent = parent();
4000 		if (el_parent)
4001 		{
4002 			if (!el_parent->get_background(true))
4003 			{
4004 				// parent of body will draw background for body
4005 				return 0;
4006 			}
4007 		}
4008 	}
4009 
4010 	return &m_bg;
4011 }
4012 
render_box(int x,int y,int max_width,bool second_pass)4013 int litehtml::html_tag::render_box(int x, int y, int max_width, bool second_pass /*= false*/)
4014 {
4015 	int parent_width = max_width;
4016 
4017 	calc_outlines(parent_width);
4018 
4019 	m_pos.clear();
4020 	m_pos.move_to(x, y);
4021 
4022 	m_pos.x += content_margins_left();
4023 	m_pos.y += content_margins_top();
4024 
4025 	int ret_width = 0;
4026 
4027 	def_value<int>	block_width(0);
4028 
4029 	if (m_display != display_table_cell && !m_css_width.is_predefined())
4030 	{
4031 		int w = calc_width(parent_width);
4032 
4033 		if (m_box_sizing == box_sizing_border_box)
4034 		{
4035 			w -= m_padding.width() + m_borders.width();
4036 		}
4037 		ret_width = max_width = block_width = w;
4038 	}
4039 	else
4040 	{
4041 		if (max_width)
4042 		{
4043 			max_width -= content_margins_left() + content_margins_right();
4044 		}
4045 	}
4046 
4047 	// check for max-width (on the first pass only)
4048 	if (!m_css_max_width.is_predefined() && !second_pass)
4049 	{
4050 		int mw = get_document()->cvt_units(m_css_max_width, m_font_size, parent_width);
4051 		if (m_box_sizing == box_sizing_border_box)
4052 		{
4053 			mw -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
4054 		}
4055 		if (max_width > mw)
4056 		{
4057 			max_width = mw;
4058 		}
4059 	}
4060 
4061 	m_floats_left.clear();
4062 	m_floats_right.clear();
4063 	m_boxes.clear();
4064 	m_cahe_line_left.invalidate();
4065 	m_cahe_line_right.invalidate();
4066 
4067 	element_position el_position;
4068 
4069 	int block_height = 0;
4070 
4071 	m_pos.height = 0;
4072 
4073 	if (get_predefined_height(block_height))
4074 	{
4075 		m_pos.height = block_height;
4076 	}
4077 
4078 	white_space ws = get_white_space();
4079 	bool skip_spaces = false;
4080 	if (ws == white_space_normal ||
4081 		ws == white_space_nowrap ||
4082 		ws == white_space_pre_line)
4083 	{
4084 		skip_spaces = true;
4085 	}
4086 
4087 	bool was_space = false;
4088 
4089 	for (auto el : m_children)
4090 	{
4091 		// we don't need process absolute and fixed positioned element on the second pass
4092 		if (second_pass)
4093 		{
4094 			el_position = el->get_element_position();
4095 			if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue;
4096 		}
4097 
4098 		// skip spaces to make rendering a bit faster
4099 		if (skip_spaces)
4100 		{
4101 			if (el->is_white_space())
4102 			{
4103 				if (was_space)
4104 				{
4105 					el->skip(true);
4106 					continue;
4107 				}
4108 				else
4109 				{
4110 					was_space = true;
4111 				}
4112 			}
4113 			else
4114 			{
4115 				was_space = false;
4116 			}
4117 		}
4118 
4119 		// place element into rendering flow
4120 		int rw = place_element(el, max_width);
4121 		if (rw > ret_width)
4122 		{
4123 			ret_width = rw;
4124 		}
4125 	}
4126 
4127 	finish_last_box(true);
4128 
4129 	if (block_width.is_default() && is_inline_box())
4130 	{
4131 		m_pos.width = ret_width;
4132 	}
4133 	else
4134 	{
4135 		m_pos.width = max_width;
4136 	}
4137 	calc_auto_margins(parent_width);
4138 
4139 	if (!m_boxes.empty())
4140 	{
4141 		if (collapse_top_margin())
4142 		{
4143 			int old_top = m_margins.top;
4144 			m_margins.top = std::max(m_boxes.front()->top_margin(), m_margins.top);
4145 			if (m_margins.top != old_top)
4146 			{
4147 				update_floats(m_margins.top - old_top, shared_from_this());
4148 			}
4149 		}
4150 		if (collapse_bottom_margin())
4151 		{
4152 			m_margins.bottom = std::max(m_boxes.back()->bottom_margin(), m_margins.bottom);
4153 			m_pos.height = m_boxes.back()->bottom() - m_boxes.back()->bottom_margin();
4154 		}
4155 		else
4156 		{
4157 			m_pos.height = m_boxes.back()->bottom();
4158 		}
4159 	}
4160 
4161 	// add the floats height to the block height
4162 	if (is_floats_holder())
4163 	{
4164 		int floats_height = get_floats_height();
4165 		if (floats_height > m_pos.height)
4166 		{
4167 			m_pos.height = floats_height;
4168 		}
4169 	}
4170 
4171 	// calculate the final position
4172 
4173 	m_pos.move_to(x, y);
4174 	m_pos.x += content_margins_left();
4175 	m_pos.y += content_margins_top();
4176 
4177 	if (get_predefined_height(block_height))
4178 	{
4179 		m_pos.height = block_height;
4180 	}
4181 
4182 	int min_height = 0;
4183 	if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
4184 	{
4185 		element::ptr el_parent = parent();
4186 		if (el_parent)
4187 		{
4188 			if (el_parent->get_predefined_height(block_height))
4189 			{
4190 				min_height = m_css_min_height.calc_percent(block_height);
4191 			}
4192 		}
4193 	}
4194 	else
4195 	{
4196 		min_height = (int)m_css_min_height.val();
4197 	}
4198 	if (min_height != 0 && m_box_sizing == box_sizing_border_box)
4199 	{
4200 		min_height -= m_padding.top + m_borders.top + m_padding.bottom + m_borders.bottom;
4201 		if (min_height < 0) min_height = 0;
4202 	}
4203 
4204 	if (m_display == display_list_item)
4205 	{
4206 		const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
4207 		if (list_image)
4208 		{
4209 			tstring url;
4210 			css::parse_css_url(list_image, url);
4211 
4212 			size sz;
4213 			const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
4214 			get_document()->container()->get_image_size(url.c_str(), list_image_baseurl, sz);
4215 			if (min_height < sz.height)
4216 			{
4217 				min_height = sz.height;
4218 			}
4219 		}
4220 
4221 	}
4222 
4223 	if (min_height > m_pos.height)
4224 	{
4225 		m_pos.height = min_height;
4226 	}
4227 
4228 	int min_width = m_css_min_width.calc_percent(parent_width);
4229 
4230 	if (min_width != 0 && m_box_sizing == box_sizing_border_box)
4231 	{
4232 		min_width -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
4233 		if (min_width < 0) min_width = 0;
4234 	}
4235 
4236 	if (min_width != 0)
4237 	{
4238 		if (min_width > m_pos.width)
4239 		{
4240 			m_pos.width = min_width;
4241 		}
4242 		if (min_width > ret_width)
4243 		{
4244 			ret_width = min_width;
4245 		}
4246 	}
4247 
4248 	ret_width += content_margins_left() + content_margins_right();
4249 
4250 	// re-render with new width
4251 	if (ret_width < max_width && !second_pass && have_parent())
4252 	{
4253 		if (m_display == display_inline_block ||
4254 			(m_css_width.is_predefined() &&
4255 			(m_float != float_none ||
4256 			m_display == display_table ||
4257 			m_el_position == element_position_absolute ||
4258 			m_el_position == element_position_fixed
4259 			))
4260 			)
4261 		{
4262 			render(x, y, ret_width, true);
4263 			m_pos.width = ret_width - (content_margins_left() + content_margins_right());
4264 		}
4265 	}
4266 
4267 	if (is_floats_holder() && !second_pass)
4268 	{
4269 		for (const auto& fb : m_floats_left)
4270 		{
4271 			fb.el->apply_relative_shift(fb.el->parent()->calc_width(m_pos.width));
4272 		}
4273 	}
4274 
4275 
4276 	return ret_width;
4277 }
4278 
render_table(int x,int y,int max_width,bool second_pass)4279 int litehtml::html_tag::render_table(int x, int y, int max_width, bool second_pass /*= false*/)
4280 {
4281 	if (!m_grid) return 0;
4282 
4283 	int parent_width = max_width;
4284 
4285 	calc_outlines(parent_width);
4286 
4287 	m_pos.clear();
4288 	m_pos.move_to(x, y);
4289 
4290 	m_pos.x += content_margins_left();
4291 	m_pos.y += content_margins_top();
4292 
4293 	def_value<int>	block_width(0);
4294 
4295 	if (!m_css_width.is_predefined())
4296 	{
4297 		max_width = block_width = calc_width(parent_width) - m_padding.width() - m_borders.width();
4298 	}
4299 	else
4300 	{
4301 		if (max_width)
4302 		{
4303 			max_width -= content_margins_left() + content_margins_right();
4304 		}
4305 	}
4306 
4307 	// Calculate table spacing
4308 	int table_width_spacing = 0;
4309 	if (m_border_collapse == border_collapse_separate)
4310 	{
4311 		table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1);
4312 	}
4313 	else
4314 	{
4315 		table_width_spacing = 0;
4316 
4317 		if (m_grid->cols_count())
4318 		{
4319 			table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left);
4320 			table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right);
4321 		}
4322 
4323 		for (int col = 1; col < m_grid->cols_count(); col++)
4324 		{
4325 			table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right);
4326 		}
4327 	}
4328 
4329 
4330 	// Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box.
4331 	// If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum
4332 	// cell width.
4333 	//
4334 	// Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
4335 
4336 	if (m_grid->cols_count() == 1 && !block_width.is_default())
4337 	{
4338 		for (int row = 0; row < m_grid->rows_count(); row++)
4339 		{
4340 			table_cell* cell = m_grid->cell(0, row);
4341 			if (cell && cell->el)
4342 			{
4343 				cell->min_width = cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
4344 				cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4345 			}
4346 		}
4347 	}
4348 	else
4349 	{
4350 		for (int row = 0; row < m_grid->rows_count(); row++)
4351 		{
4352 			for (int col = 0; col < m_grid->cols_count(); col++)
4353 			{
4354 				table_cell* cell = m_grid->cell(col, row);
4355 				if (cell && cell->el)
4356 				{
4357 					if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage)
4358 					{
4359 						int css_w = m_grid->column(col).css_width.calc_percent(block_width);
4360 						int el_w = cell->el->render(0, 0, css_w);
4361 						cell->min_width = cell->max_width = std::max(css_w, el_w);
4362 						cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4363 					}
4364 					else
4365 					{
4366 						// calculate minimum content width
4367 						cell->min_width = cell->el->render(0, 0, 1);
4368 						// calculate maximum content width
4369 						cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
4370 					}
4371 				}
4372 			}
4373 		}
4374 	}
4375 
4376 	// For each column, determine a maximum and minimum column width from the cells that span only that column.
4377 	// The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
4378 	// The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
4379 
4380 	for (int col = 0; col < m_grid->cols_count(); col++)
4381 	{
4382 		m_grid->column(col).max_width = 0;
4383 		m_grid->column(col).min_width = 0;
4384 		for (int row = 0; row < m_grid->rows_count(); row++)
4385 		{
4386 			if (m_grid->cell(col, row)->colspan <= 1)
4387 			{
4388 				m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width);
4389 				m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width);
4390 			}
4391 		}
4392 	}
4393 
4394 	// For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
4395 	// they are at least as wide as the cell. Do the same for the maximum widths.
4396 	// If possible, widen all spanned columns by approximately the same amount.
4397 
4398 	for (int col = 0; col < m_grid->cols_count(); col++)
4399 	{
4400 		for (int row = 0; row < m_grid->rows_count(); row++)
4401 		{
4402 			if (m_grid->cell(col, row)->colspan > 1)
4403 			{
4404 				int max_total_width = m_grid->column(col).max_width;
4405 				int min_total_width = m_grid->column(col).min_width;
4406 				for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++)
4407 				{
4408 					max_total_width += m_grid->column(col2).max_width;
4409 					min_total_width += m_grid->column(col2).min_width;
4410 				}
4411 				if (min_total_width < m_grid->cell(col, row)->min_width)
4412 				{
4413 					m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
4414 				}
4415 				if (max_total_width < m_grid->cell(col, row)->max_width)
4416 				{
4417 					m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
4418 				}
4419 			}
4420 		}
4421 	}
4422 
4423 	// If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
4424 	// greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
4425 	// If the used width is greater than MIN, the extra width should be distributed over the columns.
4426 	//
4427 	// If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
4428 	// CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
4429 	// less than that of the containing block, use max(MAX, CAPMIN).
4430 
4431 
4432 	int table_width = 0;
4433 	int min_table_width = 0;
4434 	int max_table_width = 0;
4435 
4436 	if (!block_width.is_default())
4437 	{
4438 		table_width = m_grid->calc_table_width(block_width - table_width_spacing, false, min_table_width, max_table_width);
4439 	}
4440 	else
4441 	{
4442 		table_width = m_grid->calc_table_width(max_width - table_width_spacing, true, min_table_width, max_table_width);
4443 	}
4444 
4445 	min_table_width += table_width_spacing;
4446 	max_table_width += table_width_spacing;
4447 	table_width += table_width_spacing;
4448 	m_grid->calc_horizontal_positions(m_borders, m_border_collapse, m_border_spacing_x);
4449 
4450 	bool row_span_found = false;
4451 
4452 	// render cells with computed width
4453 	for (int row = 0; row < m_grid->rows_count(); row++)
4454 	{
4455 		m_grid->row(row).height = 0;
4456 		for (int col = 0; col < m_grid->cols_count(); col++)
4457 		{
4458 			table_cell* cell = m_grid->cell(col, row);
4459 			if (cell->el)
4460 			{
4461 				int span_col = col + cell->colspan - 1;
4462 				if (span_col >= m_grid->cols_count())
4463 				{
4464 					span_col = m_grid->cols_count() - 1;
4465 				}
4466 				int cell_width = m_grid->column(span_col).right - m_grid->column(col).left;
4467 
4468 				if (cell->el->m_pos.width != cell_width - cell->el->content_margins_left() - cell->el->content_margins_right())
4469 				{
4470 					cell->el->render(m_grid->column(col).left, 0, cell_width);
4471 					cell->el->m_pos.width = cell_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4472 				}
4473 				else
4474 				{
4475 					cell->el->m_pos.x = m_grid->column(col).left + cell->el->content_margins_left();
4476 				}
4477 
4478 				if (cell->rowspan <= 1)
4479 				{
4480 					m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height());
4481 				}
4482 				else
4483 				{
4484 					row_span_found = true;
4485 				}
4486 
4487 			}
4488 		}
4489 	}
4490 
4491 	if (row_span_found)
4492 	{
4493 		for (int col = 0; col < m_grid->cols_count(); col++)
4494 		{
4495 			for (int row = 0; row < m_grid->rows_count(); row++)
4496 			{
4497 				table_cell* cell = m_grid->cell(col, row);
4498 				if (cell->el)
4499 				{
4500 					int span_row = row + cell->rowspan - 1;
4501 					if (span_row >= m_grid->rows_count())
4502 					{
4503 						span_row = m_grid->rows_count() - 1;
4504 					}
4505 					if (span_row != row)
4506 					{
4507 						int h = 0;
4508 						for (int i = row; i <= span_row; i++)
4509 						{
4510 							h += m_grid->row(i).height;
4511 						}
4512 						if (h < cell->el->height())
4513 						{
4514 							m_grid->row(span_row).height += cell->el->height() - h;
4515 						}
4516 					}
4517 				}
4518 			}
4519 		}
4520 	}
4521 
4522 	// Calculate vertical table spacing
4523 	int table_height_spacing = 0;
4524 	if (m_border_collapse == border_collapse_separate)
4525 	{
4526 		table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1);
4527 	}
4528 	else
4529 	{
4530 		table_height_spacing = 0;
4531 
4532 		if (m_grid->rows_count())
4533 		{
4534 			table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top);
4535 			table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
4536 		}
4537 
4538 		for (int row = 1; row < m_grid->rows_count(); row++)
4539 		{
4540 			table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom);
4541 		}
4542 	}
4543 
4544 
4545 	// calculate block height
4546 	int block_height = 0;
4547 	if (get_predefined_height(block_height))
4548 	{
4549 		block_height -= m_padding.height() + m_borders.height();
4550 	}
4551 
4552 	// calculate minimum height from m_css_min_height
4553 	int min_height = 0;
4554 	if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
4555 	{
4556 		element::ptr el_parent = parent();
4557 		if (el_parent)
4558 		{
4559 			int parent_height = 0;
4560 			if (el_parent->get_predefined_height(parent_height))
4561 			{
4562 				min_height = m_css_min_height.calc_percent(parent_height);
4563 			}
4564 		}
4565 	}
4566 	else
4567 	{
4568 		min_height = (int)m_css_min_height.val();
4569 	}
4570 
4571 	int extra_row_height = 0;
4572 	int minimum_table_height = std::max(block_height, min_height);
4573 
4574 	m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y);
4575 	m_grid->calc_vertical_positions(m_borders, m_border_collapse, m_border_spacing_y);
4576 
4577 	int table_height = 0;
4578 
4579 	// place cells vertically
4580 	for (int col = 0; col < m_grid->cols_count(); col++)
4581 	{
4582 		for (int row = 0; row < m_grid->rows_count(); row++)
4583 		{
4584 			table_cell* cell = m_grid->cell(col, row);
4585 			if (cell->el)
4586 			{
4587 				int span_row = row + cell->rowspan - 1;
4588 				if (span_row >= m_grid->rows_count())
4589 				{
4590 					span_row = m_grid->rows_count() - 1;
4591 				}
4592 				cell->el->m_pos.y = m_grid->row(row).top + cell->el->content_margins_top();
4593 				cell->el->m_pos.height = m_grid->row(span_row).bottom - m_grid->row(row).top - cell->el->content_margins_top() - cell->el->content_margins_bottom();
4594 				table_height = std::max(table_height, m_grid->row(span_row).bottom);
4595 				cell->el->apply_vertical_align();
4596 			}
4597 		}
4598 	}
4599 
4600 	if (m_border_collapse == border_collapse_collapse)
4601 	{
4602 		if (m_grid->rows_count())
4603 		{
4604 			table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
4605 		}
4606 	}
4607 	else
4608 	{
4609 		table_height += m_border_spacing_y;
4610 	}
4611 
4612 	m_pos.width = table_width;
4613 
4614 	calc_auto_margins(parent_width);
4615 
4616 	m_pos.move_to(x, y);
4617 	m_pos.x += content_margins_left();
4618 	m_pos.y += content_margins_top();
4619 	m_pos.width = table_width;
4620 	m_pos.height = table_height;
4621 
4622 	return max_table_width;
4623 }
4624 
draw_children_box(uint_ptr hdc,int x,int y,const position * clip,draw_flag flag,int zindex)4625 void litehtml::html_tag::draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
4626 {
4627 	position pos = m_pos;
4628 	pos.x += x;
4629 	pos.y += y;
4630 
4631 	document::ptr doc = get_document();
4632 
4633 	if (m_overflow > overflow_visible)
4634 	{
4635 		position border_box = pos;
4636 		border_box += m_padding;
4637 		border_box += m_borders;
4638 
4639 		border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
4640 
4641 		bdr_radius -= m_borders;
4642 		bdr_radius -= m_padding;
4643 
4644 		doc->container()->set_clip(pos, bdr_radius, true, true);
4645 	}
4646 
4647 	position browser_wnd;
4648 	doc->container()->get_client_rect(browser_wnd);
4649 
4650 	element::ptr el;
4651 	for (auto& item : m_children)
4652 	{
4653 		el = item;
4654 		if (el->is_visible())
4655 		{
4656 			switch (flag)
4657 			{
4658 			case draw_positioned:
4659 				if (el->is_positioned() && el->get_zindex() == zindex)
4660 				{
4661 					if (el->get_element_position() == element_position_fixed)
4662 					{
4663 						el->draw(hdc, browser_wnd.x, browser_wnd.y, clip);
4664 						el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true);
4665 					}
4666 					else
4667 					{
4668 						el->draw(hdc, pos.x, pos.y, clip);
4669 						el->draw_stacking_context(hdc, pos.x, pos.y, clip, true);
4670 					}
4671 					el = 0;
4672 				}
4673 				break;
4674 			case draw_block:
4675 				if (!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
4676 				{
4677 					el->draw(hdc, pos.x, pos.y, clip);
4678 				}
4679 				break;
4680 			case draw_floats:
4681 				if (el->get_float() != float_none && !el->is_positioned())
4682 				{
4683 					el->draw(hdc, pos.x, pos.y, clip);
4684 					el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
4685 					el = 0;
4686 				}
4687 				break;
4688 			case draw_inlines:
4689 				if (el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
4690 				{
4691 					el->draw(hdc, pos.x, pos.y, clip);
4692 					if (el->get_display() == display_inline_block)
4693 					{
4694 						el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
4695 						el = 0;
4696 					}
4697 				}
4698 				break;
4699 			default:
4700 				break;
4701 			}
4702 
4703 			if (el)
4704 			{
4705 				if (flag == draw_positioned)
4706 				{
4707 					if (!el->is_positioned())
4708 					{
4709 						el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4710 					}
4711 				}
4712 				else
4713 				{
4714 					if (el->get_float() == float_none &&
4715 						el->get_display() != display_inline_block &&
4716 						!el->is_positioned())
4717 					{
4718 						el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4719 					}
4720 				}
4721 			}
4722 		}
4723 	}
4724 
4725 	if (m_overflow > overflow_visible)
4726 	{
4727 		doc->container()->del_clip();
4728 	}
4729 }
4730 
draw_children_table(uint_ptr hdc,int x,int y,const position * clip,draw_flag flag,int zindex)4731 void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
4732 {
4733 	if (!m_grid) return;
4734 
4735 	position pos = m_pos;
4736 	pos.x += x;
4737 	pos.y += y;
4738 	for (int row = 0; row < m_grid->rows_count(); row++)
4739 	{
4740 		if (flag == draw_block)
4741 		{
4742 			m_grid->row(row).el_row->draw_background(hdc, pos.x, pos.y, clip);
4743 		}
4744 		for (int col = 0; col < m_grid->cols_count(); col++)
4745 		{
4746 			table_cell* cell = m_grid->cell(col, row);
4747 			if (cell->el)
4748 			{
4749 				if (flag == draw_block)
4750 				{
4751 					cell->el->draw(hdc, pos.x, pos.y, clip);
4752 				}
4753 				cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4754 			}
4755 		}
4756 	}
4757 }
4758