Skip to content

Commit 4cb4cbc

Browse files
authored
Merge pull request #103 from NuiCpp/devel
Linux Custom Scheme Handler Fix
2 parents f58becc + 73485d7 commit 4cb4cbc

File tree

2 files changed

+289
-41
lines changed

2 files changed

+289
-41
lines changed

nui/src/nui/backend/gobject.hpp

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#pragma once
2+
3+
namespace Nui
4+
{
5+
namespace Detail
6+
{
7+
template <typename T>
8+
T* referenceGObject(T* ptr)
9+
{
10+
if (ptr)
11+
g_object_ref_sink(ptr);
12+
return ptr;
13+
}
14+
15+
template <typename T>
16+
void unreferenceGObject(T* ptr)
17+
{
18+
if (ptr)
19+
g_object_unref(ptr);
20+
}
21+
}
22+
23+
template <typename T>
24+
class GObjectReference
25+
{
26+
public:
27+
struct AdoptFlag
28+
{};
29+
30+
using value_type = T;
31+
using pointer_type = value_type*;
32+
33+
GObjectReference()
34+
: ptr_{nullptr}
35+
{}
36+
37+
explicit GObjectReference(pointer_type ptr)
38+
: ptr_{ptr}
39+
{
40+
if (ptr_)
41+
Detail::referenceGObject(ptr_);
42+
}
43+
44+
GObjectReference(GObjectReference const& other)
45+
: ptr_{other.ptr_}
46+
{
47+
if (ptr_)
48+
Detail::referenceGObject(ptr_);
49+
}
50+
51+
template <typename U>
52+
GObjectReference(GObjectReference<U> const& other)
53+
: ptr_{other.get()}
54+
{
55+
if (ptr_)
56+
Detail::referenceGObject(ptr_);
57+
}
58+
59+
GObjectReference(GObjectReference&& other)
60+
: ptr_{other.release()}
61+
{}
62+
63+
~GObjectReference()
64+
{
65+
Detail::unreferenceGObject(ptr_);
66+
}
67+
68+
void clear()
69+
{
70+
using std::swap;
71+
T* ptr = nullptr;
72+
swap(ptr_, ptr);
73+
if (ptr)
74+
derefGPtr(ptr);
75+
}
76+
77+
pointer_type release()
78+
{
79+
pointer_type ptr = nullptr;
80+
using std::swap;
81+
swap(ptr_, ptr);
82+
return ptr;
83+
}
84+
85+
T* get() const
86+
{
87+
return ptr_;
88+
}
89+
T& operator*() const
90+
{
91+
return *ptr_;
92+
}
93+
T* operator->() const
94+
{
95+
return ptr_;
96+
}
97+
98+
explicit operator bool() const
99+
{
100+
return ptr_ != nullptr;
101+
}
102+
bool operator!() const
103+
{
104+
return ptr_ == nullptr;
105+
}
106+
107+
GObjectReference& operator=(GObjectReference const& other)
108+
{
109+
pointer_type optr = other.get();
110+
if (optr)
111+
Detail::referenceGObject(optr);
112+
pointer_type ptr = ptr_;
113+
ptr_ = optr;
114+
if (ptr)
115+
Detail::unreferenceGObject(ptr);
116+
return *this;
117+
}
118+
GObjectReference& operator=(GObjectReference&& other)
119+
{
120+
GObjectReference(std::move(other)).swap(*this);
121+
return *this;
122+
}
123+
GObjectReference& operator=(pointer_type optr)
124+
{
125+
pointer_type ptr = ptr_;
126+
if (optr)
127+
Detail::referenceGObject(optr);
128+
ptr_ = optr;
129+
if (ptr)
130+
Detail::unreferenceGObject(ptr);
131+
return *this;
132+
}
133+
template <typename U>
134+
GObjectReference& operator=(GObjectReference<U> const& other)
135+
{
136+
GObjectReference(other).swap(*this);
137+
return *this;
138+
}
139+
140+
void swap(GObjectReference& other)
141+
{
142+
using std::swap;
143+
swap(ptr_, other.ptr_);
144+
}
145+
static GObjectReference<T> adoptReference(T* ptr)
146+
{
147+
return GObjectReference<T>(ptr, AdoptFlag{});
148+
}
149+
150+
private:
151+
GObjectReference(pointer_type ptr, AdoptFlag)
152+
: ptr_{ptr}
153+
{}
154+
155+
private:
156+
T* ptr_;
157+
};
158+
159+
template <typename T>
160+
void swap(GObjectReference<T>& lhs, GObjectReference<T>& rhs)
161+
{
162+
lhs.swap(rhs);
163+
}
164+
165+
template <typename T, typename U>
166+
bool operator==(GObjectReference<T> const& lhs, GObjectReference<U> const& rhs)
167+
{
168+
return lhs.get() == rhs.get();
169+
}
170+
template <typename T, typename U>
171+
bool operator==(GObjectReference<T> const& lhs, T* rhs)
172+
{
173+
return lhs.get() == rhs;
174+
}
175+
template <typename T, typename U>
176+
bool operator==(T* lhs, GObjectReference<U> const& rhs)
177+
{
178+
return lhs == rhs.get();
179+
}
180+
181+
template <typename T, typename U>
182+
bool operator!=(GObjectReference<T> const& lhs, GObjectReference<U> const& rhs)
183+
{
184+
return lhs.get() != rhs.get();
185+
}
186+
template <typename T, typename U>
187+
bool operator!=(GObjectReference<T> const& lhs, T* rhs)
188+
{
189+
return lhs.get() != rhs;
190+
}
191+
template <typename T, typename U>
192+
bool operator!=(T* lhs, GObjectReference<U> const& rhs)
193+
{
194+
return lhs != rhs.get();
195+
}
196+
197+
template <typename T, typename U>
198+
GObjectReference<T> static_pointer_cast(GObjectReference<U> const& ptr)
199+
{
200+
return GObjectReference<T>(static_cast<T*>(ptr.get()));
201+
}
202+
template <typename T, typename U>
203+
GObjectReference<T> dynamic_pointer_cast(GObjectReference<U> const& ptr)
204+
{
205+
return GObjectReference<T>(dynamic_cast<T*>(ptr.get()));
206+
}
207+
}

