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