Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/objc/ModalWebViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
- (void)openRequest:(NSMutableURLRequest *)request;
- (NSDictionary *)getBrowserCookiesForCurrentUrl;
- (NSDictionary *)getBrowserCookiesForDomain:(NSString *)domain;
- (NSString *)evalJs:(NSString *)js timeout:(double)timeoutSeconds;
@end
63 changes: 53 additions & 10 deletions src/objc/ModalWebViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ - (void)viewDidLoad {
configuration.websiteDataStore = self.websiteDataStore;


WKUserContentController *contentController = [[WKUserContentController alloc] init];

if (self.interceptRequests) {
NSString *injectedJS =
@"(function() {\n"
Expand Down Expand Up @@ -122,20 +124,15 @@ - (void)viewDidLoad {
" Error.prototype = OriginalError.prototype;\n"
" Object.setPrototypeOf(Error, OriginalError);\n"
"})();\n";



WKUserScript *userScript = [[WKUserScript alloc] initWithSource:injectedJS
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];

WKUserContentController *contentController = [[WKUserContentController alloc] init];
[contentController addUserScript:userScript];
configuration.userContentController = contentController;
[contentController addScriptMessageHandler:self name:@"interceptedRequestHandler"];
} else {
WKUserContentController *contentController = [[WKUserContentController alloc] init];
configuration.userContentController = contentController;
}

configuration.userContentController = contentController;

// --- Now create the web view ---
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
Expand Down Expand Up @@ -324,6 +321,52 @@ - (NSDictionary *)getBrowserCookiesForCurrentUrl {
return result;
}

- (NSString *)evalJs:(NSString *)js timeout:(double)timeoutSeconds {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSString *result = nil;

// callAsyncJavaScript treats `js` as the body of an async function, so
// `return`, `await`, and top-level Promises all work without any wrapping.
// WebKit natively awaits any returned thenable before calling the completion handler,
// so we don't need a message-handler bridge or a custom JS wrapper.
WKWebView *webView = self.webView;
void (^evalBlock)(void) = ^{
[webView callAsyncJavaScript:js
arguments:@{}
inFrame:nil
inContentWorld:WKContentWorld.pageWorld
completionHandler:^(id _Nullable jsResult, NSError *_Nullable error) {
if (error != nil) {
NSDictionary *errorDict = @{@"error" : error.localizedDescription};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:errorDict options:0 error:nil];
result = jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]
: @"{\"error\":\"unknown error\"}";
} else {
id value = jsResult ?: [NSNull null];
NSDictionary *resultDict = @{@"result" : value};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:resultDict options:0 error:nil];
result = jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]
: @"{\"result\":null}";
}
dispatch_semaphore_signal(semaphore);
}];
};

if ([NSThread isMainThread]) {
evalBlock();
} else {
dispatch_async(dispatch_get_main_queue(), evalBlock);
}

// timeout == 0: fire-and-forget (DISPATCH_TIME_NOW = don't wait, script still runs)
dispatch_time_t deadline = timeoutSeconds == 0.0
? DISPATCH_TIME_NOW
: dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutSeconds * NSEC_PER_SEC));

BOOL timedOut = dispatch_semaphore_wait(semaphore, deadline) != 0;
return timedOut ? @"{\"result\":null}" : (result ?: @"{\"result\":null}");
}

- (void)updateCapturedCookiesWithCompletion:(void (^)(void))completion {
WKHTTPCookieStore *cookieStore =
self.webView.configuration.websiteDataStore.httpCookieStore;
Expand Down Expand Up @@ -526,14 +569,14 @@ - (void)userContentController:(WKUserContentController *)userContentController
NSDictionary *payload = message.body;
NSString *requestType = payload[@"requestType"];
NSDictionary *data = payload[@"data"];

NSDictionary *event = @{
@"event" : @"intercepted_request",
@"request_type" : requestType,
@"data" : data,
@"id" : [self nextId]
};

NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:event
options:0
Expand Down
2 changes: 1 addition & 1 deletion src/objc/OpacityObjCWrapper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ + (int)initialize:(NSString *)api_key
is_location_services_enabled, is_wifi_connected, is_rooted,
is_app_foregrounded, get_device_locale, get_screen_width,
get_screen_height, get_screen_density, get_screen_dpi, get_device_cpu,
get_device_codename, ios_webview_change_url);
get_device_codename, ios_webview_change_url, ios_eval_js);


char *err;
Expand Down
1 change: 1 addition & 0 deletions src/objc/helper_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern "C"
const char *ios_get_browser_cookies_for_domain(const char *domain);
const char *ios_get_browser_cookies_for_current_url();
void ios_webview_change_url(const char *url);
const char *ios_eval_js(const char *js, double timeout_in_seconds);

// Device information functions
double get_battery_level();
Expand Down
18 changes: 18 additions & 0 deletions src/objc/helper_functions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ void ios_webview_change_url(const char *url) {
});
}

const char *ios_eval_js(const char *js, double timeout_in_seconds) {
if (modalWebVC == nil) {
return strdup("{\"error\":\"browser not open\"}");
}

NSString *jsString = [NSString stringWithUTF8String:js];
if (jsString == nil) {
return strdup("{\"error\":\"invalid js string\"}");
}

NSString *result = [modalWebVC evalJs:jsString timeout:timeout_in_seconds];
if (result == nil) {
return strdup("{\"error\":\"nil result\"}");
}

return strdup([result UTF8String]);
}

const char *ios_get_browser_cookies_for_domain(const char *domain) {
if (modalWebVC == nil) {
return nullptr;
Expand Down
5 changes: 4 additions & 1 deletion src/objc/sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ typedef const char *(*GetDeviceCodenameFn)(void);

typedef void (*IosWebviewChangeUrlFn)(const char*);

typedef const char *(*IosEvalJsFn)(const char*, double);




Expand Down Expand Up @@ -243,7 +245,8 @@ void register_ios_callbacks(IosPrepareRequestFn ios_prepare_request,
GetScreenDpiFn get_screen_dpi,
GetDeviceCpuFn get_device_cpu,
GetDeviceCodenameFn get_device_codename,
IosWebviewChangeUrlFn ios_webview_change_url);
IosWebviewChangeUrlFn ios_webview_change_url,
IosEvalJsFn ios_eval_js);

extern void secure_set(const char *key, const char *value);

Expand Down
Loading