nui/src/nui/backend/window_impl_linux.ipp

Lines changed: 82 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
1+
#include "gobject.hpp"
2+
13
namespace Nui::Impl::Linux
24
{
5+
struct AsyncResponse
6+
{
7+
GObjectReference<GInputStream> stream;
8+
GObjectReference<WebKitURISchemeResponse> response;
9+
std::string data;
10+
};
11+
312
struct SchemeContext
413
{
514
std::size_t id;
615
std::weak_ptr<Window::LinuxImplementation> impl;
716
CustomScheme schemeInfo;
17+
std::mutex asyncResponsesGuard;
18+
std::map<int, AsyncResponse> asyncResponses;
19+
int asyncResponseCounter = 0;
20+
21+
void gcResponses()
22+
{
23+
std::lock_guard<std::mutex> asyncResponsesGuard{this->asyncResponsesGuard};
24+
std::vector<int> removals{};
25+
for (auto it = asyncResponses.begin(); it != asyncResponses.end(); ++it)
26+
{
27+
GInputStream* stream = it->second.stream.get();
28+
if (g_input_stream_is_closed(stream))
29+
{
30+
removals.push_back(it->first);
31+
break;
32+
}
33+
}
34+
for (auto const& removal : removals)
35+
asyncResponses.erase(removal);
36+
}
837
};
938
}
1039

