Skip to content

Commit

Permalink
Implement URI navigation support extension to vice plugin API, and im…
Browse files Browse the repository at this point in the history
…plement it both in browservice and retrojsvice.
  • Loading branch information
ttalvitie committed Aug 17, 2021
1 parent 543ec7b commit 7dc7b19
Show file tree
Hide file tree
Showing 14 changed files with 300 additions and 41 deletions.
14 changes: 12 additions & 2 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void Server::shutdown() {
}
}

uint64_t Server::onViceContextCreateWindowRequest(string& reason) {
uint64_t Server::onViceContextCreateWindowRequest(string& reason, optional<string> uri) {
REQUIRE_UI_THREAD();
REQUIRE(state_ != ShutdownComplete);

Expand All @@ -61,7 +61,7 @@ uint64_t Server::onViceContextCreateWindowRequest(string& reason) {
uint64_t handle = nextWindowHandle_++;
REQUIRE(handle);

shared_ptr<Window> window = Window::tryCreate(shared_from_this(), handle);
shared_ptr<Window> window = Window::tryCreate(shared_from_this(), handle, uri);
if(window) {
REQUIRE(openWindows_.emplace(handle, window).second);
return handle;
Expand Down Expand Up @@ -182,6 +182,16 @@ void Server::onViceContextNavigate(uint64_t window, int direction) {
it->second->navigate(direction);
}

void Server::onViceContextNavigateToURI(uint64_t window, string uri) {
REQUIRE_UI_THREAD();
REQUIRE(state_ != ShutdownComplete);

auto it = openWindows_.find(window);
REQUIRE(it != openWindows_.end());

it->second->navigateToURI(uri);
}

void Server::onViceContextCopyToClipboard(string text) {
REQUIRE_UI_THREAD();
REQUIRE(state_ != ShutdownComplete);
Expand Down
3 changes: 2 additions & 1 deletion src/server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ SHARED_ONLY_CLASS(Server);
void shutdown();

// ViceContextEventHandler:
virtual uint64_t onViceContextCreateWindowRequest(string& reason) override;
virtual uint64_t onViceContextCreateWindowRequest(string& reason, optional<string> uri) override;
virtual void onViceContextCloseWindow(uint64_t window) override;
virtual void onViceContextResizeWindow(
uint64_t window, int width, int height
Expand All @@ -56,6 +56,7 @@ SHARED_ONLY_CLASS(Server);
virtual void onViceContextKeyUp(uint64_t window, int key) override;
virtual void onViceContextLoseFocus(uint64_t window) override;
virtual void onViceContextNavigate(uint64_t window, int direction) override;
virtual void onViceContextNavigateToURI(uint64_t window, string uri) override;
virtual void onViceContextCopyToClipboard(string text) override;
virtual void onViceContextRequestClipboardContent() override;
virtual void onViceContextUploadFile(
Expand Down
72 changes: 59 additions & 13 deletions src/vice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
namespace browservice {

struct VicePlugin::APIFuncs {
#define FOREACH_VICE_API_FUNC \
#define FOREACH_REQUIRED_VICE_API_FUNC \
FOREACH_VICE_API_FUNC_ITEM(isAPIVersionSupported) \
FOREACH_VICE_API_FUNC_ITEM(getVersionString) \
FOREACH_VICE_API_FUNC_ITEM(initContext) \
Expand All @@ -39,8 +39,13 @@ struct VicePlugin::APIFuncs {
FOREACH_VICE_API_FUNC_ITEM(setGlobalLogCallback) \
FOREACH_VICE_API_FUNC_ITEM(setGlobalPanicCallback)

#define FOREACH_VICE_API_FUNC \
FOREACH_REQUIRED_VICE_API_FUNC \
FOREACH_VICE_API_FUNC_ITEM(isExtensionSupported) \
FOREACH_VICE_API_FUNC_ITEM(URINavigation_enable)

#define FOREACH_VICE_API_FUNC_ITEM(name) \
decltype(&vicePluginAPI_ ## name) name;
decltype(&vicePluginAPI_ ## name) name = nullptr;

FOREACH_VICE_API_FUNC
#undef FOREACH_VICE_API_FUNC_ITEM
Expand Down Expand Up @@ -136,7 +141,7 @@ shared_ptr<VicePlugin> VicePlugin::load(string filename) {

void* sym;

#define FOREACH_VICE_API_FUNC_ITEM(name) \
#define LOAD_API_FUNC(name) \
sym = dlsym(lib, "vicePluginAPI_" #name); \
if(sym == nullptr) { \
const char* err = dlerror(); \
Expand All @@ -149,18 +154,30 @@ shared_ptr<VicePlugin> VicePlugin::load(string filename) {
} \
apiFuncs->name = (decltype(apiFuncs->name))sym;

FOREACH_VICE_API_FUNC
#define FOREACH_VICE_API_FUNC_ITEM(name) LOAD_API_FUNC(name)
FOREACH_REQUIRED_VICE_API_FUNC
#undef FOREACH_VICE_API_FUNC_ITEM

uint64_t apiVersion = 1000000;
const uint64_t BasicAPIVersion = 1000000;
const uint64_t ExtensionAPIVersion = 1000001;

if(!apiFuncs->isAPIVersionSupported(apiVersion)) {
ERROR_LOG(
"Vice plugin ", filename,
" does not support API version ", apiVersion
);
REQUIRE(dlclose(lib) == 0);
return {};
uint64_t apiVersion;
if(apiFuncs->isAPIVersionSupported(ExtensionAPIVersion)) {
apiVersion = ExtensionAPIVersion;
LOAD_API_FUNC(isExtensionSupported);
if(apiFuncs->isExtensionSupported(apiVersion, "URINavigation")) {
LOAD_API_FUNC(URINavigation_enable);
}
} else {
apiVersion = BasicAPIVersion;
if(!apiFuncs->isAPIVersionSupported(apiVersion)) {
ERROR_LOG(
"Vice plugin ", filename,
" does not support API version ", apiVersion
);
REQUIRE(dlclose(lib) == 0);
return {};
}
}

apiFuncs->setGlobalLogCallback(
Expand Down Expand Up @@ -405,6 +422,35 @@ void ViceContext::start(shared_ptr<ViceContextEventHandler> eventHandler) {
void* callbackData = (void*)(new weak_ptr<ViceContext>(shared_from_this()));
#endif

if(plugin_->apiFuncs_->URINavigation_enable != nullptr) {
VicePluginAPI_URINavigation_Callbacks uriNavigationCallbacks;
memset(&uriNavigationCallbacks, 0, sizeof(VicePluginAPI_URINavigation_Callbacks));

uriNavigationCallbacks.createWindowWithURI =
CTX_CALLBACK(uint64_t, (char** msg, const char* uri), {
string sanitizedURI = sanitizeUTF8String(uri);
string reason = "Unknown reason";
uint64_t window =
self->eventHandler_->onViceContextCreateWindowRequest(reason, sanitizedURI);
if(window || msg == nullptr) {
REQUIRE(self->openWindows_.insert(window).second);
return window;
} else {
*msg = createMallocString(reason);
return 0;
}
});

uriNavigationCallbacks.navigateWindowToURI =
CTX_CALLBACK(void, (uint64_t window, const char* uri), {
REQUIRE(self->openWindows_.count(window));
string sanitizedURI = sanitizeUTF8String(uri);
self->eventHandler_->onViceContextNavigateToURI(window, sanitizedURI);
});

plugin_->apiFuncs_->URINavigation_enable(ctx_, uriNavigationCallbacks);
}

VicePluginAPI_Callbacks callbacks;
memset(&callbacks, 0, sizeof(VicePluginAPI_Callbacks));

Expand All @@ -423,7 +469,7 @@ void ViceContext::start(shared_ptr<ViceContextEventHandler> eventHandler) {
callbacks.createWindow = CTX_CALLBACK(uint64_t, (char** msg), {
string reason = "Unknown reason";
uint64_t window =
self->eventHandler_->onViceContextCreateWindowRequest(reason);
self->eventHandler_->onViceContextCreateWindowRequest(reason, {});
if(window || msg == nullptr) {
REQUIRE(self->openWindows_.insert(window).second);
return window;
Expand Down
3 changes: 2 additions & 1 deletion src/vice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ViceContextEventHandler {
public:
// To deny window creation, return 0 and optionally set msg to short
// human-readable reason for the denial.
virtual uint64_t onViceContextCreateWindowRequest(string& msg) = 0;
virtual uint64_t onViceContextCreateWindowRequest(string& msg, optional<string> uri) = 0;

virtual void onViceContextCloseWindow(uint64_t window) = 0;
virtual void onViceContextResizeWindow(
Expand Down Expand Up @@ -107,6 +107,7 @@ class ViceContextEventHandler {
virtual void onViceContextLoseFocus(uint64_t window) = 0;

virtual void onViceContextNavigate(uint64_t window, int direction) = 0;
virtual void onViceContextNavigateToURI(uint64_t window, string uri) = 0;

virtual void onViceContextCopyToClipboard(string text) = 0;
virtual void onViceContextRequestClipboardContent() = 0;
Expand Down
18 changes: 16 additions & 2 deletions src/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@ class Window::Client :

shared_ptr<Window> Window::tryCreate(
shared_ptr<WindowEventHandler> eventHandler,
uint64_t handle
uint64_t handle,
optional<string> uri
) {
REQUIRE_UI_THREAD();
REQUIRE(eventHandler);
Expand All @@ -462,7 +463,7 @@ shared_ptr<Window> Window::tryCreate(
if(!CefBrowserHost::CreateBrowser(
windowInfo,
client,
globals->config->startPage,
(uri.has_value() && !uri.value().empty()) ? uri.value() : globals->config->startPage,
browserSettings,
nullptr,
nullptr
Expand Down Expand Up @@ -543,6 +544,19 @@ void Window::navigate(int direction) {
}
}

void Window::navigateToURI(string uri) {
REQUIRE_UI_THREAD();
REQUIRE(state_ == Open);

if(!uri.empty() && browser_) {
CefRefPtr<CefFrame> frame = browser_->GetMainFrame();
if(frame) {
frame->LoadURL(uri);
rootWidget_->browserArea()->takeFocus();
}
}
}

void Window::uploadFile(shared_ptr<ViceFileUpload> file) {
REQUIRE_UI_THREAD();
REQUIRE(state_ == Open);
Expand Down
5 changes: 4 additions & 1 deletion src/window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ SHARED_ONLY_CLASS(Window);
// Returns empty pointer if CEF browser creation fails.
static shared_ptr<Window> tryCreate(
shared_ptr<WindowEventHandler> eventHandler,
uint64_t handle
uint64_t handle,
optional<string> uri
);

// Private constructor.
Expand All @@ -90,6 +91,8 @@ SHARED_ONLY_CLASS(Window);
// -1 = back, 0 = refresh, 1 = forward.
void navigate(int direction);

void navigateToURI(string uri);

void uploadFile(shared_ptr<ViceFileUpload> file);
void cancelFileUpload();

Expand Down
54 changes: 54 additions & 0 deletions vice_plugin_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ extern "C" {
*
* 6. The program destroys the plugin context using vicePluginAPI_destroyContext.
*
* API version 1000001 adds support for extensions; see the section "API version 1000001" for more
* information.
*
* General API conventions and rules:
*
* - The program and plugin communicate bidirectionally using function calls. The program directly
Expand Down Expand Up @@ -687,6 +690,57 @@ void vicePluginAPI_setGlobalPanicCallback(
void (*destructorCallback)(void* data)
);

/***************************************************************************************************
*** API version 1000001 ***
***************************/

/* API version 1000001 contains all the same functionality as API version 1000000, and adds support
* for API extensions through the function vicePluginAPI_isExtensionSupported.
*/

/* Returns 1 if the vice plugin supports API extension with given name (null-terminated and case
* sensitive), and 0 otherwise. This function may be called at any time from any thread, and the
* same plugin should always return the same result for the same extension name. If the return value
* is 1, the program may use the functions for that extensions as documented below or in other
* sources. Avoidance of name conflicts should be kept in mind when naming new extensions;
* organization names or other identifiers may be added as necessary, and extension function names
* should start with vicePluginAPI_EXTNAME_ (where EXTNAME is replaced by the name of the extension)
* where possible.
*/
int vicePluginAPI_isExtensionSupported(uint64_t apiVersion, const char* name);

/***************************************************************************************************
*** API extension "URINavigation" ***
*************************************/

/* Extension that allows the plugin to navigate windows (both existing and newly created) to
* arbitrary URIs (Uniform Resource Identifiers) through two additional callbacks in the
* VicePluginAPI_URINavigation_Callbacks structure. The extension is enabled by the program using
* vicePluginAPI_URINavigation_enable. The program should be able to handle arbitrary
* null-terminated binary data in the URI strings given by the plugin in addition to valid URIs,
* validating and sanitizing the strings if necessary.
*/

struct VicePluginAPI_URINavigation_Callbacks {
/* Variant of createWindow in VicePluginAPI_Callbacks that requests that the created window is
* initially navigated to given URI.
*/
uint64_t (*createWindowWithURI)(void*, char** msg, const char* uri);

/* Called by the plugin to request that given existing window navigates to given URI. */
void (*navigateWindowToURI)(void*, uint64_t window, const char* uri);
};
typedef struct VicePluginAPI_URINavigation_Callbacks VicePluginAPI_URINavigation_Callbacks;

/* Enables the URINavigation callbacks in given context. May only be called once for each context,
* after vicePluginAPI_initContext and before vicePluginAPI_start. The vice plugin uses the
* callbacks similarly to the callbacks given in vicePluginAPI_start.
*/
void vicePluginAPI_URINavigation_enable(
VicePluginAPI_Context* ctx,
VicePluginAPI_URINavigation_Callbacks callbacks
);

#ifdef __cplusplus
}
#endif
49 changes: 49 additions & 0 deletions viceplugins/retrojsvice/src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,15 @@ Context::~Context() {
INFO_LOG("Destroying retrojsvice plugin context");
}

void Context::URINavigation_enable(VicePluginAPI_URINavigation_Callbacks callbacks) {
APILock apiLock(this);

REQUIRE(state_ == Pending);

REQUIRE(!uriNavigationCallbacks_.has_value());
uriNavigationCallbacks_ = callbacks;
}

void Context::start(
VicePluginAPI_Callbacks callbacks,
void* callbackData
Expand Down Expand Up @@ -647,6 +656,29 @@ variant<uint64_t, string> Context::onWindowManagerCreateWindowRequest() {
}
}

variant<uint64_t, string> Context::onWindowManagerCreateWindowWithURIRequest(string uri) {
REQUIRE(threadRunningPumpEvents);
REQUIRE(state_ == Running);

if(!uriNavigationCallbacks_) {
return string("Program has not enabled URINavigation vice plugin API extension");
}

REQUIRE(uriNavigationCallbacks_->createWindowWithURI != nullptr);
char* msgC = nullptr;
uint64_t handle = uriNavigationCallbacks_->createWindowWithURI(callbackData_, &msgC, uri.c_str());

if(handle) {
REQUIRE(msgC == nullptr);
return handle;
} else {
REQUIRE(msgC != nullptr);
string msg = msgC;
free(msgC);
return msg;
}
}

void Context::onWindowManagerCloseWindow(uint64_t window) {
REQUIRE(threadRunningPumpEvents);
REQUIRE(state_ == Running);
Expand Down Expand Up @@ -748,6 +780,23 @@ FORWARD_WINDOW_EVENT(
navigate, (callbackData_, window, direction)
)

void Context::onWindowManagerNavigateToURI(uint64_t window, string uri) {
REQUIRE(threadRunningPumpEvents);
REQUIRE(state_ == Running);
REQUIRE(window);

if(!uriNavigationCallbacks_) {
WARNING_LOG(
"Window navigation to URI denied because the program has not enabled URINavigation "
"vice plugin API extension"
);
return;
}

REQUIRE(uriNavigationCallbacks_->navigateWindowToURI != nullptr);
uriNavigationCallbacks_->navigateWindowToURI(callbackData_, window, uri.c_str());
}

void Context::onWindowManagerUploadFile(
uint64_t window, string name, shared_ptr<FileUpload> file
) {
Expand Down
Loading

0 comments on commit 7dc7b19

Please sign in to comment.