diff --git a/.gitignore b/.gitignore index 1f09893..2dd8c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,8 @@ build/ .settings .project .idea +.vscode + +.cache/ +html/ diff --git a/CMakeLists.txt b/CMakeLists.txt index bffe7ef..c944775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(litebrowser CXX) find_package(PkgConfig REQUIRED) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "No build type selected, default to Release") - set(CMAKE_BUILD_TYPE "Release") + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") @@ -18,49 +18,63 @@ set(CMAKE_C_FLAGS_RELEASE "-O3") set(LITEBROWSER_PATH src) set(LITEHTML_PATH litehtml) set(CONTAINER_PATH ${LITEHTML_PATH}/containers/cairo) +set(SUPPORT_PATH ${LITEHTML_PATH}/support) -pkg_check_modules(LB_LIBS REQUIRED gdkmm-3.0 gtkmm-3.0 libcurl cairo pango pangocairo) +pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo) +pkg_check_modules(LB_ADW libadwaita-1) set(SOURCE ${LITEBROWSER_PATH}/main.cpp - ${LITEBROWSER_PATH}/html_widget.cpp - ${LITEBROWSER_PATH}/browser_wnd.cpp - ${LITEBROWSER_PATH}/web_history.cpp - ${LITEBROWSER_PATH}/http_request.cpp - ${LITEBROWSER_PATH}/web_page.cpp - ${LITEBROWSER_PATH}/http_requests_pool.cpp - ${CONTAINER_PATH}/container_cairo.cpp - ${CONTAINER_PATH}/cairo_borders.cpp - ${CONTAINER_PATH}/container_cairo_pango.cpp - ) + ${LITEBROWSER_PATH}/browser_wnd.cpp + ${SUPPORT_PATH}/gtkmm4/html_widget.cpp + ${SUPPORT_PATH}/webpage/web_history.cpp + ${SUPPORT_PATH}/webpage/draw_buffer.cpp + ${SUPPORT_PATH}/webpage/http_request.cpp + ${SUPPORT_PATH}/webpage/web_page.cpp + ${SUPPORT_PATH}/webpage/http_requests_pool.cpp + ${CONTAINER_PATH}/container_cairo.cpp + ${CONTAINER_PATH}/cairo_borders.cpp + ${CONTAINER_PATH}/container_cairo_pango.cpp + ) set(HEADERS ${LITEBROWSER_PATH}/browser_wnd.h - ${LITEBROWSER_PATH}/html_widget.h - ${LITEBROWSER_PATH}/globals.h - ${LITEBROWSER_PATH}/web_history.h - ${LITEBROWSER_PATH}/http_request.h - ${LITEBROWSER_PATH}/web_page.h - ${LITEBROWSER_PATH}/html_host.h - ${LITEBROWSER_PATH}/http_requests_pool.h - ${LITEBROWSER_PATH}/html_dumper.h - ${CONTAINER_PATH}/container_cairo.h - ${CONTAINER_PATH}/cairo_borders.h - ${CONTAINER_PATH}/container_cairo_pango.h - ${CONTAINER_PATH}/cairo_images_cache.h - ) - -option(LITEHTML_BUILD_TESTING "enable testing for litehtml" OFF) + ${LITEBROWSER_PATH}/html_dumper.h + ${SUPPORT_PATH}/gtkmm4/html_widget.h + ${SUPPORT_PATH}/webpage/web_history.h + ${SUPPORT_PATH}/webpage/http_request.h + ${SUPPORT_PATH}/webpage/web_page.h + ${SUPPORT_PATH}/webpage/html_host.h + ${SUPPORT_PATH}/webpage/http_requests_pool.h + ${SUPPORT_PATH}/webpage/draw_buffer.h + ${CONTAINER_PATH}/container_cairo.h + ${CONTAINER_PATH}/cairo_borders.h + ${CONTAINER_PATH}/container_cairo_pango.h + ${CONTAINER_PATH}/cairo_images_cache.h + ) + +if (FOR_TESTING) + set(HEADERS ${HEADERS} litehtml-tests/fonts.h) + set(SOURCE ${SOURCE} litehtml-tests/fonts.cpp) + pkg_check_modules(FONTCONFIG REQUIRED fontconfig) + add_compile_options(-DFOR_TESTING) +endif () + add_subdirectory(${LITEHTML_PATH}) # additional warnings add_compile_options(-Wall -Wextra -Wpedantic) add_executable(${PROJECT_NAME} ${SOURCE} ${HEADERS}) -include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${LB_LIBS_INCLUDE_DIRS} ${CONTAINER_PATH}) -target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS}) -target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES}) + +include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${SUPPORT_PATH}/webpage ${SUPPORT_PATH}/gtkmm4 ${LB_LIBS_INCLUDE_DIRS} ${LB_ADW_INCLUDE_DIRS} ${CONTAINER_PATH} litehtml-tests) +target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS} ${FONTCONFIG_LDFLAGS} ${LB_ADW_LDFLAGS}) +target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES} ${LB_ADW_LIBRARIES} ${FONTCONFIG_LIBRARIES}) + +if (LB_ADW_FOUND) + target_compile_definitions(${PROJECT_NAME} PUBLIC LIBADWAITA_AVAILABLE=1) +endif () set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - C_STANDARD 99 - ) + CXX_STANDARD 17 + C_STANDARD 99 + ) diff --git a/README.md b/README.md index 63e5b0c..d00fbdc 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,31 @@ A simple browser based on the [litehtml](https://github.com/litehtml/litehtml) e ## Pre-requisites on Linux -Install dependencies: +### Install required dependencies: - * vim-core for xxd +#### Fedora + ``` + dnf install gtkmm4.0-devel libcurl-devel cairo-devel pango-devel + ``` + +### Optional dependencies: + +The optional library ```libadwaita``` will add support for dark themes, High Contrast mode and some other GNOME related features. + +#### Fedora + ``` + dnf install libadwaita-devel + ``` ## Pre-requisites on Mac Install dependencies using [Homebrew](https://brew.sh/): - * [gtkmm3](https://formulae.brew.sh/formula/gtkmm3) - * [gtk+3](https://formulae.brew.sh/formula/gtk+3) + * [gtkmm4](https://formulae.brew.sh/formula/gtkmm4) + * [gtk4](https://formulae.brew.sh/formula/gtk4) ``` -brew install gtkmm3 gtk+3 +brew install gtkmm4 gtk4 ``` ## Common Build instructions diff --git a/litehtml b/litehtml index 135fbdc..1274232 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit 135fbdcae3eea2318f8ba8b2d85582322899c5fc +Subproject commit 127423217120ded1b8c03428259e2b2f33228a95 diff --git a/src/browser_wnd.cpp b/src/browser_wnd.cpp index a18cc05..019548d 100644 --- a/src/browser_wnd.cpp +++ b/src/browser_wnd.cpp @@ -1,7 +1,8 @@ -#include "globals.h" #include "browser_wnd.h" #include #include +#include +#include "html_dumper.h" struct { @@ -16,141 +17,160 @@ struct {"Obama (Wiki)", "https://en.wikipedia.org/wiki/Barack_Obama?useskin=vector"}, {"Elizabeth II (Wiki)", "https://en.wikipedia.org/wiki/Elizabeth_II?useskin=vector"}, {"std::vector", "https://en.cppreference.com/w/cpp/container/vector"}, + {"BinaryTides", "https://www.binarytides.com/"}, + {"Ubuntu Forums", "https://ubuntuforums.org/"}, }; -browser_window::browser_window(const std::string& url) : - m_prev_state(0), - m_html(this), - - m_tools_render1("Single Render"), - m_tools_render10("Render 10 Times"), - m_tools_render100("Render 100 Times"), - - m_tools_draw1("Single Draw"), - m_tools_draw10("Draw 10 Times"), - m_tools_draw100("Draw 100 Times"), +static inline void mk_button(Gtk::Button& btn, const std::string& label_text, const std::string& icon_name) +{ + btn.set_focusable(false); + btn.set_image_from_icon_name(icon_name); + btn.set_tooltip_text(label_text); +} - m_tools_dump("Dump parsed HTML") +browser_window::browser_window(const Glib::RefPtr& app, const std::string& url) : + m_prev_state(0) { - add(m_vbox); - m_vbox.show(); + set_child(m_vbox); set_titlebar(m_header); m_header.show(); - m_header.set_show_close_button(true); - m_header.property_spacing().set_value(0); + m_header.set_show_title_buttons(false); - m_header.pack_start(m_back_button); - m_back_button.show(); + auto title_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); + auto left_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 5); + auto right_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 5); + + left_box->append(m_back_button); + mk_button(m_back_button, "Go Back", "go-previous-symbolic"); m_back_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_back_clicked) ); - m_back_button.set_image_from_icon_name("go-previous-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.pack_start(m_forward_button); - m_forward_button.show(); + left_box->append(m_forward_button); + mk_button(m_forward_button, "Go Forward", "go-next-symbolic"); + m_forward_button.set_margin_end(10); m_forward_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_forward_clicked) ); - m_forward_button.set_image_from_icon_name("go-next-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.pack_start(m_stop_reload_button); - m_stop_reload_button.show(); + left_box->append(m_stop_reload_button); + mk_button(m_stop_reload_button, "Reload Page", "view-refresh-symbolic"); m_stop_reload_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_stop_reload_clicked) ); - m_stop_reload_button.set_image_from_icon_name("view-refresh-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.pack_start(m_home_button); - m_home_button.show(); + left_box->append(m_home_button); + mk_button(m_home_button, "Go Home", "go-home-symbolic"); m_home_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_home_clicked) ); - m_home_button.set_image_from_icon_name("go-home-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.set_custom_title(m_address_bar); - m_address_bar.set_hexpand_set(true); - m_address_bar.set_hexpand(); - m_address_bar.property_primary_icon_name().set_value("document-open-symbolic"); - m_address_bar.set_margin_start(32); + left_box->set_hexpand(false); + title_box->append(*left_box); - m_address_bar.show(); + title_box->append(m_address_bar); + m_address_bar.set_hexpand(true); + m_address_bar.set_halign(Gtk::Align::FILL); + m_address_bar.set_margin_start(20); + m_address_bar.set_margin_end(3); + m_address_bar.signal_activate().connect(sigc::mem_fun(*this, &browser_window::on_go_clicked)); + m_address_bar.property_primary_icon_name().set_value("insert-link-symbolic"); m_address_bar.set_text("http://www.litehtml.com/"); - m_address_bar.add_events(Gdk::KEY_PRESS_MASK); - m_address_bar.signal_key_press_event().connect( sigc::mem_fun(*this, &browser_window::on_address_key_press), false ); - m_menu_bookmarks.set_halign(Gtk::ALIGN_END); + right_box->append(m_go_button); + mk_button(m_go_button, "Go", "media-playback-start-symbolic"); + m_go_button.set_margin_end(20); + m_go_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_go_clicked) ); + + // Creating bookmarks popover + auto menu_model = Gio::Menu::create(); for(const auto& url : g_bookmarks) { - m_menu_items.emplace_back(url.name); - m_menu_bookmarks.append(m_menu_items.back()); - m_menu_items.back().signal_activate().connect( - sigc::bind( - sigc::mem_fun(m_html, &html_widget::open_url), - litehtml::string(url.url))); + std::string action; + action += "app.open_url('"; + action += url.url; + action += "')"; + menu_model->append(url.name, action); } - m_menu_bookmarks.show_all(); - m_header.pack_end(m_tools_button); - m_tools_button.set_popup(m_menu_tools); - m_tools_button.show(); - m_tools_button.set_image_from_icon_name("preferences-system-symbolic", Gtk::ICON_SIZE_BUTTON); + auto action = Gio::SimpleAction::create("open_url", Glib::VariantType("s")); + action->signal_activate().connect([this](const Glib::VariantBase& parameter) { + auto value = parameter.get_dynamic(); + m_html.open_url(value); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + m_bookmarks_popover.set_menu_model(menu_model); + m_bookmarks_popover.set_has_arrow(true); + m_bookmarks_popover.set_parent(m_bookmarks_button); + + right_box->append(m_bookmarks_button); + mk_button(m_bookmarks_button, "Bookmarks", "user-bookmarks-symbolic"); + m_bookmarks_button.signal_clicked().connect( [this]() { m_bookmarks_popover.popup(); } ); + + // Creating tools popover + menu_model = Gio::Menu::create(); + auto section_render = Gio::Menu::create(); + auto section_draw = Gio::Menu::create(); + auto section_other = Gio::Menu::create(); + + section_render->append("Single Render", "app.test_render(1)"); + section_render->append("Render 10 Times", "app.test_render(10)"); + section_render->append("Render 100 Times", "app.test_render(100)"); + section_draw->append("Single Draw", "app.test_draw(1)"); + section_draw->append("Draw 10 Times", "app.test_draw(10)"); + section_draw->append("Draw 100 Times", "app.test_draw(100)"); + section_other->append("Dump parsed HTML", "app.dump"); + + menu_model->append_section(section_render); + menu_model->append_section(section_draw); + menu_model->append_section(section_other); + + m_tools_popover.set_menu_model(menu_model); + m_tools_popover.set_has_arrow(true); + m_tools_popover.set_parent(m_tools_button); + + action = Gio::SimpleAction::create("test_render", Glib::VariantType("i")); + action->signal_activate().connect([this](const Glib::VariantBase& parameter) { + auto value = parameter.get_dynamic(); + on_render_measure(value); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + action = Gio::SimpleAction::create("test_draw", Glib::VariantType("i")); + action->signal_activate().connect([this](const Glib::VariantBase& parameter) { + auto value = parameter.get_dynamic(); + on_draw_measure(value); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + action = Gio::SimpleAction::create("dump"); + action->signal_activate().connect([this](const Glib::VariantBase& /* parameter */) { + on_dump(); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + right_box->append(m_tools_button); + mk_button(m_tools_button, "Tools", "preferences-system-symbolic"); + m_tools_button.signal_clicked().connect( [this]() { m_tools_popover.popup(); } ); + + auto win_ctls = Gtk::make_managed(Gtk::PackType::END); + right_box->append(*win_ctls); + + title_box->append(*right_box); + + title_box->set_halign(Gtk::Align::FILL); + title_box->set_hexpand(true); + m_header.set_title_widget(*title_box); + set_titlebar(m_header); - m_header.pack_end(m_bookmarks_button); - m_bookmarks_button.set_popup(m_menu_bookmarks); - m_bookmarks_button.show(); - m_bookmarks_button.set_image_from_icon_name("user-bookmarks-symbolic", Gtk::ICON_SIZE_BUTTON); + m_vbox.append(m_html); + m_html.set_expand(true); + m_html.signal_set_address().connect( sigc::mem_fun(*this, &browser_window::set_address) ); + m_html.signal_update_state().connect( sigc::mem_fun(*this, &browser_window::update_buttons) ); - m_go_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_go_clicked) ); - m_go_button.set_margin_end(32); - - m_header.pack_end(m_go_button); - m_go_button.show(); - m_go_button.set_image_from_icon_name("media-playback-start-symbolic", Gtk::ICON_SIZE_BUTTON); - - m_menu_tools.set_halign(Gtk::ALIGN_END); - m_menu_tools.append(m_tools_render1); - m_menu_tools.append(m_tools_render10); - m_menu_tools.append(m_tools_render100); - m_menu_tools.append(m_tools_draw1); - m_menu_tools.append(m_tools_draw10); - m_menu_tools.append(m_tools_draw100); - m_menu_tools.append(m_tools_dump); - - m_menu_tools.show_all(); - - m_tools_render1.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_render_measure), - 1)); - m_tools_render10.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_render_measure), - 10)); - m_tools_render100.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_render_measure), - 100)); - - m_tools_draw1.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_draw_measure), - 1)); - m_tools_draw10.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_draw_measure), - 10)); - m_tools_draw100.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_draw_measure), - 100)); - - m_tools_dump.signal_activate().connect( - sigc::mem_fun(*this, &browser_window::on_dump)); - - m_vbox.pack_start(m_scrolled_wnd, Gtk::PACK_EXPAND_WIDGET); - m_scrolled_wnd.show(); - - m_scrolled_wnd.add(m_html); - m_html.show(); - - signal_delete_event().connect(sigc::mem_fun(m_html, &html_widget::on_close), false); + signal_close_request().connect(sigc::mem_fun(m_html, &html_widget::on_close), false); set_default_size(1280, 720); @@ -159,30 +179,20 @@ browser_window::browser_window(const std::string& url) : m_html.open_url(url); } - update_buttons(); + update_buttons(0); } browser_window::~browser_window() { - + m_bookmarks_popover.unparent(); + m_tools_popover.unparent(); } void browser_window::on_go_clicked() { litehtml::string url = m_address_bar.get_text(); m_html.open_url(url); -} - -bool browser_window::on_address_key_press(GdkEventKey* event) -{ - if(event->keyval == GDK_KEY_Return) - { - m_address_bar.select_region(0, -1); - on_go_clicked(); - return true; - } - - return false; + m_html.grab_focus(); } void browser_window::on_forward_clicked() @@ -195,29 +205,17 @@ void browser_window::on_back_clicked() m_html.go_back(); } -void browser_window::update_buttons() +void browser_window::update_buttons(uint32_t) { uint32_t state = m_html.get_state(); if((m_prev_state & page_state_has_back) != (state & page_state_has_back)) { - if (state & page_state_has_back) - { - m_back_button.set_state(Gtk::STATE_NORMAL); - } else - { - m_back_button.set_state(Gtk::STATE_INSENSITIVE); - } + m_back_button.set_sensitive(state & page_state_has_back); } if((m_prev_state & page_state_has_forward) != (state & page_state_has_forward)) { - if (state & page_state_has_forward) - { - m_forward_button.set_state(Gtk::STATE_NORMAL); - } else - { - m_forward_button.set_state(Gtk::STATE_INSENSITIVE); - } + m_forward_button.set_sensitive(state & page_state_has_forward); } if((m_prev_state & page_state_downloading) != (state & page_state_downloading)) { @@ -240,11 +238,11 @@ void browser_window::on_render_measure(int number) message << time << " ms for " << number << " times rendering"; - m_pDialog.reset(new Gtk::MessageDialog(*this, message.str(), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true)); - - m_pDialog->signal_response().connect( - sigc::hide(sigc::mem_fun(*m_pDialog, &Gtk::Widget::hide))); - m_pDialog->show(); + auto dialog = Gtk::AlertDialog::create(); + dialog->set_message(message.str()); + dialog->set_buttons({"OK"}); + dialog->set_modal(true); + dialog->show(*this); } void browser_window::on_draw_measure(int number) @@ -255,16 +253,23 @@ void browser_window::on_draw_measure(int number) message << time << " ms for " << number << " times measure"; - m_pDialog.reset(new Gtk::MessageDialog(*this, message.str(), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true)); - - m_pDialog->signal_response().connect( - sigc::hide(sigc::mem_fun(*m_pDialog, &Gtk::Widget::hide))); - m_pDialog->show(); + auto dialog = Gtk::AlertDialog::create(); + dialog->set_message(message.str()); + dialog->set_buttons({"OK"}); + dialog->set_modal(true); + dialog->show(*this); } void browser_window::on_dump() { - m_html.dump("/tmp/litehtml-dump.txt"); + html_dumper cout("/tmp/litehtml-dump.txt"); + m_html.dump(cout); + auto dialog = Gtk::AlertDialog::create(); + dialog->set_message("File is saved"); + dialog->set_detail("The parsed HTML tree was saved into he file: /tmp/litehtml-dump.txt"); + dialog->set_buttons({"Close"}); + dialog->set_modal(true); + dialog->show(*this); } void browser_window::on_stop_reload_clicked() diff --git a/src/browser_wnd.h b/src/browser_wnd.h index 331b4a3..690576d 100644 --- a/src/browser_wnd.h +++ b/src/browser_wnd.h @@ -8,17 +8,15 @@ class browser_window : public Gtk::Window { public: - browser_window(const std::string& url); - virtual ~browser_window(); + browser_window(const Glib::RefPtr& app, const std::string& url); + ~browser_window() override; - void update_buttons(); + void update_buttons(uint32_t); void set_address(const std::string& text) { m_address_bar.set_text(text); } - Gtk::ScrolledWindow* get_scrolled() { return &m_scrolled_wnd; } - private: void on_go_clicked(); void on_forward_clicked(); @@ -27,11 +25,10 @@ class browser_window : public Gtk::Window void on_back_clicked(); void on_render_measure(int number); void on_draw_measure(int number); - bool on_address_key_press(GdkEventKey* event); void on_dump(); protected: - uint32_t m_prev_state; + uint32_t m_prev_state; html_widget m_html; Gtk::Entry m_address_bar; Gtk::Button m_go_button; @@ -39,23 +36,12 @@ class browser_window : public Gtk::Window Gtk::Button m_back_button; Gtk::Button m_stop_reload_button; Gtk::Button m_home_button; - Gtk::MenuButton m_bookmarks_button; - Gtk::MenuButton m_tools_button; - Gtk::VBox m_vbox; + Gtk::Button m_bookmarks_button; + Gtk::Button m_tools_button; + Gtk::Box m_vbox {Gtk::Orientation::HORIZONTAL}; Gtk::HeaderBar m_header; - Gtk::ScrolledWindow m_scrolled_wnd; - - Gtk::Menu m_menu_bookmarks; - std::vector m_menu_items; - - Gtk::Menu m_menu_tools; - Gtk::MenuItem m_tools_render1; - Gtk::MenuItem m_tools_render10; - Gtk::MenuItem m_tools_render100; - Gtk::MenuItem m_tools_draw1; - Gtk::MenuItem m_tools_draw10; - Gtk::MenuItem m_tools_draw100; - Gtk::MenuItem m_tools_dump; + Gtk::PopoverMenu m_bookmarks_popover; + Gtk::PopoverMenu m_tools_popover; std::unique_ptr m_pDialog; diff --git a/src/globals.h b/src/globals.h deleted file mode 100644 index 83a9e05..0000000 --- a/src/globals.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#if defined __APPLE__ || __OpenBSD__ -#include -#else -#include -#endif -#include -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include -#include "../litehtml/include/litehtml.h" -#include -#include -#include -#include diff --git a/src/html_host.h b/src/html_host.h deleted file mode 100644 index 3e60843..0000000 --- a/src/html_host.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef LITEBROWSER_HTML_HOST_H -#define LITEBROWSER_HTML_HOST_H - -#include - -namespace litebrowser -{ - class html_host_interface - { - public: - enum q_action - { - queue_action_redraw, - queue_action_render, - queue_action_update_state, - }; - - virtual void open_url(const std::string& url) = 0; - virtual void open_page(const litehtml::string& url, const litehtml::string& hash) = 0; - virtual void update_cursor() = 0; - virtual void scroll_to(int x, int y) = 0; - virtual void get_client_rect(litehtml::position& client) const = 0; - virtual void set_caption(const char* caption) = 0; - virtual void redraw_rect(int x, int y, int width, int height) = 0; - virtual void redraw() = 0; - virtual void render() = 0; - virtual int get_render_width() = 0; - virtual void on_page_loaded() = 0; - virtual void queue_action(q_action action) = 0; - }; -} - -#endif //LITEBROWSER_HTML_HOST_H diff --git a/src/html_widget.cpp b/src/html_widget.cpp deleted file mode 100644 index b1107c6..0000000 --- a/src/html_widget.cpp +++ /dev/null @@ -1,470 +0,0 @@ -#include "globals.h" -#include "html_widget.h" -#include "browser_wnd.h" -#include -#include - -html_widget::html_widget(browser_window* browser) -{ - m_browser = browser; - m_rendered_width = 0; - add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); -} - -html_widget::~html_widget() -{ - -} - -bool html_widget::on_draw(const Cairo::RefPtr& cr) -{ - litehtml::position pos; - - GdkRectangle rect; - gdk_cairo_get_clip_rectangle(cr->cobj(), &rect); - - pos.width = rect.width; - pos.height = rect.height; - pos.x = rect.x; - pos.y = rect.y; - - cr->rectangle(0, 0, get_allocated_width(), get_allocated_height()); - cr->set_source_rgb(1, 1, 1); - cr->fill(); - - { - auto page = current_page(); - if (page) - { - page->draw((litehtml::uint_ptr) cr->cobj(), 0, 0, &pos); - } - } - - return true; -} - -void html_widget::get_client_rect(litehtml::position& client) const -{ - client.width = get_parent()->get_allocated_width(); - client.height = get_parent()->get_allocated_height(); - client.x = 0; - client.y = 0; -} - -void html_widget::set_caption(const char* caption) -{ - if(get_parent_window()) - { - get_parent_window()->set_title(caption); - } -} - -Gtk::Allocation html_widget::get_parent_allocation() -{ - Gtk::Container* parent = get_parent(); - return parent->get_allocation(); -} - -void html_widget::open_page(const litehtml::string& url, const litehtml::string& hash) -{ - { - std::lock_guard lock(m_page_mutex); - if (m_current_page) - { - m_current_page->stop_loading(); - } - m_next_page = std::make_shared(this, 10); - m_next_page->open(url, hash); - } - m_browser->set_address(url); - m_browser->update_buttons(); -} - -void html_widget::scroll_to(int x, int y) -{ - auto vadj = m_browser->get_scrolled()->get_vadjustment(); - auto hadj = m_browser->get_scrolled()->get_hadjustment(); - vadj->set_value(vadj->get_lower() + y); - hadj->set_value(hadj->get_lower() + x); -} - -void html_widget::on_parent_size_allocate(Gtk::Allocation allocation) -{ - std::shared_ptr page = current_page(); - if(page && m_rendered_width != allocation.get_width()) - { - m_rendered_width = allocation.get_width(); - page->media_changed(); - render(); - } -} - -void html_widget::on_parent_changed(Gtk::Widget* /*previous_parent*/) -{ - Gtk::Widget* viewport = get_parent(); - if(viewport) - { - viewport->signal_size_allocate().connect(sigc::mem_fun(*this, &html_widget::on_parent_size_allocate)); - } -} - -bool html_widget::on_button_press_event(GdkEventButton *event) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->on_lbutton_down((int) event->x, (int) event->y, (int) event->x, (int) event->y); - } - return true; -} - -bool html_widget::on_button_release_event(GdkEventButton *event) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->on_lbutton_up((int) event->x, (int) event->y, (int) event->x, (int) event->y); - } - return true; -} - -bool html_widget::on_motion_notify_event(GdkEventMotion *event) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->on_mouse_over((int) event->x, (int) event->y, (int) event->x, (int) event->y); - } - return true; -} - -void html_widget::update_cursor() -{ - Gdk::CursorType cursType = Gdk::ARROW; - std::shared_ptr page = current_page(); - if(page) - { - if (page->get_cursor() == "pointer") - { - cursType = Gdk::HAND2; - } - } - if(cursType == Gdk::ARROW) - { - get_window()->set_cursor(); - } else - { - get_window()->set_cursor( Gdk::Cursor::create(cursType) ); - } -} - -long html_widget::draw_measure(int number) -{ - std::shared_ptr page = current_page(); - - if(page) - { - auto vadj = m_browser->get_scrolled()->get_vadjustment(); - auto hadj = m_browser->get_scrolled()->get_hadjustment(); - - int width = (int) hadj->get_page_size(); - int height = (int) vadj->get_page_size(); - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - auto image = (unsigned char *) g_malloc(stride * height); - - cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, - stride); - cairo_t *cr = cairo_create(surface); - - litehtml::position pos; - pos.width = width; - pos.height = height; - pos.x = 0; - pos.y = 0; - - int x = (int) (hadj->get_value() - hadj->get_lower()); - int y = (int) (vadj->get_value() - vadj->get_lower()); - - cairo_rectangle(cr, 0, 0, width, height); - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_paint(cr); - page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); - cairo_surface_write_to_png(surface, "/tmp/litebrowser.png"); - - auto t1 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < number; i++) - { - page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); - } - auto t2 = std::chrono::high_resolution_clock::now(); - - cairo_destroy(cr); - cairo_surface_destroy(surface); - g_free(image); - - return (std::chrono::duration_cast(t2 - t1)).count(); - } - return 0; -} - -long html_widget::render_measure(int number) -{ - std::shared_ptr page = current_page(); - - if(page) - { - auto t1 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < number; i++) - { - page->render(m_rendered_width); - } - auto t2 = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(t2 - t1)).count(); - } - return -1; -} - -void html_widget::on_size_allocate(Gtk::Allocation& allocation) -{ - Gtk::DrawingArea::on_size_allocate(allocation); - std::shared_ptr page = current_page(); - - if(page) - { - page->show_hash_and_reset(); - } -} - -static int action_redraw(void* data) -{ - auto ctl = (Gtk::Widget*) data; - ctl->queue_draw(); - return FALSE; -} - -static int action_render(void* data) -{ - auto ctl = (html_widget*) data; - ctl->render(); - return FALSE; -} - -static int action_update_state(void* data) -{ - auto ctl = (html_widget*) data; - ctl->browser()->update_buttons(); - return FALSE; -} - -void html_widget::redraw() -{ - g_idle_add(action_redraw, this); -} - -void html_widget::redraw_rect(int x, int y, int width, int height) -{ - queue_draw_area(x, y, width, height); -} - -int html_widget::get_render_width() -{ - return get_parent_allocation().get_width(); -} - -void html_widget::on_page_loaded() -{ - std::string url; - { - std::lock_guard lock(m_page_mutex); - m_current_page = m_next_page; - m_next_page = nullptr; - url = m_current_page->url(); - set_size_request(m_current_page->width(), m_current_page->height()); - } - scroll_to(0, 0); - redraw(); - m_browser->update_buttons(); - m_browser->set_address(url); -} - -void html_widget::show_hash(const std::string &hash) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->show_hash(hash); - } -} - -void html_widget::dump(const std::string &file_name) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->dump(file_name); - } -} - -void html_widget::open_url(const std::string &url) -{ - std::string hash; - std::string s_url = url; - - m_browser->set_address(url); - - std::string::size_type hash_pos = s_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) - { - hash = s_url.substr(hash_pos + 1); - s_url.erase(hash_pos); - } - - bool open_hash_only = false; - bool reload = false; - - auto current_url = m_history.current(); - hash_pos = current_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) - { - current_url.erase(hash_pos); - } - - if(!current_url.empty()) - { - if(m_history.current() != url) - { - if (current_url == s_url) - { - open_hash_only = true; - } - } else - { - reload = true; - } - } - if(!open_hash_only) - { - open_page(url, hash); - } else - { - show_hash(hash); - } - if(!reload) - { - m_history.url_opened(url); - } - m_browser->update_buttons(); -} - -void html_widget::render() -{ - std::shared_ptr page = current_page(); - if(page) - { - page->render(m_rendered_width); - set_size_request(page->width(), page->height()); - queue_draw(); - } -} - -bool html_widget::on_close(GdkEventAny* /*event*/) -{ - if(m_current_page) - { - m_current_page->stop_loading(); - } - if(m_next_page) - { - m_next_page->stop_loading(); - } - return false; -} - -void html_widget::go_forward() -{ - std::string url; - if(m_history.forward(url)) - { - open_url(url); - } -} - -void html_widget::go_back() -{ - std::string url; - if(m_history.back(url)) - { - open_url(url); - } -} - -uint32_t html_widget::get_state() -{ - uint32_t ret = 0; - std::string url; - if(m_history.back(url)) - { - ret |= page_state_has_back; - } - if(m_history.forward(url)) - { - ret |= page_state_has_forward; - } - { - std::lock_guard lock(m_page_mutex); - if(m_next_page) - { - ret |= page_state_downloading; - } - } - if(!(ret & page_state_downloading)) - { - std::shared_ptr page = current_page(); - if(page) - { - if(page->is_downloading()) - { - ret |= page_state_downloading; - } - } - } - return ret; -} - -void html_widget::queue_action(litebrowser::html_host_interface::q_action action) -{ - switch (action) - { - case queue_action_redraw: - g_idle_add(action_redraw, this); - break; - case queue_action_render: - g_idle_add(action_render, this); - break; - case queue_action_update_state: - g_idle_add(action_update_state, this); - break; - } -} - -void html_widget::stop_download() -{ - std::lock_guard lock(m_page_mutex); - if(m_next_page) - { - m_next_page->stop_loading(); - } else if (m_current_page) - { - m_current_page->stop_loading(); - } -} - -void html_widget::reload() -{ - std::shared_ptr page = current_page(); - if(page) - { - open_url(page->url()); - } -} diff --git a/src/html_widget.h b/src/html_widget.h deleted file mode 100644 index d8b5017..0000000 --- a/src/html_widget.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include "html_host.h" -#include "web_page.h" -#include "http_requests_pool.h" -#include "web_history.h" - -class browser_window; - -enum page_state -{ - page_state_has_back = 0x01, - page_state_has_forward = 0x02, - page_state_downloading = 0x04, -}; - -class html_widget : public Gtk::DrawingArea, - public litebrowser::html_host_interface -{ - int m_rendered_width; - browser_window* m_browser; - std::mutex m_page_mutex; - std::shared_ptr m_current_page; - std::shared_ptr m_next_page; - web_history m_history; -public: - explicit html_widget(browser_window* browser); - ~html_widget() override; - - void open_page(const litehtml::string& url, const litehtml::string& hash) override; - void update_cursor() override; - void on_parent_size_allocate(Gtk::Allocation allocation); - void on_size_allocate(Gtk::Allocation& allocation) override; - void redraw() override; - void redraw_rect(int x, int y, int width, int height) override; - int get_render_width() override; - void on_page_loaded() override; - void dump(const litehtml::string& file_name); - void open_url(const std::string& url) override; - void render() override; - void queue_action(litebrowser::html_host_interface::q_action action) override; - void go_forward(); - void go_back(); - uint32_t get_state(); - void stop_download(); - void reload(); - browser_window* browser() const { return m_browser; } - - long render_measure(int number); - long draw_measure(int number); - void show_hash(const std::string& hash); - bool on_close(GdkEventAny* event); - -protected: - bool on_draw(const Cairo::RefPtr& cr) override; - void scroll_to(int x, int y) override; - - void get_client_rect(litehtml::position& client) const override; - void set_caption(const char* caption) override; - - bool on_button_press_event(GdkEventButton* event) override; - bool on_button_release_event(GdkEventButton* event) override; - bool on_motion_notify_event(GdkEventMotion* event) override; - - void on_parent_changed(Gtk::Widget* previous_parent) override; - -private: - std::shared_ptr current_page() - { - std::lock_guard lock(m_page_mutex); - return m_current_page; - } - Gtk::Allocation get_parent_allocation(); -}; diff --git a/src/http_request.cpp b/src/http_request.cpp deleted file mode 100644 index d83b2c0..0000000 --- a/src/http_request.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include "http_request.h" - -bool litebrowser::http_request::open() -{ - m_canceled = false; - CURL* curl = curl_easy_init(); - - if(m_url[0] == '/') - { - std::set chars_to_escape = {' ', ':'}; - std::stringstream new_url; - new_url << "file://"; - for(auto ch : m_url) - { - if( chars_to_escape.find(ch) != chars_to_escape.end()) - { - new_url << '%' << std::hex << (uint32_t) ch; - } else - { - new_url << ch; - } - } - m_url = new_url.str(); - } - - curl_easy_setopt(curl, CURLOPT_URL, m_url.c_str()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) 1); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, (long) 10); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_request::write_function); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); - - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, (long) 0); - - curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, (long) 512); - curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, (long) 30); - - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, http_request::progress_function); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long) 0); - - char errMessage[1024]; - errMessage[0] = 0; - - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errMessage); - curl_easy_setopt(curl, CURLOPT_FAILONERROR, (long) 1); - if(!m_user_agent.empty()) - { - curl_easy_setopt(curl, CURLOPT_USERAGENT, m_user_agent.c_str()); - } else - { - curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0"); - } - - curl_slist* slist = nullptr; - if (!m_headers.empty()) - { - for (auto& item : m_headers) - { - slist = curl_slist_append(slist, item.c_str()); - } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); - } - - std::string post_str; - if (!m_form_data.empty()) - { - for (const auto& field : m_form_data) - { - if (!post_str.empty()) post_str += "&"; - post_str += field.first; - post_str += "="; - post_str += field.second; - } - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str.c_str()); - } - - m_session = curl; - CURLcode res = curl_easy_perform(curl); - - long httpCode = 0; - - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); - - m_session = nullptr; - curl_easy_cleanup(curl); - - if (slist) - { - curl_slist_free_all(slist); - } - m_form_data.clear(); - m_headers.clear(); - - if(m_on_finish) - { - m_on_finish(httpCode, res, errMessage, m_url); - } - return true; -} - -size_t litebrowser::http_request::write_function(void *ptr, size_t size, size_t nmemb, void *stream) -{ - auto pThis = (http_request*) stream; - - pThis->m_downloaded += size * nmemb; - if(!pThis->m_total) - { - curl_off_t sz = 0; - curl_easy_getinfo((CURL*) pThis->m_session, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &sz); - pThis->m_total = (size_t) sz; - } - if(pThis->m_on_data) - { - pThis->m_on_data(ptr, size * nmemb, pThis->m_downloaded, pThis->m_total); - } - - return size * nmemb; -} - -int litebrowser::http_request::progress_function(void* clientp, curl_off_t /*dltotal*/, curl_off_t /*dlnow*/, curl_off_t /*ultotal*/, curl_off_t /*ulnow*/) -{ - auto pThis = (http_request*) clientp; - return pThis->m_canceled ? 1 : 0; -} - diff --git a/src/http_request.h b/src/http_request.h deleted file mode 100644 index 209ad0a..0000000 --- a/src/http_request.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef LITEBROWSER_HTTP_REQUEST_H -#define LITEBROWSER_HTTP_REQUEST_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace litebrowser -{ - class http_requests_pool; - - class http_request : public std::enable_shared_from_this - { - private: - std::function m_on_data; - std::function m_on_finish; - volatile bool m_canceled; - size_t m_downloaded; - size_t m_total; - volatile void* m_session; - std::vector m_headers; - std::vector> m_form_data; - std::string m_user_agent; - std::string m_url; - public: - http_request(std::string url, - std::function cb_on_data, - std::function cb_on_finish) : - m_on_data(std::move(cb_on_data)), - m_on_finish(std::move(cb_on_finish)), - m_canceled(false), - m_downloaded(0), - m_total(0), - m_session(nullptr), - m_url(std::move(url)) - {} - ~http_request() - { - } - - bool open(); - void cancel() - { - m_canceled = true; - } - void add_header(const std::string& header) - { - m_headers.push_back(header); - } - void clear_headers() - { - m_headers.clear(); - } - void add_form_data(const std::string& field, const std::string& value) - { - m_form_data.emplace_back(field, value); - } - void clear_form_data() - { - m_form_data.clear(); - } - const std::string& get_url() { return m_url; } - private: - void thread_proc(); - static size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream ); - static int progress_function(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow ); - }; -} - -#endif //LITEBROWSER_HTTP_REQUEST_H diff --git a/src/http_requests_pool.cpp b/src/http_requests_pool.cpp deleted file mode 100644 index e0e5e0c..0000000 --- a/src/http_requests_pool.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "http_requests_pool.h" - -litebrowser::http_requests_pool::http_requests_pool(int pool_size, const std::function& cb_update_state) : - m_cancel(false), - m_cb_update_state(cb_update_state) -{ - for(int i = 0; i < pool_size; i++) - { - m_threads.emplace_back(std::make_shared(this)); - } -} - -void litebrowser::http_requests_pool::enqueue(const std::string &url, - const std::function& cb_on_data, - const std::function& cb_on_finish) -{ - { - std::unique_lock lock(m_queue_mutex); - auto request = std::make_shared(url, cb_on_data, cb_on_finish); - m_queue.push_back(request); - } - m_mutex_condition.notify_one(); -} - -void litebrowser::http_requests_pool::stop() -{ - { - std::unique_lock lock(m_queue_mutex); - m_cancel = true; - } - m_mutex_condition.notify_all(); - for (auto& active_thread : m_threads) - { - auto request = active_thread->get_request(); - if(request) - { - request->cancel(); - } - } - for (auto& active_thread : m_threads) - { - active_thread->thread().join(); - } - m_threads.clear(); -} - -bool litebrowser::http_requests_pool::is_downloading() -{ - { - std::unique_lock lock(m_queue_mutex); - if(m_cancel) return false; - if(!m_queue.empty()) return true; - } - for (auto& active_thread : m_threads) - { - auto request = active_thread->get_request(); - if(request) - { - return true; - } - } - - return false; -} - -void litebrowser::pool_thread::thread_loop() -{ - while (true) - { - { - std::unique_lock lock(m_pool->m_queue_mutex); - m_pool->m_mutex_condition.wait(lock, [this] - { - return !m_pool->m_queue.empty() || m_pool->m_cancel; - }); - if (m_pool->m_cancel) - { - if(m_pool->m_cb_update_state) - { - m_pool->m_cb_update_state(); - } - return; - } - { - std::unique_lock request_lock(m_request_mutex); - m_request = m_pool->m_queue.front(); - } - m_pool->m_queue.pop_front(); - } - if(m_pool->m_cb_update_state) - { - m_pool->m_cb_update_state(); - } - m_request->open(); - { - std::unique_lock request_lock(m_request_mutex); - m_request = nullptr; - } - if(m_pool->m_cb_update_state) - { - m_pool->m_cb_update_state(); - } - } -} diff --git a/src/http_requests_pool.h b/src/http_requests_pool.h deleted file mode 100644 index 9511c1a..0000000 --- a/src/http_requests_pool.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef LITEBROWSER_HTTP_REQUESTS_POOL_H -#define LITEBROWSER_HTTP_REQUESTS_POOL_H - -#include -#include -#include -#include -#include "http_request.h" - -namespace litebrowser -{ - class http_requests_pool; - - class pool_thread - { - http_requests_pool* m_pool; - std::shared_ptr m_request; - std::mutex m_request_mutex; - std::thread m_thread; - public: - explicit pool_thread(http_requests_pool* pool) : m_pool(pool) - { - m_thread = std::thread(&pool_thread::thread_loop, this); - } - std::shared_ptr get_request() - { - std::unique_lock request_lock(m_request_mutex); - return m_request; - } - std::thread& thread() { return m_thread; } - private: - void thread_loop(); - }; - - class http_requests_pool : public std::enable_shared_from_this - { - friend class pool_thread; - protected: - bool m_cancel; - std::mutex m_queue_mutex; - std::condition_variable m_mutex_condition; - std::list> m_queue; - std::vector> m_threads; - std::function m_queue_empty_cb; - std::function m_cb_update_state; - public: - explicit http_requests_pool(int pool_size, const std::function& cb_update_state); - ~http_requests_pool() - { - stop(); - } - - void enqueue(const std::string& url, - const std::function& cb_on_data, - const std::function& cb_on_finish); - void stop(); - bool is_downloading(); - private: - }; -} - -#endif //LITEBROWSER_HTTP_REQUESTS_POOL_H diff --git a/src/main.cpp b/src/main.cpp index de91090..3117660 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,23 +1,28 @@ -#include "globals.h" #include "browser_wnd.h" +#ifdef FOR_TESTING +#include "fonts.h" +#endif + +#ifdef LIBADWAITA_AVAILABLE +#include +#endif -int on_cmd(const Glib::RefPtr &, Glib::RefPtr &app) -{ - app->activate(); - return 0; -} int main (int argc, char *argv[]) { - Glib::RefPtr app = Gtk::Application::create(argc, argv, "litehtml.browser", Gio::APPLICATION_HANDLES_COMMAND_LINE); - app->signal_command_line().connect( - sigc::bind(sigc::ptr_fun(on_cmd), app), false); +#ifdef LIBADWAITA_AVAILABLE + adw_init(); +#endif +#ifdef FOR_TESTING + prepare_fonts_for_testing(); +#endif std::string url; if(argc > 1) { url = argv[1]; } - browser_window win(url); - return app->run(win); + // Open the main window + auto app = Gtk::Application::create("litehtml.testsuite"); + return app->make_window_and_run(argc, argv, app, url); } diff --git a/src/web_history.cpp b/src/web_history.cpp deleted file mode 100755 index f75d81e..0000000 --- a/src/web_history.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "globals.h" -#include "web_history.h" - - -web_history::web_history() -{ - m_current_item = 0; -} - -web_history::~web_history() -{ - -} - -void web_history::url_opened( const std::string& url ) -{ - if(!m_items.empty()) - { - if(m_current_item != m_items.size() - 1) - { - if(m_current_item > 0 && m_items[m_current_item - 1] == url) - { - m_current_item--; - } else if(m_current_item < m_items.size() - 1 && m_items[m_current_item + 1] == url) - { - m_current_item++; - } else - { - m_items.erase(m_items.begin() + m_current_item + 1, m_items.end()); - m_items.push_back(url); - m_current_item = m_items.size() - 1; - } - } else - { - if(m_current_item > 0 && m_items[m_current_item - 1] == url) - { - m_current_item--; - } else - { - m_items.push_back(url); - m_current_item = m_items.size() - 1; - } - } - } else - { - m_items.push_back(url); - m_current_item = m_items.size() - 1; - } -} - -bool web_history::back( std::string& url ) -{ - if(m_items.empty()) return false; - - if(m_current_item > 0) - { - url = m_items[m_current_item - 1]; - return true; - } - return false; -} - -bool web_history::forward( std::string& url ) -{ - if(m_items.empty()) return false; - - if(m_current_item < m_items.size() - 1) - { - url = m_items[m_current_item + 1]; - return true; - } - return false; -} diff --git a/src/web_history.h b/src/web_history.h deleted file mode 100755 index 22c4884..0000000 --- a/src/web_history.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -typedef std::vector string_vector; - -class web_history -{ - string_vector m_items; - string_vector::size_type m_current_item; -public: - web_history(); - virtual ~web_history(); - - void url_opened(const std::string& url); - bool back(std::string& url); - bool forward(std::string& url); - std::string current() const - { - if(!m_items.empty() && m_current_item < m_items.size()) - { - return m_items[m_current_item]; - } - return ""; - } -}; \ No newline at end of file diff --git a/src/web_page.cpp b/src/web_page.cpp deleted file mode 100644 index 2af56f5..0000000 --- a/src/web_page.cpp +++ /dev/null @@ -1,344 +0,0 @@ -#include -#include -#include "web_page.h" -#include "html_dumper.h" - -void litebrowser::text_file::on_data(void* data, size_t len, size_t /*downloaded*/, size_t /*total*/) -{ - stream.write((const char*) data, (std::streamsize) len); -} - -void litebrowser::text_file::on_page_downloaded(u_int32_t http_status, - u_int32_t err_code, - const std::string &/*err_text*/) -{ - if(err_code == 0 && (http_status == 200 || http_status == 0)) - { - data_ready = true; - } else - { - data_ready = false; - } - wait_mutex.unlock(); -} - -void litebrowser::web_page::open(const std::string &url, const std::string &hash) -{ - m_url = url; - m_base_url = url; - m_hash = hash; - - auto data = std::make_shared(); - auto cb_on_data = std::bind(&text_file::on_data, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - auto cb_on_finish = std::bind(&web_page::on_page_downloaded, this, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - http_request(m_url, cb_on_data, cb_on_finish); -} - -void litebrowser::web_page::get_client_rect(litehtml::position& client) const -{ - m_html_host->get_client_rect(client); -} - -void litebrowser::web_page::on_anchor_click(const char* url, const litehtml::element::ptr& /*el*/) -{ - if(url) - { - make_url(url, m_base_url.c_str(), m_clicked_url); - } -} - -void litebrowser::web_page::set_cursor(const char* cursor) -{ - if(cursor) - { - if(m_cursor != cursor) - { - m_cursor = cursor; - m_html_host->update_cursor(); - } - } -} - -void litebrowser::web_page::import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) -{ - std::string css_url; - make_url(url.c_str(), baseurl.c_str(), css_url); - - auto data = std::make_shared(); - auto cb_on_data = std::bind(&text_file::on_data, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - auto cb_on_finish = std::bind(&text_file::on_page_downloaded, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - http_request(css_url, cb_on_data, cb_on_finish); - data->wait(); - text = data->str(); - if(!text.empty()) - { - baseurl = css_url; - } -} - -void litebrowser::web_page::set_caption(const char* caption) -{ - m_html_host->set_caption(caption); -} - -void litebrowser::web_page::set_base_url(const char* base_url) -{ - if(base_url) - { - m_base_url = litehtml::resolve(litehtml::url(m_url), litehtml::url(base_url)).str(); - } else - { - m_base_url = m_url; - } -} - -cairo_surface_t* litebrowser::web_page::get_image(const std::string& url) -{ - return m_images.get_image(url); -} - -void litebrowser::web_page::show_hash(const litehtml::string& hash) -{ - std::unique_lock html_lock(m_html_mutex); - if(hash.empty() || !m_html) - { - m_html_host->scroll_to(0, 0); - } else - { - std::string selector = "#" + hash; - litehtml::element::ptr el = m_html->root()->select_one(selector); - if (!el) - { - selector = "[name=" + hash + "]"; - el = m_html->root()->select_one(selector); - } - if (el) - { - litehtml::position pos = el->get_placement(); - m_html_host->scroll_to(0, pos.top()); - } - } -} - -void litebrowser::web_page::make_url(const char* url, const char* basepath, litehtml::string& out) -{ - if(!basepath || !basepath[0]) - { - if(!m_base_url.empty()) - { - out = litehtml::resolve(litehtml::url(m_base_url), litehtml::url(url)).str(); - } else - { - out = url; - } - } else - { - out = litehtml::resolve(litehtml::url(basepath), litehtml::url(url)).str(); - } -} - -void litebrowser::web_page::on_mouse_over(int x, int y, int client_x, int client_y) -{ - std::unique_lock html_lock(m_html_mutex); - if(m_html) - { - litehtml::position::vector redraw_boxes; - if(m_html->on_mouse_over(x, y, client_x, client_y, redraw_boxes)) - { - for(auto& pos : redraw_boxes) - { - m_html_host->redraw_rect(pos.x, pos.y, pos.width, pos.height); - } - } - } -} - -void litebrowser::web_page::on_lbutton_down(int x, int y, int client_x, int client_y) -{ - std::unique_lock html_lock(m_html_mutex); - if(m_html) - { - litehtml::position::vector redraw_boxes; - if(m_html->on_lbutton_down(x, y, client_x, client_y, redraw_boxes)) - { - for(auto& pos : redraw_boxes) - { - m_html_host->redraw_rect(pos.x, pos.y, pos.width, pos.height); - } - } - } -} - -void litebrowser::web_page::on_lbutton_up(int x, int y, int client_x, int client_y) -{ - if(m_html) - { - { - std::unique_lock html_lock(m_html_mutex); - - litehtml::position::vector redraw_boxes; - m_clicked_url.clear(); - if (m_html->on_lbutton_up(x, y, client_x, client_y, redraw_boxes)) - { - for (auto &pos: redraw_boxes) - { - m_html_host->redraw_rect(pos.x, pos.y, pos.width, pos.height); - } - } - } - if(!m_clicked_url.empty()) - { - m_html_host->open_url(m_clicked_url); - } - } -} - -void litebrowser::web_page::on_page_downloaded(std::shared_ptr data, - u_int32_t /*http_status*/, - u_int32_t err_code, - const std::string &err_text, const std::string& url) -{ - if(err_code == 0) - { - m_url = url; - std::unique_lock html_lock(m_html_mutex); - data->set_ready(); - m_html = litehtml::document::createFromString(data->str().c_str(), this); - } else - { - std::unique_lock html_lock(m_html_mutex); - std::stringstream ss; - ss << "

