Skip to content

Commit

Permalink
Handle JCEF is not supported error (#309)
Browse files Browse the repository at this point in the history
* Changed message in case JCEF is not supported.

* Created an empty state for view load error.

* Fixed a bug regarding opening links inside the webview, instead of in a new browser window.
  • Loading branch information
asafgabai authored Mar 13, 2023
1 parent 1df63df commit 5edadae
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 79 deletions.
148 changes: 91 additions & 57 deletions src/main/java/com/jfrog/ide/idea/ui/JFrogLocalToolWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.impl.jdkDownloader.RuntimeChooserUtil;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
Expand All @@ -13,10 +15,9 @@
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.ui.*;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBPanel;
import com.intellij.ui.jcef.JBCefApp;
import com.intellij.ui.jcef.JBCefBrowser;
import com.intellij.ui.jcef.JBCefBrowserBase;
import com.intellij.util.ui.UIUtil;
import com.jfrog.ide.common.nodes.ApplicableIssueNode;
import com.jfrog.ide.common.nodes.IssueNode;
Expand All @@ -28,15 +29,13 @@
import com.jfrog.ide.idea.events.ApplicationEvents;
import com.jfrog.ide.idea.log.Logger;
import com.jfrog.ide.idea.scan.ScanManager;
import com.jfrog.ide.idea.ui.jcef.message.MessagePacker;
import com.jfrog.ide.idea.ui.utils.ComponentUtils;
import com.jfrog.ide.idea.ui.webview.WebviewManager;
import com.jfrog.ide.idea.ui.webview.WebviewObjectConverter;
import com.jfrog.ide.idea.utils.Utils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.CefLoadHandlerAdapter;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
Expand All @@ -55,40 +54,48 @@
*/
public class JFrogLocalToolWindow extends AbstractJFrogToolWindow {
private final LocalComponentsTree componentsTree;
private final JBCefBrowserBase browser;
private final OnePixelSplitter verticalSplit;
private final JPanel leftPanelContent;
private final JComponent compTreeView;
private final MessagePacker messagePacker;
private final boolean isInitialized;
private JPanel leftPanelContent;
private JComponent compTreeView;
private boolean isInitialized;
private IssueNode selectedIssue;
private Path tempDirPath;
private WebviewManager webviewManager;

/**
* @param project - Currently opened IntelliJ project
*/
public JFrogLocalToolWindow(@NotNull Project project) {
super(project);
componentsTree = LocalComponentsTree.getInstance(project);
browser = new JBCefBrowser();
JPanel leftPanel = new JBPanel<>(new BorderLayout());
verticalSplit = new OnePixelSplitter(false, 0.4f);
verticalSplit.setFirstComponent(leftPanel);
setContent(verticalSplit);
if (!JBCefApp.isSupported()) {
leftPanel.add(createJcefNotSupportedView(), 0);
return;
}

messagePacker = new MessagePacker(browser);
initVulnerabilityInfoBrowser();
JComponent browserComponent;
try {
browserComponent = initVulnerabilityInfoBrowser();
} catch (IOException | URISyntaxException e) {
Logger.getInstance().error("Local view couldn't be initialized.", e);
leftPanel.removeAll();
leftPanel.add(createLoadErrorView(), 0);
return;
}

JPanel toolbar = createActionToolbar();
toolbar.setBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM));
JPanel leftPanel = new JBPanel<>(new BorderLayout());
leftPanel.add(toolbar, BorderLayout.PAGE_START);
leftPanelContent = new JBPanel<>(new BorderLayout());
leftPanel.add(leftPanelContent);
compTreeView = createComponentsTreeView();

verticalSplit = new OnePixelSplitter(false, 0.4f);
verticalSplit.setFirstComponent(leftPanel);
setContent(verticalSplit);

refreshView();
registerListeners();
registerListeners(browserComponent);
isInitialized = true;
}

