Skip to content

Commit

Permalink
Fix Edge Browser setText method
Browse files Browse the repository at this point in the history
This contribution fixes Edge browser for win32 to allow serving webpages using setText method where the source code may refer to local resources.

contributes to #213
  • Loading branch information
amartya4256 committed Sep 10, 2024
1 parent d9159c0 commit 7189709
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
*******************************************************************************/
package org.eclipse.swt.browser;

import java.io.*;
import java.net.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.nio.file.Path;
import java.time.*;
import java.util.*;
import java.util.function.*;
Expand Down Expand Up @@ -43,6 +46,13 @@ class Edge extends WebBrowser {
static final String DATA_DIR_PROP = "org.eclipse.swt.browser.EdgeDataDir";
static final String LANGUAGE_PROP = "org.eclipse.swt.browser.EdgeLanguage";
static final String VERSIONT_PROP = "org.eclipse.swt.browser.EdgeVersion";
/**
* The URI_FOR_CUSTOM_TEXT_PAGE is the path to a temporary html file which is used
* by Edge browser to navigate to for setting html content in the
* DOM of the browser to enable it to load local resources.
*/
private static final URI URI_FOR_CUSTOM_TEXT_PAGE = setupAndGetLocationForCustomTextPage();
private static final String ABOUT_BLANK = "about:blank";

private record WebViewEnvironment(ICoreWebView2Environment environment, ArrayList<Edge> instances) {
public WebViewEnvironment(ICoreWebView2Environment environment) {
Expand All @@ -64,6 +74,8 @@ public WebViewEnvironment(ICoreWebView2Environment environment) {
boolean inNewWindow;
HashMap<Long, LocationEvent> navigations = new HashMap<>();

private String lastCustomText;

static {
NativeClearSessions = () -> {
ICoreWebView2CookieManager manager = getCookieManager();
Expand Down Expand Up @@ -171,6 +183,18 @@ public WebViewEnvironment(ICoreWebView2Environment environment) {
};
}

private static URI setupAndGetLocationForCustomTextPage() {
URI absolutePath;
try {
Path tempFile = Files.createTempFile("base", ".html");
absolutePath = tempFile.toUri();
tempFile.toFile().deleteOnExit();
} catch (IOException e) {
absolutePath = URI.create(ABOUT_BLANK);
}
return absolutePath;
}

static String wstrToString(long psz, boolean free) {
if (psz == 0) return "";
int len = OS.wcslen(psz);
Expand Down Expand Up @@ -438,6 +462,8 @@ public void create(Composite parent, int style) {
handler.Release();
}

addProgressListener(ProgressListener.completedAdapter(__ -> writeToDefaultPathDOM()));

IUnknown hostDisp = newHostObject(this::handleCallJava);
long[] hostObj = { COM.VT_DISPATCH, hostDisp.getAddress(), 0 }; // VARIANT
webView.AddHostObjectToScript("swt\0".toCharArray(), hostObj);
Expand Down Expand Up @@ -549,7 +575,11 @@ public String getText() {
public String getUrl() {
long ppsz[] = new long[1];
webView.get_Source(ppsz);
return wstrToString(ppsz[0], true);
return getExposedUrl(wstrToString(ppsz[0], true));
}

private String getExposedUrl(String url) {
return isLocationForCustomText(url) ? url = ABOUT_BLANK : url;
}

int handleCloseRequested(long pView, long pArgs) {
Expand Down Expand Up @@ -615,7 +645,7 @@ int handleNavigationStarting(long pView, long pArgs, boolean top) {
long[] ppszUrl = new long[1];
int hr = args.get_Uri(ppszUrl);
if (hr != COM.S_OK) return hr;
String url = wstrToString(ppszUrl[0], true);
String url = getExposedUrl(wstrToString(ppszUrl[0], true));
long[] pNavId = new long[1];
args.get_NavigationId(pNavId);
LocationEvent event = new LocationEvent(browser);
Expand Down Expand Up @@ -656,7 +686,7 @@ int handleSourceChanged(long pView, long pArgs) {
long[] ppsz = new long[1];
int hr = webView.get_Source(ppsz);
if (hr != COM.S_OK) return hr;
String url = wstrToString(ppsz[0], true);
String url = getExposedUrl(wstrToString(ppsz[0], true));
browser.getDisplay().asyncExec(() -> {
if (browser.isDisposed()) return;
LocationEvent event = new LocationEvent(browser);
Expand Down Expand Up @@ -906,22 +936,40 @@ public void stop() {
webView.Stop();
}

private boolean isLocationForCustomText(String location) {
try {
return URI_FOR_CUSTOM_TEXT_PAGE.equals(URI.create(location));
} catch (IllegalArgumentException e) {
return false;
}
}

private void writeToDefaultPathDOM() {
if(lastCustomText != null && getUrl().equals(ABOUT_BLANK)) {
boolean test = jsEnabled;
jsEnabled = true;
execute("document.open(); document.write(`" + lastCustomText + "`); document.close();");
jsEnabled = test;
this.lastCustomText = null;
}
}

@Override
public boolean setText(String html, boolean trusted) {
char[] data = new char[html.length() + 1];
html.getChars(0, html.length(), data, 0);
return webView.NavigateToString(data) == COM.S_OK;
return setWebpageData(URI_FOR_CUSTOM_TEXT_PAGE.toASCIIString(), null, null, html);
}

@Override
public boolean setUrl(String url, String postData, String[] headers) {
private boolean setWebpageData(String url, String postData, String[] headers, String html) {
// Feature in WebView2. Partial URLs like "www.example.com" are not accepted.
// Prepend the protocol if it's missing.
if (!url.matches("[a-z][a-z0-9+.-]*:.*")) {
url = "http://" + url;
}
int hr;
char[] pszUrl = stringToWstr(url);
if(isLocationForCustomText(url)) {
this.lastCustomText = html;
}
if (postData != null || headers != null) {
if (environment2 == null || webView_2 == null) {
SWT.error(SWT.ERROR_NOT_IMPLEMENTED, null, " [WebView2 version 88+ is required to set postData and headers]");
Expand Down Expand Up @@ -956,4 +1004,9 @@ public boolean setUrl(String url, String postData, String[] headers) {
return hr == COM.S_OK;
}

@Override
public boolean setUrl(String url, String postData, String[] headers) {
return setWebpageData(url, postData, headers, null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,6 @@ public void test_isLocationForCustomText_setUrlAfterDisposedThrowsSwtException()

@Test
public void test_isLocationForCustomText_isSetUrlNotCustomTextUrlAfterSetText() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge); // Edge doesn't fire a changed event if the URL is the same as the previous location.
URI url = getValidUrl();
AtomicBoolean locationChanged = new AtomicBoolean(false);
browser.addLocationListener(changedAdapter(event -> {
Expand All @@ -546,7 +545,6 @@ public void test_isLocationForCustomText_isSetUrlNotCustomTextUrlAfterSetText()

@Test
public void test_isLocationForCustomText_isFirstSetTextURLStillCustomTextUrlAfterSetUrl() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge); // Edge doesn't fire a changed event if the URL is the same as the previous location.
AtomicBoolean locationChanged = new AtomicBoolean(false);
browser.addLocationListener(changedAdapter(event -> locationChanged.set(true)));
URI url = getValidUrl();
Expand Down Expand Up @@ -585,7 +583,6 @@ public void test_isLocationForCustomText_isSetUrlNotCustomTextUrl() {

@Test
public void test_isLocationForCustomText() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge); // Edge doesn't fire a changed event if the URL is the same as the previous location.
AtomicBoolean locationChanged = new AtomicBoolean(false);
browser.addLocationListener(changedAdapter(e -> locationChanged.set(true)));
browser.setText("Hello world");
Expand All @@ -604,7 +601,6 @@ public void test_LocationListener_changing() {
}
@Test
public void test_LocationListener_changed() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);

AtomicBoolean changedFired = new AtomicBoolean(false);
browser.addLocationListener(changedAdapter(e -> changedFired.set(true)));
Expand All @@ -615,8 +611,6 @@ public void test_LocationListener_changed() {
}
@Test
public void test_LocationListener_changingAndOnlyThenChanged() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);

// Test proper order of events.
// Check that 'changed' is only fired after 'changing' has fired at least once.
AtomicBoolean changingFired = new AtomicBoolean(false);
Expand Down Expand Up @@ -660,8 +654,6 @@ else if (!changingFired.get())

@Test
public void test_LocationListener_then_ProgressListener() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);

AtomicBoolean locationChanged = new AtomicBoolean(false);
AtomicBoolean progressChanged = new AtomicBoolean(false);
AtomicBoolean progressChangedAfterLocationChanged = new AtomicBoolean(false);
Expand Down Expand Up @@ -755,8 +747,6 @@ public void changed(LocationEvent event) {
@Test
/** Ensue that only one changed and one completed event are fired for url changes */
public void test_LocationListener_ProgressListener_noExtraEvents() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);

AtomicInteger changedCount = new AtomicInteger(0);
AtomicInteger completedCount = new AtomicInteger(0);

Expand Down Expand Up @@ -863,7 +853,6 @@ public void test_OpenWindowListener_open_ChildPopup() {
/** Validate event order : Child's visibility should come before progress completed event */
@Test
public void test_OpenWindow_Progress_Listener_ValidateEventOrder() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);

AtomicBoolean windowOpenFired = new AtomicBoolean(false);
AtomicBoolean childCompleted = new AtomicBoolean(false);
Expand Down Expand Up @@ -1108,7 +1097,6 @@ public void test_TitleListener_event() {
assertTrue(errMsg, passed);
}


@Test
public void test_setText() {
String expectedTitle = "Website Title";
Expand Down Expand Up @@ -1215,16 +1203,16 @@ private void validateTitleChanged(String expectedTitle, Runnable browserSetFunc)
browserSetFunc.run();
shell.open();

boolean hasFinished = waitForPassCondition(() -> actualTitle.get().length() != 0
&& !actualTitle.get().contains("about:blank")); // Windows sometimes does 2 loads, one "about:blank", and one actual load.
boolean passed = hasFinished && actualTitle.get().equals(expectedTitle);
boolean passed = waitForPassCondition(() -> actualTitle.get().equals(expectedTitle));
String errMsg = "";
if (!hasFinished)
errMsg = "Test timed out. TitleListener not fired";
else if (!actualTitle.get().equals(expectedTitle)) {
errMsg = "\nExpected title and actual title do not match."
+ "\nExpected: " + expectedTitle
+ "\nActual: " + actualTitle;
if (!passed) {
if (actualTitle.get().length() == 0) {
errMsg = "Test timed out. TitleListener not fired";
} else {
errMsg = "\nExpected title and actual title do not match."
+ "\nExpected: " + expectedTitle
+ "\nActual: " + actualTitle;
}
}
assertTrue(errMsg + testLog.toString(), passed);
}
Expand Down Expand Up @@ -1344,8 +1332,6 @@ public void completed(ProgressEvent event) {
*/
@Test
public void test_VisibilityWindowListener_eventSize() {
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);

shell.setSize(200,300);
AtomicBoolean childCompleted = new AtomicBoolean(false);
AtomicReference<Point> result = new AtomicReference<>(new Point(0,0));
Expand Down

0 comments on commit 7189709

Please sign in to comment.