Skip to content

Instantly share code, notes, and snippets.

@adamjs
Created August 31, 2020 20:31
Show Gist options
  • Save adamjs/fcc9e4c0785d64f96d0763f22a93422f to your computer and use it in GitHub Desktop.
Save adamjs/fcc9e4c0785d64f96d0763f22a93422f to your computer and use it in GitHub Desktop.
//
// Assume you had some DOM element on your page with id == 'placeholder', for example:
//
// <html>
// <body>
// <div id='placeholder'></div>
// </body>
// </html>
//
// We can bind a custom function to a JSObjectRef in C to query it's location natively.
//
class MyApp : public LoadListener {
RefPtr<View> view_;
JSObjectRef getBounds_ = 0;
public:
MyApp(RefPtr<View> view) : view_(view) {
// We need to bind a load listener to get notified of the DOMReady event
view->set_load_listener(this);
}
virtual void OnDOMReady(ultralight::View* caller, uint64_t frame_id, bool is_main_frame, const String& url) override {
Ref<JSContext> context = caller->LockJSContext();
// Create a custom function in JavaScript and cache a native handle to it in 'getBounds_'
const char* script = "(function(id) { \n" \
"let bounds = document.getElementById(id).getBoundingClientRect(); \n" \
"return [bounds.x, bounds.y, bounds.width, bounds.height]; \n" \
"})";
JSStringRef script_str = JSStringCreateWithUTF8CString(script);
JSValueRef result = JSEvaluateScript(ctx, script_str, 0, 0, 0, 0);
JSStringRelease(script_str);
if (JSValueIsObject(ctx, result)) {
JSObjectRef resultObj = JSValueToObject(ctx, result, 0);
if (JSObjectIsFunction(ctx, resultObj)) {
if (getBounds_)
JSValueUnprotect(ctx, getBounds_);
getBounds_ = resultObj;
// Protect this value from being garbage-collected in case "MyApp" is allocated on the heap
JSValueProtect(ctx, getBounds_);
}
}
}
// Get the viewport bounds of a certain DOM element by ID
void GetElementBounds(const char* id, double& x, double& y, double& width, double& height) {
// Check if our "getBounds_" function is non-null
if (getBounds_) {
RefPtr<JSContext> context = view_->LockJSContext();
JSContextRef ctx = context->ctx();
// Call our cached JS function directly from C
JSStringRef str = JSStringCreateWithUTF8CString(id);
JSValueRef args[] = { JSValueMakeString(ctx, str) };
JSValueRef result = JSObjectCallAsFunction(ctx, getBounds_, 0, 1, args, 0);
JSStringRelease(str);
// Validate the result, make sure it's an object (it's actually an object with class 'Array')
if (JSValueIsObject(ctx, result)) {
JSObjectRef bounds = JSValueToObject(ctx, result, 0);
x = JSValueToNumber(ctx, JSObjectGetPropertyAtIndex(ctx, bounds, 0, 0), 0);
y = JSValueToNumber(ctx, JSObjectGetPropertyAtIndex(ctx, bounds, 1, 0), 0);
width = JSValueToNumber(ctx, JSObjectGetPropertyAtIndex(ctx, bounds, 2, 0), 0);
height = JSValueToNumber(ctx, JSObjectGetPropertyAtIndex(ctx, bounds, 3, 0), 0);
// std::cout << "Bounds are: " << x << ", " << y << ", " << width << ", " << height << std::endl;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment