JCEF: move CefStreamResourceHandler and CefLocalRequestHandler to com.intellij.ui.jcef.utils

GitOrigin-RevId: 3d735a21598cae96168aa69530c4a01625f2f9a6
This commit is contained in:
Vladimir Kharitonov
2024-12-20 19:40:41 +00:00
committed by intellij-monorepo-bot
parent 4ad3db2616
commit 7cff6c268e
7 changed files with 204 additions and 118 deletions
@@ -1,55 +1,12 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.intellij.images.editor.impl.jcef
import org.cef.browser.CefBrowser
import org.cef.browser.CefFrame
import org.cef.callback.CefCallback
import org.cef.handler.*
import org.cef.misc.BoolRef
import org.cef.network.CefRequest
import java.net.URL
private typealias CefResourceProvider = () -> CefResourceHandler?
import com.intellij.ui.jcef.utils.JBCefLocalRequestHandler
@Deprecated(replaceWith = ReplaceWith("com.intellij.ui.jcef.utils.JBCefStreamResourceHandler"),
message = "Use JBCefStreamResourceHandler instead",
level = DeprecationLevel.WARNING)
class CefLocalRequestHandler(
private val myProtocol: String,
private val myAuthority: String
) : CefRequestHandlerAdapter() {
private val myResources: MutableMap<String, CefResourceProvider> = HashMap()
private val REJECTING_RESOURCE_HANDLER: CefResourceHandler = object : CefResourceHandlerAdapter() {
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
callback.cancel()
return false
}
}
private val RESOURCE_REQUEST_HANDLER = object : CefResourceRequestHandlerAdapter() {
override fun getResourceHandler(browser: CefBrowser?, frame: CefFrame?, request: CefRequest): CefResourceHandler {
val url = URL(request.url)
url.protocol
if (!url.protocol.equals(myProtocol) || !url.authority.equals(myAuthority)) {
return REJECTING_RESOURCE_HANDLER
}
return try {
myResources[url.path]?.let { it() } ?: REJECTING_RESOURCE_HANDLER
} catch (e: RuntimeException) {
REJECTING_RESOURCE_HANDLER
}
}
}
fun addResource(resourcePath: String, resourceProvider: CefResourceProvider) {
myResources[resourcePath] = resourceProvider
}
override fun getResourceRequestHandler(browser: CefBrowser?,
frame: CefFrame?,
request: CefRequest?,
isNavigation: Boolean,
isDownload: Boolean,
requestInitiator: String?,
disableDefaultHandling: BoolRef?): CefResourceRequestHandler {
return RESOURCE_REQUEST_HANDLER
}
}
myProtocol: String,
myAuthority: String,
) : JBCefLocalRequestHandler(myProtocol, myAuthority)
@@ -2,63 +2,15 @@
package org.intellij.images.editor.impl.jcef
import com.intellij.openapi.Disposable
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Disposer
import org.cef.callback.CefCallback
import org.cef.handler.CefResourceHandler
import org.cef.misc.IntRef
import org.cef.misc.StringRef
import org.cef.network.CefRequest
import org.cef.network.CefResponse
import java.io.IOException
import com.intellij.ui.jcef.utils.JBCefStreamResourceHandler
import java.io.InputStream
class CefStreamResourceHandler(private val myStream: InputStream,
private val myMimeType: String,
parent: Disposable,
private val headers: Map<String, String> = mapOf()) : CefResourceHandler, Disposable {
init {
Disposer.register(parent, this)
}
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
callback.Continue()
return true
}
override fun getResponseHeaders(response: CefResponse, responseLength: IntRef, redirectUrl: StringRef) {
response.mimeType = myMimeType
response.status = 200
for (header in headers) {
response.setHeaderByName(header.key, header.value, true /* overwrite */)
}
}
override fun readResponse(dataOut: ByteArray, bytesToRead: Int, bytesRead: IntRef, callback: CefCallback): Boolean {
try {
bytesRead.set(myStream.read(dataOut, 0, bytesToRead))
if (bytesRead.get() != -1) {
return true
}
}
catch (e: IOException) {
callback.cancel()
}
bytesRead.set(0)
Disposer.dispose(this)
return false
}
override fun cancel() {
Disposer.dispose(this)
}
override fun dispose() {
try {
myStream.close()
}
catch (e: IOException) {
Logger.getInstance(CefStreamResourceHandler::class.java).warn("Failed to close the stream", e)
}
}
}
@Deprecated(replaceWith = ReplaceWith("com.intellij.ui.jcef.utils.JBCefStreamResourceHandler"),
message = "Use JBCefStreamResourceHandler instead",
level = DeprecationLevel.WARNING)
class CefStreamResourceHandler(
myStream: InputStream,
myMimeType: String,
parent: Disposable,
headers: Map<String, String> = mapOf(),
) : JBCefStreamResourceHandler(myStream, myMimeType, parent, headers)
@@ -20,6 +20,8 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.isTooLarge
import com.intellij.ui.jcef.*
import com.intellij.ui.jcef.utils.JBCefLocalRequestHandler
import com.intellij.ui.jcef.utils.JBCefStreamResourceHandler
import com.intellij.util.IncorrectOperationException
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@@ -180,32 +182,32 @@ class JCefImageViewer(private val myFile: VirtualFile,
init {
myDocument.addDocumentListener(this)
myRequestHandler = CefLocalRequestHandler(PROTOCOL, HOST_NAME)
myRequestHandler = JBCefLocalRequestHandler(PROTOCOL, HOST_NAME)
myRequestHandler.addResource(VIEWER_PATH) {
javaClass.getResourceAsStream("resources/image_viewer.html")?.let {
CefStreamResourceHandler(it, "text/html", this@JCefImageViewer)
JBCefStreamResourceHandler(it, "text/html", this@JCefImageViewer)
}
}
myRequestHandler.addResource(OVERLAY_SCROLLBARS_CSS_PATH) {
CefStreamResourceHandler(ByteArrayInputStream(JBCefScrollbarsHelper.getOverlayScrollbarsSourceCSS().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
JBCefStreamResourceHandler(ByteArrayInputStream(JBCefScrollbarsHelper.getOverlayScrollbarsSourceCSS().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
}
myRequestHandler.addResource(OVERLAY_SCROLLBARS_JS_PATH) {
CefStreamResourceHandler(ByteArrayInputStream(JBCefScrollbarsHelper.getOverlayScrollbarsSourceJS().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
JBCefStreamResourceHandler(ByteArrayInputStream(JBCefScrollbarsHelper.getOverlayScrollbarsSourceJS().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
}
myRequestHandler.addResource(SCROLLBARS_CSS_PATH) {
CefStreamResourceHandler(ByteArrayInputStream(JBCefScrollbarsHelper.getOverlayScrollbarStyle().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
JBCefStreamResourceHandler(ByteArrayInputStream(JBCefScrollbarsHelper.getOverlayScrollbarStyle().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
}
myRequestHandler.addResource(CHESSBOARD_CSS_PATH) {
CefStreamResourceHandler(ByteArrayInputStream(buildChessboardStyle().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
JBCefStreamResourceHandler(ByteArrayInputStream(buildChessboardStyle().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
}
myRequestHandler.addResource(GRID_CSS_PATH) {
CefStreamResourceHandler(ByteArrayInputStream(buildGridStyle().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
JBCefStreamResourceHandler(ByteArrayInputStream(buildGridStyle().toByteArray(StandardCharsets.UTF_8)), "text/css", this)
}
myRequestHandler.addResource(IMAGE_PATH) {
@@ -218,11 +220,11 @@ class JCefImageViewer(private val myFile: VirtualFile,
Logger.getInstance(JCefImageViewer::class.java).warn("Failed to read the file", e)
}
var resourceHandler: CefStreamResourceHandler? = null
var resourceHandler: JBCefStreamResourceHandler? = null
stream?.let {
try {
resourceHandler = CefStreamResourceHandler(it, mimeType, this@JCefImageViewer,
mapOf("Content-Security-Policy" to "script-src 'none'"))
resourceHandler = JBCefStreamResourceHandler(it, mimeType, this@JCefImageViewer,
mapOf("Content-Security-Policy" to "script-src 'none'"))
}
catch (_: IncorrectOperationException) { // The viewer has been disposed just return null that will reject all requests
}
+16
View File
@@ -271,3 +271,19 @@ f:com.intellij.ui.jcef.JcefShortcutProvider
- <init>():V
- s:getActions():java.util.List
- s:registerShortcuts(javax.swing.JComponent,com.intellij.ui.jcef.JBCefBrowser):V
c:com.intellij.ui.jcef.utils.JBCefLocalRequestHandler
- org.cef.handler.CefRequestHandlerAdapter
- <init>(java.lang.String,java.lang.String):V
- f:addResource(java.lang.String,kotlin.jvm.functions.Function0):V
- f:createResource(java.lang.String,kotlin.jvm.functions.Function0):java.lang.String
- getResourceRequestHandler(org.cef.browser.CefBrowser,org.cef.browser.CefFrame,org.cef.network.CefRequest,Z,Z,java.lang.String,org.cef.misc.BoolRef):org.cef.handler.CefResourceRequestHandler
c:com.intellij.ui.jcef.utils.JBCefStreamResourceHandler
- com.intellij.openapi.Disposable
- org.cef.handler.CefResourceHandler
- <init>(java.io.InputStream,java.lang.String,com.intellij.openapi.Disposable,java.util.Map):V
- b:<init>(java.io.InputStream,java.lang.String,com.intellij.openapi.Disposable,java.util.Map,I,kotlin.jvm.internal.DefaultConstructorMarker):V
- cancel():V
- dispose():V
- getResponseHeaders(org.cef.network.CefResponse,org.cef.misc.IntRef,org.cef.misc.StringRef):V
- processRequest(org.cef.network.CefRequest,org.cef.callback.CefCallback):Z
- readResponse(B[],I,org.cef.misc.IntRef,org.cef.callback.CefCallback):Z
@@ -1,9 +1,11 @@
kotlin/jvm/internal/DefaultConstructorMarker
org/cef/CefClient
org/cef/CefSettings
org/cef/browser/CefBrowser
org/cef/browser/CefFrame
org/cef/browser/CefMessageRouter
org/cef/browser/CefMessageRouter$CefMessageRouterConfig
org/cef/callback/CefCallback
org/cef/callback/CefContextMenuParams
org/cef/callback/CefMenuModel
org/cef/callback/CefSchemeHandlerFactory
@@ -23,5 +25,13 @@ org/cef/handler/CefLoadHandler$ErrorCode
org/cef/handler/CefPermissionHandler
org/cef/handler/CefRenderHandler
org/cef/handler/CefRequestHandler
org/cef/handler/CefRequestHandlerAdapter
org/cef/handler/CefResourceHandler
org/cef/handler/CefResourceRequestHandler
org/cef/misc/BoolRef
org/cef/misc/IntRef
org/cef/misc/StringRef
org/cef/network/CefCookie
org/cef/network/CefCookieManager
org/cef/network/CefRequest
org/cef/network/CefResponse
@@ -0,0 +1,72 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ui.jcef.utils
import org.cef.browser.CefBrowser
import org.cef.browser.CefFrame
import org.cef.callback.CefCallback
import org.cef.handler.*
import org.cef.misc.BoolRef
import org.cef.network.CefRequest
import java.net.URL
/**
* Handles local protocol-specific CEF resource requests for a defined `protocol` and `authority`.
*
* This class implements a mechanism to serve protocol-specific resources based on mappings provided
* through the `addResource` function. Only requests matching the configured protocol and authority are processed,
* while others are rejected.
*
* @param myProtocol The protocol to handle (e.g., "http", "file").
* @param myAuthority The authority of the requests (e.g., "localhost", "mydomain").
*/
open class JBCefLocalRequestHandler(
private val myProtocol: String,
private val myAuthority: String
) : CefRequestHandlerAdapter() {
private val myResources: MutableMap<String, () -> CefResourceHandler?> = HashMap()
private val REJECTING_RESOURCE_HANDLER: CefResourceHandler = object : CefResourceHandlerAdapter() {
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
callback.cancel()
return false
}
}
private val RESOURCE_REQUEST_HANDLER = object : CefResourceRequestHandlerAdapter() {
override fun getResourceHandler(browser: CefBrowser?, frame: CefFrame?, request: CefRequest): CefResourceHandler {
val url = URL(request.url)
url.protocol
if (!url.protocol.equals(myProtocol) || !url.authority.equals(myAuthority)) {
return REJECTING_RESOURCE_HANDLER
}
return try {
val path = url.path.trim('/')
myResources[path]?.let { it() } ?: REJECTING_RESOURCE_HANDLER
} catch (e: RuntimeException) {
println(e.message)
REJECTING_RESOURCE_HANDLER
}
}
}
fun addResource(resourcePath: String, resourceProvider: () -> CefResourceHandler?) {
val normalisedPath = resourcePath.trim('/')
myResources[normalisedPath] = resourceProvider
}
fun createResource(resourcePath: String, resourceProvider: () -> CefResourceHandler?): String {
val normalisedPath = resourcePath.trim('/')
myResources[normalisedPath] = resourceProvider
return "$myProtocol://$myAuthority/$normalisedPath"
}
override fun getResourceRequestHandler(browser: CefBrowser?,
frame: CefFrame?,
request: CefRequest?,
isNavigation: Boolean,
isDownload: Boolean,
requestInitiator: String?,
disableDefaultHandling: BoolRef?): CefResourceRequestHandler {
return RESOURCE_REQUEST_HANDLER
}
}
@@ -0,0 +1,77 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ui.jcef.utils
import com.intellij.openapi.Disposable
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Disposer
import org.cef.callback.CefCallback
import org.cef.handler.CefResourceHandler
import org.cef.misc.IntRef
import org.cef.misc.StringRef
import org.cef.network.CefRequest
import org.cef.network.CefResponse
import java.io.IOException
import java.io.InputStream
/**
* A handler for managing custom resource requests in JCEF.
* This class implements the `CefResourceHandler` interface, enabling the customization of resource
* loading from a provided input stream. It supports setting MIME types and headers for responses
* and ensures proper disposal of resources.
*
* @param myStream The input stream from which the resource will be read.
* @param myMimeType The MIME type of the response to be sent to the client.
* @param parent A `Disposable` object to which this handler is registered for automatic disposal.
* @param headers Optional headers that will be included in the response.
*/
open class JBCefStreamResourceHandler(
private val myStream: InputStream,
private val myMimeType: String,
parent: Disposable,
private val headers: Map<String, String> = mapOf(),
) : CefResourceHandler, Disposable {
init {
Disposer.register(parent, this)
}
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
callback.Continue()
return true
}
override fun getResponseHeaders(response: CefResponse, responseLength: IntRef, redirectUrl: StringRef) {
response.mimeType = myMimeType
response.status = 200
for (header in headers) {
response.setHeaderByName(header.key, header.value, true /* overwrite */)
}
}
override fun readResponse(dataOut: ByteArray, bytesToRead: Int, bytesRead: IntRef, callback: CefCallback): Boolean {
try {
bytesRead.set(myStream.read(dataOut, 0, bytesToRead))
if (bytesRead.get() != -1) {
return true
}
}
catch (_: IOException) {
callback.cancel()
}
bytesRead.set(0)
Disposer.dispose(this)
return false
}
override fun cancel() {
Disposer.dispose(this)
}
override fun dispose() {
try {
myStream.close()
}
catch (e: IOException) {
Logger.getInstance(JBCefStreamResourceHandler::class.java).warn("Failed to close the stream", e)
}
}
}