| 
									
										
										
										
											2019-10-05 10:16:27 +02:00
										 |  |  | #include <LibCore/CFile.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-29 12:24:36 +02:00
										 |  |  | #include <LibGUI/GApplication.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | #include <LibGUI/GPainter.h>
 | 
					
						
							|  |  |  | #include <LibGUI/GScrollBar.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-28 23:02:22 +02:00
										 |  |  | #include <LibHTML/DOM/Element.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-29 11:59:38 +02:00
										 |  |  | #include <LibHTML/DOM/HTMLAnchorElement.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | #include <LibHTML/Dump.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-04 15:50:04 +02:00
										 |  |  | #include <LibHTML/Frame.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | #include <LibHTML/HtmlView.h>
 | 
					
						
							|  |  |  | #include <LibHTML/Layout/LayoutNode.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-05 10:16:27 +02:00
										 |  |  | #include <LibHTML/Parser/HTMLParser.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | #include <LibHTML/RenderingContext.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HtmlView::HtmlView(GWidget* parent) | 
					
						
							|  |  |  |     : GScrollableWidget(parent) | 
					
						
							| 
									
										
										
										
											2019-10-04 15:50:04 +02:00
										 |  |  |     , m_main_frame(Frame::create()) | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | { | 
					
						
							|  |  |  |     set_frame_shape(FrameShape::Container); | 
					
						
							|  |  |  |     set_frame_shadow(FrameShadow::Sunken); | 
					
						
							|  |  |  |     set_frame_thickness(2); | 
					
						
							|  |  |  |     set_should_hide_unnecessary_scrollbars(true); | 
					
						
							|  |  |  |     set_background_color(Color::White); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 15:50:04 +02:00
										 |  |  | HtmlView::~HtmlView() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | void HtmlView::set_document(Document* document) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (document == m_document) | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-10-06 22:58:18 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_document) | 
					
						
							|  |  |  |         m_document->on_invalidate_layout = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  |     m_document = document; | 
					
						
							| 
									
										
										
										
											2019-10-06 22:58:18 +11:00
										 |  |  |     m_document->on_invalidate_layout = [this]() { layout_and_sync_size(); }; | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 15:50:04 +02:00
										 |  |  |     main_frame().set_document(document); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  |     if (document == nullptr) | 
					
						
							|  |  |  |         m_layout_root = nullptr; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_layout_root = document->create_layout_tree(document->style_resolver(), nullptr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef HTML_DEBUG
 | 
					
						
							|  |  |  |     if (document != nullptr) { | 
					
						
							| 
									
										
										
										
											2019-10-03 10:25:00 +02:00
										 |  |  |         dbgprintf("\033[33;1mLayout tree before layout:\033[0m\n"); | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  |         ::dump_tree(*m_layout_root); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     layout_and_sync_size(); | 
					
						
							|  |  |  |     update(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::layout_and_sync_size() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!m_layout_root) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 15:50:04 +02:00
										 |  |  |     main_frame().set_size(available_size()); | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  |     m_layout_root->layout(); | 
					
						
							|  |  |  |     set_content_size(m_layout_root->rect().size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef HTML_DEBUG
 | 
					
						
							| 
									
										
										
										
											2019-10-03 10:25:00 +02:00
										 |  |  |     dbgprintf("\033[33;1mLayout tree after layout:\033[0m\n"); | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  |     ::dump_tree(*m_layout_root); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::resize_event(GResizeEvent& event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GScrollableWidget::resize_event(event); | 
					
						
							|  |  |  |     layout_and_sync_size(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::paint_event(GPaintEvent& event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GFrame::paint_event(event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GPainter painter(*this); | 
					
						
							|  |  |  |     painter.add_clip_rect(widget_inner_rect()); | 
					
						
							|  |  |  |     painter.add_clip_rect(event.rect()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 21:05:52 +02:00
										 |  |  |     if (!m_layout_root) { | 
					
						
							|  |  |  |         painter.fill_rect(event.rect(), background_color()); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 21:51:28 +02:00
										 |  |  |     painter.fill_rect(event.rect(), m_document->background_color()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 12:44:22 +03:00
										 |  |  |     painter.translate(frame_thickness(), frame_thickness()); | 
					
						
							|  |  |  |     painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     RenderingContext context { painter }; | 
					
						
							|  |  |  |     m_layout_root->render(context); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-28 23:02:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::mousemove_event(GMouseEvent& event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!m_layout_root) | 
					
						
							|  |  |  |         return GScrollableWidget::mousemove_event(event); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-29 11:50:35 +02:00
										 |  |  |     bool hovered_node_changed = false; | 
					
						
							| 
									
										
										
										
											2019-10-03 09:17:27 +02:00
										 |  |  |     auto result = m_layout_root->hit_test(to_content_position(event.position())); | 
					
						
							| 
									
										
										
										
											2019-09-28 23:02:22 +02:00
										 |  |  |     if (result.layout_node) { | 
					
						
							| 
									
										
										
										
											2019-09-29 11:50:35 +02:00
										 |  |  |         auto* node = result.layout_node->node(); | 
					
						
							| 
									
										
										
										
											2019-09-29 12:24:36 +02:00
										 |  |  |         hovered_node_changed = node != m_document->hovered_node(); | 
					
						
							| 
									
										
										
										
											2019-09-29 11:50:35 +02:00
										 |  |  |         m_document->set_hovered_node(const_cast<Node*>(node)); | 
					
						
							| 
									
										
										
										
											2019-09-29 18:10:39 +02:00
										 |  |  | #ifdef HTML_DEBUG
 | 
					
						
							| 
									
										
										
										
											2019-09-29 11:50:35 +02:00
										 |  |  |         if (node) { | 
					
						
							| 
									
										
										
										
											2019-09-29 11:59:38 +02:00
										 |  |  |             if (auto* link = node->enclosing_link_element()) { | 
					
						
							|  |  |  |                 dbg() << "HtmlView: hovering over a link to " << link->href(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-09-28 23:02:22 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-29 18:10:39 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-09-28 23:02:22 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-29 12:24:36 +02:00
										 |  |  |     if (hovered_node_changed) { | 
					
						
							| 
									
										
										
										
											2019-09-29 11:50:35 +02:00
										 |  |  |         update(); | 
					
						
							| 
									
										
										
										
											2019-09-29 12:24:36 +02:00
										 |  |  |         auto* hovered_html_element = m_document->hovered_node() ? m_document->hovered_node()->enclosing_html_element() : nullptr; | 
					
						
							|  |  |  |         if (hovered_html_element && !hovered_html_element->title().is_null()) { | 
					
						
							|  |  |  |             auto screen_position = screen_relative_rect().location().translated(event.position()); | 
					
						
							|  |  |  |             GApplication::the().show_tooltip(hovered_html_element->title(), screen_position.translated(4, 4)); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             GApplication::the().hide_tooltip(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-28 23:02:22 +02:00
										 |  |  |     event.accept(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-29 12:04:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::mousedown_event(GMouseEvent& event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!m_layout_root) | 
					
						
							|  |  |  |         return GScrollableWidget::mousemove_event(event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool hovered_node_changed = false; | 
					
						
							| 
									
										
										
										
											2019-10-03 09:17:27 +02:00
										 |  |  |     auto result = m_layout_root->hit_test(to_content_position(event.position())); | 
					
						
							| 
									
										
										
										
											2019-09-29 12:04:02 +02:00
										 |  |  |     if (result.layout_node) { | 
					
						
							|  |  |  |         auto* node = result.layout_node->node(); | 
					
						
							| 
									
										
										
										
											2019-09-29 12:24:36 +02:00
										 |  |  |         hovered_node_changed = node != m_document->hovered_node(); | 
					
						
							| 
									
										
										
										
											2019-09-29 12:04:02 +02:00
										 |  |  |         m_document->set_hovered_node(const_cast<Node*>(node)); | 
					
						
							|  |  |  |         if (node) { | 
					
						
							|  |  |  |             if (auto* link = node->enclosing_link_element()) { | 
					
						
							|  |  |  |                 dbg() << "HtmlView: clicking on a link to " << link->href(); | 
					
						
							|  |  |  |                 if (on_link_click) | 
					
						
							|  |  |  |                     on_link_click(link->href()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (hovered_node_changed) | 
					
						
							|  |  |  |         update(); | 
					
						
							|  |  |  |     event.accept(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-05 10:16:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::reload() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     load(main_frame().document()->url()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HtmlView::load(const URL& url) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dbg() << "HtmlView::load: " << url; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (on_load_start) | 
					
						
							|  |  |  |         on_load_start(url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto f = CFile::construct(); | 
					
						
							|  |  |  |     f->set_filename(url.path()); | 
					
						
							|  |  |  |     if (!f->open(CIODevice::OpenMode::ReadOnly)) { | 
					
						
							|  |  |  |         dbg() << "HtmlView::load: Error: " << f->error_string(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     String html = String::copy(f->read_all()); | 
					
						
							| 
									
										
										
										
											2019-10-06 21:13:24 +02:00
										 |  |  |     auto document = parse_html(html, url); | 
					
						
							| 
									
										
										
										
											2019-10-05 10:16:27 +02:00
										 |  |  |     document->normalize(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     set_document(document); | 
					
						
							| 
									
										
										
										
											2019-10-05 10:30:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (on_title_change) | 
					
						
							|  |  |  |         on_title_change(document->title()); | 
					
						
							| 
									
										
										
										
											2019-10-05 10:16:27 +02:00
										 |  |  | } |