Expand All @@ -102,7 +109,7 @@ public JPanel createActionToolbar() {
/**
* Register the issues tree listeners.
*/
public void registerListeners() {
public void registerListeners(JComponent browserComponent) {
// Xray credentials were set listener
appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, () -> ApplicationManager.getApplication().invokeLater(this::onConfigurationChange));

Expand All @@ -115,7 +122,7 @@ public void registerListeners() {

selectedIssue = (IssueNode) e.getNewLeadSelectionPath().getLastPathComponent();
updateIssueOrLicenseInWebview(selectedIssue);
verticalSplit.setSecondComponent(browser.getComponent());
verticalSplit.setSecondComponent(browserComponent);
});
projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_STARTED, () -> {
setLeftPanelContent(compTreeView);
Expand All @@ -138,56 +145,84 @@ private void refreshView() {

@SuppressWarnings("UnstableApiUsage")
private JComponent createReadyEnvView() {
JPanel noCredentialsPanel = new JBPanel<>();
noCredentialsPanel.setLayout(new BoxLayout(noCredentialsPanel, BoxLayout.PAGE_AXIS));
JPanel readyEnvPanel = new JBPanel<>();
readyEnvPanel.setLayout(new BoxLayout(readyEnvPanel, BoxLayout.PAGE_AXIS));

// "We're all set!"
HyperlinkLabel allSetLabel = new HyperlinkLabel();
JBLabel allSetLabel = new JBLabel();
allSetLabel.setText("We're all set.");
ComponentUtils.addCenteredHyperlinkLabel(noCredentialsPanel, allSetLabel);
ComponentUtils.addCenteredComponent(readyEnvPanel, allSetLabel);

// "Scan your project"
HyperlinkLabel scanLink = new HyperlinkLabel();
scanLink.setTextWithHyperlink("<hyperlink>Scan your project</hyperlink>");
scanLink.addHyperlinkListener(e -> ScanManager.getInstance(project).startScan());
ComponentUtils.addCenteredHyperlinkLabel(noCredentialsPanel, scanLink);
ComponentUtils.addCenteredComponent(readyEnvPanel, scanLink);

return ComponentUtils.createUnsupportedPanel(noCredentialsPanel);
return ComponentUtils.createUnsupportedPanel(readyEnvPanel);
}

private void setLeftPanelContent(JComponent component) {
leftPanelContent.removeAll();
leftPanelContent.add(component, 0);
@SuppressWarnings("UnstableApiUsage")
private JComponent createJcefNotSupportedView() {
JPanel jcefNotSupportedPanel = new JBPanel<>();
jcefNotSupportedPanel.setLayout(new BoxLayout(jcefNotSupportedPanel, BoxLayout.PAGE_AXIS));

// "Thank you for installing the JFrog IDEA Plugin!"
JBLabel thanksLabel = new JBLabel();
thanksLabel.setText("Thank you for installing the JFrog IntelliJ IDEA Plugin!");
ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, thanksLabel);

// "The plugin uses a component named JCEF that seem to be missing in your IDE."
JBLabel pluginNeedsJcefLabel = new JBLabel();
pluginNeedsJcefLabel.setText("The plugin uses a component named JCEF that seem to be missing in your IDE.");
ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, pluginNeedsJcefLabel);

// "To make JCEF available in your IDE, you’ll need to have the IDE use a different boot runtime."
JBLabel replaceBootRuntimeLabel = new JBLabel();
replaceBootRuntimeLabel.setText("To make JCEF available in your IDE, you’ll need to have the IDE use a different boot runtime.");
ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, replaceBootRuntimeLabel);

// "Click here, choose a boot runtime with JCEF, restart the IDE and come back."
HyperlinkLabel scanLink = new HyperlinkLabel();
scanLink.setTextWithHyperlink("<hyperlink>Click here</hyperlink>, choose a boot runtime with JCEF, restart the IDE and come back.");
scanLink.addHyperlinkListener(e -> RuntimeChooserUtil.INSTANCE.showRuntimeChooserPopup());
ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, scanLink);

return ComponentUtils.createUnsupportedPanel(jcefNotSupportedPanel);
}

