From c6f2cd75b3e4f17204ce7ed4bf78ca7427bbe552 Mon Sep 17 00:00:00 2001 From: XePeleato Date: Thu, 17 Sep 2020 02:15:58 +0200 Subject: [PATCH 1/2] Use Webview based logger --- .../uilogger/UiLoggerController.java | 87 ++++++++++++------- .../gearth/ui/logger/uilogger/logger.css | 28 +++--- .../gearth/ui/logger/uilogger/logger.html | 11 +++ 3 files changed, 82 insertions(+), 44 deletions(-) create mode 100644 G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.html diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java index 064d6c0..c29ea06 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java +++ b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java @@ -6,18 +6,16 @@ import gearth.protocol.HMessage; import gearth.protocol.HPacket; import gearth.ui.logger.loggerdisplays.PacketLogger; import javafx.application.Platform; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; +import javafx.concurrent.Worker; import javafx.event.ActionEvent; import javafx.fxml.Initializable; import javafx.scene.control.CheckMenuItem; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; import javafx.stage.Stage; -import org.fxmisc.flowless.VirtualizedScrollPane; -import org.fxmisc.richtext.StyleClassedTextArea; -import org.fxmisc.richtext.model.StyleSpansBuilder; import java.net.URL; import java.util.*; @@ -37,7 +35,7 @@ public class UiLoggerController implements Initializable { public CheckMenuItem chkMessageHash; public Label lblHarbleAPI; - private StyleClassedTextArea area; + private WebView webView; private Stage stage; @@ -55,20 +53,22 @@ public class UiLoggerController implements Initializable { @Override public void initialize(URL arg0, ResourceBundle arg1) { - area = new StyleClassedTextArea(); - area.getStyleClass().add("dark"); - area.setWrapText(true); + webView = new WebView(); - VirtualizedScrollPane vsPane = new VirtualizedScrollPane<>(area); - borderPane.setCenter(vsPane); + borderPane.setCenter(webView); - synchronized (appendLater) { - initialized = true; - if (!appendLater.isEmpty()) { - appendLog(appendLater); - appendLater.clear(); - } - } + webView.getEngine().getLoadWorker().stateProperty().addListener((observableValue, oldState, newState) -> { + if (newState == Worker.State.SUCCEEDED) + synchronized (appendLater) { + initialized = true; + if (!appendLater.isEmpty()) { + appendLog(appendLater); + appendLater.clear(); + } + } + }); + + webView.getEngine().load(getClass().getResource("/gearth/ui/logger/uilogger/logger.html").toString()); } private static String cleanTextContent(String text) { @@ -168,27 +168,54 @@ public class UiLoggerController implements Initializable { private synchronized void appendLog(List elements) { Platform.runLater(() -> { - StringBuilder sb = new StringBuilder(); - StyleSpansBuilder> styleSpansBuilder = new StyleSpansBuilder<>(0); for (Element element : elements) { - sb.append(element.text); + String script = "$('#output').append('" + + escapeMessage(element.text) + "');"; - styleSpansBuilder.add(Collections.singleton(element.className), element.text.length()); + try { + executejQuery(webView.getEngine(), script); + } catch (Exception e) { + System.out.println("Malformed JS message " + script); + } } - int oldLen = area.getLength(); - area.appendText(sb.toString()); -// System.out.println(sb.toString()); - area.setStyleSpans(oldLen, styleSpansBuilder.create()); - if (autoScroll) { -// area.moveTo(area.getLength()); - area.requestFollowCaret(); + webView.getEngine().executeScript("window.scrollTo(0, document.body.scrollHeight);"); } }); } + // escapes logger text so that there are no javascript errors + private String escapeMessage(String text) { + return text + .replace("\n\r", "
") + .replace("\n", "
") + .replace("\r", "
") + .replace("'", "\\'"); + } + + private static Object executejQuery(final WebEngine engine, String script) { + return engine.executeScript( + "(function(window, document, version, callback) { " + + "var j, d;" + + "var loaded = false;" + + "if (!(j = window.jQuery) || version > j.fn.jquery || callback(j, loaded)) {" + + " var script = document.createElement(\"script\");" + + " script.type = \"text/javascript\";" + + " script.src = \"http://code.jquery.com/jquery-1.7.2.min.js\";" + + " script.onload = script.onreadystatechange = function() {" + + " if (!loaded && (!(d = this.readyState) || d == \"loaded\" || d == \"complete\")) {" + + " callback((j = window.jQuery).noConflict(1), loaded = true);" + + " j(script).remove();" + + " }" + + " };" + + " document.documentElement.childNodes[0].appendChild(script) " + + "} " + + "})(window, document, \"1.7.2\", function($, jquery_loaded) {" + script + "});" + ); + } + public void setStage(Stage stage) { this.stage = stage; } @@ -233,6 +260,6 @@ public class UiLoggerController implements Initializable { } public void clearText(ActionEvent actionEvent) { - area.clear(); + webView.getEngine().executeScript("$('#output').html = \\'\\'"); } } diff --git a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css index e3554ad..d83a56d 100644 --- a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css +++ b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css @@ -1,47 +1,47 @@ /* packet logger css */ .text { - -fx-fill: #a9a9a9; + color: #a9a9a9; } .messageinfo { - -fx-fill: #D0D3D4; + color: #D0D3D4; } .blocked, .replaced { - -fx-fill: #ffff00; + color: #ffff00; } .incoming { - -fx-fill: #b22222; + color: #b22222; } .outgoing { - -fx-fill: #0066cc; + color: #0066cc; } .structure, .skipped { - -fx-fill: cyan; + color: cyan; } .dark { - -fx-background-color: #000000; + background-color: #000000; + color: #a9a9a9; } .caret { - -fx-stroke: #ffffff; + stroke: #ffffff; } .label { - -fx-text-fill: #000000 !important; + color: #000000 !important; } .menu-bar .text, .menu .text { - -fx-text-fill: #000000 !important; - -fx-fill: #000000 !important; - -fx-padding: -2 0 -2 0 !important; + color: #000000 !important; + padding: -2px 0 -2px 0 !important; } .scroll-bar:vertical { - -fx-pref-width: 16.5; - -fx-padding: 1; + width: 17px; + padding: 1px; } diff --git a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.html b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.html new file mode 100644 index 0000000..c45d9a3 --- /dev/null +++ b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.html @@ -0,0 +1,11 @@ + + + + + G-Earth Logger + + + +
+ + From f1fcc389c8072f6109a9fc6348805bb45cf14b07 Mon Sep 17 00:00:00 2001 From: XePeleato Date: Sun, 27 Sep 2020 16:29:11 +0200 Subject: [PATCH 2/2] Fix issues with Webview logger * Decreased left margin * Autoscroll goes all the way until the last packet * Clear text fixed * Still the first crypto-related packets don't wrap properly --- G-Earth/pom.xml | 7 +- .../logger/ExtensionLoggerController.java | 98 +++++++++------- .../uilogger/UiLoggerController.java | 106 +++++++----------- .../gearth/ui/logger/uilogger/logger.css | 2 +- 4 files changed, 105 insertions(+), 108 deletions(-) diff --git a/G-Earth/pom.xml b/G-Earth/pom.xml index fb8e3c7..adbc5be 100644 --- a/G-Earth/pom.xml +++ b/G-Earth/pom.xml @@ -144,11 +144,6 @@ json 20190722 - - org.fxmisc.richtext - richtextfx - 0.10.5 - org.jsoup jsoup @@ -172,4 +167,4 @@ https://oss.sonatype.org/content/repositories/snapshots - \ No newline at end of file + diff --git a/G-Earth/src/main/java/gearth/ui/extensions/logger/ExtensionLoggerController.java b/G-Earth/src/main/java/gearth/ui/extensions/logger/ExtensionLoggerController.java index 58f88a6..a8a5115 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/logger/ExtensionLoggerController.java +++ b/G-Earth/src/main/java/gearth/ui/extensions/logger/ExtensionLoggerController.java @@ -1,13 +1,12 @@ package gearth.ui.extensions.logger; import javafx.application.Platform; +import javafx.concurrent.Worker; import javafx.fxml.Initializable; -import javafx.scene.control.ScrollPane; import javafx.scene.layout.BorderPane; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; import javafx.stage.Stage; -import org.fxmisc.flowless.VirtualizedScrollPane; -import org.fxmisc.richtext.StyleClassedTextArea; -import org.fxmisc.richtext.model.StyleSpansBuilder; import java.net.URL; import java.util.*; @@ -16,57 +15,42 @@ public class ExtensionLoggerController implements Initializable { public BorderPane borderPane; private Stage stage = null; - private StyleClassedTextArea area; + private WebView webView; private volatile boolean initialized = false; - private final List appendOnLoad = new ArrayList<>(); + private final List appendOnLoad = new LinkedList<>(); @Override public void initialize(URL arg0, ResourceBundle arg1) { - area = new StyleClassedTextArea(); - area.getStyleClass().add("white"); - area.setWrapText(true); - area.setEditable(false); + webView = new WebView(); - VirtualizedScrollPane vsPane = new VirtualizedScrollPane<>(area); - borderPane.setCenter(vsPane); + borderPane.setCenter(webView); - synchronized (appendOnLoad) { - initialized = true; - if (!appendOnLoad.isEmpty()) { + webView.getEngine().getLoadWorker().stateProperty().addListener((observableValue, oldState, newState) -> { + if (newState == Worker.State.SUCCEEDED) { + initialized = true; + webView.prefHeightProperty().bind(stage.heightProperty()); + webView.prefWidthProperty().bind(stage.widthProperty()); appendLog(appendOnLoad); - appendOnLoad.clear(); } - } + }); + + webView.getEngine().load(getClass().getResource("/gearth/ui/logger/uilogger/logger.html").toString()); } - private synchronized void appendLog(List elements) { + private synchronized void appendLog(List html) { Platform.runLater(() -> { - StringBuilder sb = new StringBuilder(); - StyleSpansBuilder> styleSpansBuilder = new StyleSpansBuilder<>(0); + String script = "document.getElementById('output').innerHTML += '" + String.join("", html) + "';"; + webView.getEngine().executeScript(script); - for (Element element : elements) { - sb.append(element.text); - - styleSpansBuilder.add(Collections.singleton(element.className), element.text.length()); - } - - int oldLen = area.getLength(); - area.appendText(sb.toString()); -// System.out.println(sb.toString()); - area.setStyleSpans(oldLen, styleSpansBuilder.create()); - -// if (autoScroll) { - area.moveTo(area.getLength()); - area.requestFollowCaret(); -// } + executejQuery(webView.getEngine(), "$('html, body').animate({scrollTop:$(document).height()}, 'slow');"); }); } void log(String s) { s = cleanTextContent(s); - ArrayList elements = new ArrayList<>(); + List elements = new LinkedList<>(); String classname, text; if (s.startsWith("[") && s.contains("]")) { @@ -82,9 +66,9 @@ public class ExtensionLoggerController implements Initializable { int index = text.indexOf(" --> ") + 5; String extensionAnnouncement = text.substring(0, index); text = text.substring(index); - elements.add(new Element(extensionAnnouncement, "black")); + elements.add(divWithClass(extensionAnnouncement, "black")); } - elements.add(new Element(text + "\n", classname.toLowerCase())); + elements.add(divWithClass(text, classname.toLowerCase())); synchronized (appendOnLoad) { if (initialized) { @@ -114,4 +98,42 @@ public class ExtensionLoggerController implements Initializable { return text.trim(); } + private String divWithClass(String content, String klass) { + return escapeMessage("
" + content + "
"); + } + + private String spanWithClass(String content, String klass) { + return escapeMessage("" + content + ""); + } + + private String escapeMessage(String text) { + return text + .replace("\n\r", "
") + .replace("\n", "
") + .replace("\r", "
") + .replace("'", "\\'"); + } + + private static Object executejQuery(final WebEngine engine, String script) { + return engine.executeScript( + "(function(window, document, version, callback) { " + + "var j, d;" + + "var loaded = false;" + + "if (!(j = window.jQuery) || version > j.fn.jquery || callback(j, loaded)) {" + + " var script = document.createElement(\"script\");" + + " script.type = \"text/javascript\";" + + " script.src = \"http://code.jquery.com/jquery-1.7.2.min.js\";" + + " script.onload = script.onreadystatechange = function() {" + + " if (!loaded && (!(d = this.readyState) || d == \"loaded\" || d == \"complete\")) {" + + " callback((j = window.jQuery).noConflict(1), loaded = true);" + + " j(script).remove();" + + " }" + + " };" + + " document.documentElement.childNodes[0].appendChild(script) " + + "} " + + "})(window, document, \"1.7.2\", function($, jquery_loaded) {" + script + "});" + ); + } + + } diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java index c29ea06..965bedd 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java +++ b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java @@ -49,7 +49,7 @@ public class UiLoggerController implements Initializable { private boolean alwaysOnTop = false; private volatile boolean initialized = false; - private final List appendLater = new ArrayList<>(); + private final List appendLater = new LinkedList<>(); @Override public void initialize(URL arg0, ResourceBundle arg1) { @@ -58,19 +58,18 @@ public class UiLoggerController implements Initializable { borderPane.setCenter(webView); webView.getEngine().getLoadWorker().stateProperty().addListener((observableValue, oldState, newState) -> { - if (newState == Worker.State.SUCCEEDED) - synchronized (appendLater) { - initialized = true; - if (!appendLater.isEmpty()) { - appendLog(appendLater); - appendLater.clear(); - } - } + if (newState == Worker.State.SUCCEEDED) { + initialized = true; + webView.prefHeightProperty().bind(stage.heightProperty()); + webView.prefWidthProperty().bind(stage.widthProperty()); + appendLog(appendLater); + } }); webView.getEngine().load(getClass().getResource("/gearth/ui/logger/uilogger/logger.html").toString()); } + private static String cleanTextContent(String text) { // // strips off all non-ASCII characters // text = text.replaceAll("[^\\x00-\\x7F]", ""); @@ -84,7 +83,7 @@ public class UiLoggerController implements Initializable { return text.trim(); } - public void appendMessage(HPacket packet, int types) { + public synchronized void appendMessage(HPacket packet, int types) { boolean isBlocked = (types & PacketLogger.MESSAGE_TYPE.BLOCKED.getValue()) != 0; boolean isReplaced = (types & PacketLogger.MESSAGE_TYPE.REPLACED.getValue()) != 0; boolean isIncoming = (types & PacketLogger.MESSAGE_TYPE.INCOMING.getValue()) != 0; @@ -92,7 +91,7 @@ public class UiLoggerController implements Initializable { if (isIncoming && !viewIncoming) return; if (!isIncoming && !viewOutgoing) return; - ArrayList elements = new ArrayList<>(); + StringBuilder packetHtml = new StringBuilder("
"); String expr = packet.toExpression(isIncoming ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER); @@ -104,84 +103,57 @@ public class UiLoggerController implements Initializable { packet.headerId() ); - if ( message != null && !(viewMessageName && !viewMessageHash && message.getName() == null)) { + if (message != null && !(viewMessageName && !viewMessageHash && message.getName() == null)) { if (viewMessageName && message.getName() != null) { - elements.add(new Element("["+message.getName()+"]", "messageinfo")); + packetHtml.append(divWithClass("[" + message.getName() + "]", "messageinfo")); } if (viewMessageHash) { - elements.add(new Element("["+message.getHash()+"]", "messageinfo")); + packetHtml.append(divWithClass("[" + message.getHash() + "]", "messageinfo")); } - elements.add(new Element("\n", "")); } } - if (isBlocked) elements.add(new Element("[Blocked]\n", "blocked")); - else if (isReplaced) elements.add(new Element("[Replaced]\n", "replaced")); + if (isBlocked) packetHtml.append(divWithClass("[Blocked]", "blocked")); + else if (isReplaced) packetHtml.append(divWithClass("[Replaced]", "replaced")); - if (isIncoming) { - // handle skipped eventually - elements.add(new Element("Incoming[", "incoming")); - elements.add(new Element(String.valueOf(packet.headerId()), "")); - elements.add(new Element("]", "incoming")); - elements.add(new Element(" <- ", "")); - if (skiphugepackets && packet.length() > 8000) { - elements.add(new Element("", "skipped")); - } - else { - elements.add(new Element(packet.toString(), "incoming")); - } - } else { - elements.add(new Element("Outgoing[", "outgoing")); - elements.add(new Element(String.valueOf(packet.headerId()), "")); - elements.add(new Element("]", "outgoing")); + packetHtml + .append("
") + .append(isIncoming ? spanWithClass("Incoming[", "incoming") : + spanWithClass("Outgoing[", "outgoing")) + .append(packet.headerId()) + .append(spanWithClass("]", isIncoming ? "incoming" : "outgoing")) + .append(isIncoming ? " <- " : " -> ") + .append(skiphugepackets && packet.length() > 8000 ? + divWithClass("", "skipped") : + divWithClass(packet.toString(), isIncoming ? "incoming" : "outgoing")); - elements.add(new Element(" -> ", "")); - - if (skiphugepackets && packet.length() > 8000) { - elements.add(new Element("", "skipped")); - } - else { - elements.add(new Element(packet.toString(), "outgoing")); - } - } String cleaned = cleanTextContent(expr); if (cleaned.equals(expr)) { if (!expr.equals("") && displayStructure && packet.length() <= 2000) - elements.add(new Element("\n" + cleanTextContent(expr), "structure")); + packetHtml.append(divWithClass(cleanTextContent(expr), "structure")); } - elements.add(new Element("\n--------------------\n", "")); + packetHtml.append(divWithClass("--------------------", "")); synchronized (appendLater) { if (initialized) { - appendLog(elements); - } - else { - appendLater.addAll(elements); + appendLog(Collections.singletonList(packetHtml.toString())); + } else { + appendLater.add(packetHtml.toString()); } } - } - private synchronized void appendLog(List elements) { + private synchronized void appendLog(List html) { Platform.runLater(() -> { - - for (Element element : elements) { - String script = "$('#output').append('" - + escapeMessage(element.text) + "');"; - - try { - executejQuery(webView.getEngine(), script); - } catch (Exception e) { - System.out.println("Malformed JS message " + script); - } - } + String script = "document.getElementById('output').innerHTML += '" + String.join("", html) + "';"; + webView.getEngine().executeScript(script); if (autoScroll) { - webView.getEngine().executeScript("window.scrollTo(0, document.body.scrollHeight);"); + executejQuery(webView.getEngine(), "$('html, body').animate({scrollTop:$(document).height()}, 'slow');"); } }); } @@ -260,6 +232,14 @@ public class UiLoggerController implements Initializable { } public void clearText(ActionEvent actionEvent) { - webView.getEngine().executeScript("$('#output').html = \\'\\'"); + executejQuery(webView.getEngine(), "$('#output').empty();"); + } + + private String divWithClass(String content, String klass) { + return escapeMessage("
" + content + "
"); + } + + private String spanWithClass(String content, String klass) { + return escapeMessage("" + content + ""); } } diff --git a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css index d83a56d..5831a15 100644 --- a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css +++ b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css @@ -38,7 +38,7 @@ .menu-bar .text, .menu .text { color: #000000 !important; - padding: -2px 0 -2px 0 !important; + /*padding: -2px 0 -2px 0 !important;*/ } .scroll-bar:vertical {