Skip to content

Commit 9c49a0a

Browse files
committed
wip
1 parent 4f4e3d2 commit 9c49a0a

File tree

4 files changed

+182
-71
lines changed

4 files changed

+182
-71
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/util/JcefBrowserUtil.kt

Lines changed: 5 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,18 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonq.util
55

6+
import com.github.rli.cefschemeremotetest.toolWindow.JBCefLocalRequestHandler
7+
import com.github.rli.cefschemeremotetest.toolWindow.JBCefStreamResourceHandler
68
import com.intellij.openapi.Disposable
79
import com.intellij.openapi.util.Disposer
810
import com.intellij.ui.jcef.JBCefApp
9-
import com.intellij.ui.jcef.JBCefBrowser
11+
import com.intellij.ui.jcef.JBCefBrowserBase
1012
import com.intellij.ui.jcef.JBCefBrowserBuilder
1113
import com.intellij.ui.jcef.JBCefClient
12-
import org.cef.browser.CefBrowser
13-
import org.cef.browser.CefFrame
14-
import org.cef.handler.CefRequestHandlerAdapter
15-
import org.cef.handler.CefResourceHandler
16-
import org.cef.handler.CefResourceRequestHandler
17-
import org.cef.handler.CefResourceRequestHandlerAdapter
18-
import org.cef.misc.BoolRef
19-
import org.cef.network.CefRequest
2014
import software.aws.toolkits.jetbrains.services.amazonq.webview.AssetResourceHandler
21-
import java.io.IOException
15+
import software.aws.toolkits.jetbrains.services.amazonq.webview.Browser
2216

