如何将 javascript 从 Java 代码注入到 Android WebView 中的 IFrame?
How do I inject javascript to an IFrame from Java code into an Android WebView?
我正在编写一个 Android 应用程序,它在 WebView 中显示网页。其中一些包括 IFrame。我无法控制要显示的页面。
该应用程序的部分功能是将 javascript 代码从我的 Java 代码注入页面(添加 JavaScript 接口和脚本)。我可以很容易地在主页上做到这一点 ,但在 IFrame.
中做到这一点似乎是一项更具挑战性的任务
看起来 WebViewClient 的 onPageFinished 事件 class(注入脚本的适当位置)只针对主页触发,而不针对其中的 IFrames。
关于如何做到这一点有什么想法吗?
据我所知,执行此操作的唯一方法是使用 WebViewClient 并覆盖 shouldInterceptRequest。寻找您想要注入内容的 iframe 的 url,将 url 手动加载到流或文本中,使用字符串操作注入您想要添加的内容或您想要删除的任何内容,然后将其作为 WebResourceResponse 而不是原始请求发送。
这是一些可以转换为 java:
的 kotlin 代码
webView.webViewClient = object : WebViewClient() {
private fun shouldInjectToIframe(url: String?): Boolean {
return !url.isNullOrBlank() && url.indexOf("<string to look for>") > -1
}
private fun injectToIframe(url: String): WebResourceResponse? {
Log.d(TAG, "Intercepted $url")
val latch = CountDownLatch(1)
var res: InputStream? = null
val call = App.instance?.okHttpClient?.newCall(Request.Builder().url(url).build())
call?.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
latch.countDown()
}
override fun onResponse(call: Call, response: Response) {
res = response.body?.byteStream()
latch.countDown()
}
})
latch.await()
val reader = BufferedReader(res?.reader())
var content: String
try {
content = reader.readText()
} finally {
reader.close()
}
var scriptToInject = "\n<script>\n" +
" (function() {\n" +
" alert('hi');\n" +
" })()\n" +
"</script>\n"
val newContent = "${content.split("</head>")[0]}${scriptToInject}</head>${content.split("</head>")[1]}"
Log.d(TAG, "Inject script to iframe: $newContent")
val inStream = newContent.byteInputStream()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return WebResourceResponse(
"text/html",
"utf-8",
inStream
)
val statusCode = 200
val reasonPhase = "OK"
val responseHeaders: MutableMap<String, String> = HashMap()
return WebResourceResponse("text/html", "utf-8", statusCode, reasonPhase, responseHeaders, inStream)
}
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
if (shouldInjectToIframe(url)) return injectToIframe(url!!)
if (Util.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return super.shouldInterceptRequest(view, url)
return null
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse? {
if (shouldInjectToIframe(request.url.toString())) return injectToIframe(request.url.toString())
return super.shouldInterceptRequest(view, request)
}
}
我正在编写一个 Android 应用程序,它在 WebView 中显示网页。其中一些包括 IFrame。我无法控制要显示的页面。
该应用程序的部分功能是将 javascript 代码从我的 Java 代码注入页面(添加 JavaScript 接口和脚本)。我可以很容易地在主页上做到这一点 ,但在 IFrame.
中做到这一点似乎是一项更具挑战性的任务看起来 WebViewClient 的 onPageFinished 事件 class(注入脚本的适当位置)只针对主页触发,而不针对其中的 IFrames。
关于如何做到这一点有什么想法吗?
据我所知,执行此操作的唯一方法是使用 WebViewClient 并覆盖 shouldInterceptRequest。寻找您想要注入内容的 iframe 的 url,将 url 手动加载到流或文本中,使用字符串操作注入您想要添加的内容或您想要删除的任何内容,然后将其作为 WebResourceResponse 而不是原始请求发送。
这是一些可以转换为 java:
的 kotlin 代码 webView.webViewClient = object : WebViewClient() {
private fun shouldInjectToIframe(url: String?): Boolean {
return !url.isNullOrBlank() && url.indexOf("<string to look for>") > -1
}
private fun injectToIframe(url: String): WebResourceResponse? {
Log.d(TAG, "Intercepted $url")
val latch = CountDownLatch(1)
var res: InputStream? = null
val call = App.instance?.okHttpClient?.newCall(Request.Builder().url(url).build())
call?.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
latch.countDown()
}
override fun onResponse(call: Call, response: Response) {
res = response.body?.byteStream()
latch.countDown()
}
})
latch.await()
val reader = BufferedReader(res?.reader())
var content: String
try {
content = reader.readText()
} finally {
reader.close()
}
var scriptToInject = "\n<script>\n" +
" (function() {\n" +
" alert('hi');\n" +
" })()\n" +
"</script>\n"
val newContent = "${content.split("</head>")[0]}${scriptToInject}</head>${content.split("</head>")[1]}"
Log.d(TAG, "Inject script to iframe: $newContent")
val inStream = newContent.byteInputStream()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return WebResourceResponse(
"text/html",
"utf-8",
inStream
)
val statusCode = 200
val reasonPhase = "OK"
val responseHeaders: MutableMap<String, String> = HashMap()
return WebResourceResponse("text/html", "utf-8", statusCode, reasonPhase, responseHeaders, inStream)
}
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
if (shouldInjectToIframe(url)) return injectToIframe(url!!)
if (Util.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return super.shouldInterceptRequest(view, url)
return null
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse? {
if (shouldInjectToIframe(request.url.toString())) return injectToIframe(request.url.toString())
return super.shouldInterceptRequest(view, request)
}
}