Skip to content

Commit 4f8098e

Browse files
committed
Refactor UrlOpener to pimpl and add CanOpen
Move URL validation and shared UrlOpener logic into a new core `src/url_opener.cpp`, and switch each platform backend to a concrete `UrlOpener::Impl` class. This removes the old `url_opener_internal` launcher helpers and standardizes platform result construction. The C API was regenerated to add `native_url_opener_can_open`, rename error code constants, and simplify result string ownership/freeing. Tests were updated to validate `CanOpen` and `Open` behavior through the public singleton API.
1 parent 2619340 commit 4f8098e

13 files changed

Lines changed: 429 additions & 341 deletions

src/capi/url_opener_c.cpp

Lines changed: 59 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,95 @@
1+
// AUTO-GENERATED. DO NOT EDIT.
2+
// Any manual changes WILL BE LOST when this file is regenerated.
3+
14
#include "url_opener_c.h"
25

3-
#include <exception>
6+
#include <cstdlib>
7+
#include <cstring>
48
#include <string>
59

610
#include "../url_opener.h"
7-
#include "string_utils_c.h"
811

9-
using namespace nativeapi;
12+
#include <cstdio>
1013

1114
namespace {
1215

13-
native_url_open_error_code_t ToCErrorCode(UrlOpenErrorCode code) {
14-
switch (code) {
15-
case UrlOpenErrorCode::kNone:
16-
return NATIVE_URL_OPEN_ERROR_NONE;
17-
case UrlOpenErrorCode::kInvalidUrlEmpty:
18-
return NATIVE_URL_OPEN_ERROR_INVALID_URL_EMPTY;
19-
case UrlOpenErrorCode::kInvalidUrlMissingScheme:
20-
return NATIVE_URL_OPEN_ERROR_INVALID_URL_MISSING_SCHEME;
21-
case UrlOpenErrorCode::kInvalidUrlUnsupportedScheme:
22-
return NATIVE_URL_OPEN_ERROR_INVALID_URL_UNSUPPORTED_SCHEME;
23-
case UrlOpenErrorCode::kUnsupportedPlatform:
24-
return NATIVE_URL_OPEN_ERROR_UNSUPPORTED_PLATFORM;
25-
case UrlOpenErrorCode::kInvocationFailed:
26-
return NATIVE_URL_OPEN_ERROR_INVOCATION_FAILED;
16+
char* DupString(const std::string& value) {
17+
if (value.empty()) {
18+
return nullptr;
19+
}
20+
const auto size = value.size() + 1;
21+
auto* buffer = static_cast<char*>(std::malloc(size));
22+
if (!buffer) {
23+
return nullptr;
24+
}
25+
std::memcpy(buffer, value.c_str(), size);
26+
return buffer;
27+
}
28+
29+
native_url_open_error_code_t ToCUrlOpenErrorCode(nativeapi::UrlOpenErrorCode value) {
30+
switch (value) {
31+
case nativeapi::UrlOpenErrorCode::kNone:
32+
return NATIVE_URL_OPEN_ERROR_CODE_NONE;
33+
case nativeapi::UrlOpenErrorCode::kInvalidUrlEmpty:
34+
return NATIVE_URL_OPEN_ERROR_CODE_INVALID_URL_EMPTY;
35+
case nativeapi::UrlOpenErrorCode::kInvalidUrlMissingScheme:
36+
return NATIVE_URL_OPEN_ERROR_CODE_INVALID_URL_MISSING_SCHEME;
37+
case nativeapi::UrlOpenErrorCode::kInvalidUrlUnsupportedScheme:
38+
return NATIVE_URL_OPEN_ERROR_CODE_INVALID_URL_UNSUPPORTED_SCHEME;
39+
case nativeapi::UrlOpenErrorCode::kUnsupportedPlatform:
40+
return NATIVE_URL_OPEN_ERROR_CODE_UNSUPPORTED_PLATFORM;
41+
case nativeapi::UrlOpenErrorCode::kInvocationFailed:
42+
return NATIVE_URL_OPEN_ERROR_CODE_INVOCATION_FAILED;
2743
default:
28-
return NATIVE_URL_OPEN_ERROR_INVOCATION_FAILED;
44+
return NATIVE_URL_OPEN_ERROR_CODE_NONE;
2945
}
3046
}
3147

32-
native_url_open_result_t MakeUrlOpenResult(bool success,
33-
native_url_open_error_code_t error_code,
34-
const std::string& message) {
48+
native_url_open_result_t ToCUrlOpenResult(const nativeapi::UrlOpenResult& value) {
3549
native_url_open_result_t result = {};
36-
result.success = success;
37-
result.error_code = error_code;
38-
result.error_message = message.empty() ? nullptr : to_c_str(message);
50+
result.success = value.success;
51+
result.error_code = ToCUrlOpenErrorCode(value.error_code);
52+
result.error_message = DupString(value.error_message);
3953
return result;
4054
}
4155

4256
} // namespace
4357

4458
bool native_url_opener_is_supported(void) {
4559
try {
46-
return UrlOpener::GetInstance().IsSupported();
60+
return nativeapi::UrlOpener::GetInstance().IsSupported();
4761
} catch (...) {
62+
fprintf(stderr, "[nativeapi] %s: unexpected exception\n", "native_url_opener_is_supported");
4863
return false;
4964
}
5065
}
5166

52-
native_url_open_result_t native_url_opener_open(const char* url) {
53-
if (!url) {
54-
return MakeUrlOpenResult(false, NATIVE_URL_OPEN_ERROR_INVALID_URL_EMPTY, "URL is empty.");
67+
bool native_url_opener_can_open(const char* url) {
68+
try {
69+
return nativeapi::UrlOpener::GetInstance().CanOpen(std::string(url ? url : ""));
70+
} catch (...) {
71+
fprintf(stderr, "[nativeapi] %s: unexpected exception\n", "native_url_opener_can_open");
72+
return false;
5573
}
74+
}
5675

76+
native_url_open_result_t native_url_opener_open(const char* url) {
5777
try {
58-
const UrlOpenResult result = UrlOpener::GetInstance().Open(std::string(url));
59-
return MakeUrlOpenResult(result.success, ToCErrorCode(result.error_code), result.error_message);
60-
} catch (const std::exception& e) {
61-
return MakeUrlOpenResult(false, NATIVE_URL_OPEN_ERROR_INVOCATION_FAILED, e.what());
78+
const auto cpp_result = nativeapi::UrlOpener::GetInstance().Open(std::string(url ? url : ""));
79+
return ToCUrlOpenResult(cpp_result);
6280
} catch (...) {
63-
return MakeUrlOpenResult(false, NATIVE_URL_OPEN_ERROR_INVOCATION_FAILED,
64-
"Unexpected error while opening URL.");
81+
fprintf(stderr, "[nativeapi] %s: unexpected exception\n", "native_url_opener_open");
82+
native_url_open_result_t result = {};
83+
result.success = false;
84+
return result;
6585
}
6686
}
6787

68-
void native_url_open_result_free(native_url_open_result_t* result) {
69-
if (!result) {
88+
void native_url_open_result_free(native_url_open_result_t* value) {
89+
if (!value) {
7090
return;
7191
}
72-
73-
if (result->error_message) {
74-
free_c_str(result->error_message);
75-
result->error_message = nullptr;
76-
}
77-
result->success = false;
78-
result->error_code = NATIVE_URL_OPEN_ERROR_NONE;
92+
std::free(value->error_message);
93+
value->error_message = nullptr;
7994
}
95+

src/capi/url_opener_c.h

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// AUTO-GENERATED. DO NOT EDIT.
2+
// Any manual changes WILL BE LOST when this file is regenerated.
3+
14
#pragma once
25

36
#include <stdbool.h>
@@ -12,46 +15,32 @@
1215
extern "C" {
1316
#endif
1417

15-
/**
16-
* @brief Error codes returned by URL opening APIs.
17-
*/
1818
typedef enum {
19-
NATIVE_URL_OPEN_ERROR_NONE = 0,
20-
NATIVE_URL_OPEN_ERROR_INVALID_URL_EMPTY = 1,
21-
NATIVE_URL_OPEN_ERROR_INVALID_URL_MISSING_SCHEME = 2,
22-
NATIVE_URL_OPEN_ERROR_INVALID_URL_UNSUPPORTED_SCHEME = 3,
23-
NATIVE_URL_OPEN_ERROR_UNSUPPORTED_PLATFORM = 4,
24-
NATIVE_URL_OPEN_ERROR_INVOCATION_FAILED = 5,
19+
NATIVE_URL_OPEN_ERROR_CODE_NONE = 0,
20+
NATIVE_URL_OPEN_ERROR_CODE_INVALID_URL_EMPTY = 1,
21+
NATIVE_URL_OPEN_ERROR_CODE_INVALID_URL_MISSING_SCHEME = 2,
22+
NATIVE_URL_OPEN_ERROR_CODE_INVALID_URL_UNSUPPORTED_SCHEME = 3,
23+
NATIVE_URL_OPEN_ERROR_CODE_UNSUPPORTED_PLATFORM = 4,
24+
NATIVE_URL_OPEN_ERROR_CODE_INVOCATION_FAILED = 5,
2525
} native_url_open_error_code_t;
2626

27-
/**
28-
* @brief Result payload for URL open attempts.
29-
*/
3027
typedef struct {
3128
bool success;
3229
native_url_open_error_code_t error_code;
3330
char* error_message;
3431
} native_url_open_result_t;
3532

36-
/**
37-
* @brief Check whether URL opening is supported on this platform.
38-
*/
3933
FFI_PLUGIN_EXPORT
4034
bool native_url_opener_is_supported(void);
4135

42-
/**
43-
* @brief Attempt to open URL with the system default browser.
44-
*
45-
* Caller must release result.error_message via native_url_open_result_free().
46-
*/
36+
FFI_PLUGIN_EXPORT
37+
bool native_url_opener_can_open(const char* url);
38+
4739
FFI_PLUGIN_EXPORT
4840
native_url_open_result_t native_url_opener_open(const char* url);
4941

50-
/**
51-
* @brief Free owned memory inside a native_url_open_result_t.
52-
*/
5342
FFI_PLUGIN_EXPORT
54-
void native_url_open_result_free(native_url_open_result_t* result);
43+
void native_url_open_result_free(native_url_open_result_t* value);
5544

5645
#ifdef __cplusplus
5746
}
Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,26 @@
11
#include "../../url_opener.h"
2-
#include "../../url_opener_internal.h"
32

43
namespace nativeapi {
54
namespace {
65

7-
UrlLaunchOutcome LaunchUnsupported(const std::string& url) {
8-
(void)url;
9-
return {false, "URL opening is not implemented on Android in this native layer."};
10-
}
6+
class AndroidUrlOpenerImpl final : public UrlOpener::Impl {
7+
public:
8+
bool IsSupported() const override { return false; }
119

12-
} // namespace
10+
UrlOpenResult Open(const std::string& url) const override {
11+
(void)url;
12+
UrlOpenResult result;
13+
result.success = false;
14+
result.error_code = UrlOpenErrorCode::kUnsupportedPlatform;
15+
result.error_message = "URL opening is not implemented on Android in this native layer.";
16+
return result;
17+
}
18+
};
1319

14-
UrlOpener& UrlOpener::GetInstance() {
15-
static UrlOpener instance;
16-
return instance;
17-
}
20+
} // namespace
1821

19-
bool UrlOpener::IsSupported() const {
20-
return false;
21-
}
22+
UrlOpener::UrlOpener() : pimpl_(std::make_unique<AndroidUrlOpenerImpl>()) {}
2223

23-
UrlOpenResult UrlOpener::Open(const std::string& url) const {
24-
UrlOpenResult result = OpenUrlWithLauncher(url, LaunchUnsupported);
25-
if (!result.success && result.error_code == UrlOpenErrorCode::kInvocationFailed) {
26-
result.error_code = UrlOpenErrorCode::kUnsupportedPlatform;
27-
}
28-
return result;
29-
}
24+
UrlOpener::~UrlOpener() = default;
3025

3126
} // namespace nativeapi

0 commit comments

Comments
 (0)