diff --git a/AUTHORS b/AUTHORS index 8b8de4941..ab420d217 100644 --- a/AUTHORS +++ b/AUTHORS @@ -26,3 +26,4 @@ Mohamed El Sayed Edwin Ludik Japnit Singh Dmitry Kandalov +Kazuya Chikamatsu diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f30b06d4..5dd7877fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ### Fixed - Silent failure when opening Flutter projects without `.idea` directory in IntelliJ IDEA, by removing `FlutterProjectOpenProcessor` and migrating configuration logic to `FlutterInitializer`. (#8845) +- Gutter buttons not running tests with non-ASCII characters in their names. (#7985) +- Freeze from JX Browser close. (#8864) ## 90.0.0 diff --git a/src/io/flutter/jxbrowser/EmbeddedBrowserEngine.java b/src/io/flutter/jxbrowser/EmbeddedBrowserEngine.java index 6198beb33..31277e8b7 100644 --- a/src/io/flutter/jxbrowser/EmbeddedBrowserEngine.java +++ b/src/io/flutter/jxbrowser/EmbeddedBrowserEngine.java @@ -5,10 +5,11 @@ */ package io.flutter.jxbrowser; -import com.intellij.openapi.application.ApplicationListener; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; +import io.flutter.utils.OpenApiUtils; import com.teamdev.jxbrowser.engine.Engine; import com.teamdev.jxbrowser.engine.EngineOptions; import com.teamdev.jxbrowser.engine.PasswordStore; @@ -22,7 +23,7 @@ import static com.teamdev.jxbrowser.engine.RenderingMode.HARDWARE_ACCELERATED; import static com.teamdev.jxbrowser.engine.RenderingMode.OFF_SCREEN; -public class EmbeddedBrowserEngine { +public class EmbeddedBrowserEngine implements Disposable { private static final @NotNull Logger LOG = PluginLogger.createLogger(EmbeddedBrowserEngine.class); private final Engine engine; @@ -60,22 +61,22 @@ public EmbeddedBrowserEngine() { } engine = temp; - ApplicationManager.getApplication().addApplicationListener(new ApplicationListener() { - @Override - public boolean canExitApplication() { - try { - if (engine != null && !engine.isClosed()) { - engine.close(); - } + } + + @Override + public void dispose() { + OpenApiUtils.safeExecuteOnPooledThread(() -> { + try { + if (engine != null && !engine.isClosed()) { + engine.close(); } - catch (Exception ex) { - if (FlutterSettings.getInstance().isFilePathLoggingEnabled()) { - LOG.info(ex); - } else { - LOG.info("Exception when closing JX Browser engine: " + ex.getMessage()); - } + } + catch (Exception ex) { + if (FlutterSettings.getInstance().isFilePathLoggingEnabled()) { + LOG.info(ex); + } else { + LOG.info("Exception when closing JX Browser engine: " + ex.getMessage()); } - return true; } }); } diff --git a/src/io/flutter/jxbrowser/EmbeddedJxBrowser.java b/src/io/flutter/jxbrowser/EmbeddedJxBrowser.java index 76cce7bca..e1d915f20 100644 --- a/src/io/flutter/jxbrowser/EmbeddedJxBrowser.java +++ b/src/io/flutter/jxbrowser/EmbeddedJxBrowser.java @@ -28,6 +28,7 @@ import io.flutter.settings.FlutterSettings; import io.flutter.utils.AsyncUtils; import io.flutter.utils.JxBrowserUtils; +import io.flutter.utils.OpenApiUtils; import io.flutter.utils.ZoomLevelSelector; import io.flutter.view.EmbeddedBrowser; import io.flutter.view.EmbeddedTab; @@ -98,7 +99,18 @@ public void loadUrl(String url) { @Override public void close() { - this.browser.close(); + OpenApiUtils.safeExecuteOnPooledThread(() -> { + try { + this.browser.close(); + } + catch (Exception ex) { + if (FlutterSettings.getInstance().isFilePathLoggingEnabled()) { + LOG.info(ex); + } else { + LOG.info("Exception when closing JX Browser instance: " + ex.getMessage()); + } + } + }); } @Override diff --git a/src/io/flutter/run/common/CommonTestConfigUtils.java b/src/io/flutter/run/common/CommonTestConfigUtils.java index ba5f72d82..7e5427833 100644 --- a/src/io/flutter/run/common/CommonTestConfigUtils.java +++ b/src/io/flutter/run/common/CommonTestConfigUtils.java @@ -130,13 +130,16 @@ public String findTestName(@Nullable PsiElement elt) { final DartCallExpression call = findEnclosingTestCall(elt, getTestsFromOutline(elt.getContainingFile())); if (call == null) return null; + return extractTestName(call); + } + + @VisibleForTesting + @Nullable + public String extractTestName(@NotNull DartCallExpression call) { final DartStringLiteralExpression lit = DartSyntax.getArgument(call, 0, DartStringLiteralExpression.class); if (lit == null) return null; - final String name = DartSyntax.unquote(lit); - if (name == null) return null; - - return StringUtil.escapeProperty(name, false); + return DartSyntax.unquote(lit); } /** diff --git a/src/io/flutter/view/EmbeddedBrowser.java b/src/io/flutter/view/EmbeddedBrowser.java index 0bcb847b3..a5326cc3e 100644 --- a/src/io/flutter/view/EmbeddedBrowser.java +++ b/src/io/flutter/view/EmbeddedBrowser.java @@ -69,14 +69,15 @@ public void projectClosing(@NotNull Project project) { final Map tabs = windows.get(window); for (final String tabName : tabs.keySet()) { final BrowserTab tab = tabs.get(tabName); - if (tab.embeddedTab != null) { - try { - tab.embeddedTab.close(); + final EmbeddedTab embeddedTab = tab.embeddedTab; + if (embeddedTab != null) { + try { + embeddedTab.close(); + } + catch (Exception ex) { + logger().info(ex); + } } - catch (Exception ex) { - logger().info(ex); - } - } } tabs.clear(); } diff --git a/src/io/flutter/widgetpreview/WidgetPreviewPanel.java b/src/io/flutter/widgetpreview/WidgetPreviewPanel.java index c016fd126..3f530478c 100644 --- a/src/io/flutter/widgetpreview/WidgetPreviewPanel.java +++ b/src/io/flutter/widgetpreview/WidgetPreviewPanel.java @@ -264,7 +264,12 @@ public void dispose() { // Dispose the browser tab final EmbeddedTab tab = browserTabRef.getAndSet(null); if (tab != null) { - tab.close(); + try { + tab.close(); + } + catch (Exception ex) { + LOG.info(ex); + } } } } diff --git a/testSrc/unit/io/flutter/run/common/CommonTestConfigUtilsTest.java b/testSrc/unit/io/flutter/run/common/CommonTestConfigUtilsTest.java new file mode 100644 index 000000000..2c0bd768f --- /dev/null +++ b/testSrc/unit/io/flutter/run/common/CommonTestConfigUtilsTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2026 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +package io.flutter.run.common; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.source.tree.LeafPsiElement; +import com.jetbrains.lang.dart.psi.DartCallExpression; +import io.flutter.AbstractDartElementTest; +import io.flutter.dart.DartSyntax; +import io.flutter.run.test.TestConfigUtils; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class CommonTestConfigUtilsTest extends AbstractDartElementTest { + @Test + public void extractTestNameShouldNotEscapeNonAscii() throws Exception { + run(() -> { + final PsiElement testKeyword = setUpDartElement( + "main() { test('テスト', () {}); }", "test", LeafPsiElement.class); + final DartCallExpression call = + DartSyntax.findEnclosingFunctionCall(testKeyword, "test"); + assertNotNull(call); + + final String name = TestConfigUtils.getInstance().extractTestName(call); + assertEquals("テスト", name); + }); + } +} diff --git a/tool/kokoro/deploy.sh b/tool/kokoro/deploy.sh index dfefc78c4..9aac60ca1 100755 --- a/tool/kokoro/deploy.sh +++ b/tool/kokoro/deploy.sh @@ -10,6 +10,26 @@ echo "kokoro build start" echo "kokoro build finished" echo "kokoro deploy start" -./bin/plugin deploy --channel=dev + +KOKORO_TOKEN_FILE="${KOKORO_KEYSTORE_DIR}/${FLUTTER_KEYSTORE_ID}_${FLUTTER_KEYSTORE_NAME}" +if [ ! -f "$KOKORO_TOKEN_FILE" ]; then + echo "Error: Keystore token file not found at $KOKORO_TOKEN_FILE" + exit 1 +fi +TOKEN=$(cat "$KOKORO_TOKEN_FILE") + +ZIP_FILE="build/distributions/flutter-intellij-kokoro.zip" +if [ ! -f "$ZIP_FILE" ]; then + echo "Error: Zip file not found at $ZIP_FILE" + exit 1 +fi + +echo "Uploading $ZIP_FILE to JetBrains Marketplace..." +curl -if --fail \ + --header "Authorization: Bearer $TOKEN" \ + -F pluginId=9212 \ + -F file=@"$ZIP_FILE" \ + -F channel=dev \ + https://plugins.jetbrains.com/plugin/uploadPlugin echo "kokoro deploy finished"