diff --git a/python/rest/src/com/jetbrains/rest/editor/RestJavaFxHtmlPanel.java b/python/rest/src/com/jetbrains/rest/editor/RestJavaFxHtmlPanel.java index 0d9d3e7a4cac..cf1fe7ef0934 100644 --- a/python/rest/src/com/jetbrains/rest/editor/RestJavaFxHtmlPanel.java +++ b/python/rest/src/com/jetbrains/rest/editor/RestJavaFxHtmlPanel.java @@ -1,12 +1,25 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.rest.editor; +import com.intellij.ide.BrowserUtil; import com.intellij.ide.ui.LafManager; import com.intellij.ide.ui.LafManagerListener; import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.javafx.JavaFxHtmlPanel; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Worker; import javafx.scene.text.FontSmoothingType; +import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.EventTarget; +import org.w3c.dom.html.HTMLAnchorElement; import java.net.URL; @@ -18,6 +31,8 @@ public class RestJavaFxHtmlPanel extends JavaFxHtmlPanel implements RestPreviewP runInPlatformWhenAvailable(() -> { final WebView webView = getWebViewGuaranteed(); webView.fontSmoothingTypeProperty().setValue(FontSmoothingType.LCD); + + webView.getEngine().getLoadWorker().stateProperty().addListener(new HyperlinkRedirectListener(webView)); updateLaf(LafManager.getInstance().getCurrentLookAndFeel() instanceof DarculaLookAndFeelInfo); }); } @@ -34,9 +49,52 @@ public class RestJavaFxHtmlPanel extends JavaFxHtmlPanel implements RestPreviewP } private void updateLaf(boolean isDarcula) { - runInPlatformWhenAvailable(() -> { - final WebView webView = getWebViewGuaranteed(); - webView.getEngine().setUserStyleSheetLocation(getStyle(isDarcula).toExternalForm()); - }); + ApplicationManager.getApplication().invokeLater(() -> + runInPlatformWhenAvailable(() -> { + final WebView webView = getWebViewGuaranteed(); + webView.getEngine().setUserStyleSheetLocation(getStyle(isDarcula).toExternalForm()); + } + )); + } + + private static class HyperlinkRedirectListener implements ChangeListener{ + private static final String EVENT_TYPE_CLICK = "click"; + private final WebEngine myEngine; + + public HyperlinkRedirectListener(WebView view) { + myEngine = view.getEngine(); + } + + @Override + public void changed(ObservableValue observable, Worker.State oldValue, Worker.State newValue) { + if (newValue == Worker.State.SUCCEEDED) { + final EventListener listener = makeHyperLinkListener(); + addListenerToAllHyperlinkItems(listener); + } + } + private void addListenerToAllHyperlinkItems(EventListener listener) { + final Document doc = myEngine.getDocument(); + if (doc != null) { + final NodeList nodeList = doc.getElementsByTagName("a"); + for (int i = 0; i < nodeList.getLength(); i++) { + ((EventTarget)nodeList.item(i)).addEventListener(EVENT_TYPE_CLICK, listener, false); + } + } + } + + @NotNull + private static EventListener makeHyperLinkListener() { + return new EventListener() { + @Override + public void handleEvent(Event ev) { + EventTarget target = ev.getCurrentTarget(); + HTMLAnchorElement anchorElement = (HTMLAnchorElement) target; + String href = anchorElement.getHref(); + if (href == null) return; + BrowserUtil.browse(href); + ev.preventDefault(); + } + }; + } } }