diff --git a/include/litehtml/url.h b/include/litehtml/url.h index fae3db0a..d651b7c3 100644 --- a/include/litehtml/url.h +++ b/include/litehtml/url.h @@ -103,6 +103,9 @@ class url { return !fragment_.empty(); } + static string encode(const string& str); + static string decode(const string& str); + protected: string str_; diff --git a/src/url.cpp b/src/url.cpp index dbae382a..b1b0fba2 100644 --- a/src/url.cpp +++ b/src/url.cpp @@ -31,9 +31,9 @@ #include #include - #include "codepoint.h" #include "url_path.h" +#include namespace litehtml { @@ -94,35 +94,87 @@ url::url(const string& str) path_ = tmp; } -url::url(const string& scheme, - const string& authority, - const string& path, - const string& query, - const string& fragment) -: scheme_(scheme) -, authority_(authority) -, path_(path) -, query_(query) -, fragment_(fragment) +url::url(const string& scheme, const string& authority, const string& path, const string& query, + const string& fragment) : + scheme_(scheme), + authority_(authority), + path_(path), + query_(query), + fragment_(fragment) { - std::stringstream tss; + std::stringstream tss; + + if(!scheme_.empty()) + { + tss << scheme_ << ":"; + } + if(!authority_.empty()) + { + tss << "//" << authority_; + } + if(!path_.empty()) + { + tss << path_; + } + if(!query_.empty()) + { + tss << "?" << query_; + } + if(!fragment_.empty()) + { + tss << "#" << fragment_; + } + str_ = tss.str(); +} - if (!scheme_.empty()) { - tss << scheme_ << ":"; - } - if (!authority_.empty()) { - tss << "//" << authority_; - } - if (!path_.empty()) { - tss << path_; - } - if (!query_.empty()) { - tss << "?" << query_; - } - if (!fragment_.empty()) { - tss << "#" << fragment_; - } - str_ = tss.str(); +string url::encode(const string& str) +{ + std::ostringstream encoded; + encoded << std::hex << std::uppercase; + + for(unsigned char c : str) + { + if(isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') + { + encoded << c; + } else + { + encoded << '%' << std::setw(2) << int((unsigned char) c); + } + } + + return encoded.str(); +} + +string url::decode(const string& str) +{ + string decoded; + size_t i = 0; + + while(i < str.size()) + { + char c = str[i]; + if(c == '%') + { + if(i + 2 >= str.size()) + { + break; + } + + // Decode the percent-encoded character + char hex[3] = {str[i + 1], str[i + 2], '\0'}; + c = static_cast(std::strtol(hex, nullptr, 16)); + i += 2; // Skip the next two characters + } else if(c == '+') + { + // Replace '+' with space + c = ' '; + } + decoded += c; + i++; + } + + return decoded; } url resolve(const url& b, const url& r) diff --git a/support/webpage/draw_buffer.cpp b/support/draw_buffer/draw_buffer.cpp similarity index 76% rename from support/webpage/draw_buffer.cpp rename to support/draw_buffer/draw_buffer.cpp index fd09828e..bb9266f4 100644 --- a/support/webpage/draw_buffer.cpp +++ b/support/draw_buffer/draw_buffer.cpp @@ -6,10 +6,11 @@ /// Note, the actual position of the draw buffer can be rounded according to the scale factor. /// Use get_left() and get_top() to know the actual position. /// -/// @param page webpage to be redraw if the position was changed +/// @param cb_draw the callback for drawing the page /// @param left new horizontal position /// @param top new vertical position -void litebrowser::draw_buffer::on_scroll(std::shared_ptr page, int left, int top) +/// @param fixed_boxes fixed boxes to be redrawn +void litebrowser::draw_buffer::on_scroll(const draw_page_function_t& cb_draw, int left, int top, const litehtml::position::vector& fixed_boxes) { if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) return; @@ -26,7 +27,7 @@ void litebrowser::draw_buffer::on_scroll(std::shared_ptr { m_left = left; m_top = top; - redraw(page); + redraw(cb_draw); } else { int shift_x = m_left - left; @@ -58,43 +59,42 @@ void litebrowser::draw_buffer::on_scroll(std::shared_ptr if(rec_clean.x > m_left) { - redraw_area(page, m_left, rec_clean.y, rec_clean.x - m_left, rec_clean.height); + redraw_area(cb_draw, m_left, rec_clean.y, rec_clean.x - m_left, rec_clean.height); } if(clean_right < right) { - redraw_area(page, clean_right, rec_clean.y, right - clean_right, rec_clean.height); + redraw_area(cb_draw, clean_right, rec_clean.y, right - clean_right, rec_clean.height); } if(rec_clean.y > m_top) { - redraw_area(page, m_left, m_top, m_width, rec_clean.y - m_top); + redraw_area(cb_draw, m_left, m_top, m_width, rec_clean.y - m_top); } if(clean_bottom < bottom) { - redraw_area(page, m_left, clean_bottom, m_width, bottom - clean_bottom); + redraw_area(cb_draw, m_left, clean_bottom, m_width, bottom - clean_bottom); } - litehtml::position::vector fixed_boxes = page->get_fixed_boxes(); + for(const auto& box : fixed_boxes) { - redraw_area(page, m_left + box.left(), m_top + box.top(), box.width, box.height); - redraw_area(page, m_left + box.left() + shift_x, m_top + box.top() + shift_y, box.width, box.height); + redraw_area(cb_draw, m_left + box.left(), m_top + box.top(), box.width, box.height); + redraw_area(cb_draw, m_left + box.left() + shift_x, m_top + box.top() + shift_y, box.width, box.height); } } } } -/// @brief Reraw the defined area of the buffer +/// @brief Redraw the defined area of the buffer /// -/// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, +/// All coordinated are not scaled. The actual rectangle could be different, according to the scale factor, /// but it must always cover the requested. /// -/// @param page webpage to be redraw +/// @param cb_draw the callback for drawing the page /// @param x left position of the area /// @param y top position of the area /// @param width width of the area /// @param height height of the area -void litebrowser::draw_buffer::redraw_area(std::shared_ptr page, int x, int y, int width, - int height) +void litebrowser::draw_buffer::redraw_area(const draw_page_function_t& cb_draw, int x, int y, int width, int height) { if(m_draw_buffer) { @@ -134,10 +134,7 @@ void litebrowser::draw_buffer::redraw_area(std::shared_ptrdraw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); - } + cb_draw(cr, -m_left, -m_top, &pos); cairo_destroy(cr); } diff --git a/support/webpage/draw_buffer.h b/support/draw_buffer/draw_buffer.h similarity index 72% rename from support/webpage/draw_buffer.h rename to support/draw_buffer/draw_buffer.h index be3b72a5..dbd5d35f 100644 --- a/support/webpage/draw_buffer.h +++ b/support/draw_buffer/draw_buffer.h @@ -2,16 +2,18 @@ #define LITEBROWSER_DRAW_BUFFER_H #include +#include +#include #include -#include "web_page.h" +#include "litehtml/types.h" namespace litebrowser { /// @brief Draw Buffer Class /// /// This class performs the draw operations into the cairo surface. - /// The application draws everything to the buffer, then buffer are - /// drawn on widged or window. + /// The application draws everything to the buffer, then buffer is + /// drawn on widget or window. class draw_buffer { cairo_surface_t* m_draw_buffer = nullptr; @@ -23,6 +25,8 @@ namespace litebrowser int m_min_int_position = 1; public: + using draw_page_function_t = std::function; + ~draw_buffer() { if(m_draw_buffer) @@ -50,9 +54,9 @@ namespace litebrowser double get_scale_factor() const { return m_scale_factor; } /// @brief Set scale factor for draw buffer - /// @param page the webpage to be redraw if required + /// @param cb_draw the callback for drawing the page /// @param scale the scale factor to be applied - void set_scale_factor(std::shared_ptr page, double scale) + void set_scale_factor(const draw_page_function_t& cb_draw, double scale) { if(m_scale_factor != scale) { @@ -68,7 +72,7 @@ namespace litebrowser } m_draw_buffer = nullptr; create_draw_buffer(m_width, m_height); - redraw(page); + redraw(cb_draw); } } @@ -76,8 +80,8 @@ namespace litebrowser /// @param width surface width (not scaled) /// @param height surface height (not scaled) /// @param scale_factor scale factor - /// @return poiter to the cairo surface - cairo_surface_t* make_surface(int width, int height, double scale_factor) + /// @return pointer to the cairo surface + static cairo_surface_t* make_surface(int width, int height, double scale_factor) { return cairo_image_surface_create(CAIRO_FORMAT_RGB24, std::ceil((double) width * scale_factor), @@ -109,14 +113,14 @@ namespace litebrowser } /// @brief Call this function when widget size changed - /// @param page webpage to be redraw if buffer size changed + /// @param cb_draw the callback for drawing the page /// @param width new draw buffer width /// @param height new draw buffer height - void on_size_allocate(std::shared_ptr page, int width, int height) + void on_size_allocate(const draw_page_function_t& cb_draw, int width, int height) { if(create_draw_buffer(width, height)) { - redraw(page); + redraw(cb_draw); } } @@ -125,32 +129,33 @@ namespace litebrowser /// Note, the actual position of the draw buffer can be rounded according to the scale factor. /// Use get_left() and get_top() to know the actual position. /// - /// @param page webpage to be redraw if the position was changed + /// @param cb_draw the callback for drawing the page /// @param left new horizontal position /// @param top new vertical position - void on_scroll(std::shared_ptr page, int left, int top); + /// @param fixed_boxes fixed boxes to be redrawn + void on_scroll(const draw_page_function_t& cb_draw, int left, int top, const litehtml::position::vector& fixed_boxes); - /// @brief Reraw the defined area of the buffer + /// @brief Redraw the defined area of the buffer /// - /// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, + /// All coordinated are not scaled. The actual rectangle could be different, according to the scale factor, /// but it must always cover the requested. /// - /// @param page webpage to be redraw + /// @param cb_draw the callback for drawing the page /// @param x left position of the area /// @param y top position of the area /// @param width width of the area /// @param height height of the area - void redraw_area(std::shared_ptr page, int x, int y, int width, int height); + void redraw_area(const draw_page_function_t& cb_draw, int x, int y, int width, int height); /// @brief Redraw entire buffer - /// @param page webpage to be redraw - void redraw(std::shared_ptr page) + /// @param cb_draw the callback for drawing the page + void redraw(const draw_page_function_t& cb_draw) { - redraw_area(page, m_left, m_top, m_width, m_height); + redraw_area(cb_draw, m_left, m_top, m_width, m_height); } private: - inline int fix_position(int pos) + [[nodiscard]] int fix_position(int pos) const { return (pos / m_min_int_position) * m_min_int_position; } @@ -172,7 +177,6 @@ namespace litebrowser int denominator = 100; int common_divisor = get_common_divisor(numerator, denominator); - numerator /= common_divisor; return denominator / common_divisor; } }; diff --git a/support/gtkmm4/html_widget.cpp b/support/gtkmm4/html_widget.cpp index 9d6365ea..d31d4fb8 100644 --- a/support/gtkmm4/html_widget.cpp +++ b/support/gtkmm4/html_widget.cpp @@ -180,7 +180,7 @@ cairo_surface_t *html_widget::load_image(const std::string &path) } -void html_widget::open_page(const litehtml::string& url, const litehtml::string& hash) +void html_widget::open_page(const litehtml::string& url, const litehtml::string& fragment) { { std::lock_guard lock(m_page_mutex); @@ -189,7 +189,7 @@ void html_widget::open_page(const litehtml::string& url, const litehtml::string& m_current_page->stop_loading(); } m_next_page = std::make_shared(this, m_notifier, 10); - m_next_page->open(url, hash); + m_next_page->open(url, fragment); } m_sig_set_address.emit(url); m_sig_update_state.emit(get_state()); @@ -203,7 +203,7 @@ void html_widget::scroll_to(int x, int y) void html_widget::on_redraw() { - m_draw_buffer.redraw(current_page()); + m_draw_buffer.redraw(get_draw_function(current_page())); queue_draw(); } @@ -416,16 +416,16 @@ void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) { m_rendered_width = width; m_rendered_height = height; - m_draw_buffer.on_size_allocate(page, width, height); + m_draw_buffer.on_size_allocate(get_draw_function(page), width, height); page->media_changed(); page->render(m_rendered_width); update_view_port(page); - m_draw_buffer.redraw(page); + m_draw_buffer.redraw(get_draw_function(page)); queue_draw(); } } else { - m_draw_buffer.on_size_allocate(page, width, height); + m_draw_buffer.on_size_allocate(get_draw_function(page), width, height); } } @@ -459,9 +459,11 @@ void html_widget::allocate_scrollbars(int width, int height) void html_widget::on_vadjustment_changed() { - m_draw_buffer.on_scroll( current_page(), - (int) m_hadjustment->get_value(), - (int) m_vadjustment->get_value()); + auto page = current_page(); + m_draw_buffer.on_scroll(get_draw_function(page), + (int) m_hadjustment->get_value(), + (int) m_vadjustment->get_value(), + page ? page->get_fixed_boxes() : litehtml::position::vector{}); if(m_do_force_redraw_on_adjustment) { @@ -474,9 +476,12 @@ void html_widget::on_vadjustment_changed() void html_widget::on_hadjustment_changed() { - m_draw_buffer.on_scroll( current_page(), - (int) m_hadjustment->get_value(), - (int) m_vadjustment->get_value()); + auto page = current_page(); + m_draw_buffer.on_scroll(get_draw_function(page), + (int) m_hadjustment->get_value(), + (int) m_vadjustment->get_value(), + page ? page->get_fixed_boxes() : litehtml::position::vector{}); + if(m_do_force_redraw_on_adjustment) { force_redraw(); @@ -512,21 +517,17 @@ void html_widget::on_realize() { Gtk::Widget::on_realize(); - auto native = get_native(); - if(native) + if(auto native = get_native()) { - auto surface = native->get_surface(); - if(surface) + if(auto surface = native->get_surface()) { surface->property_scale().signal_changed().connect([this]() { - auto native = get_native(); - if(native) + if(auto native = get_native()) { - auto surface = native->get_surface(); - if(surface) + if(auto surface = native->get_surface()) { - m_draw_buffer.set_scale_factor(current_page(), surface->get_scale()); + m_draw_buffer.set_scale_factor(get_draw_function(current_page()), surface->get_scale()); queue_draw(); } } @@ -592,7 +593,7 @@ void html_widget::redraw_boxes(const litehtml::position::vector& boxes) if(!rect.has_zero_area()) { - m_draw_buffer.redraw_area(current_page(), rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()); + m_draw_buffer.redraw_area(get_draw_function(current_page()), rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()); queue_draw(); } } @@ -619,37 +620,37 @@ void html_widget::on_page_loaded(uint64_t web_page_id) m_sig_update_state.emit(get_state()); } -void html_widget::show_hash(const std::string &hash) +void html_widget::show_fragment(const std::string &fragment) { std::shared_ptr page = current_page(); if(page) { - page->show_hash(hash); + page->show_fragment(fragment); } } void html_widget::open_url(const std::string &url) { - std::string hash; + std::string fragment; std::string s_url = url; m_sig_set_address.emit(url); - std::string::size_type hash_pos = s_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) + std::string::size_type fragment_pos = s_url.find_first_of(L'#'); + if(fragment_pos != std::wstring::npos) { - hash = s_url.substr(hash_pos + 1); - s_url.erase(hash_pos); + fragment = s_url.substr(fragment_pos + 1); + s_url.erase(fragment_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) + fragment_pos = current_url.find_first_of(L'#'); + if(fragment_pos != std::wstring::npos) { - current_url.erase(hash_pos); + current_url.erase(fragment_pos); } if(!current_url.empty()) @@ -667,10 +668,10 @@ void html_widget::open_url(const std::string &url) } if(!open_hash_only) { - open_page(url, hash); + open_page(url, fragment); } else { - show_hash(hash); + show_fragment(fragment); } if(!reload) { @@ -686,7 +687,7 @@ void html_widget::render() { page->render(m_draw_buffer.get_width()); update_view_port(page); - m_draw_buffer.redraw(page); + m_draw_buffer.redraw(get_draw_function(page)); queue_draw(); } } diff --git a/support/gtkmm4/html_widget.h b/support/gtkmm4/html_widget.h index ef5f15d0..f98064c8 100644 --- a/support/gtkmm4/html_widget.h +++ b/support/gtkmm4/html_widget.h @@ -4,7 +4,7 @@ #include "html_host.h" #include "web_page.h" #include "web_history.h" -#include "draw_buffer.h" +#include "../draw_buffer/draw_buffer.h" #include enum page_state @@ -235,7 +235,7 @@ class html_widget : public Gtk::Widget, std::string get_html_source(); long render_measure(int number); long draw_measure(int number); - void show_hash(const std::string& hash); + void show_fragment(const std::string& fragment); bool on_close(); void dump(litehtml::dumper& cout); @@ -246,7 +246,7 @@ class html_widget : public Gtk::Widget, double get_dpi() override; int get_screen_width() override; int get_screen_height() override; - void open_page(const litehtml::string& url, const litehtml::string& hash); + void open_page(const litehtml::string& url, const litehtml::string& fragment); void update_cursor() override; void redraw_boxes(const litehtml::position::vector& boxes) override; int get_render_width() override; @@ -287,6 +287,16 @@ class html_widget : public Gtk::Widget, queue_draw(); while (g_main_context_iteration(nullptr, false)) {} } + litebrowser::draw_buffer::draw_page_function_t get_draw_function(const std::shared_ptr& page) + { + return [this, page](cairo_t* cr, int x, int y, const litehtml::position* clip) + { + if (page) + { + page->draw((litehtml::uint_ptr) cr, x, y, clip); + } + }; + } public: // Signals types diff --git a/support/webpage/web_page.cpp b/support/webpage/web_page.cpp index 2f90600b..fdbb7806 100644 --- a/support/webpage/web_page.cpp +++ b/support/webpage/web_page.cpp @@ -21,7 +21,7 @@ void litebrowser::text_file::on_page_downloaded(u_int32_t http_status, wait_mutex.unlock(); } -void litebrowser::web_page::open(const std::string &url, const std::string &hash) +void litebrowser::web_page::open(const std::string &url, const std::string &fragment) { litehtml::url l_url(url); @@ -39,11 +39,15 @@ void litebrowser::web_page::open(const std::string &url, const std::string &hash m_url = url; } m_base_url = m_url; - m_hash = hash; + m_fragment = fragment; auto data = std::make_shared(); - auto cb_on_data = [data](auto && PH1, auto && PH2, auto && PH3, auto && PH4) mutable { data->on_data(std::forward(PH1), std::forward(PH2), std::forward(PH3), std::forward(PH4)); }; - auto cb_on_finish = std::bind(&web_page::on_page_downloaded, shared_from_this(), data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); + auto cb_on_data = [data](void* in_data, size_t len, size_t /*downloaded*/, size_t /*total*/) { data->on_data(in_data, len, 0, 0); }; + auto shared_this = shared_from_this(); + auto cb_on_finish = [shared_this, data](u_int32_t http_status, u_int32_t err_code, const std::string &err_text, const std::string& url) + { + shared_this->on_page_downloaded(data, http_status, err_code, err_text, url); + }; http_request(m_url, cb_on_data, cb_on_finish); } @@ -78,8 +82,11 @@ void litebrowser::web_page::import_css(litehtml::string& text, const litehtml::s 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); + auto cb_on_data = [data](void* in_data, size_t len, size_t /*downloaded*/, size_t /*total*/) { data->on_data(in_data, len, 0, 0); }; + auto cb_on_finish = [data](u_int32_t http_status, u_int32_t err_code, const std::string &err_text, const std::string& /*url*/) + { + data->on_page_downloaded(http_status, err_code, err_text); + }; http_request(css_url, cb_on_data, cb_on_finish); data->wait(); text = data->str(); @@ -110,21 +117,17 @@ 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) +void litebrowser::web_page::show_fragment(const litehtml::string& fragment) { std::lock_guard html_lock(m_html_mutex); - if(hash.empty() || !m_html) + if(fragment.empty() || !m_html) { m_html_host->scroll_to(0, 0); } else { - std::string selector = "#" + hash; + auto escaped_hash = litehtml::get_escaped_string(fragment); + std::string selector = ":is([id=\"" + escaped_hash + "\"],[name=\"" + escaped_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(); @@ -260,8 +263,13 @@ void litebrowser::web_page::load_image(const char *src, const char *baseurl, boo 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); + auto cb_on_data = [data](void* in_data, size_t len, size_t /*downloaded*/, size_t /*total*/) { data->on_data(in_data, len, 0, 0); }; + auto shared_this = shared_from_this(); + auto cb_on_finish = [shared_this, data](u_int32_t http_status, u_int32_t err_code, const std::string &err_text, const std::string& url) + { + shared_this->on_image_downloaded(data, http_status, err_code, err_text, url); + }; + http_request(url, cb_on_data, cb_on_finish); } } diff --git a/support/webpage/web_page.h b/support/webpage/web_page.h index 9a3dca5f..6080b416 100644 --- a/support/webpage/web_page.h +++ b/support/webpage/web_page.h @@ -68,7 +68,7 @@ namespace litebrowser std::recursive_mutex m_html_mutex; litehtml::string m_cursor; litehtml::string m_clicked_url; - std::string m_hash; + std::string m_fragment; html_host_interface* m_html_host; cairo_images_cache m_images; litebrowser::http_requests_pool m_requests_pool; @@ -89,7 +89,7 @@ namespace litebrowser [[nodiscard]] const std::string& get_html_source() const { return m_html_source; } - void open(const litehtml::string& url, const litehtml::string& hash); + void open(const litehtml::string& url, const litehtml::string& fragment); void get_viewport(litehtml::position& viewport) const override; void on_anchor_click(const char* url, const litehtml::element::ptr& el) override; @@ -105,13 +105,13 @@ namespace litebrowser int get_screen_width() const override; int get_screen_height() const override; - void show_hash(const litehtml::string& hash); - void show_hash_and_reset() + void show_fragment(const litehtml::string& fragment); + void show_fragment_and_reset() { - if(!m_hash.empty() && m_html) + if(!m_fragment.empty() && m_html) { - show_hash(m_hash); - m_hash = ""; + show_fragment(m_fragment); + m_fragment = ""; } }