Added SDK

This commit is contained in:
Andrew Zambazos
2026-06-11 14:01:22 +12:00
commit c0395a49bd
2155 changed files with 451005 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
#include "Browser.h"
#include "UI.h"
#include <Ultralight/Renderer.h>
#include <Ultralight/platform/Config.h>
#include <Ultralight/platform/Platform.h>
#include <sstream>
Browser::Browser(bool use_gpu, bool force_repaint, bool show_stats)
{
Settings settings;
settings.force_cpu_renderer = !use_gpu;
Config config;
config.scroll_timer_delay = 1.0 / 90.0;
config.force_repaint = force_repaint;
#ifdef UL_EMBED_FILES
// Use our custom FileSystem
file_system_ = std::make_unique<EmbeddedFileSystem>();
Platform::instance().set_file_system(file_system_.get());
#endif
app_ = App::Create(settings, config);
window_ = Window::Create(app_->main_monitor(), 1024, 768, false,
kWindowFlags_Resizable | kWindowFlags_Titled | kWindowFlags_Maximizable);
if (show_stats)
window_->EnableFrameStatistics();
SetTitle("Welcome!");
// Create the UI
ui_.reset(new UI(this));
window_->set_listener(ui_.get());
}
Browser::~Browser()
{
window_->set_listener(nullptr);
ui_.reset();
window_ = nullptr;
app_ = nullptr;
}
void Browser::Run()
{
app_->Run();
}
void Browser::SetTitle(const String& title)
{
std::ostringstream str;
str << "Ultralight MiniBrowser - " << title.utf8().data();
window_->SetTitle(str.str().c_str());
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <AppCore/AppCore.h>
#include <memory>
#ifdef UL_EMBED_FILES
#include "platform/EmbeddedFileSystem.h"
#endif
using namespace ultralight;
class UI;
class Browser {
public:
Browser(bool use_gpu, bool force_repaint, bool show_stats);
~Browser();
void Run();
void SetTitle(const String& title);
RefPtr<App> app() { return app_; }
RefPtr<Window> window() { return window_; }
protected:
RefPtr<App> app_;
RefPtr<Window> window_;
std::unique_ptr<UI> ui_;
#ifdef UL_EMBED_FILES
std::unique_ptr<EmbeddedFileSystem> file_system_;
#endif
};
+287
View File
@@ -0,0 +1,287 @@
#include "Page.h"
#include "UI.h"
#include <iostream>
#include <string>
#define INSPECTOR_DRAG_HANDLE_HEIGHT 10
Page::Page(UI* ui, uint32_t width, uint32_t height, int x, int y)
: ui_(ui)
, container_width_(width)
, container_height_(height)
{
overlay_ = Overlay::Create(ui->window(), width, height, x, y);
view()->set_view_listener(this);
view()->set_load_listener(this);
view()->set_download_listener(this);
view()->set_network_listener(this);
}
Page::~Page()
{
view()->set_network_listener(nullptr);
view()->set_download_listener(nullptr);
view()->set_view_listener(nullptr);
view()->set_load_listener(nullptr);
}
void Page::Show()
{
overlay_->Show();
overlay_->Focus();
if (inspector_overlay_)
inspector_overlay_->Show();
}
void Page::Hide()
{
overlay_->Hide();
overlay_->Unfocus();
if (inspector_overlay_)
inspector_overlay_->Hide();
}
void Page::ToggleInspector()
{
if (!inspector_overlay_) {
view()->CreateLocalInspectorView();
} else {
if (inspector_overlay_->is_hidden()) {
inspector_overlay_->Show();
} else {
inspector_overlay_->Hide();
}
}
// Force resize to update layout
Resize(container_width_, container_height_);
}
bool Page::IsInspectorShowing() const
{
if (!inspector_overlay_)
return false;
return !inspector_overlay_->is_hidden();
}
IntRect Page::GetInspectorResizeDragHandle() const
{
if (!IsInspectorShowing())
return IntRect::MakeEmpty();
int drag_handle_height_px = (uint32_t)std::round(INSPECTOR_DRAG_HANDLE_HEIGHT * ui_->window()->scale());
// This drag handle should span the width of the UI and be centered vertically at the boundary between
// the page overlay and inspector overlay.
int drag_handle_x = (int)inspector_overlay_->x();
int drag_handle_y = (int)inspector_overlay_->y() - drag_handle_height_px / 2;
return { drag_handle_x, drag_handle_y, drag_handle_x + (int)inspector_overlay_->width(),
drag_handle_y + drag_handle_height_px };
}
int Page::GetInspectorHeight() const
{
if (inspector_overlay_)
return inspector_overlay_->height();
return 0;
}
void Page::SetInspectorHeight(int height)
{
if (height > 2) {
inspector_overlay_->Resize(inspector_overlay_->width(), height);
// Trigger a resize to perform re-layout / re-size of content overlay
Resize(container_width_, container_height_);
}
}
void Page::Resize(uint32_t width, uint32_t height)
{
container_width_ = width;
container_height_ = height;
uint32_t content_height = container_height_;
if (inspector_overlay_ && !inspector_overlay_->is_hidden()) {
uint32_t inspector_height_px = inspector_overlay_->height();
content_height -= inspector_height_px;
}
if (content_height < 1)
content_height = 1;
overlay_->Resize(container_width_, content_height);
if (inspector_overlay_ && !inspector_overlay_->is_hidden()) {
inspector_overlay_->Resize(container_width_, inspector_overlay_->height());
inspector_overlay_->MoveTo(0, overlay_->y() + overlay_->height());
}
}
void Page::OnChangeTitle(View* caller, const String& title)
{
ui_->SetTitle(title);
}
void Page::OnChangeURL(View* caller, const String& url)
{
ui_->SetURL(url);
}
void Page::OnChangeTooltip(View* caller, const String& tooltip) {}
void Page::OnChangeCursor(View* caller, Cursor cursor)
{
ui_->SetCursor(cursor);
}
void Page::OnAddConsoleMessage(View* caller, const ConsoleMessage& msg)
{
std::cout << "[OnAddConsoleMessage]\n\t"
<< "\n\tsource:\t" << (uint32_t)msg.source()
<< "\n\ttype:\t" << (uint32_t)msg.type()
<< "\n\tlevel:\t" << (uint32_t)msg.level()
<< "\n\tmessage:\t" << msg.message().utf8().data()
<< "\n\tline_number:\t" << msg.line_number()
<< "\n\tcolumn_number:\t" << msg.column_number()
<< "\n\tsource_id:\t" << msg.source_id().utf8().data()
<< "\n\tnum_arguments:\t" << msg.num_arguments() << std::endl;
uint32_t num_args = msg.num_arguments();
if (num_args > 0) {
SetJSContext(msg.argument_context());
for (uint32_t i = 0; i < num_args; i++) {
String arg_str = JSValue(msg.argument_at(i)).ToString();
std::cout << "\n\t[" << i << "]:\t" << arg_str.utf8().data();
}
std::cout << std::endl;
}
}
RefPtr<View> Page::OnCreateChildView(ultralight::View* caller,
const String& opener_url, const String& target_url,
bool is_popup, const IntRect& popup_rect)
{
// TODO: handle child view in new window
return nullptr;
}
RefPtr<View> Page::OnCreateInspectorView(ultralight::View* caller, bool is_local,
const String& inspected_url)
{
if (inspector_overlay_)
return nullptr;
inspector_overlay_ = Overlay::Create(ui_->window(), container_width_, container_height_ / 2, 0, 0);
// Force resize to update layout
Resize(container_width_, container_height_);
inspector_overlay_->Show();
return inspector_overlay_->view();
}
void Page::OnBeginLoading(View* caller, uint64_t frame_id, bool is_main_frame, const String& url)
{
ui_->UpdatePageNavigation(caller->is_loading(), caller->CanGoBack(), caller->CanGoForward());
std::cout << "[OnBeginLoading]\n\t"
<< "\n\tframe_id:\t" << frame_id
<< "\n\tis_main_frame:\t" << is_main_frame
<< "\n\turl:\t" << url.utf8().data()
<< std::endl;
}
void Page::OnFinishLoading(View* caller, uint64_t frame_id, bool is_main_frame, const String& url)
{
ui_->UpdatePageNavigation(caller->is_loading(), caller->CanGoBack(), caller->CanGoForward());
std::cout << "[OnFinishLoading]\n\t"
<< "\n\tframe_id:\t" << frame_id << "\n\tis_main_frame:\t" << is_main_frame
<< "\n\turl:\t" << url.utf8().data() << std::endl;
}
void Page::OnFailLoading(View* caller, uint64_t frame_id, bool is_main_frame, const String& url,
const String& description, const String& error_domain, int error_code)
{
if (is_main_frame) {
char error_code_str[16];
sprintf(error_code_str, "%d", error_code);
String html_string = "<html><head><style>";
html_string += "* { font-family: sans-serif; }";
html_string += "body { background-color: #CCC; color: #555; padding: 4em; }";
html_string += "dt { font-weight: bold; padding: 1em; }";
html_string += "</style></head><body>";
html_string += "<h2>A Network Error was Encountered</h2>";
html_string += "<dl>";
html_string += "<dt>URL</dt><dd>" + url + "</dd>";
html_string += "<dt>Description</dt><dd>" + description + "</dd>";
html_string += "<dt>Error Domain</dt><dd>" + error_domain + "</dd>";
html_string += "<dt>Error Code</dt><dd>" + String(error_code_str) + "</dd>";
html_string += "</dl></body></html>";
view()->LoadHTML(html_string);
}
std::cout << "[OnFailLoading]\n\t"
<< "\n\tframe_id:\t" << frame_id << "\n\tis_main_frame:\t" << is_main_frame
<< "\n\turl:\t" << url.utf8().data() << "\n\tdescription:\t"
<< description.utf8().data() << "\n\terror_domain:\t" << error_domain.utf8().data()
<< "\n\terror_code:\t" << error_code << std::endl;
}
void Page::OnWindowObjectReady(ultralight::View* caller, uint64_t frame_id, bool is_main_frame,
const String& url)
{
std::cout << "[OnWindowObjectReady]\n\t"
<< "\n\tframe_id:\t" << frame_id << "\n\tis_main_frame:\t" << is_main_frame
<< "\n\turl:\t" << url.utf8().data() << std::endl;
}
void Page::OnDOMReady(ultralight::View* caller, uint64_t frame_id, bool is_main_frame,
const String& url)
{
std::cout << "[OnDOMReady]\n\t"
<< "\n\tframe_id:\t" << frame_id << "\n\tis_main_frame:\t" << is_main_frame
<< "\n\turl:\t" << url.utf8().data() << std::endl;
}
void Page::OnUpdateHistory(View* caller)
{
ui_->UpdatePageNavigation(caller->is_loading(), caller->CanGoBack(), caller->CanGoForward());
}
bool Page::OnRequestDownload(View* caller, DownloadId id, const String& url)
{
printf("Page::OnRequestDownload [%d] %s\n", id, url.utf8().data());
return true;
}
void Page::OnBeginDownload(View* caller, DownloadId id, const String& url, const String& filename,
int64_t expected_content_length)
{
printf("Page::OnBeginDownload [%d] %s\n", id, url.utf8().data());
}
void Page::OnReceiveDataForDownload(View* caller, DownloadId id, RefPtr<Buffer> data)
{
printf("Page::OnReceiveDataForDownload [%d]\n", id);
}
void Page::OnFinishDownload(View* caller, DownloadId id)
{
printf("Page::OnFinishDownload [%d]\n", id);
}
void Page::OnFailDownload(View* caller, DownloadId id)
{
printf("Page::OnFailDownload [%d]\n", id);
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#include <AppCore/AppCore.h>
#include <Ultralight/Listener.h>
class UI;
using namespace ultralight;
/**
* Browser Page implementation. Renders the actual page content in bottom pane.
*/
class Page : public ViewListener,
public LoadListener,
public DownloadListener,
public NetworkListener {
public:
Page(UI* ui, uint32_t width, uint32_t height, int x, int y);
~Page();
void set_ready_to_close(bool ready) { ready_to_close_ = ready; }
bool ready_to_close() { return ready_to_close_; }
RefPtr<View> view() { return overlay_->view(); }
void Show();
void Hide();
void ToggleInspector();
bool IsInspectorShowing() const;
IntRect GetInspectorResizeDragHandle() const;
int GetInspectorHeight() const;
void SetInspectorHeight(int height);
void Resize(uint32_t width, uint32_t height);
// Inherited from ViewListener
virtual void OnChangeTitle(View* caller, const String& title) override;
virtual void OnChangeURL(View* caller, const String& url) override;
virtual void OnChangeTooltip(View* caller, const String& tooltip) override;
virtual void OnChangeCursor(View* caller, Cursor cursor) override;
virtual void OnAddConsoleMessage(View* caller, const ConsoleMessage& msg) override;
virtual RefPtr<View> OnCreateChildView(ultralight::View* caller,
const String& opener_url, const String& target_url,
bool is_popup, const IntRect& popup_rect) override;
virtual RefPtr<View> OnCreateInspectorView(ultralight::View* caller, bool is_local,
const String& inspected_url) override;
// Inherited from LoadListener
virtual void OnBeginLoading(View* caller, uint64_t frame_id,
bool is_main_frame, const String& url) override;
virtual void OnFinishLoading(View* caller, uint64_t frame_id,
bool is_main_frame, const String& url) override;
virtual void OnFailLoading(View* caller, uint64_t frame_id,
bool is_main_frame, const String& url, const String& description,
const String& error_domain, int error_code) override;
virtual void OnWindowObjectReady(View* caller, uint64_t frame_id, bool is_main_frame,
const String& url) override;
virtual void OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame,
const String& url) override;
virtual void OnUpdateHistory(View* caller) override;
// Inherited from DownloadListener
virtual DownloadId NextDownloadId(View* caller) override { return next_download_id_++; }
virtual bool OnRequestDownload(View* caller, DownloadId id,
const String& url) override;
virtual void OnBeginDownload(View* caller, DownloadId id, const String& url,
const String& filename, int64_t expected_content_length) override;
virtual void OnReceiveDataForDownload(View* caller, DownloadId id,
RefPtr<Buffer> data) override;
virtual void OnFinishDownload(View* caller, DownloadId id) override;
virtual void OnFailDownload(View* caller, DownloadId id) override;
// Inherited from NetworkListener
virtual bool OnNetworkRequest(View* caller, NetworkRequest& request) override { return true; }
protected:
UI* ui_;
RefPtr<Overlay> overlay_;
RefPtr<Overlay> inspector_overlay_;
bool ready_to_close_ = false;
uint32_t container_width_, container_height_;
DownloadId next_download_id_ = 0;
};
+212
View File
@@ -0,0 +1,212 @@
#include "UI.h"
static UI* g_ui = 0;
#define UI_HEIGHT 41
UI::UI(Browser* browser)
: browser_(browser)
, cur_cursor_(Cursor::kCursor_Pointer)
, is_resizing_inspector_(false)
, is_over_inspector_resize_drag_handle_(false)
{
uint32_t window_width = window()->width();
ui_height_ = (uint32_t)std::round(UI_HEIGHT * window()->scale());
overlay_ = Overlay::Create(window(), window_width, ui_height_, 0, 0);
g_ui = this;
view()->set_load_listener(this);
view()->set_view_listener(this);
view()->LoadURL("file:///ui/ui.html");
}
UI::~UI()
{
view()->set_load_listener(nullptr);
view()->set_view_listener(nullptr);
g_ui = nullptr;
}
bool UI::OnKeyEvent(const ultralight::KeyEvent& evt)
{
// Consume all F2 key events
if (evt.virtual_key_code == KeyCodes::GK_F2) {
if (evt.type == KeyEvent::kType_RawKeyDown) {
App::instance()->renderer()->StartRemoteInspectorServer("0.0.0.0", 7676);
}
return false;
}
return true;
}
bool UI::OnMouseEvent(const ultralight::MouseEvent& evt)
{
if (page_ && page_->IsInspectorShowing()) {
float x_px = std::round(evt.x * window()->scale());
float y_px = std::round(evt.y * window()->scale());
if (is_resizing_inspector_) {
int resize_delta = inspector_resize_begin_mouse_y_ - y_px;
int new_inspector_height = inspector_resize_begin_height_ + resize_delta;
page_->SetInspectorHeight(new_inspector_height);
if (evt.type == MouseEvent::kType_MouseUp) {
is_resizing_inspector_ = false;
}
return false;
}
IntRect drag_handle = page_->GetInspectorResizeDragHandle();
bool over_drag_handle = drag_handle.Contains(Point(x_px, y_px));
if (over_drag_handle && !is_over_inspector_resize_drag_handle_) {
// We entered the drag area
window()->SetCursor(Cursor::kCursor_NorthSouthResize);
is_over_inspector_resize_drag_handle_ = true;
} else if (!over_drag_handle && is_over_inspector_resize_drag_handle_) {
// We left the drag area, restore previous cursor
window()->SetCursor(cur_cursor_);
is_over_inspector_resize_drag_handle_ = false;
}
if (over_drag_handle && evt.type == MouseEvent::kType_MouseDown && !is_resizing_inspector_) {
is_resizing_inspector_ = true;
inspector_resize_begin_mouse_y_ = y_px;
inspector_resize_begin_height_ = page_->GetInspectorHeight();
}
return !over_drag_handle;
}
return true;
}
void UI::OnClose(ultralight::Window* window) { App::instance()->Quit(); }
void UI::OnResize(ultralight::Window* window, uint32_t width, uint32_t height)
{
int page_height = window->height() - ui_height_;
if (page_height < 1)
page_height = 1;
overlay_->Resize(window->width(), ui_height_);
if (page())
page()->Resize(window->width(), (uint32_t)page_height);
}
void UI::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url)
{
// Set the context for all subsequent JS* calls
RefPtr<JSContext> locked_context = view()->LockJSContext();
SetJSContext(locked_context->ctx());
JSObject global = JSGlobalObject();
updateBack = global["updateBack"];
updateForward = global["updateForward"];
updateLoading = global["updateLoading"];
updateURL = global["updateURL"];
global["OnBack"] = BindJSCallback(&UI::OnBack);
global["OnForward"] = BindJSCallback(&UI::OnForward);
global["OnRefresh"] = BindJSCallback(&UI::OnRefresh);
global["OnStop"] = BindJSCallback(&UI::OnStop);
global["OnToggleTools"] = BindJSCallback(&UI::OnToggleTools);
global["OnRequestChangeURL"] = BindJSCallback(&UI::OnRequestChangeURL);
CreatePage();
}
void UI::OnBack(const JSObject& obj, const JSArgs& args)
{
if (page())
page()->view()->GoBack();
}
void UI::OnForward(const JSObject& obj, const JSArgs& args)
{
if (page())
page()->view()->GoForward();
}
void UI::OnRefresh(const JSObject& obj, const JSArgs& args)
{
if (page())
page()->view()->Reload();
}
void UI::OnStop(const JSObject& obj, const JSArgs& args)
{
if (page())
page()->view()->Stop();
}
void UI::OnToggleTools(const JSObject& obj, const JSArgs& args)
{
if (page())
page()->ToggleInspector();
}
void UI::OnRequestChangeURL(const JSObject& obj, const JSArgs& args)
{
if (args.size() == 1) {
ultralight::String url = args[0];
if (page())
page()->view()->LoadURL(url);
}
}
void UI::CreatePage()
{
int page_height = window()->height() - ui_height_;
if (page_height < 1)
page_height = 1;
page_.reset(new Page(this, window()->width(), (uint32_t)page_height, 0, ui_height_));
page_->view()->LoadURL("file:///ui/welcome.html");
}
void UI::UpdatePageNavigation(bool is_loading, bool can_go_back, bool can_go_forward)
{
SetLoading(is_loading);
SetCanGoBack(can_go_back);
SetCanGoForward(can_go_forward);
}
void UI::SetLoading(bool is_loading)
{
RefPtr<JSContext> lock(view()->LockJSContext());
updateLoading({ is_loading });
}
void UI::SetCanGoBack(bool can_go_back)
{
RefPtr<JSContext> lock(view()->LockJSContext());
updateBack({ can_go_back });
}
void UI::SetCanGoForward(bool can_go_forward)
{
RefPtr<JSContext> lock(view()->LockJSContext());
updateForward({ can_go_forward });
}
void UI::SetTitle(const String& title) { browser_->SetTitle(title); }
void UI::SetURL(const ultralight::String& url)
{
RefPtr<JSContext> lock(view()->LockJSContext());
updateURL({ url });
}
void UI::SetCursor(ultralight::Cursor cursor)
{
cur_cursor_ = cursor;
if (App::instance())
window()->SetCursor(cursor);
}
+85
View File
@@ -0,0 +1,85 @@
#pragma once
#include <AppCore/AppCore.h>
#include "Page.h"
#include "Browser.h"
#include <map>
#include <memory>
using ultralight::JSObject;
using ultralight::JSArgs;
using ultralight::JSFunction;
using namespace ultralight;
class Console;
/**
* Browser UI implementation. Renders the toolbar/addressbar/tabs in top pane.
*/
class UI : public WindowListener,
public LoadListener,
public ViewListener {
public:
UI(Browser* browser);
~UI();
// Inherited from WindowListener
virtual bool OnKeyEvent(const ultralight::KeyEvent& evt) override;
virtual bool OnMouseEvent(const ultralight::MouseEvent& evt) override;
virtual void OnClose(ultralight::Window* window) override;
virtual void OnResize(ultralight::Window* window, uint32_t width, uint32_t height) override;
// Inherited from LoadListener
virtual void OnDOMReady(View* caller, uint64_t frame_id,
bool is_main_frame, const String& url) override;
// Inherited from ViewListener
virtual void OnChangeCursor(ultralight::View* caller, Cursor cursor) override { SetCursor(cursor); }
// Called by UI JavaScript
void OnBack(const JSObject& obj, const JSArgs& args);
void OnForward(const JSObject& obj, const JSArgs& args);
void OnRefresh(const JSObject& obj, const JSArgs& args);
void OnStop(const JSObject& obj, const JSArgs& args);
void OnToggleTools(const JSObject& obj, const JSArgs& args);
void OnRequestChangeURL(const JSObject& obj, const JSArgs& args);
RefPtr<Window> window() { return browser_->window(); }
protected:
void CreatePage();
void UpdatePageNavigation(bool is_loading, bool can_go_back, bool can_go_forward);
void SetLoading(bool is_loading);
void SetCanGoBack(bool can_go_back);
void SetCanGoForward(bool can_go_forward);
void SetTitle(const String& title);
void SetURL(const String& url);
void SetCursor(Cursor cursor);
Page* page() { return page_.get(); }
RefPtr<View> view() { return overlay_->view(); }
Browser* browser_;
RefPtr<Overlay> overlay_;
int ui_height_;
int page_height_;
float scale_;
std::unique_ptr<Page> page_;
Cursor cur_cursor_;
bool is_resizing_inspector_;
bool is_over_inspector_resize_drag_handle_;
int inspector_resize_begin_height_;
int inspector_resize_begin_mouse_y_;
JSFunction updateBack;
JSFunction updateForward;
JSFunction updateLoading;
JSFunction updateURL;
JSFunction addTab;
JSFunction updateTab;
JSFunction closeTab;
friend class Page;
};
+31
View File
@@ -0,0 +1,31 @@
#include "Browser.h"
#include <AppCore/Dialogs.h>
static bool use_gpu_renderer = true;
static bool force_repaints = false;
static bool show_statistics = true;
void ShowOptionDialog()
{
// Show a message box to select whether or not to use the GPU renderer
ultralight::ButtonResult result = ultralight::ShowMessageBox("MiniBrowser Options",
"Use the GPU renderer?\n\n(Yes = GPU, No = CPU)", ultralight::DialogIcon::Question, ultralight::ButtonType::YesNo);
use_gpu_renderer = result == ultralight::ButtonResult::Yes;
// Show a message box to select whether or not to force repaints
result = ultralight::ShowMessageBox("MiniBrowser Options",
"Force repaints to benchmark performance?\n\n(Yes = Force, No = Don't force)", ultralight::DialogIcon::Question, ultralight::ButtonType::YesNo);
force_repaints = result == ultralight::ButtonResult::Yes;
}
int main()
{
ShowOptionDialog();
Browser browser(use_gpu_renderer, force_repaints, show_statistics);
browser.Run();
return 0;
}
@@ -0,0 +1,136 @@
#include "EmbeddedFileSystem.h"
#include "EmbeddedFiles.h"
#include <Ultralight/Buffer.h>
#include <Ultralight/String.h>
#include <Ultralight/platform/FileSystem.h>
#include <Ultralight/platform/Platform.h>
static const char* FileExtensionToMimeType(const char* ext);
namespace ultralight {
inline static std::string ToString(const String& str)
{
return std::string(str.utf8().data(), str.utf8().length());
}
EmbeddedFileSystem::EmbeddedFileSystem()
{
}
bool EmbeddedFileSystem::FileExists(const String& file_path)
{
auto& embedded_files = GetEmbeddedFiles();
return embedded_files.find(ToString(file_path)) != embedded_files.end();
}
String EmbeddedFileSystem::GetFileMimeType(const String& file_path)
{
std::string file_path_utf8 = ToString(file_path);
size_t last_dot_pos = file_path_utf8.rfind('.');
std::string ext = (last_dot_pos != std::string::npos) ? file_path_utf8.substr(last_dot_pos + 1) : "";
return String(FileExtensionToMimeType(ext.c_str()));
}
String EmbeddedFileSystem::GetFileCharset(const String& file_path)
{
return "utf-8";
}
RefPtr<Buffer> EmbeddedFileSystem::OpenFile(const String& file_path)
{
auto& embedded_files = GetEmbeddedFiles();
auto it = embedded_files.find(ToString(file_path));
if (it != embedded_files.end()) {
return Buffer::Create((void*)it->second.first, it->second.second, nullptr, nullptr);
}
return nullptr;
}
} // namespace ultralight
const char* FileExtensionToMimeType(const char* ext)
{
static const std::unordered_map<std::string, const char*> mime_types = {
{ "html", "text/html" },
{ "htm", "text/html" },
{ "css", "text/css" },
{ "js", "application/javascript" },
{ "json", "application/json" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "png", "image/png" },
{ "gif", "image/gif" },
{ "webp", "image/webp" },
{ "svg", "image/svg+xml" },
{ "ico", "image/x-icon" },
{ "txt", "text/plain" },
{ "csv", "text/csv" },
{ "xml", "text/xml" },
{ "pdf", "application/pdf" },
{ "doc", "application/msword" },
{ "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
{ "ppt", "application/vnd.ms-powerpoint" },
{ "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
{ "xls", "application/vnd.ms-excel" },
{ "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
{ "mp3", "audio/mpeg" },
{ "wav", "audio/wav" },
{ "mp4", "video/mp4" },
{ "avi", "video/x-msvideo" },
{ "mov", "video/quicktime" },
{ "flv", "video/x-flv" },
{ "webm", "video/webm" },
{ "zip", "application/zip" },
{ "rar", "application/x-rar-compressed" },
{ "7z", "application/x-7z-compressed" },
{ "tar", "application/x-tar" },
{ "gz", "application/gzip" },
{ "mpg", "video/mpeg" },
{ "mpeg", "video/mpeg" },
{ "ogg", "application/ogg" },
{ "ogv", "video/ogg" },
{ "oga", "audio/ogg" },
{ "otf", "font/otf" },
{ "ttf", "font/ttf" },
{ "woff", "font/woff" },
{ "woff2", "font/woff2" },
{ "eot", "application/vnd.ms-fontobject" },
{ "sfnt", "font/sfnt" },
{ "bin", "application/octet-stream" },
{ "exe", "application/octet-stream" },
{ "dll", "application/octet-stream" },
{ "psd", "image/vnd.adobe.photoshop" },
{ "ai", "application/postscript" },
{ "eps", "application/postscript" },
{ "ps", "application/postscript" },
{ "m4a", "audio/m4a" },
{ "m4v", "video/x-m4v" },
{ "bmp", "image/bmp" },
{ "tiff", "image/tiff" },
{ "tif", "image/tiff" },
{ "mkv", "video/x-matroska" },
{ "mpa", "video/mpeg" },
{ "mpe", "video/mpeg" },
{ "mid", "audio/midi" },
{ "midi", "audio/midi" },
{ "3gp", "video/3gpp" },
{ "3g2", "video/3gpp2" },
{ "aif", "audio/aiff" },
{ "aiff", "audio/aiff" },
{ "aac", "audio/aac" },
{ "au", "audio/basic" },
{ "wasm", "application/wasm" },
{ "xhtml", "application/xhtml+xml" },
{ "qt", "video/quicktime" }
};
auto it = mime_types.find(std::string(ext));
if (it != mime_types.end()) {
return it->second;
}
return "application/octet-stream"; // default MIME type if not found
}
@@ -0,0 +1,24 @@
#pragma once
#include <Ultralight/platform/FileSystem.h>
namespace ultralight {
///
/// Custom file system implementation that loads files from resources embedded in the executable.
///
class EmbeddedFileSystem : public FileSystem {
public:
EmbeddedFileSystem();
virtual ~EmbeddedFileSystem() = default;
virtual bool FileExists(const String& file_path) override;
virtual String GetFileMimeType(const String& file_path) override;
virtual String GetFileCharset(const String& file_path) override;
virtual RefPtr<Buffer> OpenFile(const String& file_path) override;
};
} // namespace ultralight