feat: Implement functional web browsing with HTTP + HTML rendering

Implemented the missing browser engine functionality to make TUT actually
browse the web.

Browser Engine Changes:
- Integrate HttpClient to fetch URLs via GET requests
- Integrate HtmlRenderer to parse and render HTML content
- Implement proper error handling for failed HTTP requests
- Add relative URL resolution for links (absolute and relative paths)
- Store title, content, and links from rendered pages

Tested with https://tldp.org/HOWTO/HOWTO-INDEX/howtos.html:
 Successfully fetches and displays web pages
 Renders HTML with proper formatting (headings, lists, links)
 Extracts and numbers clickable links
 Displays page titles

The browser is now fully functional for basic text-based web browsing!
This commit is contained in:
m1ngsama 2025-12-31 17:19:01 +08:00
parent eea499e56e
commit 6baa6517ca
4 changed files with 64 additions and 17 deletions

View file

@ -4,6 +4,8 @@
*/
#include "core/browser_engine.hpp"
#include "core/http_client.hpp"
#include "renderer/html_renderer.hpp"
namespace tut {
@ -15,6 +17,9 @@ public:
std::vector<LinkInfo> links_;
std::vector<std::string> history_;
size_t history_index_{0};
HttpClient http_client_;
HtmlRenderer renderer_;
};
BrowserEngine::BrowserEngine() : impl_(std::make_unique<Impl>()) {}
@ -22,14 +27,66 @@ BrowserEngine::BrowserEngine() : impl_(std::make_unique<Impl>()) {}
BrowserEngine::~BrowserEngine() = default;
bool BrowserEngine::loadUrl(const std::string& url) {
// TODO: 实现 HTTP 请求和 HTML 解析
impl_->current_url_ = url;
return true;
// 发送 HTTP 请求
auto response = impl_->http_client_.get(url);
if (!response.isSuccess()) {
// 加载失败,设置错误内容
impl_->title_ = "Error";
impl_->content_ = "Failed to load page: " +
(response.error.empty()
? "HTTP " + std::to_string(response.status_code)
: response.error);
impl_->links_.clear();
return false;
}
// 渲染 HTML
return loadHtml(response.body);
}
bool BrowserEngine::loadHtml(const std::string& html) {
// TODO: 实现 HTML 解析
impl_->content_ = html;
// 渲染 HTML
RenderOptions options;
options.show_links = true;
options.use_colors = true;
auto result = impl_->renderer_.render(html, options);
impl_->title_ = result.title;
impl_->content_ = result.text;
impl_->links_ = result.links;
// 解析相对 URL
if (!impl_->current_url_.empty()) {
for (auto& link : impl_->links_) {
if (link.url.find("://") == std::string::npos) {
// 简单的相对 URL 解析
if (!link.url.empty() && link.url[0] == '/') {
// 绝对路径
size_t scheme_end = impl_->current_url_.find("://");
if (scheme_end != std::string::npos) {
size_t host_end = impl_->current_url_.find('/', scheme_end + 3);
std::string base = (host_end != std::string::npos)
? impl_->current_url_.substr(0, host_end)
: impl_->current_url_;
link.url = base + link.url;
}
} else if (link.url.find("://") == std::string::npos &&
!link.url.empty() && link.url[0] != '#') {
// 相对路径
size_t last_slash = impl_->current_url_.rfind('/');
if (last_slash != std::string::npos) {
std::string base = impl_->current_url_.substr(0, last_slash + 1);
link.url = base + link.url;
}
}
}
}
}
return true;
}

View file

@ -11,18 +11,10 @@
#include <memory>
#include <optional>
#include <vector>
#include "types.hpp"
namespace tut {
/**
* @brief
*/
struct LinkInfo {
std::string url; ///< 链接 URL
std::string text; ///< 链接文本
int line{0}; ///< 所在行号
};
/**
* @brief
*

View file

@ -10,11 +10,10 @@
#include <string>
#include <vector>
#include <memory>
#include "../core/types.hpp"
namespace tut {
struct LinkInfo;
/**
* @brief
*/

View file

@ -11,11 +11,10 @@
#include <vector>
#include <functional>
#include <memory>
#include "../core/types.hpp"
namespace tut {
struct LinkInfo;
/**
* @brief
*