Update litehtml_viewer to build on Windows
[claws.git] / src / plugins / litehtml_viewer / litehtml / html_tag.cpp
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
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
36 litehtml::html_tag::~html_tag()
37 {
38
39 }
40
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
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
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
74 const litehtml::tchar_t* litehtml::html_tag::get_tagName() const
75 {
76         return m_tag.c_str();
77 }
78
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::classic());
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
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
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
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
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
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
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
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
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
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
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
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
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
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
526 bool litehtml::html_tag::is_white_space() const
527 {
528         return false;
529 }
530
531 int litehtml::html_tag::get_font_size() const
532 {
533         return m_font_size;
534 }
535
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
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
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
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
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
957 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
1043 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
1066 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
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
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
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
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
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
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
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
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
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
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
1649 void litehtml::html_tag::parse_attributes()
1650 {
1651         for(auto& el : m_children)
1652         {
1653                 el->parse_attributes();
1654         }
1655 }
1656
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
1665 bool litehtml::html_tag::is_body()  const
1666 {
1667         return false;
1668 }
1669
1670 void litehtml::html_tag::set_data( const tchar_t* data )
1671 {
1672
1673 }
1674
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
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
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
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
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
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
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
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
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
2012 bool litehtml::html_tag::is_break() const
2013 {
2014         return false;
2015 }
2016
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::classic());
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
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
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
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
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
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
2493 int litehtml::html_tag::line_height() const
2494 {
2495         return m_line_height;
2496 }
2497
2498 bool litehtml::html_tag::is_replaced() const
2499 {
2500         return false;
2501 }
2502
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
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
2584 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
2629 litehtml::style_display litehtml::html_tag::get_display() const
2630 {
2631         return m_display;
2632 }
2633
2634 litehtml::element_float litehtml::html_tag::get_float() const
2635 {
2636         return m_float;
2637 }
2638
2639 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
2655 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
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
2711 litehtml::white_space litehtml::html_tag::get_white_space() const
2712 {
2713         return m_white_space;
2714 }
2715
2716 litehtml::vertical_align litehtml::html_tag::get_vertical_align() const
2717 {
2718         return m_vertical_align;
2719 }
2720
2721 litehtml::css_length litehtml::html_tag::get_css_left() const
2722 {
2723         return m_css_offsets.left;
2724 }
2725
2726 litehtml::css_length litehtml::html_tag::get_css_right() const
2727 {
2728         return m_css_offsets.right;
2729 }
2730
2731 litehtml::css_length litehtml::html_tag::get_css_top() const
2732 {
2733         return m_css_offsets.top;
2734 }
2735
2736 litehtml::css_length litehtml::html_tag::get_css_bottom() const
2737 {
2738         return m_css_offsets.bottom;
2739 }
2740
2741
2742 litehtml::css_offsets litehtml::html_tag::get_css_offsets() const
2743 {
2744         return m_css_offsets;
2745 }
2746
2747 litehtml::element_clear litehtml::html_tag::get_clear() const
2748 {
2749         return m_clear;
2750 }
2751
2752 litehtml::css_length litehtml::html_tag::get_css_width() const
2753 {
2754         return m_css_width;
2755 }
2756
2757 litehtml::css_length litehtml::html_tag::get_css_height() const
2758 {
2759         return m_css_height;
2760 }
2761
2762 size_t litehtml::html_tag::get_children_count() const
2763 {
2764         return m_children.size();
2765 }
2766
2767 litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const
2768 {
2769         return m_children[idx];
2770 }
2771
2772 void litehtml::html_tag::set_css_width( css_length& w )
2773 {
2774         m_css_width = w;
2775 }
2776
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
2810 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
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
2924 litehtml::visibility litehtml::html_tag::get_visibility() const
2925 {
2926         return m_visibility;
2927 }
2928
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
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
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
3016 int litehtml::html_tag::get_zindex() const
3017 {
3018         return m_z_index;
3019 }
3020
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
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
3273 litehtml::overflow litehtml::html_tag::get_overflow() const
3274 {
3275         return m_overflow;
3276 }
3277
3278 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
3310 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
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
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
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
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
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
3494 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
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
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
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
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
3602 void litehtml::html_tag::add_style( const litehtml::style& st )
3603 {
3604         m_style.combine(st);
3605 }
3606
3607 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
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
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
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
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
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
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
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
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 }