[ui] fix loading plugin icons in html view (IJPL-43721)

Co-authored-by: Victor Turansky <victor.turansky@jetbrains.com>

Merge-request: IJ-MR-126946
Merged-by: Victor Turansky <victor.turansky@jetbrains.com>

GitOrigin-RevId: 11aa1c46f9348b3c4713ff5f35bec6abdce0e642
This commit is contained in:
Konstantin Ulitin
2024-08-08 10:26:15 +00:00
committed by intellij-monorepo-bot
parent 0c1287808f
commit d58d166cd4
4 changed files with 54 additions and 27 deletions

View File

@@ -8,6 +8,7 @@ import com.intellij.AbstractBundle
import com.intellij.DynamicBundle
import com.intellij.icons.AllIcons
import com.intellij.ide.IconLayerProvider
import com.intellij.ide.plugins.PluginManager
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.ide.plugins.cl.PluginAwareClassLoader
import com.intellij.openapi.diagnostic.logger
@@ -257,6 +258,12 @@ class CoreIconManager : IconManager, CoreAwareIconManager {
return plugin.content.modules.firstOrNull { it.name == moduleId }?.requireDescriptor()?.classLoader
}
}
override fun getClassLoaderByClassName(className: String): ClassLoader? {
val pluginId = PluginManager.getPluginByClassNameAsNoAccessToClass(className)
val plugin = PluginManagerCore.getPlugin(pluginId)
return plugin?.classLoader
}
}
private class IconLayer(@JvmField val flagMask: Int, @JvmField val icon: Icon) {

View File

@@ -107,6 +107,9 @@ interface IconManager {
fun getPluginAndModuleId(classLoader: ClassLoader): Pair<String, String?> = "com.intellij" to null
fun getClassLoader(pluginId: String, moduleId: String?): ClassLoader? = IconManager::class.java.classLoader
@Internal
fun getClassLoaderByClassName(className: String): ClassLoader? = IconManager::class.java.classLoader
}
private object DummyIconManager : IconManager {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// 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.icons
import com.github.benmanes.caffeine.cache.Cache
@@ -144,34 +144,39 @@ fun isReflectivePath(path: String): Boolean {
@Internal
fun getReflectiveIcon(path: String, classLoader: ClassLoader): Icon? {
try {
var dotIndex = path.lastIndexOf('.')
val fieldName = path.substring(dotIndex + 1)
val builder = StringBuilder(path.length + 20)
builder.append(path, 0, dotIndex)
var separatorIndex = -1
do {
dotIndex = path.lastIndexOf('.', dotIndex - 1)
// if starts with a lower case, char - it is a package name
if (dotIndex == -1 || path[dotIndex + 1].isLowerCase()) {
break
}
if (separatorIndex != -1) {
builder.setCharAt(separatorIndex, '$')
}
separatorIndex = dotIndex
}
while (true)
if (!builder[0].isLowerCase()) {
if (separatorIndex != -1) {
builder.setCharAt(separatorIndex, '$')
}
builder.insert(0, if (path.startsWith("AllIcons.")) "com.intellij.icons." else "icons.")
}
val aClass = classLoader.loadClass(builder.toString())
val fieldName = path.substring(path.lastIndexOf('.') + 1)
val className = getClassNameByIconPath(path)
val aClass = classLoader.loadClass(className)
return LOOKUP.findStaticGetter(aClass, fieldName, Icon::class.java).invoke() as Icon
}
catch (e: Throwable) {
logger<CachedImageIcon>().warn("Cannot get reflective icon (path=$path)", e)
return null
}
}
internal fun getClassNameByIconPath(path: String): String {
var dotIndex = path.lastIndexOf('.')
val builder = StringBuilder(path.length + 20)
builder.append(path, 0, dotIndex)
var separatorIndex = -1
do {
dotIndex = path.lastIndexOf('.', dotIndex - 1)
// if starts with a lower case, char - it is a package name
if (dotIndex == -1 || path[dotIndex + 1].isLowerCase()) {
break
}
if (separatorIndex != -1) {
builder.setCharAt(separatorIndex, '$')
}
separatorIndex = dotIndex
}
while (true)
if (!builder[0].isLowerCase()) {
if (separatorIndex != -1) {
builder.setCharAt(separatorIndex, '$')
}
builder.insert(0, if (path.startsWith("AllIcons.")) "com.intellij.icons." else "icons.")
}
return builder.toString()
}

View File

@@ -6,6 +6,9 @@ package com.intellij.util.ui
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.util.findIconUsingNewImplementation
import com.intellij.openapi.util.text.HtmlChunk
import com.intellij.ui.IconManager
import com.intellij.ui.icons.getClassNameByIconPath
import com.intellij.ui.icons.isReflectivePath
import com.intellij.ui.scale.JBUIScale
import com.intellij.util.asSafely
import com.intellij.util.text.nullize
@@ -32,7 +35,7 @@ import kotlin.math.max
*/
class ExtendableHTMLViewFactory internal constructor(
private val extensions: List<(Element, View) -> View?>,
private val base: ViewFactory = HTMLEditorKit().viewFactory
private val base: ViewFactory = HTMLEditorKit().viewFactory,
) : HTMLFactory() {
internal constructor(vararg extensions: (Element, View) -> View?) : this(extensions.asList())
@@ -190,7 +193,16 @@ private class IconExtension(private val existingIconProvider: (key: String) -> I
if (existingIcon != null) {
return existingIcon
}
return findIconUsingNewImplementation(path = src, classLoader = ExtendableHTMLViewFactory::class.java.classLoader)
val classLoader = if (isReflectivePath(src)) {
val className = getClassNameByIconPath(src)
IconManager.getInstance().getClassLoaderByClassName(className)
} else null
return findIconUsingNewImplementation(
path = src,
classLoader = classLoader ?: ExtendableHTMLViewFactory::class.java.classLoader,
)
}
}