Created
August 31, 2020 20:31
-
-
Save adamjs/fcc9e4c0785d64f96d0763f22a93422f to your computer and use it in GitHub Desktop.
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
// | |
// 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