mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-20 03:52:15 +02:00
fix: CORS issue with JS loading
This commit is contained in:
parent
84dea7a48f
commit
bf1f7fbba7
@ -993,6 +993,7 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
protected open fun createWebView(): WebView {
|
||||
val resourceHandler = ViewerResourceHandler(this)
|
||||
val webView: WebView = MyWebView(this).apply {
|
||||
scrollBarStyle = View.SCROLLBARS_OUTSIDE_OVERLAY
|
||||
with(settings) {
|
||||
@ -1010,7 +1011,7 @@ abstract class AbstractFlashcardViewer :
|
||||
isScrollbarFadingEnabled = true
|
||||
// Set transparent color to prevent flashing white when night mode enabled
|
||||
setBackgroundColor(Color.argb(1, 0, 0, 0))
|
||||
CardViewerWebClient(this@AbstractFlashcardViewer).apply {
|
||||
CardViewerWebClient(resourceHandler, this@AbstractFlashcardViewer).apply {
|
||||
webViewClient = this
|
||||
this@AbstractFlashcardViewer.webViewClient = this
|
||||
}
|
||||
@ -2233,6 +2234,7 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
inner class CardViewerWebClient internal constructor(
|
||||
private val resourceHandler: ViewerResourceHandler,
|
||||
private val onPageFinishedCallback: OnPageFinishedCallback? = null
|
||||
) : WebViewClient(), JavascriptEvaluator {
|
||||
private var pageFinishedFired = true
|
||||
@ -2266,6 +2268,7 @@ abstract class AbstractFlashcardViewer :
|
||||
if (url.toString().startsWith("file://")) {
|
||||
url.path?.let { path -> migrationService?.migrateFileImmediately(File(path)) }
|
||||
}
|
||||
resourceHandler.shouldInterceptRequest(request)?.let { return it }
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Brayan Oliveira <brayandso.dev@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.ichi2.anki
|
||||
|
||||
import android.content.Context
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import androidx.core.net.toFile
|
||||
import com.ichi2.utils.AssetHelper.guessMimeType
|
||||
import timber.log.Timber
|
||||
import java.io.FileInputStream
|
||||
|
||||
class ViewerResourceHandler(context: Context) {
|
||||
private val mediaDir = CollectionHelper.getMediaDirectory(context).path
|
||||
|
||||
/**
|
||||
* Loads resources from `collection.media` when requested by JS scripts.
|
||||
*
|
||||
* Differently from common media requests, scripts' requests have an `Origin` header
|
||||
* and are susceptible to CORS policy, so `Access-Control-Allow-Origin` is necessary.
|
||||
*/
|
||||
fun shouldInterceptRequest(request: WebResourceRequest): WebResourceResponse? {
|
||||
val url = request.url
|
||||
if (request.method != "GET" || url.scheme != "file" || "Origin" !in request.requestHeaders) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
val file = url.toFile()
|
||||
if (file.parent != mediaDir || !file.exists()) {
|
||||
return null
|
||||
}
|
||||
val inputStream = FileInputStream(file)
|
||||
return WebResourceResponse(guessMimeType(file.path), null, inputStream).apply {
|
||||
responseHeaders = mapOf("Access-Control-Allow-Origin" to "*")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.d("File couldn't be loaded")
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import android.webkit.CookieManager
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebResourceError
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.FrameLayout
|
||||
@ -37,6 +38,7 @@ import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.ichi2.anki.CollectionHelper
|
||||
import com.ichi2.anki.R
|
||||
import com.ichi2.anki.ViewerResourceHandler
|
||||
import com.ichi2.anki.dialogs.TtsVoicesDialogFragment
|
||||
import com.ichi2.anki.localizedErrorMessage
|
||||
import com.ichi2.anki.snackbar.showSnackbar
|
||||
@ -121,7 +123,15 @@ abstract class CardViewerFragment(@LayoutRes layout: Int) : Fragment(layout) {
|
||||
}
|
||||
|
||||
private fun onCreateWebViewClient(savedInstanceState: Bundle?): WebViewClient {
|
||||
val resourceHandler = ViewerResourceHandler(requireContext())
|
||||
return object : WebViewClient() {
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest
|
||||
): WebResourceResponse? {
|
||||
return resourceHandler.shouldInterceptRequest(request)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
viewModel.onPageFinished(isAfterRecreation = savedInstanceState != null)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user