-
Notifications
You must be signed in to change notification settings - Fork 8
Add EventManager implementation for handling events from WebView and IDE #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
019571d
initial implementation of event handler
kerenr-jfrog 5b3849a
initial receiver implementation
kerenr-jfrog 5eba322
receiver works properly
kerenr-jfrog 1645776
CR fixes
kerenr-jfrog 5b9e97e
added tests for receiver and sender in event manager
kerenr-jfrog daced6a
fix test
kerenr-jfrog File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
48 changes: 48 additions & 0 deletions
48
bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/events/EventManager.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package com.jfrog.ide.eclipse.ui.webview.events; | ||
|
|
||
| import org.cef.browser.CefBrowser; | ||
| import com.jfrog.ide.common.webview.events.WebviewEvent; | ||
|
|
||
| /** | ||
| * The EventManager is responsible for managing events between the IDE and the Webview. | ||
| * It handles the creation of a receiver and sender, allowing communication between the components. | ||
| */ | ||
| public class EventManager { | ||
| private final static String IDE_SEND_FUNC_NAME = "sendMessageToIdeFunc"; | ||
| private final Receiver receiver; | ||
| private final Sender sender; | ||
|
|
||
| /** | ||
| * Constructs a new EventManager with the provided CefBrowser. | ||
| * Note: The eventManager must be created before the webview is initialized. | ||
| * | ||
| * @param cefBrowser The JBCefBrowser associated with the webview. | ||
| * @param project The Project associated with the IDE. | ||
| */ | ||
| public EventManager(CefBrowser cefBrowser) { | ||
| this.receiver = new Receiver(cefBrowser); | ||
| this.sender = new Sender(cefBrowser); | ||
| } | ||
|
|
||
| /** | ||
| * Invoked when the webview finishes loading. | ||
| * Creates the IDE send function body and sends it to the webview. | ||
| * Finally, it runs onLoadEvent, if provided. | ||
| * | ||
| * @param onLoadEnd A {@link Runnable} to run when the webview finishes loading. | ||
| */ | ||
| public void onWebviewLoadEnd() { | ||
| String ideSendFuncBody = this.receiver.createIdeSendFuncBody(IDE_SEND_FUNC_NAME); | ||
| this.sender.sendIdeSendFunc(IDE_SEND_FUNC_NAME, ideSendFuncBody); | ||
| } | ||
|
|
||
| /** | ||
| * Sends an event of the specified type and data to the webview. | ||
| * | ||
| * @param type The type of the webview event. | ||
| * @param data The data associated with the event. | ||
| */ | ||
| public void send(WebviewEvent.Type type, Object data) { | ||
| this.sender.sendEvent(type, data); | ||
| } | ||
| } |
64 changes: 64 additions & 0 deletions
64
bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/events/Receiver.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.jfrog.ide.eclipse.ui.webview.events; | ||
|
|
||
| import org.cef.browser.CefBrowser; | ||
| import org.cef.browser.CefFrame; | ||
| import org.cef.browser.CefMessageRouter; | ||
| import org.cef.callback.CefQueryCallback; | ||
| import org.cef.handler.CefMessageRouterHandlerAdapter; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.jfrog.ide.common.webview.events.IdeEvent; | ||
| import com.jfrog.ide.eclipse.log.Logger; | ||
|
|
||
| import static com.jfrog.ide.common.utils.Utils.createMapper; | ||
|
|
||
|
|
||
| /** | ||
| * The Receiver class is responsible for handling events received from the webview in the IDE. | ||
| * It sets up the necessary query handling and provides a mechanism to process the received events. | ||
| */ | ||
| public class Receiver { | ||
| private final CefMessageRouter messageRouter; | ||
|
|
||
| public Receiver(CefBrowser cefBrowser) { | ||
| this.messageRouter = CefMessageRouter.create(); | ||
| this.messageRouter.addHandler(new CefMessageRouterHandlerAdapter() { | ||
| @Override | ||
| public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, String request, boolean persistent, CefQueryCallback callback) { | ||
| try { | ||
| IdeEvent event = unpack(request); | ||
| handler(event); | ||
| callback.success("Request processed successfully."); | ||
| } catch (JsonProcessingException e) { | ||
| Logger.getInstance().error(String.format("Failed to parse event from the Webview: %s", e.getMessage())); | ||
| callback.failure(500,String.format("Invalid request format: %s", e.getMessage())); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| }, true); | ||
| cefBrowser.getClient().addMessageRouter(messageRouter); | ||
| } | ||
|
|
||
| public static IdeEvent unpack(String raw) throws JsonProcessingException { | ||
| ObjectMapper ow = createMapper(); | ||
| return ow.readValue(raw, IdeEvent.class); | ||
| } | ||
|
|
||
| public String createIdeSendFuncBody(String ideSendFunctionName) { | ||
| // This JS function sends a message to the Java side using the message router. | ||
| return String.format("window['%s'] = obj => { let raw = JSON.stringify(obj); cefQuery({request: raw}); };",ideSendFunctionName); | ||
| } | ||
|
|
||
| /** | ||
| * Handles the received IdeEvent. | ||
| * | ||
| * @param event The received IdeEvent to handle. | ||
| */ | ||
| private void handler(IdeEvent event) { | ||
| // TODO: add logic for handling Webview events such as: JUMP_TO_CODE | ||
| Logger.getInstance().info(String.format("Received event from the webview: %s", event.getType())); | ||
| } | ||
|
|
||
| } | ||
81 changes: 81 additions & 0 deletions
81
bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/events/Sender.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| package com.jfrog.ide.eclipse.ui.webview.events; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.jfrog.ide.common.webview.events.WebviewEvent; | ||
| import com.jfrog.ide.eclipse.log.Logger; | ||
|
|
||
| import org.cef.browser.CefBrowser; | ||
|
|
||
| import static com.jfrog.ide.common.utils.Utils.createMapper; | ||
|
|
||
| import javax.swing.SwingUtilities; | ||
|
|
||
| /** | ||
| * The Sender class is responsible for sending events from the IDE to the webview. | ||
| * It utilizes a CefBrowser instance to execute JavaScript code in the webview. | ||
| */ | ||
| public class Sender { | ||
| CefBrowser browser; | ||
|
|
||
| /** | ||
| * @param browser The CefBrowser instance associated with the webview. | ||
| */ | ||
| public Sender(CefBrowser browser) { | ||
| this.browser = browser; | ||
| } | ||
|
|
||
| /** | ||
| * Packs the webview event into a JSON string representation. | ||
| * | ||
| * @param type The type of the webview event. | ||
| * @param data The data associated with the webview event. | ||
| * @return The JSON string representation of the packed webview event. | ||
| * @throws JsonProcessingException If an error occurs during JSON processing. | ||
| */ | ||
| public static String pack(WebviewEvent.Type type, Object data) throws JsonProcessingException { | ||
| ObjectMapper ow = createMapper(); | ||
| return ow.writeValueAsString(new WebviewEvent(type, data)); | ||
| } | ||
|
|
||
| /** | ||
| * Sends the IDE send function to the webview. This function allows sending data back from the webview to the IDE. | ||
| * | ||
| * @param ideSendFuncName The name of the IDE send function. | ||
| * @param ideSendFuncBody The body of the IDE send function. | ||
| */ | ||
| public void sendIdeSendFunc(String ideSendFuncName, String ideSendFuncBody) { | ||
| // Send the function to jcef, this must be first before updating the webview. | ||
| // Otherwise, the webview will not find any methods to use and will drop the request. | ||
| this.send(ideSendFuncBody); | ||
| // Update JFrog webview with the function to be used in order to send data back to the IDE. | ||
| this.sendEvent(WebviewEvent.Type.SET_EMITTER, "return " + ideSendFuncName); | ||
| } | ||
|
|
||
| /** | ||
| * Sends a webview event with the specified type and data to the webview. | ||
| * | ||
| * @param type The type of the webview event. | ||
| * @param data The data associated with the webview event. | ||
| */ | ||
| public void sendEvent(WebviewEvent.Type type, Object data) { | ||
| try { | ||
| String raw = pack(type, data); | ||
| Logger.getInstance().debug(String.format("Sending data to jfrog webview: %s", raw)); | ||
| this.send(String.format("window.postMessage( %s )", raw)); | ||
| } catch (JsonProcessingException e) { | ||
| Logger.getInstance().error(String.format("Failed to pack the Webview event: %s", e.getMessage())); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Sends the specified event to the webview by executing the JavaScript code. | ||
| * this action must be done on the SWT thread | ||
| * @param event The JavaScript code to be executed in the webview. | ||
| */ | ||
| public void send(String event) { | ||
| SwingUtilities.invokeLater(() -> { | ||
kerenr-jfrog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| browser.executeJavaScript(event, "", 0); | ||
| }); | ||
| } | ||
| } | ||
76 changes: 76 additions & 0 deletions
76
tests/src/main/java/com/jfrog/ide/eclipse/webview/CefBrowserStub.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package com.jfrog.ide.eclipse.webview; | ||
|
|
||
| import java.awt.Component; | ||
| import java.awt.Point; | ||
| import java.awt.image.BufferedImage; | ||
| import java.util.Vector; | ||
| import java.util.concurrent.CompletableFuture; | ||
|
|
||
| import org.cef.CefClient; | ||
| import org.cef.browser.CefBrowser; | ||
| import org.cef.browser.CefDevToolsClient; | ||
| import org.cef.browser.CefFrame; | ||
| import org.cef.callback.CefPdfPrintCallback; | ||
| import org.cef.callback.CefRunFileDialogCallback; | ||
| import org.cef.callback.CefStringVisitor; | ||
| import org.cef.handler.CefDialogHandler.FileDialogMode; | ||
| import org.cef.handler.CefRenderHandler; | ||
| import org.cef.handler.CefWindowHandler; | ||
| import org.cef.misc.CefPdfPrintSettings; | ||
| import org.cef.network.CefRequest; | ||
|
|
||
| class CefBrowserStub implements CefBrowser { | ||
| public String lastJs; public int jsCallCount = 0; | ||
| @Override public void executeJavaScript(String code, String url, int line) { lastJs = code; jsCallCount++; } | ||
| @Override public CefClient getClient() { throw new UnsupportedOperationException(); } | ||
| @Override public String getURL() { throw new UnsupportedOperationException(); } | ||
| @Override public void loadURL(String url) { throw new UnsupportedOperationException(); } | ||
| @Override public void reload() { throw new UnsupportedOperationException(); } | ||
| @Override public void stopLoad() { throw new UnsupportedOperationException(); } | ||
| @Override public void goBack() { throw new UnsupportedOperationException(); } | ||
| @Override public void goForward() { throw new UnsupportedOperationException(); } | ||
| @Override public boolean isLoading() { throw new UnsupportedOperationException(); } | ||
| @Override public void close(boolean force) { throw new UnsupportedOperationException(); } | ||
| @Override public void setFocus(boolean enable) { throw new UnsupportedOperationException(); } | ||
| @Override public void setZoomLevel(double zoomLevel) { throw new UnsupportedOperationException(); } | ||
| @Override public double getZoomLevel() { throw new UnsupportedOperationException(); } | ||
| @Override public void startDownload(String url) { throw new UnsupportedOperationException(); } | ||
| @Override public void print() { throw new UnsupportedOperationException(); } | ||
| @Override public void stopFinding(boolean clearSelection) { throw new UnsupportedOperationException(); } | ||
| @Override public boolean canGoBack() { return false; } | ||
| @Override public boolean canGoForward() { return false; } | ||
| @Override public void closeDevTools() { } | ||
| @Override public void createImmediately() { } | ||
| @Override public CompletableFuture<BufferedImage> createScreenshot(boolean arg0) { return null; } | ||
| @Override public boolean doClose() { return false; } | ||
| @Override public void find(String arg0, boolean arg1, boolean arg2, boolean arg3) { } | ||
| @Override public CefDevToolsClient getDevToolsClient() { return null; } | ||
| @Override public CefFrame getFocusedFrame() { return null; } | ||
| @Override public CefFrame getFrameByIdentifier(String arg0) { return null; } | ||
| @Override public CefFrame getFrameByName(String arg0) { return null; } | ||
| @Override public int getFrameCount() { return 0; } | ||
| @Override public Vector<String> getFrameIdentifiers() { return null; } | ||
| @Override public Vector<String> getFrameNames() { return null; } | ||
| @Override public int getIdentifier() { return 0; } | ||
| @Override public CefFrame getMainFrame() { return null; } | ||
| @Override public CefRenderHandler getRenderHandler() { return null; } | ||
| @Override public void getSource(CefStringVisitor arg0) { } | ||
| @Override public void getText(CefStringVisitor arg0) { } | ||
| @Override public Component getUIComponent() { return null; } | ||
| @Override public CefWindowHandler getWindowHandler() { return null; } | ||
| @Override public CompletableFuture<Integer> getWindowlessFrameRate() { return null; } | ||
| @Override public boolean hasDocument() { return false; } | ||
| @Override public boolean isPopup() { return false; } | ||
| @Override public void loadRequest(CefRequest arg0) { } | ||
| @Override public void onBeforeClose() { } | ||
| @Override public void openDevTools() { } | ||
| @Override public void openDevTools(Point arg0) { } | ||
| @Override public void printToPDF(String arg0, CefPdfPrintSettings arg1, CefPdfPrintCallback arg2) { } | ||
| @Override public void reloadIgnoreCache() { } | ||
| @Override public void replaceMisspelling(String arg0) { } | ||
| @Override public void runFileDialog(FileDialogMode arg0, String arg1, String arg2, Vector<String> arg3, int arg4, CefRunFileDialogCallback arg5) { } | ||
| @Override public void setCloseAllowed() { } | ||
| @Override public void setWindowVisibility(boolean arg0) { } | ||
| @Override public void setWindowlessFrameRate(int arg0) { } | ||
| @Override public void viewSource() { } | ||
| } |
64 changes: 64 additions & 0 deletions
64
tests/src/main/java/com/jfrog/ide/eclipse/webview/ReceiverTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.jfrog.ide.eclipse.webview; | ||
|
|
||
| import com.jfrog.ide.eclipse.ui.webview.events.Receiver; | ||
| import com.jfrog.ide.common.webview.events.IdeEvent; | ||
| import junit.framework.TestCase; | ||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
|
|
||
| public class ReceiverTest extends TestCase { | ||
| public void testUnpack() throws JsonProcessingException { | ||
| String json = "{\"type\":\"JUMP_TO_CODE\",\"data\":{\"filePath\":\"/path/to/file.java\",\"lineNumber\":42}}"; | ||
| IdeEvent event = Receiver.unpack(json); | ||
| assertNotNull(event); | ||
| assertEquals("JUMP_TO_CODE", event.getType().toString()); | ||
| assertNotNull(event.getData()); | ||
| assertTrue((event.getData().toString()).contains("filePath")); | ||
| } | ||
|
|
||
| public void testUnpackWithNullData() throws JsonProcessingException { | ||
| String json = "{\"type\":\"JUMP_TO_CODE\",\"data\":null}"; | ||
| IdeEvent event = Receiver.unpack(json); | ||
| assertNotNull(event); | ||
| assertEquals("JUMP_TO_CODE", event.getType().toString()); | ||
| assertNull(event.getData()); | ||
| } | ||
|
|
||
| public void testUnpackWithExtraFields() throws JsonProcessingException { | ||
| String json = "{\"type\":\"JUMP_TO_CODE\",\"data\":{\"filePath\":\"path\"},\"extra\":\"data\"}"; | ||
| IdeEvent event = Receiver.unpack(json); | ||
| assertNotNull(event); | ||
| assertEquals("JUMP_TO_CODE", event.getType().toString()); | ||
| assertNotNull(event.getData()); | ||
| assertTrue(event.getData().toString().contains("filePath")); | ||
| } | ||
|
|
||
| public void testUnpackWithInvalidJson() { | ||
| String json = "{type:BAD_JSON}"; | ||
| try { | ||
| Receiver.unpack(json); | ||
| fail("Expected JsonProcessingException"); | ||
| } catch (JsonProcessingException e) { | ||
| // Expected | ||
| } | ||
| } | ||
|
|
||
| public void testUnpackWithMissingType() throws JsonProcessingException { | ||
| String json = "{\"data\":{}}"; | ||
| IdeEvent event = Receiver.unpack(json); | ||
| assertNotNull(event); | ||
| assertTrue(json.contains("data")); | ||
| assertNull(event.getType()); | ||
|
|
||
| } | ||
|
|
||
| public void testUnpackWithDifferentEventType() throws JsonProcessingException { | ||
| String json = "{\"type\":\"SOME_EVENT\",\"data\":{\"value\":42}}"; | ||
| try | ||
| { | ||
| Receiver.unpack(json); | ||
| fail("Expected JsonProcessingException"); | ||
| } catch (JsonProcessingException e) { | ||
| // Expected | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.