23-
fun createBrowser(parent: Disposable): JBCefBrowser {
17+
fun createBrowser(parent: Disposable): JBCefBrowserBase {
2418
val client = JBCefApp.getInstance().createClient().apply {
2519
setProperty(JBCefClient.Properties.JS_QUERY_POOL_SIZE, 5)
2620
}
@@ -31,48 +25,4 @@ fun createBrowser(parent: Disposable): JBCefBrowser {
3125
.setClient(client)
3226
.setOffScreenRendering(true)
3327
.build()
34-
.also { browser ->
35-
browser.jbCefClient.addRequestHandler(
36-
object : CefRequestHandlerAdapter() {
37-
override fun getResourceRequestHandler(
38-
browser: CefBrowser,
39-
frame: CefFrame,
40-
request: CefRequest,
41-
isNavigation: Boolean,
42-
isDownload: Boolean,
43-
requestInitiator: String,
44-
disableDefaultHandling: BoolRef
45-
): CefResourceRequestHandler? {
46-
return null
47-
// println("pewpewpew")
48-
// if (request.url.contains("mynah")) {
49-
// println("mynahmynahmynah")
50-
//
51-
// return object : CefResourceRequestHandlerAdapter() {
52-
// override fun getResourceHandler(browser: CefBrowser, frame: CefFrame, request: CefRequest): CefResourceHandler? {
53-
// val resourceUri = request.url ?: return null
54-
// if (!resourceUri.startsWith("http://mynah/")) return null
55-
//
56-
// val resource = resourceUri.replace("http://mynah/", "/assets/")
57-
// val resourceInputStream = this.javaClass.getResourceAsStream(resource)
58-
//
59-
// try {
60-
// resourceInputStream.use {
61-
// if (resourceInputStream != null) {
62-
// println("assetsassetsassets")
63-
// return AssetResourceHandler(resourceInputStream.readAllBytes())
64-
// }
65-
// return null
66-
// }
67-
// } catch (e: IOException) {
68-
// throw RuntimeException(e)
69-
// }
70-
// }
71-
// }
72-
// }
73-
return super.getResourceRequestHandler(browser, frame, request, isNavigation, isDownload, requestInitiator, disableDefaultHandling)
74-
}
75-
}, browser.cefBrowser
76-
)
77-
}
7828
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
package software.aws.toolkits.jetbrains.services.amazonq.webview
55

66
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
7+
import com.github.rli.cefschemeremotetest.toolWindow.JBCefLocalRequestHandler
8+
import com.github.rli.cefschemeremotetest.toolWindow.JBCefStreamResourceHandler
79
import com.intellij.openapi.Disposable
810
import com.intellij.openapi.util.Disposer
911
import com.intellij.ui.jcef.JBCefJSQuery
@@ -19,7 +21,9 @@ class Browser(parent: Disposable) : Disposable {
1921

2022
val jcefBrowser = createBrowser(parent)
2123

22-
val receiveMessageQuery = JBCefJSQuery.create(jcefBrowser)
24+
val receiveMessageQuery = JBCefJSQuery.create(jcefBrowser).also {
25+
Disposer.register(this, it)
26+
}
2327

2428
fun init(
2529
isCodeTransformAvailable: Boolean,
@@ -29,16 +33,7 @@ class Browser(parent: Disposable) : Disposable {
2933
isCodeTestAvailable: Boolean,
3034
highlightCommand: HighlightCommand?,
3135
) {
32-
// register the scheme handler to route http://mynah/ URIs to the resources/assets directory on classpath
33-
// CefApp.getInstance()
34-
// .registerSchemeHandlerFactory(
35-
// "http",
36-
// "mynah",
37-
// AssetResourceHandler.AssetResourceHandlerFactory(),
38-
// )
39-
println("aaaaaaaaa did a load")
4036
loadWebView(isCodeTransformAvailable, isFeatureDevAvailable, isDocAvailable, isCodeScanAvailable, isCodeTestAvailable, highlightCommand)
41-
println("loadloadload")
4237
}
4338

4439
override fun dispose() {
@@ -64,10 +59,27 @@ class Browser(parent: Disposable) : Disposable {
6459
// setup empty state. The message request handlers use this for storing state
6560
// that's persistent between page loads.
6661
jcefBrowser.setProperty("state", "")
67-
// load the web app
68-
jcefBrowser.loadHTML(
62+
63+
val handler = JBCefLocalRequestHandler("http", "toolkitasset")
64+
handler.addResource("webview/chat.html") {
6965
getWebviewHTML(isCodeTransformAvailable, isFeatureDevAvailable, isDocAvailable, isCodeScanAvailable, isCodeTestAvailable, highlightCommand)
70-
)
66+
.byteInputStream().let {
67+
JBCefStreamResourceHandler(it, "text/html", this)
68+
}
69+
}
70+
71+
handler.addResource("mynah/js/mynah-ui.js") {
72+
AssetResourceHandler::class.java.getResourceAsStream("/mynah-ui/assets/js/mynah-ui.js")?.let {
73+
JBCefStreamResourceHandler(it, "text/javascript", this)
74+
}
75+
}
76+
jcefBrowser.jbCefClient.addRequestHandler(handler, jcefBrowser.cefBrowser)
77+
Disposer.register(this) {
78+
jcefBrowser.jbCefClient.removeRequestHandler(handler, jcefBrowser.cefBrowser)
79+
}
80+
81+
// load the web app
82+
jcefBrowser.loadURL("http://toolkitasset/webview/chat.html")
7183
}
7284

7385
/**
@@ -85,7 +97,7 @@ class Browser(parent: Disposable) : Disposable {
8597
val postMessageToJavaJsCode = receiveMessageQuery.inject("JSON.stringify(message)")
8698

8799
val jsScripts = """
88-
<script type="text/javascript" src="$WEB_SCRIPT_URI" defer onload="init()"></script>
100+
<script type="text/javascript" src="http://toolkitasset/mynah/js/mynah-ui.js" defer onload="init()"></script>
89101
<script type="text/javascript">
90102
const init = () => {
91103
mynahUI.createMynahUI(
@@ -115,14 +127,12 @@ class Browser(parent: Disposable) : Disposable {
115127
$jsScripts
116128
</head>
117129
<body>
118-
loadingloadingloading
119130
</body>
120131
</html>
121132
""".trimIndent()
122133
}
123134

124135
companion object {
125-
private const val WEB_SCRIPT_URI = "http://127.0.0.1:8000/js/mynah-ui.js"
126136
private const val MAX_ONBOARDING_PAGE_COUNT = 3
127137
private val OBJECT_MAPPER = jacksonObjectMapper()
128138
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
3+
package com.github.rli.cefschemeremotetest.toolWindow
4+
5+
import org.cef.browser.CefBrowser
6+
import org.cef.browser.CefFrame
7+
import org.cef.callback.CefCallback
8+
import org.cef.handler.*
9+
import org.cef.misc.BoolRef
10+
import org.cef.network.CefRequest
11+
import java.net.URL
12+
13+
/**
14+
* Handles local protocol-specific CEF resource requests for a defined `protocol` and `authority`.
15+
*
16+
* This class implements a mechanism to serve protocol-specific resources based on mappings provided
17+
* through the `addResource` function. Only requests matching the configured protocol and authority are processed,
18+
* while others are rejected.
19+
*
20+
* @param myProtocol The protocol to handle (e.g., "http", "file").
21+
* @param myAuthority The authority of the requests (e.g., "localhost", "mydomain").
22+
*/
23+
open class JBCefLocalRequestHandler(
24+
private val myProtocol: String,
25+
private val myAuthority: String
26+
) : CefRequestHandlerAdapter() {
27+
private val myResources: MutableMap<String, () -> CefResourceHandler?> = HashMap()
28+
29+
private val REJECTING_RESOURCE_HANDLER: CefResourceHandler = object : CefResourceHandlerAdapter() {
30+
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
31+
callback.cancel()
32+
return false
33+
}
34+
}
35+
36+
private val RESOURCE_REQUEST_HANDLER = object : CefResourceRequestHandlerAdapter() {
37+
override fun getResourceHandler(browser: CefBrowser?, frame: CefFrame?, request: CefRequest): CefResourceHandler {
38+
val url = URL(request.url)
39+
url.protocol
40+
if (!url.protocol.equals(myProtocol) || !url.authority.equals(myAuthority)) {
41+
return REJECTING_RESOURCE_HANDLER
42+
}
43+
return try {
44+
val path = url.path.trim('/')
45+
myResources[path]?.let { it() } ?: REJECTING_RESOURCE_HANDLER
46+
} catch (e: RuntimeException) {
47+
println(e.message)
48+
REJECTING_RESOURCE_HANDLER
49+
}
50+
}
51+
}
52+
53+
fun addResource(resourcePath: String, resourceProvider: () -> CefResourceHandler?) {
54+
val normalisedPath = resourcePath.trim('/')
55+
myResources[normalisedPath] = resourceProvider
56+
}
57+
58+
fun createResource(resourcePath: String, resourceProvider: () -> CefResourceHandler?): String {
59+
val normalisedPath = resourcePath.trim('/')
60+
myResources[normalisedPath] = resourceProvider
61+
return "$myProtocol://$myAuthority/$normalisedPath"
62+
}
63+
64+
override fun getResourceRequestHandler(browser: CefBrowser?,
65+
frame: CefFrame?,
66+
request: CefRequest?,
67+
isNavigation: Boolean,
68+
isDownload: Boolean,
69+
requestInitiator: String?,
70+
disableDefaultHandling: BoolRef?): CefResourceRequestHandler {
71+
return RESOURCE_REQUEST_HANDLER
72+
}
73+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
3+
package com.github.rli.cefschemeremotetest.toolWindow
4+
5+
import com.intellij.openapi.Disposable
6+
import com.intellij.openapi.diagnostic.Logger
7+
import com.intellij.openapi.util.Disposer
8+
import org.cef.callback.CefCallback
9+
import org.cef.handler.CefResourceHandler
10+
import org.cef.misc.IntRef
11+
import org.cef.misc.StringRef
12+
import org.cef.network.CefRequest
13+
import org.cef.network.CefResponse
14+
import java.io.IOException
15+
import java.io.InputStream
16+
17+
/**
18+
* A handler for managing custom resource requests in JCEF.
19+
* This class implements the `CefResourceHandler` interface, enabling the customization of resource
20+
* loading from a provided input stream. It supports setting MIME types and headers for responses
21+
* and ensures proper disposal of resources.
22+
*
23+
* @param myStream The input stream from which the resource will be read.
24+
* @param myMimeType The MIME type of the response to be sent to the client.
25+
* @param parent A `Disposable` object to which this handler is registered for automatic disposal.
26+
* @param headers Optional headers that will be included in the response.
27+
*/
28+
open class JBCefStreamResourceHandler(
29+
private val myStream: InputStream,
30+
private val myMimeType: String,
31+
parent: Disposable,
32+
private val headers: Map<String, String> = mapOf(),
33+
) : CefResourceHandler, Disposable {
34+
init {
35+
Disposer.register(parent, this)
36+
}
37+
38+
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
39+
callback.Continue()
40+
return true
41+
}
42+
43+
override fun getResponseHeaders(response: CefResponse, responseLength: IntRef, redirectUrl: StringRef) {
44+
response.mimeType = myMimeType
45+
response.status = 200
46+
for (header in headers) {
47+
response.setHeaderByName(header.key, header.value, true /* overwrite */)
48+
}
49+
}
50+
51+
override fun readResponse(dataOut: ByteArray, bytesToRead: Int, bytesRead: IntRef, callback: CefCallback): Boolean {
52+
try {
53+
bytesRead.set(myStream.read(dataOut, 0, bytesToRead))
54+
if (bytesRead.get() != -1) {
55+
return true
56+
}
57+
}
58+
catch (_: IOException) {
59+
callback.cancel()
60+
}
61+
bytesRead.set(0)
62+
Disposer.dispose(this)
63+
return false
64+
}
65+
66+
override fun cancel() {
67+
Disposer.dispose(this)
68+
}
69+
70+
override fun dispose() {
71+
try {
72+
myStream.close()
73+
}
74+
catch (e: IOException) {
75+
Logger.getInstance(JBCefStreamResourceHandler::class.java).warn("Failed to close the stream", e)
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)