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