-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
#include <emscripten.h> | ||
|
||
#define _EM_JS_INLINE(ret, c_name, js_name, params, code) \ | ||
extern "C" { \ | ||
ret c_name params EM_IMPORT(js_name); \ | ||
EMSCRIPTEN_KEEPALIVE \ | ||
__attribute__((section("em_js"), aligned(1))) inline char __em_js__##js_name[] = \ | ||
#params "<::>" code; \ | ||
} | ||
|
||
#define EM_JS_INLINE(ret, name, params, ...) _EM_JS_INLINE(ret, name, name, params, #__VA_ARGS__) | ||
|
||
namespace emscripten_browser_clipboard { | ||
|
||
/////////////////////////////////// Interface ////////////////////////////////// | ||
|
||
using paste_handler = void(*)(std::string&&, void*); | ||
using copy_handler = char const*(*)(void*); | ||
|
||
inline void paste(paste_handler callback, void *callback_data = nullptr); | ||
inline void copy(copy_handler callback, void *callback_data = nullptr); | ||
inline void copy(std::string const &content); | ||
|
||
///////////////////////////////// Implementation /////////////////////////////// | ||
|
||
namespace detail { | ||
|
||
EM_JS_INLINE(void, paste_js, (paste_handler callback, void *callback_data), { | ||
/// Register the given callback to handle paste events. Callback data pointer is passed through to the callback. | ||
/// Paste handler callback signature is: | ||
/// void my_handler(std::string const &paste_data, void *callback_data = nullptr); | ||
document.addEventListener('paste', (event) => { | ||
Module["ccall"]('emscripten_browser_clipboard_detail_paste_return', 'number', ['string', 'number', 'number'], [event.clipboardData.getData('text/plain'), callback, callback_data]); | ||
}); | ||
}); | ||
|
||
EM_JS_INLINE(void, copy_js, (copy_handler callback, void *callback_data), { | ||
/// Register the given callback to handle copy events. Callback data pointer is passed through to the callback. | ||
/// Copy handler callback signature is: | ||
/// char const *my_handler(void *callback_data = nullptr); | ||
document.addEventListener('copy', (event) => { | ||
const content_ptr = Module["ccall"]('emscripten_browser_clipboard_detail_copy_return', 'number', ['number', 'number'], [callback, callback_data]); | ||
event.clipboardData.setData('text/plain', UTF8ToString(content_ptr)); | ||
event.preventDefault(); | ||
}); | ||
}); | ||
|
||
EM_JS_INLINE(void, copy_async_js, (char const *content_ptr), { | ||
/// Attempt to copy the provided text asynchronously | ||
navigator.clipboard.writeText(UTF8ToString(content_ptr)); | ||
}); | ||
|
||
} // namespace detail | ||
|
||
inline void paste(paste_handler callback, void *callback_data) { | ||
/// C++ wrapper for javascript paste call | ||
detail::paste_js(callback, callback_data); | ||
} | ||
|
||
inline void copy(copy_handler callback, void *callback_data) { | ||
/// C++ wrapper for javascript copy call | ||
detail::copy_js(callback, callback_data); | ||
} | ||
|
||
inline void copy(std::string const &content) { | ||
/// C++ wrapper for javascript copy call | ||
detail::copy_async_js(content.c_str()); | ||
} | ||
|
||
namespace detail { | ||
|
||
extern "C" { | ||
|
||
EMSCRIPTEN_KEEPALIVE inline int emscripten_browser_clipboard_detail_paste_return(char const *paste_data, paste_handler callback, void *callback_data); | ||
|
||
EMSCRIPTEN_KEEPALIVE inline int emscripten_browser_clipboard_detail_paste_return(char const *paste_data, paste_handler callback, void *callback_data) { | ||
/// Call paste callback - this function is called from javascript when the paste event occurs | ||
callback(paste_data, callback_data); | ||
return 1; | ||
} | ||
|
||
EMSCRIPTEN_KEEPALIVE inline char const *emscripten_browser_clipboard_detail_copy_return(copy_handler callback, void *callback_data); | ||
|
||
EMSCRIPTEN_KEEPALIVE inline char const *emscripten_browser_clipboard_detail_copy_return(copy_handler callback, void *callback_data) { | ||
/// Call paste callback - this function is called from javascript when the paste event occurs | ||
return callback(callback_data); | ||
} | ||
|
||
} | ||
|
||
} // namespace detail | ||
|
||
} // namespace emscripten_browser_clipboard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#include "clipboard.h" | ||
#include <imgui/imgui.h> | ||
#include <imgui/imgui_internal.h> | ||
#include "emscripten_browser_clipboard.h" | ||
#include "secure_cleanse.h" | ||
|
||
#define DEBUG_CLIPBOARD | ||
|
||
#ifdef NDEBUG | ||
// clipboard debugging must always be disabled in release builds for security | ||
#undef DEBUG_CLIPBOARD | ||
#endif // NDEBUG | ||
|
||
#ifdef DEBUG_CLIPBOARD | ||
#include <iostream> | ||
#include <iomanip> | ||
#endif // DEBUG_CLIPBOARD | ||
|
||
namespace gui { | ||
|
||
clipboard::clipboard() { | ||
/// Default constructor | ||
emscripten_browser_clipboard::paste([](std::string &&paste_data, void *callback_data){ | ||
/// Callback to handle clipboard paste from browser | ||
auto &parent{*static_cast<clipboard*>(callback_data)}; | ||
#ifdef DEBUG_CLIPBOARD | ||
std::cout << "DEBUG: Clipboard: browser paste event with data " << std::quoted(paste_data) << std::endl; | ||
#endif // DEBUG_CLIPBOARD | ||
parent.content = std::move(paste_data); | ||
}, this); | ||
|
||
/* | ||
emscripten_browser_clipboard::copy([](void *callback_data){ | ||
/// Callback to offer data for clipboard copy to browser | ||
auto &parent{*static_cast<clipboard*>(callback_data)}; | ||
#ifdef DEBUG_CLIPBOARD | ||
std::cout << "DEBUG: Clipboard: browser copy event, sending data " << std::quoted(parent.content) << std::endl; | ||
#endif // DEBUG_CLIPBOARD | ||
return parent.content.c_str(); | ||
}); | ||
*/ | ||
} | ||
|
||
clipboard::~clipboard() { | ||
/// Default destructor | ||
scrub(); // clean up memory for security reasons on destruction | ||
} | ||
|
||
void clipboard::set_imgui_callbacks() { | ||
/// Set imgui clipboard callbacks - must be done after imgui context is initialised | ||
#ifdef DEBUG_CLIPBOARD | ||
std::cout << "DEBUG: Clipboard: Setting imgui callbacks" << std::endl; | ||
#endif // DEBUG_CLIPBOARD | ||
|
||
ImGuiPlatformIO &imgui_platform_io{ImGui::GetPlatformIO()}; | ||
imgui_platform_io.Platform_ClipboardUserData = this; | ||
|
||
imgui_platform_io.Platform_GetClipboardTextFn = [](ImGuiContext *ctx){ | ||
/// Callback for imgui, to return clipboard content | ||
auto &parent{*static_cast<clipboard*>(ctx->PlatformIO.Platform_ClipboardUserData)}; | ||
|
||
#ifdef DEBUG_CLIPBOARD | ||
std::cout << "DEBUG: Clipboard: imgui requested content, returning " << std::quoted(parent.content) << std::endl; | ||
#endif // DEBUG_CLIPBOARD | ||
return parent.content.c_str(); | ||
}; | ||
|
||
imgui_platform_io.Platform_SetClipboardTextFn = [](ImGuiContext *ctx, char const *text){ | ||
/// Callback for imgui, to set clipboard content | ||
auto &parent{*static_cast<clipboard*>(ctx->PlatformIO.Platform_ClipboardUserData)}; | ||
|
||
parent.content = text; | ||
#ifdef DEBUG_CLIPBOARD | ||
std::cout << "DEBUG: Clipboard: imgui set content, now " << std::quoted(parent.content) << std::endl; | ||
#endif // DEBUG_CLIPBOARD | ||
emscripten_browser_clipboard::copy(parent.content); | ||
}; | ||
} | ||
|
||
void clipboard::scrub() { | ||
/// Wipe memory securely | ||
#ifdef DEBUG_CLIPBOARD | ||
std::cout << "DEBUG: Clipboard: Scrubbing" << std::endl; | ||
#endif // DEBUG_CLIPBOARD | ||
secure_cleanse(content); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
|
||
namespace gui { | ||
|
||
class clipboard { | ||
std::string content; | ||
|
||
public: | ||
clipboard(); | ||
~clipboard(); | ||
|
||
void set_imgui_callbacks(); | ||
|
||
void scrub(); | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#pragma once | ||
|
||
namespace secure_cleanse_impl { | ||
|
||
// Pointer to memset is volatile so that compiler must de-reference | ||
// the pointer and can't assume that it points to any function in | ||
// particular (such as memset, which it then might further "optimize") | ||
typedef void* (*memset_t)(void*, int, size_t); | ||
static volatile memset_t memset_func = memset; | ||
|
||
} | ||
|
||
inline void secure_cleanse(void *ptr, size_t len); | ||
inline void secure_cleanse(std::string &target); | ||
|
||
inline void secure_cleanse(void *ptr, size_t len) { | ||
/// Securely erase a block of memory | ||
secure_cleanse_impl::memset_func(ptr, 0, len); | ||
} | ||
|
||
inline void secure_cleanse(std::string &target) { | ||
/// Securely erase a string | ||
target.resize(target.capacity(), 0); | ||
secure_cleanse(&target[0], target.size()); | ||
target.clear(); | ||
} |