У меня есть приложение Android (веб-браузер), которое использует веб-представление по умолчанию для загрузки веб-страниц. Перегружая метод shouldInterceptRequest
, я загружаю страницу библиотекой OkHTTP3.
Я делаю это потому, что хочу внедрить JavaScript в каждую страницу перед выполнением любого Javascript. Это не может быть сделано с evaluateJavascript
, потому что он асинхронный и, следовательно, нет никакой гарантии, что код будет выполнен первым. Вот код для этого:
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
val httpRequestBuilder = Request.Builder()
when (request.method) {
"GET" -> httpRequestBuilder.get()
}
httpRequestBuilder.url(url)
httpRequestBuilder.headers(Headers.of(request.requestHeaders))
val httpRequest = httpRequestBuilder.build()
val httpResponse = OkHttpClient().newCall(httpRequest).execute()
httpResponse?.run {
val contentType = body()?.contentType()
val mime = contentType?.type() + "/" + contentType?.subtype()
val encoding = contentType?.charset()?.name() ?: "utf-8"
val message = if (message().isEmpty()) {
"OK"
} else {
message()
}
val header = HashMap<String, String>()
headers().toMultimap().forEach { entry ->
header[entry.key] = entry.value[0]
}
val contentStream = if (contentType?.subtype() == "html") {
object : InputStream() {
val streams = arrayOf(
ByteArrayInputStream("<script type=\"text/javascript\">".toByteArray()),
context.assets.open("MyJS.js"),
ByteArrayInputStream("</script>".toByteArray()),
body()?.byteStream()!!
)
var currentIndex = 0
override fun read(): Int {
while (currentIndex < streams.size) {
val res = streams[currentIndex].read()
if (res != -1) {
return res
}
currentIndex++;
}
return -1;
}
}
} else {
body()?.byteStream()!!
}
return WebResourceResponse(mime, encoding, code(), message, header, contentStream)
}
Внедренный JavaScript существенно модифицирует некоторые прототипы. Также он пытается внедрить себя в фреймы и фреймы на странице, чтобы сделать те же изменения. Это часть скрипта, которая внедряет себя:
(function() {
var self = document.currentScript;
var observer = new window.MutationObserver(function (changes) {
for (var index = 0; index < changes.length; index++) {
var change = changes[index];
if (change.type == "childList") {
for (var i = 0; i < change.addedNodes.length; i++) {
var node = change.addedNodes[i];
switch (node.nodeName) {
case "IFRAME":
var orig_sandbox = node.sandbox;
node.sandbox = "allow-scripts";
node.contentDocument.write("<script type=\"text/javascript\">" + self.text + "<\\/script>");
node.sandbox = orig_sandbox;
break;
case "FRAME":
node.contentDocument.write("<script type=\"text/javascript\">" + self.text + "<\\/script>");
}
}
}
}
});
observer.observe(document.documentElement, {childList: true, subtree: true});
})();
Цель состоит в том, чтобы внедрить этот JavaScript в кадры, которые не загружаются из сети (что в любом случае обрабатывается методом shouldInterceptRequest
), но в кадрах, которые не загружаются из сети или создаются другим JavaScript.
Теперь у меня проблема в том, что это не работает. Не вводится в подкадры.
Кто-нибудь знает возможность внедрить Javascript в такие фреймы?