├── .gitignore
├── .idea
├── inspectionProfiles
│ └── Project_Default.xml
├── libraries
│ └── org_java_websocket_Java_WebSocket_1_3_7.xml
├── markdown-exported-files.xml
├── markdown-history.xml
├── markdown-navigator.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── .travis.yml
├── JavaFx-WebView-Debugger.iml
├── LICENSE
├── META-INF
└── MANIFEST.MF
├── README.md
├── VERSION.md
├── assets
├── RoughLoadSequence.png
└── RoughLoadSequence.scap
├── images
└── DevTools.png
├── pom.xml
└── src
└── main
├── java
└── com
│ └── vladsch
│ └── javafx
│ └── webview
│ └── debugger
│ ├── DevToolsDebugProxy.java
│ ├── DevToolsDebuggerJsBridge.java
│ ├── DevToolsDebuggerServer.java
│ ├── JfxConsoleApiArgs.java
│ ├── JfxDebugProxyJsBridge.java
│ ├── JfxDebugProxyJsBridgeDelegate.java
│ ├── JfxDebuggerAccess.java
│ ├── JfxDebuggerConnector.java
│ ├── JfxDebuggerProxy.java
│ ├── JfxScriptArgAccessor.java
│ ├── JfxScriptArgAccessorDelegate.java
│ ├── JfxScriptStateProvider.java
│ ├── JfxWebSocketServer.java
│ └── LogHandler.java
├── javadoc
├── overview.html
└── overview.md
└── resources
├── console-test.js
└── markdown-navigator.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | /lib/
3 | /target/
4 | /.idea/workspace.xml
5 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch) 5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | *
13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import com.sun.javafx.application.PlatformImpl;
29 | import com.sun.javafx.scene.web.Debugger;
30 | import com.vladsch.boxed.json.BoxedJsArray;
31 | import com.vladsch.boxed.json.BoxedJsNumber;
32 | import com.vladsch.boxed.json.BoxedJsObject;
33 | import com.vladsch.boxed.json.BoxedJsString;
34 | import com.vladsch.boxed.json.BoxedJsValue;
35 | import com.vladsch.boxed.json.BoxedJson;
36 | import com.vladsch.boxed.json.MutableJsArray;
37 | import com.vladsch.boxed.json.MutableJsObject;
38 | import javafx.application.Platform;
39 | import javafx.scene.web.WebEngine;
40 | import javafx.util.Callback;
41 | import netscape.javascript.JSException;
42 | import netscape.javascript.JSObject;
43 | import org.jetbrains.annotations.NotNull;
44 | import org.jetbrains.annotations.Nullable;
45 |
46 | import javax.json.JsonValue;
47 | import javax.swing.SwingUtilities;
48 | import java.lang.reflect.Field;
49 | import java.util.ArrayList;
50 | import java.util.Arrays;
51 | import java.util.HashMap;
52 | import java.util.HashSet;
53 | import java.util.concurrent.atomic.AtomicBoolean;
54 | import java.util.function.Consumer;
55 |
56 | public class DevToolsDebugProxy implements Debugger, JfxDebuggerProxy, Callback
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import com.vladsch.boxed.json.BoxedJsObject;
29 | import com.vladsch.boxed.json.BoxedJsValue;
30 | import com.vladsch.boxed.json.BoxedJson;
31 | import javafx.application.Platform;
32 | import javafx.scene.web.WebEngine;
33 | import javafx.scene.web.WebView;
34 | import netscape.javascript.JSException;
35 | import netscape.javascript.JSObject;
36 | import org.jetbrains.annotations.NotNull;
37 | import org.jetbrains.annotations.Nullable;
38 |
39 | import javax.json.JsonValue;
40 | import java.io.IOException;
41 | import java.io.InputStream;
42 | import java.io.InputStreamReader;
43 | import java.io.StringWriter;
44 | import java.util.Map;
45 | import java.util.function.Consumer;
46 |
47 | public class DevToolsDebuggerJsBridge {
48 | final @NotNull JfxDebuggerAccess myJfxDebuggerAccess;
49 | final @NotNull JfxScriptArgAccessor myJfxScriptArgAccessor;
50 | final @NotNull JfxDebugProxyJsBridge myJfxDebugProxyJsBridge;
51 | final @NotNull WebView myWebView;
52 | final @NotNull DevToolsDebugProxy myDebugger;
53 | final @Nullable JfxScriptStateProvider myStateProvider;
54 | @Nullable String myJSEventHandledBy;
55 | final LogHandler LOG = LogHandler.getInstance();
56 |
57 | private final long myNanos = System.nanoTime();
58 | private final long myMilliNanos = System.currentTimeMillis() * 1000000;
59 | @Nullable Object myConsoleArg = null;
60 | @Nullable Object myArg = null;
61 | @Nullable DevToolsDebuggerServer myDebuggerServer;
62 | final int myInstance;
63 | final boolean mySuppressNoMarkdownException;
64 |
65 | public DevToolsDebuggerJsBridge(@NotNull final WebView webView, final @NotNull WebEngine engine, int instance, @Nullable JfxScriptStateProvider stateProvider) {
66 | this(webView, engine, instance, stateProvider, false);
67 | }
68 |
69 | public DevToolsDebuggerJsBridge(@NotNull final WebView webView, final @NotNull WebEngine engine, int instance, @Nullable JfxScriptStateProvider stateProvider, boolean suppressNoMarkdownException) {
70 | myWebView = webView;
71 | myInstance = instance;
72 | myStateProvider = stateProvider;
73 | myJfxDebuggerAccess = new JfxDebuggerAccessImpl();
74 | myJfxScriptArgAccessor = new JfxScriptArgAccessorDelegate(new JfxScriptArgAccessorImpl());
75 | myJfxDebugProxyJsBridge = new JfxDebugProxyJsBridgeDelegate(new JfxDebugProxyJsBridgeImpl());
76 | myDebugger = new DevToolsDebugProxy(engine, myJfxDebuggerAccess);
77 | mySuppressNoMarkdownException = suppressNoMarkdownException;
78 | }
79 |
80 | protected @NotNull JfxDebugProxyJsBridge getJfxDebugProxyJsBridge() {
81 | return myJfxDebugProxyJsBridge;
82 | }
83 |
84 | /**
85 | * Called when page reload instigated
86 | *
87 | * Will not be invoked if {@link #pageReloading()} is called before invoking reload on WebView engine side
88 | *
89 | * Will always be invoked if page reload requested by Chrome dev tools
90 | *
91 | * Override in a subclass to get notified of this event
92 | */
93 | protected void pageReloadStarted() {
94 |
95 | }
96 |
97 | /**
98 | * Inject JSBridge and initialize the JavaScript helper
99 | *
100 | * Call when engine transitions state to READY after a page load is started in WebView or requested
101 | * by Chrome dev tools.
102 | *
103 | * This means after either the {@link #pageReloading()} is called or {@link #pageReloadStarted()}
104 | * is invoked to inform of page reloading operation.
105 | */
106 | public void connectJsBridge() {
107 | JSObject jsObject = (JSObject) myWebView.getEngine().executeScript("window");
108 | jsObject.setMember("__MarkdownNavigatorArgs", myJfxScriptArgAccessor); // this interface stays for the duration, does not give much
109 | jsObject.setMember("__MarkdownNavigator", getJfxDebugProxyJsBridge()); // this interface is captured by the helper script since incorrect use can bring down the whole app
110 | try {
111 | if (mySuppressNoMarkdownException) {
112 | myWebView.getEngine().executeScript("var markdownNavigator; markdownNavigator && markdownNavigator.setJsBridge(window.__MarkdownNavigator);");
113 | } else {
114 | myWebView.getEngine().executeScript("markdownNavigator.setJsBridge(window.__MarkdownNavigator);");
115 | }
116 | } catch (JSException e) {
117 | e.printStackTrace();
118 | LOG.warn("jsBridgeHelperScript: exception", e);
119 | }
120 | jsObject.removeMember("__MarkdownNavigator");
121 | }
122 |
123 | /**
124 | * InputStream for the JSBridge Helper script to inject during page loading
125 | *
126 | * Override if you want to customize the jsBridge helper script
127 | *
128 | * @return input stream
129 | */
130 | public InputStream getJsBridgeHelperAsStream() {
131 | return DevToolsDebuggerJsBridge.class.getResourceAsStream("/markdown-navigator.js");
132 | }
133 |
134 | /**
135 | * InputStream for the JSBridge Helper script to inject during page loading
136 | *
137 | * Override if you want to customize the jsBridge helper script
138 | *
139 | * @return input stream
140 | */
141 | public String getJsBridgeHelperURL() {
142 | return String.valueOf(DevToolsDebuggerJsBridge.class.getResource("/markdown-navigator.js"));
143 | }
144 |
145 | /**
146 | * Called before standard helper script is written
147 | *
148 | * @param writer where to add prefix script if one is desired
149 | */
150 | protected void jsBridgeHelperScriptSuffix(final StringWriter writer) {
151 |
152 | }
153 |
154 | /**
155 | * Called after standard helper script is written
156 | *
157 | * @param writer where to add suffix script if one is desired
158 | */
159 | protected void jsBridgeHelperScriptPrefix(final StringWriter writer) {
160 |
161 | }
162 |
163 | /**
164 | * Call to signal to debug server that a page is about to be reloaded
165 | *
166 | * Should be called only if page reloads triggered from WebView side should
167 | * not generate call backs on pageReloadStarted()
168 | */
169 | public void pageReloading() {
170 | if (myDebuggerServer != null) {
171 | myDebuggerServer.pageReloading();
172 | }
173 | }
174 |
175 | /**
176 | * Called by JavaScript helper to signal all script operations are complete
177 | *
178 | * Used to prevent rapid page reload requests from Chrome dev tools which has
179 | * a way of crashing the application with a core dump on Mac and probably the same
180 | * on other OS implementations.
181 | *
182 | * Override if need to be informed of this event
183 | */
184 | protected void pageLoadComplete() {
185 |
186 | }
187 |
188 | /**
189 | * Used to get the handled by JavaScript handler
190 | *
191 | * This is needed to allow js to signal that an event has been handled. Java registered
192 | * event listeners ignore the JavaScript event.stopPropagation() and event.preventDefault()
193 | *
194 | * To use this properly clear this field after getting its value in the event listener.
195 | *
196 | * @return string passed by to {@link JfxDebugProxyJsBridge#setEventHandledBy(String)} by JavaScript
197 | */
198 | public @Nullable String getJSEventHandledBy() {
199 | return myJSEventHandledBy;
200 | }
201 |
202 | /**
203 | * Used to clear the handled by JavaScript handler
204 | *
205 | * This is needed to allow js to signal that an event has been handled. Java registered
206 | * event listeners ignore the JavaScript event.stopPropagation() and event.preventDefault()
207 | *
208 | * To use this properly clear this field after getting its value in the event listener.
209 | */
210 | public void clearJSEventHandledBy() {
211 | myJSEventHandledBy = null;
212 | }
213 |
214 | /**
215 | * Launch a debugger instance
216 | *
217 | * @param port debugger port
218 | * @param onFailure call back on failure
219 | * @param onStart call back on success
220 | */
221 | public void startDebugServer(final int port, @Nullable Consumer
248 | * Server is not accessible after this call even before the onStopped callback is invoked.
249 | *
250 | * @param onStopped call back to execute when the server stops, parameter true if server shut down, false if only disconnected (other instances running), null was not connected to debug server
251 | */
252 | public void stopDebugServer(@Nullable Consumer
390 | * Should be used at the top of the body to
391 | *
392 | * @param sb appendable
393 | */
394 | public void appendStateString(Appendable sb) {
395 | // output all the state vars
396 | if (myStateProvider != null) {
397 | try {
398 | for (Map.Entry
412 | * Should be used at the top of the body to
413 | *
414 | * @return text of the state initialization to be included in the HTML page between <script> tags.
415 | */
416 | public String getStateString() {
417 | // output all the state vars
418 | StringBuilder sb = new StringBuilder();
419 | appendStateString(sb);
420 | return sb.toString();
421 | }
422 |
423 | private class JfxScriptArgAccessorImpl implements JfxScriptArgAccessor {
424 | JfxScriptArgAccessorImpl() {}
425 |
426 | @Override
427 | public @Nullable Object getArg() {
428 | return myArg;
429 | }
430 |
431 | @Override
432 | public @Nullable Object getConsoleArg() {
433 | return myConsoleArg;
434 | }
435 | }
436 |
437 | long timestamp() {
438 | return (System.nanoTime() - myNanos) + myMilliNanos;
439 | }
440 |
441 | /*
442 | * Interface implementation for Call backs from JavaScript used by the JSBridge helper
443 | * script markdown-navigator.js
444 | */
445 | private class JfxDebugProxyJsBridgeImpl implements JfxDebugProxyJsBridge {
446 | JfxDebugProxyJsBridgeImpl() {}
447 |
448 | /**
449 | * Called by JavaScript helper to signal all script operations are complete
450 | *
451 | * Used to prevent rapid page reload requests from Chrome dev tools which has
452 | * a way of crashing the application with a core dump on Mac and probably the same
453 | * on other OS implementations.
454 | *
455 | * Override if need to be informed of this event but make sure super.pageLoadComplete()
456 | * is called.
457 | */
458 | @Override
459 | public void pageLoadComplete() {
460 | if (myDebuggerServer != null) {
461 | myDebuggerServer.pageLoadComplete();
462 | }
463 | DevToolsDebuggerJsBridge.this.pageLoadComplete();
464 | }
465 |
466 | @Override
467 | public void consoleLog(final @NotNull String type, final @NotNull JSObject args) {
468 | try {
469 | long timestamp = timestamp();
470 | // funnel it to the debugger console
471 | if (myDebuggerServer != null) {
472 | myDebuggerServer.log(type, timestamp, args);
473 | }
474 | } catch (Throwable e) {
475 | LOG.debug(String.format("[%d] Exception in consoleLog: ", myInstance));
476 | LOG.error(e);
477 | }
478 | }
479 |
480 | @Override
481 | public void println(final @Nullable String text) {
482 | System.out.println(text == null ? "null" : text);
483 | }
484 |
485 | @Override
486 | public void print(final @Nullable String text) {
487 | System.out.println(text == null ? "null" : text);
488 | }
489 |
490 | @Override
491 | public void setEventHandledBy(final String handledBy) {
492 | myJSEventHandledBy = handledBy;
493 | }
494 |
495 | @Override
496 | public @Nullable Object getState(final String name) {
497 | // convert to JSObject
498 | if (myStateProvider != null) {
499 | BoxedJsValue state = myStateProvider.getState().get(name);
500 | if (state.isValid()) {
501 | return myWebView.getEngine().executeScript("(" + state.toString() + ")");
502 | }
503 | }
504 | return null;
505 | }
506 |
507 | @Override
508 | public void setState(final @NotNull String name, final @Nullable Object state) {
509 | if (myStateProvider != null) {
510 | BoxedJsObject scriptState = myStateProvider.getState();
511 | scriptState.remove(name);
512 |
513 | if (state != null) {
514 | // convert JSObject to Element others to attributes
515 | try {
516 | BoxedJsValue value = null;
517 | if (state instanceof Integer) value = BoxedJson.boxedOf((int) state);
518 | else if (state instanceof Boolean) value = BoxedJson.boxedOf((boolean) state);
519 | else if (state instanceof Double) value = BoxedJson.boxedOf((double) state);
520 | else if (state instanceof Float) value = BoxedJson.boxedOf((float) state);
521 | else if (state instanceof Long) value = BoxedJson.boxedOf((long) state);
522 | else if (state instanceof Character) value = BoxedJson.boxedOf(String.valueOf((char) state));
523 | else if (state instanceof String) value = BoxedJson.boxedOf((String) state);
524 | else if (state instanceof JSObject) {
525 | // need to convert to JSON string
526 | myArg = state;
527 | String jsonString = (String) myWebView.getEngine().executeScript("JSON.stringify(window.__MarkdownNavigatorArgs.getArg(), null, 0)");
528 | myArg = null;
529 |
530 | value = BoxedJson.boxedFrom(jsonString);
531 | }
532 | if (value != null && value.isValid()) {
533 | scriptState.put(name, value);
534 | }
535 | } catch (Throwable e) {
536 | LOG.error(e);
537 | }
538 | }
539 |
540 | // strictly a formality since the state is mutable, but lets provider save/cache or whatever else
541 | myStateProvider.setState(scriptState);
542 | }
543 | }
544 |
545 | /**
546 | * Callback from JavaScript to initiate a debugger pause
547 | */
548 | public void debugBreak() {
549 | if (myDebuggerServer != null) {
550 | myDebuggerServer.debugBreak();
551 | }
552 | }
553 | }
554 | }
555 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/DevToolsDebuggerServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import com.sun.javafx.scene.web.Debugger;
29 | import javafx.application.Platform;
30 | import netscape.javascript.JSObject;
31 | import org.jetbrains.annotations.NotNull;
32 | import org.jetbrains.annotations.Nullable;
33 |
34 | import java.net.InetSocketAddress;
35 | import java.nio.channels.NotYetConnectedException;
36 | import java.util.HashMap;
37 | import java.util.function.Consumer;
38 |
39 | public class DevToolsDebuggerServer implements JfxDebuggerConnector {
40 | final static HashMap
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import com.vladsch.boxed.json.BoxedJsValue;
29 |
30 | public class JfxConsoleApiArgs {
31 | private final Object[] myArgs;
32 | private final String myLogType;
33 | private final BoxedJsValue[] myJsonParams;
34 | private long myTimestamp;
35 | private String myPausedParam;
36 |
37 | public JfxConsoleApiArgs(final Object[] args, final String logType, final long timestamp) {
38 | myArgs = args;
39 | myLogType = logType;
40 | myJsonParams = new BoxedJsValue[args.length];
41 | myTimestamp = timestamp;
42 | myPausedParam = null;
43 | }
44 |
45 | public String getPausedParam() {
46 | return myPausedParam;
47 | }
48 |
49 | public void setPausedParam(final String pausedParam) {
50 | myPausedParam = pausedParam;
51 | }
52 |
53 | public void setParamJson(int paramIndex, BoxedJsValue paramJson) {
54 | myJsonParams[paramIndex] = paramJson;
55 | }
56 |
57 | public void clearAll() {
58 | int iMax = myArgs.length;
59 | for (int i = 0; i < iMax; i++) {
60 | myArgs[i] = null;
61 | myJsonParams[i] = null;
62 | }
63 | }
64 |
65 | public long getTimestamp() {
66 | return myTimestamp;
67 | }
68 |
69 | public Object[] getArgs() {
70 | return myArgs;
71 | }
72 |
73 | public String getLogType() {
74 | return myLogType;
75 | }
76 |
77 | public BoxedJsValue[] getJsonParams() {
78 | return myJsonParams;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxDebugProxyJsBridge.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import netscape.javascript.JSObject;
29 |
30 | public interface JfxDebugProxyJsBridge {
31 | void consoleLog(String type, JSObject args);
32 | void println(String text);
33 | void print(String text);
34 | void pageLoadComplete();
35 | void setEventHandledBy(String handledBy);
36 | Object getState(String name);
37 | void setState(String name, Object state);
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxDebugProxyJsBridgeDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import netscape.javascript.JSObject;
29 |
30 | public class JfxDebugProxyJsBridgeDelegate implements JfxDebugProxyJsBridge {
31 | final private JfxDebugProxyJsBridge myBridge;
32 |
33 | public JfxDebugProxyJsBridgeDelegate(final JfxDebugProxyJsBridge bridge) {
34 | myBridge = bridge;
35 | }
36 |
37 | @Override public void consoleLog(final String type, final JSObject args) {myBridge.consoleLog(type, args);}
38 |
39 | @Override public void println(final String text) {myBridge.println(text);}
40 |
41 | @Override public void print(final String text) {myBridge.print(text);}
42 |
43 | @Override public void pageLoadComplete() {myBridge.pageLoadComplete();}
44 |
45 | @Override public void setEventHandledBy(final String handledBy) {myBridge.setEventHandledBy(handledBy);}
46 |
47 | @Override public Object getState(final String name) {return myBridge.getState(name);}
48 |
49 | @Override public void setState(final String name, final Object state) {myBridge.setState(name, state);}
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxDebuggerAccess.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | public interface JfxDebuggerAccess {
29 | String setArg(Object arg);
30 | void clearArg();
31 | Object eval(String script);
32 | void pageReloadStarted();
33 | String jsBridgeHelperScript();
34 | void onConnectionOpen();
35 | void onConnectionClosed();
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxDebuggerConnector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | public interface JfxDebuggerConnector extends JfxDebuggerProxy {
29 | void sendMessageToBrowser(String data);
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxDebuggerProxy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import netscape.javascript.JSObject;
29 | import org.jetbrains.annotations.Nullable;
30 |
31 | public interface JfxDebuggerProxy {
32 | void onOpen();
33 | void onClosed(int code, String reason, boolean remote);
34 | void log(String type, final long timestamp, final JSObject args);
35 | void debugBreak();
36 | void pageReloading();
37 | void reloadPage();
38 | void setDebugOnLoad(DebugOnLoad debugOnLoad);
39 | DebugOnLoad getDebugOnLoad();
40 | boolean isDebuggerPaused();
41 | void releaseDebugger(final boolean shuttingDown, @Nullable Runnable runnable);
42 | void removeAllBreakpoints(@Nullable Runnable runAfter);
43 | void pageLoadComplete();
44 |
45 | enum DebugOnLoad {
46 | NONE,
47 | ON_BRIDGE_CONNECT,
48 | ON_INJECT_HELPER,
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxScriptArgAccessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | public interface JfxScriptArgAccessor {
29 | Object getArg();
30 | Object getConsoleArg();
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxScriptArgAccessorDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | public class JfxScriptArgAccessorDelegate implements JfxScriptArgAccessor {
29 | final private JfxScriptArgAccessor myAccessor;
30 |
31 | public JfxScriptArgAccessorDelegate(final JfxScriptArgAccessor accessor) {
32 | myAccessor = accessor;
33 | }
34 |
35 | @Override public Object getArg() {
36 | return myAccessor.getArg();
37 | }
38 |
39 | @Override public Object getConsoleArg() {
40 | return myAccessor.getConsoleArg();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxScriptStateProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import com.vladsch.boxed.json.BoxedJsObject;
29 | import org.jetbrains.annotations.NotNull;
30 |
31 | public interface JfxScriptStateProvider {
32 | @NotNull BoxedJsObject getState();
33 | void setState(@NotNull BoxedJsObject state);
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/vladsch/javafx/webview/debugger/JfxWebSocketServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import org.java_websocket.WebSocket;
29 | import org.java_websocket.exceptions.WebsocketNotConnectedException;
30 | import org.java_websocket.framing.CloseFrame;
31 | import org.java_websocket.handshake.ClientHandshake;
32 | import org.java_websocket.server.WebSocketServer;
33 | import org.jetbrains.annotations.NotNull;
34 | import org.jetbrains.annotations.Nullable;
35 |
36 | import java.net.InetSocketAddress;
37 | import java.nio.ByteBuffer;
38 | import java.nio.channels.NotYetConnectedException;
39 | import java.util.HashMap;
40 | import java.util.concurrent.atomic.AtomicInteger;
41 | import java.util.function.Consumer;
42 |
43 | public class JfxWebSocketServer extends WebSocketServer {
44 | public static final String WEB_SOCKET_RESOURCE = "/?%s";
45 | private static final String CHROME_DEBUG_URL = "chrome-devtools://devtools/bundled/inspector.html?ws=localhost:";
46 |
47 | final private HashMap
4 | * Copyright (c) 2018-2020 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | package com.vladsch.javafx.webview.debugger;
27 |
28 | import org.jetbrains.annotations.NotNull;
29 |
30 | public abstract class LogHandler {
31 | // NOTE: this avoids conflicts with loading Logger in IntelliJ, set LogHandler.LOG_HANDLER by application
32 | public static LogHandler LOG_HANDLER = LogHandler.NULL;
33 |
34 | public static LogHandler getInstance() {
35 | return LOG_HANDLER;
36 | }
37 |
38 | public abstract void trace(@NotNull String message);
39 |
40 | public abstract void trace(@NotNull String message, @NotNull Throwable t);
41 |
42 | public abstract void trace(@NotNull Throwable t);
43 |
44 | public abstract boolean isTraceEnabled();
45 |
46 | public abstract void debug(@NotNull String message);
47 |
48 | public abstract void debug(@NotNull String message, @NotNull Throwable t);
49 |
50 | public abstract void debug(@NotNull Throwable t);
51 |
52 | public abstract void error(@NotNull String message);
53 |
54 | public abstract void error(@NotNull String message, @NotNull Throwable t);
55 |
56 | public abstract void error(@NotNull Throwable t);
57 |
58 | public abstract void info(@NotNull String message);
59 |
60 | public abstract void info(@NotNull String message, @NotNull Throwable t);
61 |
62 | public abstract void info(@NotNull Throwable t);
63 |
64 | public abstract boolean isDebugEnabled();
65 |
66 | public abstract void warn(@NotNull String message);
67 |
68 | public abstract void warn(@NotNull String message, @NotNull Throwable t);
69 |
70 | public abstract void warn(@NotNull Throwable t);
71 |
72 | final public static LogHandler NULL = new LogHandler() {
73 | public @Override
74 | void trace(@NotNull String message) {}
75 |
76 | public @Override
77 | void trace(@NotNull String message, @NotNull Throwable t) {}
78 |
79 | public @Override
80 | void trace(@NotNull Throwable t) {}
81 |
82 | public @Override
83 | boolean isTraceEnabled() {return false;}
84 |
85 | public @Override
86 | void debug(@NotNull String message) {}
87 |
88 | public @Override
89 | void debug(@NotNull String message, @NotNull Throwable t) {}
90 |
91 | public @Override
92 | void debug(@NotNull Throwable t) {}
93 |
94 | public @Override
95 | void error(@NotNull String message) {}
96 |
97 | public @Override
98 | void error(@NotNull String message, @NotNull Throwable t) {}
99 |
100 | public @Override
101 | void error(@NotNull Throwable t) {}
102 |
103 | public @Override
104 | void info(@NotNull String message) {}
105 |
106 | public @Override
107 | void info(@NotNull String message, @NotNull Throwable t) {}
108 |
109 | public @Override
110 | void info(@NotNull Throwable t) {}
111 |
112 | public @Override
113 | boolean isDebugEnabled() {return false;}
114 |
115 | public @Override
116 | void warn(@NotNull String message) {}
117 |
118 | public @Override
119 | void warn(@NotNull String message, @NotNull Throwable t) {}
120 |
121 | public @Override
122 | void warn(@NotNull Throwable t) {}
123 | };
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/javadoc/overview.html:
--------------------------------------------------------------------------------
1 |
2 | JavaFX WebView Debugger Library implementing WebSocket server for JavaFX WebView debugger to Chrome Dev Tools. Creates a comfortable debugging environment for script debugging in JavaFX WebView:
4 | * Copyright (c) 2018-2018 Vladimir Schneider (https://github.com/vsch)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE
23 | *
24 | */
25 |
26 | function testConsoleLog() {
27 | "use strict";
28 |
29 | let onLoadScroll = {x: window.pageXOffset, y: window.pageYOffset};
30 |
31 | console.group("Console Tests");
32 | console.group("Log Types");
33 | console.log("Log text", onLoadScroll, null);
34 | console.warn("Warning text", onLoadScroll, null);
35 | console.error("Error text", onLoadScroll, null);
36 | console.debug("Debug text", onLoadScroll, null);
37 | console.groupEnd();
38 |
39 | console.table(onLoadScroll);
40 | console.assert(false, "Assertion failed");
41 | console.groupEnd();
42 | }
43 |
44 | function testDebugBreak() {
45 | debugBreak();
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/src/main/resources/markdown-navigator.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=__MarkdownNavigatorHelperModuleSource__
2 | /*
3 | * The MIT License (MIT)
4 | *
5 | * Copyright (c) 2018-2018 Vladimir Schneider (https://github.com/vsch)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in all
15 | * copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | * SOFTWARE
24 | *
25 | */
26 |
27 | // noinspection ES6ConvertVarToLetConst
28 | var markdownNavigator;
29 |
30 | // do nothing until jsBridge is established
31 | // noinspection ES6ConvertVarToLetConst
32 | var debugBreak = () => {
33 | };
34 |
35 | // lets keep this around for our scripts
36 | // noinspection ES6ConvertVarToLetConst
37 | var __api = console.__commandLineAPI || {};
38 |
39 | // map console functions to our bridge
40 | // noinspection ES6ConvertVarToLetConst
41 | var console = (() => {
42 | let __tmp = {
43 | logDebugBreak: () => {
44 | },
45 | consoleLog: (type, args) => {
46 | markdownNavigator.consoleLog(type, args);
47 | },
48 | println: text => markdownNavigator.println(text),
49 | print: text => markdownNavigator.print(text),
50 | };
51 |
52 | return {
53 | assert: function () { return __tmp.consoleLog("assert", arguments); },
54 | clear: function () { return __tmp.consoleLog("clear", arguments); },
55 | count: function () { return __tmp.consoleLog("count", arguments); },
56 | debug: function () { return __tmp.consoleLog("debug", arguments); },
57 | dir: function () { return __tmp.consoleLog("dir", arguments); },
58 | dirxml: function () { return __tmp.consoleLog("dirxml", arguments); },
59 | error: function () { return __tmp.consoleLog("error", arguments); },
60 | exception: function () { return __tmp.consoleLog("exception", arguments); },
61 | group: function () { return __tmp.consoleLog("startGroup", arguments); },
62 | groupCollapsed: function () { return __tmp.consoleLog("startGroupCollapsed", arguments); },
63 | groupEnd: function () { return __tmp.consoleLog("endGroup", arguments); },
64 | info: function () { return __tmp.consoleLog("info", arguments); },
65 | log: function () { return __tmp.consoleLog("log", arguments); },
66 | profile: function () { return __tmp.consoleLog("profile", arguments); },
67 | profileEnd: function () { return __tmp.consoleLog("profileEnd", arguments); },
68 | select: function () { return __tmp.consoleLog("select", arguments); },
69 | table: function () { return __tmp.consoleLog("table", arguments); },
70 | time: function () { return __tmp.consoleLog("time", arguments); },
71 | timeEnd: function () { return __tmp.consoleLog("timeEnd", arguments); },
72 | trace: function () { return __tmp.consoleLog("trace", arguments); },
73 | warn: function () { return __tmp.consoleLog("warning", arguments); },
74 | print: text => __tmp.print(text),
75 | println: text => __tmp.println(text),
76 | setJsBridge: (jsBridge) => {
77 | __tmp = jsBridge;
78 | // function for cached logs
79 | return ((type, args) => {
80 | __tmp.consoleLog(type, args);
81 | });
82 | },
83 | };
84 | })();
85 |
86 | // noinspection JSValidateTypes
87 | window.console = console;
88 |
89 | markdownNavigator = (function () {
90 | "use strict";
91 |
92 | // FIX: change this for real implementation using computed CSS properties of element
93 | // and an overlay element
94 | const HIGHLIGHT = "markdown-navigator-highlight";
95 | const HIGHLIGHT_STYLE = document.createElement("style");
96 |
97 | // just so we get a color chooser in IDEA, uncomment
98 | HIGHLIGHT_STYLE.textContent = `.${HIGHLIGHT} {
99 | background-color: rgba(255, 0, 255, 0.07) !important;
100 | }`;
101 |
102 | let __markdownNavigator,
103 | __tmp = {
104 | __state: {},
105 | __onJsBridge: [],
106 | __onJsConsole: [],
107 | __consoleSetJsBridge: console.setJsBridge, // functions to use before JSBridge is established
108 | onJsBridge: () => {
109 | },
110 | onJsConsole: () => {
111 | },
112 | },
113 | __consoleLog = (type, args) => {
114 | },
115 | __lastHighlight = null;
116 |
117 | delete console["setJsBridge"];
118 |
119 | __tmp.onJsBridge = op => {
120 | __tmp.__onJsBridge[__tmp.__onJsBridge.length] = op;
121 | };
122 |
123 | __tmp.onJsConsole = op => {
124 | __tmp.__onJsConsole[__tmp.__onJsConsole.length] = op;
125 | };
126 |
127 | let __unbridged = {
128 | // functions to be replaced with real ones when JsBridge is established
129 | setEventHandledBy: handledBy => {
130 | },
131 |
132 | getState: name => {
133 | return __tmp.__state[name] || null;
134 | },
135 |
136 | setState: (name, state) => {
137 | __tmp.__state[name] = state;
138 | },
139 |
140 | toggleTask: function (pos) {
141 | __tmp.onJsBridge(() => {
142 | __markdownNavigator.toggleTask(pos);
143 | });
144 | },
145 |
146 | onJsBridge: function (op) {
147 | __tmp.onJsBridge(op);
148 | },
149 |
150 | // functions mimicking jsBridge until it is connected
151 | consoleLog: (type, args) => {
152 | __tmp.onJsConsole(() => {
153 | __consoleLog(type, args);
154 | });
155 | },
156 |
157 | println: text => {
158 | __tmp.onJsConsole(() => {
159 | console.println(text);
160 | });
161 | },
162 |
163 | print: text => {
164 | __tmp.onJsConsole(() => {
165 | console.print(text);
166 | });
167 | },
168 |
169 | highlightNode: (nodeOrdinals) => {
170 | function getChildNode(node, nodeOrdinal) {
171 | let iMax = node.childNodes.length;
172 | let index = 0;
173 | for (let i = 0; i < iMax; i++) {
174 | let child = node.childNodes.item(i);
175 | if (child.nodeName.startsWith("#") && (child.nodeName !== "#text" || child.textContent.trim() === "")) {
176 | // skip non-element nodes or empty text
177 | continue;
178 | }
179 |
180 | if (index === nodeOrdinal) {
181 | return child;
182 | }
183 | index++;
184 | }
185 | return null;
186 | }
187 |
188 | // need to find the node, path is ordinal in parent, in reverse order, all the way to document, first index to be ignored
189 | let iMax = nodeOrdinals.length;
190 | let node = document;
191 | try {
192 | for (let i = iMax; i-- > 1;) {
193 | let nodeOrdinal = nodeOrdinals[i];
194 | let child = getChildNode(node, nodeOrdinal);
195 |
196 | if (child === null) {
197 | if (i === 1) {
198 | // if last one take the parent
199 | break;
200 | }
201 |
202 | console.error("Node ordinal not in children", nodeOrdinal, node, nodeOrdinals);
203 | node = null;
204 | break;
205 | }
206 |
207 | if (child.nodeName.startsWith("#")) {
208 | // FIX: when overlays are implemented to handle non-element nodes, highlight the child
209 | break;
210 | }
211 |
212 | node = child;
213 | }
214 | // console.debug("Final node", node);
215 | if (node !== null && node !== __lastHighlight) {
216 | __unbridged.hideHighlight();
217 | node.classList.add(HIGHLIGHT);
218 | __lastHighlight = node;
219 | }
220 | } catch (e) {
221 | console.error(e)
222 | }
223 | },
224 |
225 | hideHighlight: () => {
226 | if (__lastHighlight) {
227 | __lastHighlight.classList.remove(HIGHLIGHT);
228 | if (__lastHighlight.classList.length === 0) {
229 | __lastHighlight.removeAttribute("class");
230 | }
231 | __lastHighlight = null;
232 | }
233 | },
234 |
235 | setJsBridge: jsBridge => {
236 | // map to real JsBridge
237 | __consoleLog = __tmp.__consoleSetJsBridge(jsBridge);
238 | __markdownNavigator = jsBridge;
239 | debugBreak = jsBridge.debugBreak;
240 |
241 | // we don't need these anymore
242 | delete __unbridged["consoleLog"];
243 | delete __unbridged["setJsBridge"];
244 | delete __unbridged["println"];
245 | delete __unbridged["print"];
246 |
247 | // these now point directly to the jsBridge implementations or invocations
248 | __unbridged.setEventHandledBy = (name) => jsBridge.setEventHandledBy(name);
249 | __unbridged.getState = (name) => jsBridge.getState(name);
250 | __unbridged.setState = (name, state) => jsBridge.setState(name, state);
251 | __unbridged.toggleTask = position => jsBridge.toggleTask(position);
252 | __unbridged.onJsBridge = op => op();
253 |
254 | document.querySelector("head").appendChild(HIGHLIGHT_STYLE);
255 | console.debug(`Created ${HIGHLIGHT} style element`, HIGHLIGHT_STYLE);
256 |
257 | // dump any accumulated console/print before bridge connected
258 | for (const __onJsConsoleItem of __tmp.__onJsConsole) {
259 | try {
260 | __onJsConsoleItem();
261 | } catch (e) {
262 | console.println("onJsConsole exception: calling " + __onJsConsoleItem);
263 | console.println("onJsConsole exception: " + e);
264 | }
265 | }
266 |
267 | // save any state changes requested before jsBridge was setup
268 | console.groupCollapsed("cachedState");
269 | for (let f in __tmp.__state) {
270 | if (__tmp.__state.hasOwnProperty(f)) {
271 | console.log(f, __tmp.__state[f]);
272 | // console.println(name + " = " + JSON.stringify( __state[name],null,2));
273 | __markdownNavigator.setState(f, __tmp.__state[f]);
274 | }
275 | }
276 | console.groupEnd();
277 |
278 | // run any ops needed on connection
279 | for (const __onJsBridgeItem of __tmp.__onJsBridge) {
280 | console.debug("onLoad", __onJsBridgeItem);
281 | try {
282 | __onJsBridgeItem();
283 | } catch (e) {
284 | console.println("onJsBridge exception: calling " + __onJsBridgeItem);
285 | console.println("onJsBridge exception: " + e);
286 | }
287 | }
288 | console.groupEnd();
289 |
290 | __tmp = null;
291 |
292 | // signal JsBridge connection complete
293 | __markdownNavigator.pageLoadComplete();
294 | },
295 | };
296 |
297 | return __unbridged;
298 | })();
299 |
300 |
--------------------------------------------------------------------------------
DOMNodeRemoved
event."
765 | // }
766 | BoxedJsNumber jsParentId = json.evalJsNumber("params.parentNodeId");
767 | BoxedJsNumber jsNodeId = json.getJsonNumber("params.nodeId");
768 | BoxedJsObject jsParentParams = myNodeIdMap.getOrDefault(jsParentId.intValue(), BoxedJsValue.HAD_NULL_OBJECT);
769 | boolean handled = false;
770 | if (jsParentId.isValid() && jsNodeId.isValid() && jsParentParams.isValid()) {
771 | // we now parse for nodes, remove this node and process it as new
772 | logMessage(String.format("Removing child %d of node: %d", jsNodeId.intValue(), jsParentId.intValue()));
773 | BoxedJsArray jsNodes = jsParentParams.getJsonArray("children");
774 | addNodeChildren(jsParentId.intValue(), jsNodes, jsNodeId.intValue(), null, 0);
775 | handled = true;
776 | }
777 |
778 | if (!handled) {
779 | // did not add
780 | logMessage(String.format("Did not insert child node for %s", param));
781 | break;
782 | }
783 | break;
784 | }
785 |
786 | case "DOM.childNodeInserted": {
787 | // {
788 | // "name": "childNodeInserted",
789 | // "parameters": [
790 | // {
791 | // "name": "parentNodeId",
792 | // "$ref": "NodeId",
793 | // "description": "Id of the node that has changed."
794 | // },
795 | // {
796 | // "name": "previousNodeId",
797 | // "$ref": "NodeId",
798 | // "description": "If of the previous siblint."
799 | // },
800 | // {
801 | // "name": "node",
802 | // "$ref": "Node",
803 | // "description": "Inserted node data."
804 | // }
805 | // ],
806 | // "description": "Mirrors DOMNodeInserted
event."
807 | // },
808 | BoxedJsObject jsNode = json.evalJsObject("params.node");
809 | BoxedJsNumber jsPreviousNodeId = json.evalJsNumber("params.previousNodeId");
810 | BoxedJsNumber jsParentId = json.evalJsNumber("params.parentNodeId");
811 | BoxedJsObject jsParentParams = myNodeIdMap.getOrDefault(jsParentId.intValue(), BoxedJsValue.HAD_NULL_OBJECT);
812 | BoxedJsArray jsNodes = jsParentParams.getJsonArray("children");
813 | boolean handled = false;
814 | //"parentId":93,
815 | // "nodes": [
816 | if (jsNode.isValid() && jsPreviousNodeId.isValid() && jsParentId.isValid() && jsNodes.isValid()) {
817 | logMessage(String.format("Inserting child of node: %d: %s", jsParentId.intValue(), jsNode.toString()));
818 | addNodeChildren(jsParentId.intValue(), jsNodes, 0, jsNode, jsPreviousNodeId.intValue());
819 | handled = true;
820 | }
821 |
822 | if (!handled) {
823 | // did not add
824 | logMessage(String.format("Did not insert child node for %s", param));
825 | break;
826 | }
827 | }
828 | }
829 |
830 | BoxedJsObject jsError = json.get("error").asJsObject();
831 | BoxedJsObject jsResult = json.get("result").asJsObject();
832 | if (jsId.isValid() && (jsError.isValid() || jsResult.isValid())) {
833 | // may need to further massage the content
834 | int id = jsId.intValue();
835 |
836 | if (myAsyncResultMap.containsKey(id)) {
837 | // this one is a replacement
838 | int resultType = myAsyncResultMap.remove(id);
839 | switch (resultType) {
840 | case RUNTIME_EVALUATE_SCRIPT: {
841 | runOnEvalRunnables = true;
842 | break;
843 | }
844 |
845 | case RUNTIME_COMPILE_SCRIPT: {
846 | // information from the last Debugger.scriptParsed
847 | // now just harmlessly mapped to noop
848 | Integer remoteId = myAsyncIdMap.get(id);
849 | if (remoteId != null) {
850 | logMessage(String.format("Compile script done, request %d mapped to %d", remoteId, id));
851 | }
852 | break;
853 | }
854 |
855 | case RUNTIME_LOG_API: {
856 | // accumulating log API
857 | //{"result":{"result":{"type":"object","objectId":"{\"injectedScriptId\":1,\"id\":5}","className":"Object","description":"Object","preview":{"type":"object","description":"Object","lossless":true,"properties":[{"name":"x","type":"number","value":"0"},{"name":"y","type":"number","value":"79.27999999999997"}]}},"wasThrown":false},"id":36}
858 | myJfxDebuggerAccess.clearArg();
859 | myRuntimeEvaluateArgResult = json;
860 | logMessage(String.format("Getting Runtime.evaluate param: %s", json.toString()));
861 | return null;
862 | }
863 |
864 | case RUNTIME_LOG_STACK: {
865 | logMessage(String.format("Skipping log stack trace, request %d", id));
866 | return null;
867 | }
868 |
869 | case RUNTIME_SKIP: {
870 | logMessage(String.format("Skipping request %d, %s", id, param));
871 | return null;
872 | }
873 |
874 | case DEBUGGER_PAUSED: {
875 | logMessage(String.format("Skipping debug paused request %d, %s", id, param));
876 | return null;
877 | }
878 |
879 | case BREAK_POINT_REMOVE: {
880 | String toRemove = myBreakpointsToRemove.remove(id);
881 | if (toRemove != null) {
882 | logMessage(String.format("Removing breakpoint request %d, %s", id, param));
883 | myBreakpoints.remove(toRemove);
884 | }
885 | }
886 |
887 | case REQUEST_JS_BRIDGE: {
888 | if (mySuppressPageReloadRequest) {
889 | mySuppressPageReloadRequest = false;
890 | } else {
891 | // able to debug our jsBridge connection if issuing pause here
892 | if (myDebugOnLoad == DebugOnLoad.ON_BRIDGE_CONNECT) {
893 | myDebugOnLoad = DebugOnLoad.NONE;
894 | debugBreak(null);
895 | }
896 | logMessage(String.format("Skipping response and requesting JSBridge: %s", param));
897 | myJfxDebuggerAccess.pageReloadStarted();
898 | return null;
899 | }
900 | }
901 |
902 | case DOM_GET_DOCUMENT: {
903 | // {"result":{"root":{"nodeId":13,"nodeType":9,"nodeName":"#document","localName":"","nodeValue":"","childNodeCount":1,"children":[{"nodeId":14,"nodeType":1,"nodeName":"HTML","localName":"html","nodeValue":"","childNodeCount":1,"children":[{"nodeId":15,"nodeType":1,"nodeName":"HEAD","localName":"head","nodeValue":"","childNodeCount":9,"attributes":[]}],"attributes":[]}],"frameId":"0.1","documentURL":"file:///Users/vlad/src/sites/public/mn-resources/preview_2.html?2","baseURL":"file:///Users/vlad/src/sites/public/mn-resources/preview_2.html?2","xmlVersion":""}},"id":63}
904 | BoxedJsObject jsRoot = json.eval("result.root").asJsObject();
905 |
906 | if (jsRoot.isValid()) {
907 | BoxedJsArray jsChildren = jsRoot.get("children").asJsArray();
908 | int parentId = jsRoot.getJsNumber("nodeId").asJsNumber().intValue();
909 |
910 | logMessage(String.format("Got DOM Root of node: %d", parentId));
911 |
912 | if (addNodeChildren(parentId, jsChildren, 0, null, 0)) {
913 | logMessage(String.format("Adding DOM Root node: %d", parentId));
914 | myNodeIdMap.put(parentId, jsRoot);
915 | myRootNodeId = parentId;
916 | } else {
917 | // did not add
918 | logMessage(String.format("Did not add DOM Root node: %d, %s", parentId, param));
919 | }
920 | }
921 | }
922 | }
923 | }
924 |
925 | // Request:
926 | // {"id":411,"method":"Debugger.setBreakpointByUrl","params":{"lineNumber":16,"url":"file:///Users/vlad/src/sites/public/mn-resources/admonition.js","columnNumber":0,"condition":""}}
927 | // Response:
928 | // {"result":{"breakpointId":"file:///Users/vlad/src/sites/public/mn-resources/admonition.js:16:0","locations":[{"scriptId":"183","lineNumber":16,"columnNumber":0}]},"id":162}
929 | // Request:
930 | // {"id":454,"method":"Debugger.removeBreakpoint","params":{"breakpointId":"file:///Users/vlad/src/sites/public/mn-resources/admonition.js:16:0"}}
931 | // Response:
932 | // {"result":{},"id":177}
933 |
934 | // Request:
935 | // {"id":449,"method":"Debugger.getPossibleBreakpoints","params":{"start":{"scriptId":"220","lineNumber":14,"columnNumber":0},"end":{"scriptId":"220","lineNumber":15,"columnNumber":0},"restrictToFunction":false}}
936 | // Response: Need to send back break points or at least to remove all break points before resuming for disconnect. Otherwise the sucker stops waiting for a response
937 | // {"error":{"code":-32601,"message":"'Debugger.getPossibleBreakpoints' was not found"},"id":174}
938 | // see if break point created, we will remember it
939 | try {
940 | BoxedJsString breakpointId = jsResult.getJsString("breakpointId");
941 | if (breakpointId.isValid()) {
942 | myBreakpoints.put(breakpointId.getString(), param);
943 | }
944 | } catch (Throwable throwable) {
945 | String message = throwable.getMessage();
946 | logMessage("Exception on getting breakpointId" + message);
947 | }
948 |
949 | // change id to what the remote expects if we inserted or stripped some calls
950 | Integer remoteId = myAsyncIdMap.remove(id);
951 | if (remoteId != null) {
952 | json.put("id", remoteId);
953 | logMessage(String.format("request %d mapped back to remote %d", id, remoteId));
954 | changed = true;
955 | }
956 | }
957 |
958 | if (changed) {
959 | changedParam = json.toString();
960 | }
961 |
962 | if (runOnEvalRunnables && myOnEvalDoneRunnable != null) {
963 | // schedule the next request
964 | String finalChangedParam = changedParam;
965 | final Runnable runnable = myOnEvalDoneRunnable;
966 | myOnEvalDoneRunnable = null;
967 |
968 | yieldDebugger(() -> {
969 | myWaitingForEvaluateScript = false;
970 | runnable.run();
971 | if (myCallback != null) {
972 | myCallback.call(finalChangedParam);
973 | }
974 | });
975 | } else {
976 | myWaitingForEvaluateScript = false;
977 | if (myCallback != null) {
978 | myCallback.call(changedParam);
979 | }
980 | }
981 |
982 | return null;
983 | }
984 |
985 | boolean addNodeChildren(int parentId, @NotNull BoxedJsArray jsChildren, int removedNode, @Nullable BoxedJsObject jsInsertedNode, int afterSibling) {
986 | if (jsChildren.isValid()) {
987 | int iMax = jsChildren.size();
988 | int offset = 0;
989 | int previousNodeId = 0;
990 |
991 | if (jsInsertedNode == null || !jsInsertedNode.isValid()) {
992 | afterSibling = 0;
993 | jsInsertedNode = null;
994 | }
995 |
996 | for (int i = 0; i < iMax; i++) {
997 | BoxedJsObject jsNode = jsChildren.getJsObject(i + offset);
998 | BoxedJsNumber jsNodeId = jsNode.getJsNumber("nodeId");
999 | if (jsNodeId.isValid()) {
1000 | int nodeId = jsNodeId.intValue();
1001 | if (removedNode == nodeId) {
1002 | jsChildren.remove(i + offset);
1003 | offset--;
1004 | } else if (jsInsertedNode != null && afterSibling == previousNodeId) {
1005 | jsNode.put("parentId", parentId);
1006 | jsNode.put("ordinal", i + offset);
1007 |
1008 | nodeId = jsInsertedNode.getJsNumber("nodeId").intValue();
1009 | myNodeIdMap.put(nodeId, jsInsertedNode);
1010 | jsChildren.add(i + offset, jsInsertedNode);
1011 |
1012 | offset++;
1013 | // now recurse to add node's children
1014 | addNodeChildren(nodeId, jsNode.getJsArray("children"), 0, null, 0);
1015 | } else {
1016 | jsNode.put("parentId", parentId);
1017 | jsNode.put("ordinal", i + offset);
1018 | myNodeIdMap.put(nodeId, jsNode);
1019 |
1020 | // now recurse to add node's children
1021 | addNodeChildren(jsNodeId.intValue(), jsNode.getJsArray("children"), 0, null, 0);
1022 | }
1023 |
1024 | previousNodeId = nodeId;
1025 | }
1026 | }
1027 |
1028 | if (jsInsertedNode != null && afterSibling == previousNodeId) {
1029 | BoxedJsObject jsNode = jsInsertedNode;
1030 | BoxedJsNumber jsNodeId = jsNode.get("nodeId").asJsNumber();
1031 | if (jsNodeId.isValid()) {
1032 | jsNode.put("parentId", parentId);
1033 | jsNode.put("ordinal", iMax + offset);
1034 | myNodeIdMap.put(jsNodeId.intValue(), jsNode);
1035 |
1036 | // now recurse to add node's children
1037 | addNodeChildren(jsNodeId.intValue(), jsNode.getJsArray("children"), 0, null, 0);
1038 | }
1039 | }
1040 |
1041 | BoxedJsObject jsParentParams = myNodeIdMap.get(parentId);
1042 | if (jsParentParams == null) {
1043 | jsParentParams = BoxedJson.boxedFrom(String.format("{\"nodeId\":%d }", parentId));
1044 | }
1045 |
1046 | // save updated details for the node
1047 | jsParentParams.put("children", jsChildren);
1048 | jsParentParams.put("childCount", jsChildren.size());
1049 | myNodeIdMap.put(parentId, jsParentParams);
1050 | return true;
1051 | }
1052 | return false;
1053 | }
1054 |
1055 | @Override
1056 | public void sendMessage(final String message) {
1057 | // pre-process messages here and possibly filter/add other messages
1058 | String changedMessage = message;
1059 | BoxedJsObject json = BoxedJson.boxedFrom(message);
1060 | boolean changed = false;
1061 | String evalScript = null;
1062 |
1063 | final BoxedJsString jsonMethod = json.getJsonString("method");
1064 | final BoxedJsNumber jsId = json.getJsonNumber("id");
1065 | final int id = jsId.isValid() ? jsId.intValue() : 0;
1066 |
1067 | if (jsonMethod.isValid()) {
1068 | String method = jsonMethod.getString();
1069 | switch (method) {
1070 | case "Runtime.compileScript": {
1071 | // change to harmless crap and fake the response
1072 | json = BoxedJson.boxedFrom(String.format("{\"id\":%s,\"method\":\"Runtime.enable\"}", jsId.intValue(myDebuggerId)));
1073 | changed = true;
1074 | myAsyncResultMap.put(myDebuggerId, RUNTIME_COMPILE_SCRIPT);
1075 | logMessage(String.format("Faking compileScript, request %d", jsId.intValue()));
1076 | break;
1077 | }
1078 |
1079 | case "Runtime.evaluate": {
1080 | // grab the evaluate expression and eval it so we get the right context for the call
1081 | // then run this and pass it to dev tools
1082 | // {"id":250,"method":"Runtime.evaluate","params":{"expression":"window.__MarkdownNavigatorArgs.getConsoleArg()","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":4,"returnByValue":false,"generatePreview":true,"userGesture":true,"awaitPromise":false}}
1083 | BoxedJsString jsEvalExpression = json.evalJsString("params.expression");
1084 | BoxedJsValue jsAwaitPromise = json.evalJsBoolean("params.awaitPromise");
1085 | BoxedJsValue jsSilent = json.evalJsBoolean("params.silent");
1086 | BoxedJsValue jsUserGesture = json.evalJsBoolean("params.userGesture");
1087 | BoxedJsValue jsReturnByValue = json.evalJsBoolean("params.returnByValue");
1088 | BoxedJsNumber jsContextId = json.evalJsNumber("params.contextId");
1089 | BoxedJsValue jsIncludeConsoleAPI = json.evalJsBoolean("params.includeCommandLineAPI");
1090 |
1091 | boolean isPageContext = false;
1092 | if (jsContextId.isValid()) {
1093 | int contextId = jsContextId.intValue();
1094 | if (myOldPageContextIds.contains(contextId)) {
1095 | contextId = myLastPageContextId;
1096 | }
1097 | isPageContext = contextId == myLastPageContextId;
1098 | }
1099 |
1100 | // lets make sure we can properly execute it
1101 | if (isPageContext
1102 | && jsEvalExpression.isValid()
1103 | && jsSilent.isFalse()
1104 | && jsAwaitPromise.isFalse()
1105 | && jsReturnByValue.isFalse()
1106 | && jsId.isValid()
1107 | ) {
1108 |
1109 | final String evalExpression;
1110 | if (jsIncludeConsoleAPI.isTrue()) {
1111 | String expression = jsEvalExpression.getString();
1112 | evalExpression = String.format("with (__api) { %s }", expression);
1113 | } else {
1114 | evalExpression = jsEvalExpression.getString();
1115 | }
1116 |
1117 | // {"result":{"result":{"type":"undefined"},"wasThrown":false},"id":38}
1118 | // {"result":{"result":{"type":"object","objectId":"{\"injectedScriptId\":2,\"id\":116}","className":"Object","description":"Object","preview":{"type":"object","description":"Object","lossless":false,"properties":[{"name":"setEventHandledBy","type":"function","value":""},{"name":"getState","type":"function","value":""},{"name":"setState","type":"function","value":""},{"name":"toggleTask","type":"function","value":""},{"name":"onJsBridge","type":"function","value":""}]}},"wasThrown":false},"id":56}
1119 | Object resultObject = myJfxDebuggerAccess.eval(evalExpression);
1120 |
1121 | // now we get the type for the return result
1122 | BoxedJsObject jsResult = getArgParam(resultObject);
1123 | if (jsResult != null) {
1124 | jsResult.evalSet("id", jsId);
1125 | if (resultObject instanceof JSException) {
1126 | // change message to error
1127 | // {"result":{"result":{"type":"object","objectId":"{\"injectedScriptId\":4,\"id\":350}","subtype":"error","className":"ReferenceError","description":"ReferenceError: Can't find variable: b"},"wasThrown":true},"id":119}
1128 | BoxedJsObject jsResultResult = jsResult.evalJsObject("result.result");
1129 | jsResultResult.evalSet("subType", "error");
1130 | String errorMsg = ((JSException) resultObject).getMessage();
1131 | int pos = errorMsg.indexOf(':');
1132 | if (pos > 0) {
1133 | jsResultResult.evalSet("className", errorMsg.substring(0, pos)); // set the class name
1134 | }
1135 | jsResultResult.evalSet("description", errorMsg);
1136 | jsResultResult.remove("preview");
1137 | jsResult.evalSet("result.wasThrown", true);
1138 | }
1139 | // this is what the real debugger responds with when getting an exception in the evaluate
1140 | // {"result":{"result":{"type":"object","objectId":"{\"injectedScriptId\":9,\"id\":123}","subtype":"error","className":"ReferenceError","description":"ReferenceError: Can't find variable: b"},"wasThrown":true},"id":64}
1141 | String param = jsResult.toString();
1142 | logMessage(String.format("Returning emulated Runtime.evaluate result, request %d: %s", jsId.intValue(), param));
1143 | // send to dev tools
1144 | if (myCallback != null) {
1145 | myCallback.call(param);
1146 | }
1147 | }
1148 | return;
1149 | } else {
1150 | // execute old code which does not give the right stack frame but won't mess up the debugger either
1151 | myAsyncResultMap.put(myDebuggerId, RUNTIME_EVALUATE_SCRIPT);
1152 | myWaitingForEvaluateScript = true;
1153 | logMessage(String.format("Waiting for evaluateScript, request %d", jsId.intValue()));
1154 | }
1155 | break;
1156 | }
1157 |
1158 | case "Debugger.pause": {
1159 | evalScript = EMPTY_EVAL_SCRIPT;
1160 | myDebuggerState = DebuggerState.PAUSED;
1161 | break;
1162 | }
1163 |
1164 | case "Debugger.stepOver": {
1165 | myDebuggerState = DebuggerState.STEP_OVER;
1166 | break;
1167 | }
1168 | case "Debugger.stepInto": {
1169 | myDebuggerState = DebuggerState.STEP_INTO;
1170 | break;
1171 | }
1172 | case "Debugger.stepOut": {
1173 | myDebuggerState = DebuggerState.STEP_OUT;
1174 | break;
1175 | }
1176 | case "Debugger.resume": {
1177 | myDebuggerState = DebuggerState.RUNNING;
1178 | break;
1179 | }
1180 | case "Page.reload": {
1181 | // need to make sure debugger is not paused
1182 | if (myPageReloadStarted) {
1183 | return;
1184 | }
1185 | myPageReloadStarted = true;
1186 | break;
1187 | }
1188 |
1189 | // FIX: need to figure out what the correct response to this would be
1190 | // probably a list of break point ids
1191 | // Request:
1192 | // {"id":449,"method":"Debugger.getPossibleBreakpoints","params":{"start":{"scriptId":"220","lineNumber":14,"columnNumber":0},"end":{"scriptId":"220","lineNumber":15,"columnNumber":0},"restrictToFunction":false}}
1193 | // Response: Need to send back break points or at least to remove all break points before resuming for disconnect. Otherwise the sucker stops waiting for a response
1194 | // {"error":{"code":-32601,"message":"'Debugger.getPossibleBreakpoints' was not found"},"id":174}
1195 | // however WebView does send this when it parses a script:
1196 | // {"method":"Debugger.breakpointResolved","params":{"breakpointId":"http://localhost/mn-resources/console-test.js:31:0","location":{"scriptId":"288","lineNumber":31,"columnNumber":0}}}
1197 | case "Debugger.setBreakpointByUrl": {
1198 | // nothing to do, we grab all created, however if one already exists the effing debugger responds with an
1199 | // error instead of returning the pre-existing id. Typical Java mindset.
1200 | // Request:
1201 | // {"id":411,"method":"Debugger.setBreakpointByUrl","params":{"lineNumber":16,"url":"file:///Users/vlad/src/sites/public/mn-resources/admonition.js","columnNumber":0,"condition":""}}
1202 | // Response:
1203 | // {"result":{"breakpointId":"file:///Users/vlad/src/sites/public/mn-resources/admonition.js:16:0","locations":[{"scriptId":"183","lineNumber":16,"columnNumber":0}]},"id":162}
1204 | BoxedJsObject params = json.eval("params").asJsObject();
1205 | BoxedJsNumber jsColumnNumber = params.getJsNumber("columnNumber");
1206 | BoxedJsNumber jsLineNumber = params.getJsNumber("lineNumber");
1207 | if (jsLineNumber.isValid() && jsColumnNumber.isValid()) {
1208 | int lineNumber = jsLineNumber.intValue();
1209 | int columnNumber = jsColumnNumber.intValue();
1210 | String url = params.get("url").asJsString().getString();
1211 | if (!url.isEmpty() && lineNumber > 0) {
1212 | String breakPointId = String.format("%s:%d:%d", url, lineNumber, columnNumber);
1213 | for (String key : myBreakpoints.keySet()) {
1214 | if (key.equals(breakPointId)) {
1215 | // this one is it
1216 | BoxedJsObject jsResult = BoxedJson.boxedFrom(myBreakpoints.get(key));
1217 | jsResult.evalSet("id", json.get("id").asJsNumber());
1218 | if (myCallback != null) {
1219 | myCallback.call(jsResult.toString());
1220 | }
1221 | return;
1222 | }
1223 | }
1224 | }
1225 | }
1226 | break;
1227 | }
1228 |
1229 | case "Debugger.removeBreakpoint": {
1230 | // Request:
1231 | // {"id":454,"method":"Debugger.removeBreakpoint","params":{"breakpointId":"file:///Users/vlad/src/sites/public/mn-resources/admonition.js:16:0"}}
1232 | // Response:
1233 | // {"result":{},"id":177}
1234 | BoxedJsString jsBreakpointId = json.eval("params.breakpointId").asJsString();
1235 | if (jsBreakpointId.isValid()) {
1236 | // we will remove it on the response, if it is in out list
1237 | if (myBreakpoints.containsKey(jsBreakpointId.getString())) {
1238 | myAsyncResultMap.put(myDebuggerId, BREAK_POINT_REMOVE);
1239 | myBreakpointsToRemove.put(myDebuggerId, jsBreakpointId.getString());
1240 | }
1241 | }
1242 | break;
1243 | }
1244 |
1245 | case "DOM.getDocument": {
1246 | // Request:
1247 | // {"id":63,"method":"DOM.getDocument"}
1248 | // Response:
1249 | // {"result":{"root":{"nodeId":13,"nodeType":9,"nodeName":"#document","localName":"","nodeValue":"","childNodeCount":1,"children":[{"nodeId":14,"nodeType":1,"nodeName":"HTML","localName":"html","nodeValue":"","childNodeCount":1,"children":[{"nodeId":15,"nodeType":1,"nodeName":"HEAD","localName":"head","nodeValue":"","childNodeCount":9,"attributes":[]}],"attributes":[]}],"frameId":"0.1","documentURL":"file:///Users/vlad/src/sites/public/mn-resources/preview_2.html?2","baseURL":"file:///Users/vlad/src/sites/public/mn-resources/preview_2.html?2","xmlVersion":""}},"id":63}
1250 | myAsyncResultMap.put(myDebuggerId, DOM_GET_DOCUMENT);
1251 | break;
1252 | }
1253 |
1254 | case "Overlay.setPausedInDebuggerMessage": {
1255 | // Request:
1256 | // {"id":108,"method":"Overlay.setPausedInDebuggerMessage"}
1257 | // Response:
1258 | // {"result":{},"id":177}
1259 | if (myDebugger != null && myDebugger.isEnabled() && jsId.isValid()) {
1260 | int responseId = jsId.intValue();
1261 | yieldDebugger(() -> {
1262 | call(String.format("{\"result\":{},\"id\":%d}", responseId));
1263 | // we now figure out the selector and invoke js helper
1264 | //myJfxDebuggerAccess.eval("");
1265 | });
1266 | }
1267 |
1268 | return;
1269 | }
1270 |
1271 | case "Overlay.highlightNode": {
1272 | // Request:
1273 | // {"id":43,"method":"Overlay.highlightNode","params":{"highlightConfig":{"showInfo":true,"showRulers":false,"showExtensionLines":false,"contentColor":{"r":111,"g":168,"b":220,"a":0.66},"paddingColor":{"r":147,"g":196,"b":125,"a":0.55},"borderColor":{"r":255,"g":229,"b":153,"a":0.66},"marginColor":{"r":246,"g":178,"b":107,"a":0.66},"eventTargetColor":{"r":255,"g":196,"b":196,"a":0.66},"shapeColor":{"r":96,"g":82,"b":177,"a":0.8},"shapeMarginColor":{"r":96,"g":82,"b":127,"a":0.6},"displayAsMaterial":true,"cssGridColor":{"r":75,"g":0,"b":130}},"nodeId":2}}
1274 | // Response:
1275 | // {"result":{},"id":177}
1276 | if (myDebugger != null && myDebugger.isEnabled() && jsId.isValid()) {
1277 | int responseId = jsId.intValue();
1278 | BoxedJsObject jsParams = json.get("params").asJsObject();
1279 | BoxedJsNumber jsNodeId = jsParams.getJsonNumber("nodeId");
1280 | if (jsNodeId.isValid()) {
1281 | // build path based on index of child in parent
1282 | StringBuilder sb = new StringBuilder();
1283 | sb.append("[0");
1284 | addNodeSelectorPath(jsNodeId.intValue(), sb);
1285 | sb.append(']');
1286 | String nodeSelectorPath = sb.toString();
1287 |
1288 | // validate that we get the same node after traversing the path from document
1289 | if (nodeSelectorPath.length() >= 5) {
1290 | // print out the nodes in the path
1291 | String path = nodeSelectorPath.substring(3, nodeSelectorPath.length() - 1);
1292 | String[] parts = path.split(",");
1293 |
1294 | int iMax = parts.length;
1295 | int nodeId = myRootNodeId;
1296 |
1297 | for (int i = iMax; i-- > 0; ) {
1298 | int index = Integer.parseInt(parts[i]);
1299 | BoxedJsObject jsParentParams = myNodeIdMap.get(nodeId);
1300 | if (jsParentParams != null && jsParentParams.isValid()) {
1301 | BoxedJsArray jsChildren = jsParentParams.getJsonArray("children");
1302 | if (jsChildren.isValid()) {
1303 | BoxedJsObject jsNode = jsChildren.getJsonObject(index);
1304 | if (jsNode.isValid() && jsNode.getJsonNumber("nodeId").isValid()) {
1305 | nodeId = jsNode.getInt("nodeId");
1306 | } else {
1307 | logMessage(String.format("Invalid child at %d for node %d: %s", index, nodeId, jsParentParams));
1308 | }
1309 | } else {
1310 | logMessage(String.format("Invalid node children for %d: %s", nodeId, jsParentParams));
1311 | }
1312 | } else {
1313 | logMessage(String.format("No node information for %d in path %s", nodeId, path));
1314 | }
1315 | }
1316 |
1317 | if (nodeId != jsNodeId.intValue()) {
1318 | logMessage(String.format("Wrong node %d for requested %d from path %s", nodeId, jsNodeId.intValue(), path));
1319 | }
1320 |
1321 | yieldDebugger(() -> {
1322 | call(String.format("{\"result\":{},\"id\":%d}", responseId));
1323 | // we now figure out the selector and invoke js helper
1324 | BoxedJsObject paramJson = argParamJson();
1325 | // {"id":250,"method":"Runtime.evaluate","params":{"expression":"window.__MarkdownNavigatorArgs.getConsoleArg()","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":4,"returnByValue":false,"generatePreview":true,"userGesture":true,"awaitPromise":false}}
1326 | //final String argScript = "window.injectedCode = { tests: () => { return \"I'm injected\"; } };";
1327 | final String argScript = "markdownNavigator.highlightNode(" + nodeSelectorPath + ")";
1328 | paramJson.evalSet("id", myDebuggerId)
1329 | .evalSet("params.expression", argScript)
1330 | .evalSet("params.includeCommandLineAPI", true)
1331 | .evalSet("params.objectGroup", "console")
1332 | .evalSet("params.userGesture", false)
1333 | .evalSet("params.generatePreview", false)
1334 | .evalSet("params.contextId", myLastPageContextId)
1335 | ;
1336 |
1337 | logMessage(String.format("invoking highlightNode helper nodeId %d, request %d", jsNodeId.intValue(), myDebuggerId));
1338 |
1339 | myAsyncResultMap.put(myDebuggerId, RUNTIME_SKIP);
1340 | myDebuggerId++;
1341 | debuggerSend(paramJson.toString(), null);
1342 | });
1343 | } else if (!nodeSelectorPath.equals("[0]")) {
1344 | logMessage(String.format("Invalid path %s, for nodeId %d", nodeSelectorPath, jsNodeId.intValue()));
1345 | }
1346 | }
1347 | }
1348 |
1349 | return;
1350 | }
1351 |
1352 | case "Overlay.hideHighlight": {
1353 | // Request:
1354 | // {"id":64,"method":"Overlay.hideHighlight"}
1355 | // Response:
1356 | // {"result":{},"id":177}
1357 | if (myDebugger != null && myDebugger.isEnabled() && jsId.isValid()) {
1358 | int responseId = jsId.intValue();
1359 | yieldDebugger(() -> {
1360 | call(String.format("{\"result\":{},\"id\":%d}", responseId));
1361 | // call jsHelper for that
1362 | BoxedJsObject paramJson = argParamJson();
1363 | //final String argScript = "window.injectedCode = { tests: () => { return \"I'm injected\"; } };";
1364 | final String argScript = "markdownNavigator.hideHighlight()";
1365 | paramJson.evalSet("id", myDebuggerId)
1366 | .evalSet("params.expression", argScript)
1367 | .evalSet("params.includeCommandLineAPI", true)
1368 | .evalSet("params.objectGroup", "console")
1369 | .evalSet("params.userGesture", false)
1370 | .evalSet("params.generatePreview", false)
1371 | .evalSet("params.contextId", myLastPageContextId)
1372 | ;
1373 |
1374 | logMessage(String.format("invoking setHideHighlight helper, request %d", myDebuggerId));
1375 | myAsyncResultMap.put(myDebuggerId, RUNTIME_SKIP);
1376 | myDebuggerId++;
1377 | debuggerSend(paramJson.toString(), null);
1378 | });
1379 | }
1380 |
1381 | return;
1382 | }
1383 | }
1384 | }
1385 |
1386 | if (id > 0) {
1387 | if (id != myDebuggerId) {
1388 | // need to change ids
1389 | json.put("id", myDebuggerId);
1390 | changed = true;
1391 |
1392 | // will need to re-map id on result when it is ready
1393 | myAsyncIdMap.put(myDebuggerId, id);
1394 | logMessage(String.format("Mapping request %d to %d", id, myDebuggerId));
1395 | }
1396 |
1397 | myDebuggerId++;
1398 | }
1399 |
1400 | if (!myOldPageContextIds.isEmpty()) {
1401 | // change old page context id to latest
1402 | String contextIdPath = "params.contextId";
1403 | BoxedJsNumber contextIdNumber = json.eval(contextIdPath).asJsNumber();
1404 | Integer contextId = contextIdNumber.isValid() ? contextIdNumber.intValue() : null;
1405 |
1406 | if (contextId == null) {
1407 | contextIdPath = "params.executionContextId";
1408 | contextIdNumber = json.eval(contextIdPath).asJsNumber();
1409 | contextId = contextIdNumber.isValid() ? contextIdNumber.intValue() : null;
1410 | }
1411 |
1412 | if (contextId != null && myOldPageContextIds.contains(contextId)) {
1413 | json.evalSet(contextIdPath, myLastPageContextId);
1414 | logMessage(String.format("Mapping old context id %d to %d", contextId, myLastPageContextId));
1415 | changed = true;
1416 | }
1417 | }
1418 |
1419 | if (changed) {
1420 | changedMessage = json.toString();
1421 | }
1422 |
1423 | if (myDebugger != null && myDebugger.isEnabled()) {
1424 | debuggerSend(changedMessage, null);
1425 | if (evalScript != null) {
1426 | myJfxDebuggerAccess.eval(evalScript);
1427 | }
1428 | }
1429 | }
1430 |
1431 | void addNodeSelectorPath(int nodeId, StringBuilder out) {
1432 | if (nodeId != myRootNodeId) {
1433 | BoxedJsObject jsNode = myNodeIdMap.getOrDefault(nodeId, BoxedJsValue.HAD_NULL_OBJECT);
1434 | if (jsNode.isValid()) {
1435 | // add node selector to list, these are reverse order
1436 | // format is: ,ordinal in parent
1437 | BoxedJsNumber jsParentId = jsNode.get("parentId").asJsNumber();
1438 | BoxedJsNumber jsOrdinal = jsNode.get("ordinal").asJsNumber();
1439 | if (jsParentId.isValid() && jsOrdinal.isValid()) {
1440 | out.append(',').append(jsOrdinal.intValue());
1441 | addNodeSelectorPath(jsParentId.intValue(), out);
1442 | }
1443 | }
1444 | }
1445 | }
1446 |
1447 | @Override
1448 | public Callback
10 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/javadoc/overview.md:
--------------------------------------------------------------------------------
1 | **JavaFX WebView Debugger**
2 |
3 | Library implementing WebSocket server for JavaFX WebView debugger to Chrome Dev Tools.
4 |
5 | Creates a comfortable debugging environment for script debugging in JavaFX WebView:
6 |
7 | * all `console` functions: `assert`, `clear`, `count`, `debug`, `dir`, `dirxml`, `error`,
8 | `exception`, `group`, `groupCollapsed`, `groupEnd`, `info`, `log`, `profile`, `profileEnd`,
9 | `select`, `table`, `time`, `timeEnd`, `trace`, `warn`. With added extras to output to the Java
10 | console: `print`, `println`
11 | * all commandLineAPI functions implemented by JavaFX WebView: `$`, `$$`, `$x`, `dir`, `dirxml`,
12 | `keys`, `values`, `profile`, `profileEnd`, `table`, `monitorEvents`, `unmonitorEvents`,
13 | `inspect`, `copy`, `clear`, `getEventListeners`, `$0`, `$_`, `$exception`
14 |
--------------------------------------------------------------------------------
/src/main/resources/console-test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | * console
functions: assert
, clear
, count
, debug
, dir
, dirxml
, error
,
11 | exception
, group
, groupCollapsed
, groupEnd
, info
, log
, profile
, profileEnd
,
12 | select
, table
, time
, timeEnd
, trace
, warn
. With added extras to output to the Java
13 | console: print
, println
$
, $$
, $x
, dir
, dirxml
,
15 | keys
, values
, profile
, profileEnd
, table
, monitorEvents
, unmonitorEvents
,
16 | inspect
, copy
, clear
, getEventListeners
, $0
, $_
, $exception