private void initVulnerabilityInfoBrowser() {
if (!JBCefApp.isSupported()) {
Logger.getInstance().error("Could not open the issue details view - JCEF is not supported");
return;
}
private JComponent createLoadErrorView() {
JPanel loadErrorPanel = new JBPanel<>();
loadErrorPanel.setLayout(new BoxLayout(loadErrorPanel, BoxLayout.PAGE_AXIS));

try {
tempDirPath = Files.createTempDirectory("jfrog-idea-plugin");
Utils.extractFromResources("/jfrog-ide-webview", tempDirPath);
} catch (IOException | URISyntaxException e) {
Logger.getInstance().error(e.getMessage());
return;
}
// "The view couldn't be loaded."
JBLabel viewNotLoadedLabel = new JBLabel();
viewNotLoadedLabel.setText("The view couldn't be loaded.");
ComponentUtils.addCenteredComponent(loadErrorPanel, viewNotLoadedLabel);

browser.getJBCefClient().addLoadHandler(new CefLoadHandlerAdapter() {
@Override
public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) {
updateIssueOrLicenseInWebview(selectedIssue);
}
// "Check the Notifications / Event Log for more information."
JBLabel checkLogsLabel = new JBLabel();
checkLogsLabel.setText("Check the Notifications / Event Log for more information.");
ComponentUtils.addCenteredComponent(loadErrorPanel, checkLogsLabel);

@Override
public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, String errorText, String failedUrl) {
Logger.getInstance().error("An error occurred while opening the issue details view: " + errorText);
}
}, browser.getCefBrowser());
return ComponentUtils.createUnsupportedPanel(loadErrorPanel);
}

private void setLeftPanelContent(JComponent component) {
leftPanelContent.removeAll();
leftPanelContent.add(component, 0);
}

private JComponent initVulnerabilityInfoBrowser() throws IOException, URISyntaxException {
tempDirPath = Files.createTempDirectory("jfrog-idea-plugin");
Utils.extractFromResources("/jfrog-ide-webview", tempDirPath);

String pageUri = tempDirPath.resolve("index.html").toFile().toURI().toString();
browser.loadURL(pageUri);
webviewManager = new WebviewManager();
Disposer.register(this, webviewManager);

CefBrowser browser = webviewManager.createBrowser(pageUri, () -> updateIssueOrLicenseInWebview(selectedIssue));
return (JComponent) browser.getUIComponent();
}

