Skip to content

Commit 12d2f88

Browse files
author
Tyschenko
committed
feat: Inject Javascript on iOS to detect all iFrames inside the current page and report them to the React app
1 parent 29ded70 commit 12d2f88

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

apple/IFrameDetector.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation/Foundation.h>
2+
3+
NS_ASSUME_NONNULL_BEGIN
4+
5+
NSString* getIFrameDetectorScript(void);
6+
7+
NS_ASSUME_NONNULL_END

apple/IFrameDetector.m

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#import "IFrameDetector.h"
2+
3+
/**
4+
* JavaScript code to detect all iFrames in the page and report their URLs
5+
* This script runs after page load and also monitors for dynamically added iFrames
6+
*/
7+
NSString* getIFrameDetectorScript(void) {
8+
return @"(function() {\n"
9+
" if (window.iframeDetectorInjected) return;\n"
10+
" window.iframeDetectorInjected = true;\n"
11+
" \n"
12+
" function collectIFrameUrls() {\n"
13+
" const iframes = document.getElementsByTagName('iframe');\n"
14+
" const urls = [];\n"
15+
" \n"
16+
" for (let i = 0; i < iframes.length; i++) {\n"
17+
" const iframe = iframes[i];\n"
18+
" const src = iframe.src;\n"
19+
" \n"
20+
" if (src && src.trim() !== '' && (src.startsWith('http://') || src.startsWith('https://') || src.startsWith('//'))) {\n"
21+
" const normalizedUrl = src.startsWith('//') ? 'https:' + src : src;\n"
22+
" urls.push(normalizedUrl);\n"
23+
" }\n"
24+
" }\n"
25+
" \n"
26+
" return urls;\n"
27+
" }\n"
28+
" \n"
29+
" function reportIFrames() {\n"
30+
" try {\n"
31+
" const urls = collectIFrameUrls();\n"
32+
" if (urls.length > 0) {\n"
33+
" const message = {\n"
34+
" type: 'IFRAME_DETECTED',\n"
35+
" iframeUrls: urls\n"
36+
" };\n"
37+
" \n"
38+
" if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {\n"
39+
" window.ReactNativeWebView.postMessage(JSON.stringify(message));\n"
40+
" }\n"
41+
" }\n"
42+
" } catch (e) {\n"
43+
" console.error('Error reporting iFrames:', e);\n"
44+
" }\n"
45+
" }\n"
46+
" \n"
47+
" // Initial check for iFrames\n"
48+
" if (document.readyState === 'loading') {\n"
49+
" document.addEventListener('DOMContentLoaded', reportIFrames);\n"
50+
" } else {\n"
51+
" // Document already loaded\n"
52+
" setTimeout(reportIFrames, 100);\n"
53+
" }\n"
54+
" \n"
55+
" // Monitor for dynamically added iFrames\n"
56+
" const observer = new MutationObserver(function(mutations) {\n"
57+
" let shouldCheck = false;\n"
58+
" mutations.forEach(function(mutation) {\n"
59+
" if (mutation.type === 'childList') {\n"
60+
" mutation.addedNodes.forEach(function(node) {\n"
61+
" if (node.nodeType === Node.ELEMENT_NODE) {\n"
62+
" if (node.tagName === 'IFRAME' || node.querySelector('iframe')) {\n"
63+
" shouldCheck = true;\n"
64+
" }\n"
65+
" }\n"
66+
" });\n"
67+
" }\n"
68+
" });\n"
69+
" \n"
70+
" if (shouldCheck) {\n"
71+
" setTimeout(reportIFrames, 100);\n"
72+
" }\n"
73+
" });\n"
74+
" \n"
75+
" observer.observe(document.body || document.documentElement, {\n"
76+
" childList: true,\n"
77+
" subtree: true\n"
78+
" });\n"
79+
" \n"
80+
" // Also check periodically as a fallback\n"
81+
" setInterval(reportIFrames, 5000);\n"
82+
"})();";
83+
}

apple/RNCWebViewImpl.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import <React/RCTConvert.h>
1010
#import <React/RCTAutoInsetsProtocol.h>
1111
#import "RNCWKProcessPoolManager.h"
12+
#import "IFrameDetector.h"
1213
#if !TARGET_OS_OSX
1314
#import <UIKit/UIKit.h>
1415
#else
@@ -126,6 +127,7 @@ @interface RNCWebViewImpl () <WKUIDelegate, WKNavigationDelegate, WKScriptMessag
126127
@property (nonatomic, strong) WKUserScript *injectedObjectJsonScript;
127128
@property (nonatomic, strong) WKUserScript *atStartScript;
128129
@property (nonatomic, strong) WKUserScript *atEndScript;
130+
@property (nonatomic, strong) WKUserScript *iframeDetectorScript;
129131
@end
130132

131133
@implementation RNCWebViewImpl
@@ -185,6 +187,10 @@ - (instancetype)initWithFrame:(CGRect)frame
185187
_disablePromptDuringLoading = YES;
186188

187189
_enableApplePay = NO;
190+
191+
self.iframeDetectorScript = [[WKUserScript alloc] initWithSource:getIFrameDetectorScript()
192+
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
193+
forMainFrameOnly:NO];
188194
#if TARGET_OS_IOS
189195
_savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
190196
_savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
@@ -2010,6 +2016,9 @@ - (void)resetupScripts:(WKWebViewConfiguration *)wkWebViewConfig {
20102016
if (self.atEndScript) {
20112017
[wkWebViewConfig.userContentController addUserScript:self.atEndScript];
20122018
}
2019+
if (self.iframeDetectorScript) {
2020+
[wkWebViewConfig.userContentController addUserScript:self.iframeDetectorScript];
2021+
}
20132022
}
20142023
// Whether or not messaging is enabled, add the startup script if it exists.
20152024
if (self.atStartScript) {

0 commit comments

Comments
 (0)