Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public void completed(final ProgressEvent event) {
});

webViewAssetProvider.setContent(browser);

// Block F5/Refresh - the webview uses setText() with no backing URL,
// so browser refresh navigates to about:blank or file:/// causing a blank screen.
browser.addLocationListener(new RefreshBlockingLocationListener());
browser.addKeyListener(new RefreshBlockingKeyListener());
}

super.setupView(parent);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.eclipse.amazonq.views;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;

/**
* Blocks F5 keypress to prevent browser refresh in the chat webview.
* The webview uses setText() with no backing URL, so F5 would navigate
* to about:blank, wiping the UI.
*/
final class RefreshBlockingKeyListener extends KeyAdapter {

@Override
public void keyPressed(final KeyEvent e) {
if (shouldBlockKey(e.keyCode)) {
e.doit = false;
}
}

/**
* Determines whether the given key code should be blocked.
* Package-private for testability.
*/
static boolean shouldBlockKey(final int keyCode) {
return keyCode == SWT.F5;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.eclipse.amazonq.views;

import org.eclipse.swt.browser.LocationAdapter;
import org.eclipse.swt.browser.LocationEvent;

/**
* Blocks browser navigation to prevent the chat webview from going blank on
* refresh. The webview uses setText() with no backing URL, so any refresh or
* navigation attempt will leave the rendered content. On different platforms,
* refresh may navigate to about:blank (Windows/Linux) or file:/// (macOS WebKit).
* This listener blocks all navigation since the webview should never navigate away.
*/
final class RefreshBlockingLocationListener extends LocationAdapter {

@Override
public void changing(final LocationEvent event) {
if (shouldBlockNavigation(event.location)) {
event.doit = false;
}
}

/**
* Determines whether navigation to the given location should be blocked.
* Blocks about:blank and file:// URLs which are triggered by refresh on
* different platforms. The webview content is set via setText() and should
* never navigate to any URL.
* Package-private for testability.
*/
static boolean shouldBlockNavigation(final String location) {
if (location == null || location.isEmpty()) {
return false;
}
return "about:blank".equals(location) || location.startsWith("file:");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.eclipse.amazonq.views;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.eclipse.swt.SWT;
import org.junit.jupiter.api.Test;

public final class RefreshBlockingKeyListenerTest {

@Test
void testBlocksF5KeyPress() {
assertTrue(RefreshBlockingKeyListener.shouldBlockKey(SWT.F5),
"F5 keypress should be blocked");
}

@Test
void testAllowsF4KeyPress() {
assertFalse(RefreshBlockingKeyListener.shouldBlockKey(SWT.F4),
"F4 keypress should not be blocked");
}

@Test
void testAllowsF6KeyPress() {
assertFalse(RefreshBlockingKeyListener.shouldBlockKey(SWT.F6),
"F6 keypress should not be blocked");
}

@Test
void testAllowsEnterKey() {
assertFalse(RefreshBlockingKeyListener.shouldBlockKey(SWT.CR),
"Enter key should not be blocked");
}

@Test
void testAllowsRegularCharacterKey() {
assertFalse(RefreshBlockingKeyListener.shouldBlockKey('a'),
"Regular character keys should not be blocked");
}

@Test
void testAllowsEscapeKey() {
assertFalse(RefreshBlockingKeyListener.shouldBlockKey(SWT.ESC),
"Escape key should not be blocked");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.eclipse.amazonq.views;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

public final class RefreshBlockingLocationListenerTest {

@Test
void testBlocksNavigationToAboutBlank() {
assertTrue(RefreshBlockingLocationListener.shouldBlockNavigation("about:blank"),
"Navigation to about:blank should be blocked");
}

@Test
void testBlocksNavigationToFileProtocol() {
assertTrue(RefreshBlockingLocationListener.shouldBlockNavigation("file:///"),
"Navigation to file:/// should be blocked (macOS WebKit reload)");
}

@Test
void testBlocksNavigationToFileProtocolWithPath() {
assertTrue(RefreshBlockingLocationListener.shouldBlockNavigation("file:///tmp/test.html"),
"Navigation to file:// URLs should be blocked");
}

@Test
void testAllowsNavigationToHttpsUrl() {
assertFalse(RefreshBlockingLocationListener.shouldBlockNavigation("https://example.com"),
"Navigation to HTTPS URLs should be allowed");
}

@Test
void testAllowsNavigationWhenLocationIsNull() {
assertFalse(RefreshBlockingLocationListener.shouldBlockNavigation(null),
"Navigation should be allowed when location is null");
}

@Test
void testAllowsNavigationToEmptyString() {
assertFalse(RefreshBlockingLocationListener.shouldBlockNavigation(""),
"Navigation should be allowed when location is empty");
}
}
Loading