/**
Expand All @@ -209,14 +244,14 @@ private JComponent createComponentsTreeView() {
private void updateIssueOrLicenseInWebview(IssueNode vulnerabilityOrViolation) {
if (vulnerabilityOrViolation instanceof VulnerabilityNode) {
VulnerabilityNode issue = (VulnerabilityNode) vulnerabilityOrViolation;
messagePacker.send(WebviewObjectConverter.convertIssueToDepPage(issue));
webviewManager.sendMessage(WebviewObjectConverter.convertIssueToDepPage(issue));
} else if (vulnerabilityOrViolation instanceof ApplicableIssueNode) {
ApplicableIssueNode node = (ApplicableIssueNode) vulnerabilityOrViolation;
messagePacker.send(WebviewObjectConverter.convertIssueToDepPage(node.getIssue()));
webviewManager.sendMessage(WebviewObjectConverter.convertIssueToDepPage(node.getIssue()));
navigateToFile(node);
} else if (vulnerabilityOrViolation instanceof LicenseViolationNode) {
LicenseViolationNode license = (LicenseViolationNode) vulnerabilityOrViolation;
messagePacker.send(WebviewObjectConverter.convertLicenseToDepPage(license));
webviewManager.sendMessage(WebviewObjectConverter.convertLicenseToDepPage(license));
}
}

Expand Down Expand Up @@ -259,7 +294,6 @@ public void onConfigurationChange() {
@Override
public void dispose() {
super.dispose();
browser.dispose();
try {
FileUtils.deleteDirectory(tempDirPath.toFile());
} catch (IOException e) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/jfrog/ide/idea/ui/JFrogToolWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.ui.content.ContentManager;
import com.jfrog.ide.idea.log.Logger;
import org.jetbrains.annotations.NotNull;


Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package com.jfrog.ide.idea.ui.jcef.message;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.intellij.ui.jcef.JBCefBrowserBase;
import com.jfrog.ide.idea.log.Logger;

import static com.jfrog.ide.idea.ui.jcef.message.PackedMessage.IDE_SEND_FUNCTION_NAME;
import org.cef.browser.CefBrowser;

public class MessagePacker implements MessagePipe {
JBCefBrowserBase browser;
CefBrowser browser;

public MessagePacker(JBCefBrowserBase browser) {
public MessagePacker(CefBrowser browser) {
this.browser = browser;
}

Expand All @@ -24,11 +22,8 @@ public void send(Object data) {
}

private void send(String raw) {
browser.getCefBrowser().executeJavaScript(
"window." + IDE_SEND_FUNCTION_NAME + "=" + raw,
browser.getCefBrowser().getURL(), 0);
browser.getCefBrowser().executeJavaScript(
browser.executeJavaScript(
"window.postMessage(" + raw + ")",
browser.getCefBrowser().getURL(), 0);
browser.getURL(), 0);
}
}
24 changes: 12 additions & 12 deletions src/main/java/com/jfrog/ide/idea/ui/utils/ComponentUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,41 +41,41 @@ public static JComponent createNoCredentialsView() {
noCredentialsPanel.setLayout(new BoxLayout(noCredentialsPanel, BoxLayout.PAGE_AXIS));

// "Thank you for installing the JFrog plugin"
HyperlinkLabel thanksLabel = new HyperlinkLabel();
JBLabel thanksLabel = new JBLabel();
thanksLabel.setText("Thank you for installing the JFrog IntelliJ IDEA Plugin!");
addCenteredHyperlinkLabel(noCredentialsPanel, thanksLabel);
addCenteredComponent(noCredentialsPanel, thanksLabel);

// "If you already have a JFrog environment, configure its connection details."
HyperlinkLabel configLink = new HyperlinkLabel();
configLink.setTextWithHyperlink("If you already have a JFrog environment,<hyperlink>configure</hyperlink> its connection details.");
configLink.addHyperlinkListener(e -> ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class));
addCenteredHyperlinkLabel(noCredentialsPanel, configLink);
addCenteredComponent(noCredentialsPanel, configLink);

// "Don't have a JFrog environment? Get one for FREE!"
HyperlinkLabel getFreeLink = new HyperlinkLabel();
getFreeLink.setTextWithHyperlink("Don't have a JFrog environment?<hyperlink>Get one for FREE!</hyperlink>");
getFreeLink.addHyperlinkListener(e -> BrowserUtil.browse("https://github.com/jfrog/jfrog-idea-plugin#set-up-a-free-jfrog-environment-in-the-cloud"));
addCenteredHyperlinkLabel(noCredentialsPanel, getFreeLink);
addCenteredComponent(noCredentialsPanel, getFreeLink);

// "Read more about the plugin."
HyperlinkLabel readMoreLink = new HyperlinkLabel();
readMoreLink.setTextWithHyperlink("<hyperlink>Read more</hyperlink> about the plugin.");
readMoreLink.addHyperlinkListener(e -> BrowserUtil.browse("https://github.com/jfrog/jfrog-idea-plugin#readme"));
addCenteredHyperlinkLabel(noCredentialsPanel, readMoreLink);
addCenteredComponent(noCredentialsPanel, readMoreLink);

return createUnsupportedPanel(noCredentialsPanel);
}

/**
* Add centered HyperlinkLabel to the input panel.
* Add centered {@link JComponent} to the input panel.
*
* @param panel - The input panel
* @param hyperlinkLabel - The hyperlink label
* @param panel - The input panel
* @param component - The component to add
*/
public static void addCenteredHyperlinkLabel(JPanel panel, HyperlinkLabel hyperlinkLabel) {
hyperlinkLabel.setMaximumSize(hyperlinkLabel.getPreferredSize());
hyperlinkLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(hyperlinkLabel);
public static void addCenteredComponent(JPanel panel, JComponent component) {
component.setMaximumSize(component.getPreferredSize());
component.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(component);
}

@SuppressWarnings("UnstableApiUsage")
Expand Down
Loading

0 comments on commit 5edadae

Please sign in to comment.