@@ -19,7 +48,10 @@ std::size_t strlenLimited(char const* str, std::size_t limit)
1948
extern "C" {
2049
void uriSchemeRequestCallback(WebKitURISchemeRequest* request, gpointer userData)
2150
{
51+
using namespace std::string_literals;
52+
2253
auto* schemeContext = static_cast<Nui::Impl::Linux::SchemeContext*>(userData);
54+
schemeContext->gcResponses();
2355

2456
// const auto path = std::string_view{webkit_uri_scheme_request_get_path(request)};
2557
// const auto scheme = std::string_view{webkit_uri_scheme_request_get_scheme(request)};
@@ -102,50 +134,59 @@ extern "C" {
102134
.method = std::string{cmethod},
103135
});
104136

105-
GInputStream* stream;
106-
stream = g_memory_input_stream_new_from_data(
107-
responseObj.body.c_str(), static_cast<gssize>(responseObj.body.size()), nullptr);
108-
auto deleteStream = Nui::ScopeExit{[stream] {
109-
g_object_unref(stream);
110-
}};
111-
112-
WebKitURISchemeResponse* response;
113-
response = webkit_uri_scheme_response_new(stream, static_cast<gint64>(responseObj.body.size()));
114-
115-
std::string contentType;
116-
if (responseObj.headers.find("Content-Type") != responseObj.headers.end())
117-
{
118-
auto range = responseObj.headers.equal_range("Content-Type");
119-
for (auto it = range.first; it != range.second; ++it)
120-
contentType += it->second + "; ";
121-
}
122-
else
123-
contentType = "application/octet-stream";
124-
125-
webkit_uri_scheme_response_set_content_type(response, contentType.c_str());
126-
webkit_uri_scheme_response_set_status(response, static_cast<guint>(responseObj.statusCode), nullptr);
127-
128-
auto* responseHeaders = soup_message_headers_new(SOUP_MESSAGE_HEADERS_RESPONSE);
129-
auto deleteResponseHeaders = Nui::ScopeExit{[responseHeaders] {
130-
g_object_unref(responseHeaders);
131-
}};
132-
for (auto const& [key, value] : responseObj.headers)
133-
soup_message_headers_append(responseHeaders, key.c_str(), value.c_str());
134-
135-
if (responseObj.headers.find("Access-Control-Allow-Origin") == responseObj.headers.end() &&
136-
!schemeInfo.allowedOrigins.empty())
137-
{
138-
auto const& front = schemeInfo.allowedOrigins.front();
139-
soup_message_headers_append(responseHeaders, "Access-Control-Allow-Origin", front.c_str());
140-
}
141-
142-
webkit_uri_scheme_response_set_http_headers(response, responseHeaders);
143-
webkit_uri_scheme_request_finish_with_response(request, response);
137+
using Nui::GObjectReference;
138+
139+
std::lock_guard<std::mutex> asyncResponsesGuard{schemeContext->asyncResponsesGuard};
140+
++schemeContext->asyncResponseCounter;
141+
schemeContext->asyncResponses[schemeContext->asyncResponseCounter] = Nui::Impl::Linux::AsyncResponse{};
142+
auto& asyncResponse = schemeContext->asyncResponses[schemeContext->asyncResponseCounter];
143+
asyncResponse.data = std::move(responseObj.body);
144+
145+
asyncResponse.stream = Nui::GObjectReference<GInputStream>::adoptReference(g_memory_input_stream_new_from_data(
146+
asyncResponse.data.data(), static_cast<gssize>(asyncResponse.data.size()), nullptr));
147+
148+
asyncResponse.response = Nui::GObjectReference<WebKitURISchemeResponse>::adoptReference(
149+
webkit_uri_scheme_response_new(asyncResponse.stream.get(), static_cast<gint64>(asyncResponse.data.size())));
150+
151+
const std::string contentType = [&]() {
152+
if (responseObj.headers.find("Content-Type") != responseObj.headers.end())
153+
{
154+
std::string contentType;
155+
auto range = responseObj.headers.equal_range("Content-Type");
156+
for (auto it = range.first; it != range.second; ++it)
157+
contentType += it->second + "; ";
158+
contentType.pop_back();
159+
contentType.pop_back();
160+
return contentType;
161+
}
162+
return "application/octet-stream"s;
163+
}();
164+
165+
webkit_uri_scheme_response_set_content_type(asyncResponse.response.get(), contentType.c_str());
166+
webkit_uri_scheme_response_set_status(
167+
asyncResponse.response.get(), static_cast<guint>(responseObj.statusCode), nullptr);
168+
169+
auto setHeaders = [&]() {
170+
auto* responseHeaders = soup_message_headers_new(SOUP_MESSAGE_HEADERS_RESPONSE);
171+
for (auto const& [key, value] : responseObj.headers)
172+
soup_message_headers_append(responseHeaders, key.c_str(), value.c_str());
173+
174+
if (responseObj.headers.find("Access-Control-Allow-Origin") == responseObj.headers.end() &&
175+
!schemeInfo.allowedOrigins.empty())
176+
{
177+
auto const& front = schemeInfo.allowedOrigins.front();
178+
soup_message_headers_append(responseHeaders, "Access-Control-Allow-Origin", front.c_str());
179+
}
180+
webkit_uri_scheme_response_set_http_headers(asyncResponse.response.get(), responseHeaders);
181+
};
182+
183+
setHeaders();
184+
webkit_uri_scheme_request_finish_with_response(request, asyncResponse.response.get());
144185
}
145186

146-
void uriSchemeDestroyNotify(void*)
187+
void uriSchemeDestroyNotify(void* userData)
147188
{
148-
// Happens when everything else is already dead.
189+
// Useless, because called when everything is already destroyed
149190
}
150191
}
151192

0 commit comments

Comments
 (0)