Skip to content

Commit 61a759b

Browse files
authored
✨ Support multiple running instance for inspectors (#6772)
Resolves #6189. https://github.com/flutter/flutter-intellij/assets/15884415/b5a3438b-4fbd-4fa8-9027-fd3fe0f11963 ## Implementation details - `Browser` is a singleton in the previous implementation. The PR updates it to be stored as a mapping between the tab name (the unique device name) and the browser instance. - `Content`s are also cached to ensure they can be added after removing the empty content. ## Additional context `FlutterPerformanceView` should be able to apply the same update but I didn't investigate it yet. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent e738af5 commit 61a759b

File tree

3 files changed

+60
-23
lines changed

3 files changed

+60
-23
lines changed

flutter-idea/src/io/flutter/jxbrowser/EmbeddedBrowser.java

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
import java.awt.*;
3939
import java.io.File;
40+
import java.util.HashMap;
41+
import java.util.Map;
4042
import java.util.Objects;
4143
import java.util.concurrent.CompletableFuture;
4244
import java.util.concurrent.atomic.AtomicBoolean;
@@ -50,7 +52,8 @@ public static EmbeddedBrowser getInstance(@NotNull Project project) {
5052
return Objects.requireNonNull(project.getService(EmbeddedBrowser.class));
5153
}
5254

53-
private Browser browser;
55+
private final Map<@NotNull String, @NotNull Browser> browsers = new HashMap<>();
56+
private final Map<@NotNull String, @NotNull Content> contents = new HashMap<>();
5457
private CompletableFuture<DevToolsUrl> devToolsUrlFuture;
5558

5659
private EmbeddedBrowser(Project project) {
@@ -61,18 +64,46 @@ private EmbeddedBrowser(Project project) {
6164
System.setProperty("jxbrowser.logging.level", "ALL");
6265
}
6366

67+
ProjectManager.getInstance().addProjectManagerListener(project, new ProjectManagerListener() {
68+
@Override
69+
public void projectClosing(@NotNull Project project) {
70+
for (final String key: browsers.keySet()) {
71+
final Browser browser = browsers.get(key);
72+
if (browser != null) {
73+
try {
74+
browser.close();
75+
} catch (Exception ex) {
76+
LOG.info(ex);
77+
}
78+
browsers.remove(key);
79+
}
80+
}
81+
contents.clear();
82+
}
83+
});
84+
}
85+
86+
private void openBrowserInstanceFor(String tabName) {
6487
try {
6588
final Engine engine = EmbeddedBrowserEngine.getInstance().getEngine();
6689
if (engine == null) {
6790
return;
6891
}
69-
70-
this.browser = engine.newBrowser();
71-
browser.settings().enableTransparentBackground();
72-
browser.on(ConsoleMessageReceived.class, event -> {
92+
final Browser newBrowser = engine.newBrowser();
93+
newBrowser.settings().enableTransparentBackground();
94+
newBrowser.on(ConsoleMessageReceived.class, event -> {
7395
final ConsoleMessage consoleMessage = event.consoleMessage();
7496
LOG.info("Browser message(" + consoleMessage.level().name() + "): " + consoleMessage.message());
7597
});
98+
final Browser oldBrowser = browsers.get(tabName);
99+
if (oldBrowser != null) {
100+
try {
101+
oldBrowser.close();
102+
} catch (Exception ex) {
103+
LOG.info(ex);
104+
}
105+
}
106+
browsers.put(tabName, newBrowser);
76107
}
77108
catch (UnsupportedRenderingModeException ex) {
78109
// Skip using a transparent background if an exception is thrown.
@@ -81,22 +112,7 @@ private EmbeddedBrowser(Project project) {
81112
LOG.info(ex);
82113
FlutterInitializer.getAnalytics().sendExpectedException("jxbrowser-setup", ex);
83114
}
84-
85115
resetUrl();
86-
87-
ProjectManager.getInstance().addProjectManagerListener(project, new ProjectManagerListener() {
88-
@Override
89-
public void projectClosing(@NotNull Project project) {
90-
if (browser != null) {
91-
try {
92-
browser.close();
93-
} catch (Exception ex) {
94-
LOG.info(ex);
95-
}
96-
browser = null;
97-
}
98-
}
99-
});
100116
}
101117

102118
/**
@@ -109,8 +125,13 @@ public void resetUrl() {
109125
this.devToolsUrlFuture = new CompletableFuture<>();
110126
}
111127

112-
public void openPanel(ContentManager contentManager, String tabName, DevToolsUrl devToolsUrl, Runnable onBrowserUnavailable) {
128+
public void openPanel(ContentManager contentManager, @NotNull String tabName, DevToolsUrl devToolsUrl, Runnable onBrowserUnavailable) {
129+
final Browser firstBrowser = browsers.get(tabName);
130+
if (browsers.get(tabName) == null) {
131+
openBrowserInstanceFor(tabName);
132+
}
113133
// If the browser failed to start during setup, run unavailable callback.
134+
final Browser browser = browsers.get(tabName);
114135
if (browser == null) {
115136
onBrowserUnavailable.run();
116137
return;
@@ -141,6 +162,17 @@ public void openPanel(ContentManager contentManager, String tabName, DevToolsUrl
141162
}
142163

143164
contentManager.removeAllContents(false);
165+
if (contents.get(tabName) != null) {
166+
contents.remove(tabName);
167+
}
168+
for (final Content content: contents.values()) {
169+
contentManager.addContent(content);
170+
}
171+
final Content previousContent = contents.get(tabName);
172+
if (previousContent != null) {
173+
contentManager.setSelectedContent(previousContent, true);
174+
return;
175+
}
144176
final Content content = contentManager.getFactory().createContent(null, tabName, false);
145177

146178
// Creating Swing component for rendering web content
@@ -158,6 +190,8 @@ public void openPanel(ContentManager contentManager, String tabName, DevToolsUrl
158190
// TODO(helin24): Use differentiated icons for each tab and copy from devtools toolbar.
159191
content.setIcon(FlutterIcons.Phone);
160192
contentManager.addContent(content);
193+
contentManager.setSelectedContent(content, true);
194+
contents.put(tabName, content);
161195

162196
// This is for pulling up Chrome inspector for debugging purposes.
163197
browser.set(PressKeyCallback.class, params -> {
@@ -227,7 +261,9 @@ private void updateUrlAndReload(Function<DevToolsUrl, DevToolsUrl> newDevToolsUr
227261
// Reload is no longer needed - either URL has been reset or there has been no change.
228262
return;
229263
}
230-
browser.navigation().loadUrl(devToolsUrl.getUrlString());
264+
for (final Browser browser: browsers.values()) {
265+
browser.navigation().loadUrl(devToolsUrl.getUrlString());
266+
}
231267
});
232268
}
233269
}

flutter-idea/src/io/flutter/run/FlutterDevice.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public String toString() {
111111
/**
112112
* Given a collection of devices, return a unique name for this device.
113113
*/
114+
@NotNull
114115
public String getUniqueName(Collection<FlutterDevice> devices) {
115116
for (final FlutterDevice other : devices) {
116117
if (other == this) {
@@ -141,6 +142,7 @@ public static FlutterDevice getTester() {
141142
return new FlutterDevice("flutter-tester", "Flutter test device", null, false);
142143
}
143144

145+
@NotNull
144146
public String presentationName() {
145147
if (category() != null) {
146148
return deviceName() + " (" + category() + ")";

flutter-idea/src/io/flutter/view/FlutterView.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,6 @@ private void replacePanelLabel(ToolWindow toolWindow, JComponent label) {
705705
if (contentManager.isDisposed()) {
706706
return;
707707
}
708-
contentManager.removeAllContents(true);
709708

710709
final JPanel panel = new JPanel(new BorderLayout());
711710
panel.add(label, BorderLayout.CENTER);

0 commit comments

Comments
 (0)