Impossible to load page

" << std::endl; - ss << "

Error #" << err_code << ": " << err_text << "

" << std::endl; - m_html = litehtml::document::createFromString(ss.str().c_str(), this); - } - if (m_html) - { - std::unique_lock html_lock(m_html_mutex); - int render_width = m_html_host->get_render_width(); - m_html->render(render_width); - } - m_html_host->on_page_loaded(); -} - -void litebrowser::web_page::dump(const litehtml::string& file_name) -{ - if(m_html) - { - std::unique_lock html_lock(m_html_mutex); - - html_dumper dumper(file_name); - m_html->dump(dumper); - dumper.print(); - } -} - -void litebrowser::web_page::on_image_downloaded(std::shared_ptr data, - u_int32_t http_status, - u_int32_t err_code, - const std::string &/*err_text*/, - const std::string& /*url*/) -{ - data->close(); - if(!data->path().empty() && !err_code && (http_status == 200 || http_status == 0)) - { - Glib::RefPtr ptr; - - try - { - ptr = Gdk::Pixbuf::create_from_file(data->path()); - } catch (...) { } - if(ptr) - { - { - m_images.add_image(data->url(), surface_from_pixbuf(ptr)); - } - if(data->redraw_only()) - { - m_html_host->queue_action(html_host_interface::queue_action_redraw); - } else - { - m_html_host->queue_action(html_host_interface::queue_action_render); - } - } - } - unlink(data->path().c_str()); -} - -void litebrowser::web_page::load_image(const char *src, const char *baseurl, bool redraw_on_ready) -{ - std::string url; - make_url(src, baseurl, url); - - if(m_images.reserve(url)) - { - auto data = std::make_shared(url, redraw_on_ready); - auto cb_on_data = std::bind(&image_file::on_data, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - auto cb_on_finish = std::bind(&web_page::on_image_downloaded, this, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - http_request(url, cb_on_data, cb_on_finish); - } -} - -cairo_surface_t* litebrowser::web_page::surface_from_pixbuf(const Glib::RefPtr& bmp) -{ - cairo_surface_t* ret; - - if(bmp->get_has_alpha()) - { - ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bmp->get_width(), bmp->get_height()); - } else - { - ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, bmp->get_width(), bmp->get_height()); - } - - Cairo::RefPtr surface(new Cairo::Surface(ret, false)); - Cairo::RefPtr ctx = Cairo::Context::create(surface); - Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0); - ctx->paint(); - - return ret; -} - -void litebrowser::web_page::http_request(const std::string &url, - const std::function &cb_on_data, - const std::function &cb_on_finish) -{ - m_requests_pool.enqueue(url, cb_on_data, cb_on_finish); -} - -void litebrowser::web_page::on_pool_update_state() -{ - m_html_host->queue_action(html_host_interface::queue_action_update_state); -} - -double litebrowser::web_page::get_screen_dpi() const -{ - GdkScreen* screen = gdk_screen_get_default(); - return gdk_screen_get_resolution(screen); -} - -////////////////////////////////////////////////////////// - -litebrowser::image_file::image_file(std::string url, bool redraw_on_ready) : - m_fd(-1), - m_url(std::move(url)), - m_redraw_on_ready(redraw_on_ready) -{ -} - -void litebrowser::image_file::on_data(void *data, size_t len, size_t /*downloaded*/, size_t /*total*/) -{ - if(m_fd < 0) - { - char nameBuff[] = "/tmp/litebrowser-XXXXXX"; - m_fd = mkstemp(nameBuff); - if(m_fd >= 0) - { - m_path = nameBuff; - } - } - if(m_fd >= 0) - { - write(m_fd, data, len); - } -} diff --git a/src/web_page.h b/src/web_page.h deleted file mode 100644 index 014ca13..0000000 --- a/src/web_page.h +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef LITEBROWSER_WEB_PAGE_H -#define LITEBROWSER_WEB_PAGE_H - -#include "container_cairo_pango.h" -#include -#include "html_host.h" -#include "http_requests_pool.h" -#include "cairo_images_cache.h" - -namespace litebrowser -{ - class text_file - { - std::mutex wait_mutex; - std::stringstream stream; - bool data_ready; - public: - text_file() : data_ready(false) - { - wait_mutex.lock(); - } - - void set_ready() { data_ready = true; } - - std::string str() const { return data_ready ? stream.str() : ""; } - void wait() - { - wait_mutex.lock(); - } - void on_data(void* data, size_t len, size_t downloaded, size_t total); - void on_page_downloaded(u_int32_t http_status, u_int32_t err_code, const std::string& err_text); - }; - - class image_file - { - int m_fd; - std::string m_path; - std::string m_url; - bool m_redraw_on_ready; - public: - explicit image_file(std::string url, bool redraw_on_ready); - void on_data(void* data, size_t len, size_t downloaded, size_t total); - void close() const - { - if(m_fd > 0) - { - ::close(m_fd); - } - } - const std::string& path() const { return m_path; } - const std::string& url() const { return m_url; } - bool redraw_only() const { return m_redraw_on_ready; } - }; - - class web_page : public container_cairo_pango, - public std::enable_shared_from_this - { - litehtml::string m_url; - litehtml::string m_base_url; - litehtml::document::ptr m_html; - std::mutex m_html_mutex; - litehtml::string m_cursor; - litehtml::string m_clicked_url; - std::string m_hash; - html_host_interface* m_html_host; - cairo_images_cache m_images; - litebrowser::http_requests_pool m_requests_pool; - - public: - explicit web_page(html_host_interface* html_host, int pool_size) : - m_html_host(html_host), - m_requests_pool(pool_size, std::bind(&web_page::on_pool_update_state, this)) - {} - - void open(const litehtml::string& url, const litehtml::string& hash); - - void get_client_rect(litehtml::position& client) const override; - void on_anchor_click(const char* url, const litehtml::element::ptr& el) override; - void set_cursor(const char* cursor) override; - void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) override; - void set_caption(const char* caption) override; - void set_base_url(const char* base_url) override; - cairo_surface_t* get_image(const std::string& url) override; - void make_url( const char* url, const char* basepath, litehtml::string& out ) override; - void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; - static cairo_surface_t* surface_from_pixbuf(const Glib::RefPtr& bmp); - double get_screen_dpi() const override; - int get_screen_width() const override - { - return Gdk::screen_width(); - } - int get_screen_height() const override - { - return Gdk::screen_height(); - } - - void show_hash(const litehtml::string& hash); - void show_hash_and_reset() - { - if(!m_hash.empty() && m_html) - { - show_hash(m_hash); - m_hash = ""; - } - } - - void on_mouse_over(int x, int y, int client_x, int client_y); - void on_lbutton_down(int x, int y, int client_x, int client_y); - void on_lbutton_up(int x, int y, int client_x, int client_y); - const std::string& get_cursor() const { return m_cursor; } - void draw(litehtml::uint_ptr hdc, int x, int y, const litehtml::position* clip) - { - std::unique_lock html_lock(m_html_mutex); - if(m_html) m_html->draw(hdc, x, y, clip); - } - int render(int max_width) - { - std::unique_lock html_lock(m_html_mutex); - return m_html ? m_html->render(max_width) : 0; - } - const std::string& url() const { return m_url; } - int width() const { return m_html ? m_html->width() : 0; } - int height() const { return m_html ? m_html->height() : 0; } - bool media_changed() - { - std::unique_lock html_lock(m_html_mutex); - return m_html && m_html->media_changed(); - } - void stop_loading() - { - m_requests_pool.stop(); - } - bool is_downloading() - { - return m_requests_pool.is_downloading(); - } - void dump(const litehtml::string& file_name); - private: - void http_request(const std::string& url, - const std::function& cb_on_data, - const std::function& cb_on_finish); - void on_page_downloaded(std::shared_ptr data, u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url); - void on_image_downloaded(std::shared_ptr data, u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url); - void on_pool_update_state(); - }; -} - -#endif //LITEBROWSER_WEB_PAGE_H