Skip to content

Commit

Permalink
Don't send the request to the flutter api for subframes. Also improve…
Browse files Browse the repository at this point in the history
… tests and comments
  • Loading branch information
petermnt committed Mar 21, 2024
1 parent e56e33d commit 7ffecb4
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ public void onReceivedError(
@Override
public boolean shouldOverrideUrlLoading(
@NonNull WebView view, @NonNull WebResourceRequest request) {
flutterApi.requestLoading(this, view, request, reply -> {});
if (!request.isForMainFrame()) {
// The client is only allowed to stop navigations that target the main frame because
// overridden URLs are passed to `loadUrl` and `loadUrl` cannot load a subframe.
return false;
}

// Since we cannot call loadUrl for a subframe, we currently only allow the delegate to stop
// navigations that target the main frame, if the request is not for the main frame
// we just return false to allow the navigation.
//
// For more details see: https://github.com/flutter/flutter/issues/25329#issuecomment-464863209
return request.isForMainFrame() && returnValueForShouldOverrideUrlLoading;
flutterApi.requestLoading(this, view, request, reply -> {});
return returnValueForShouldOverrideUrlLoading;
}

// Legacy codepath for < 24; newer versions use the variant above.
Expand Down Expand Up @@ -192,14 +192,14 @@ public void onReceivedError(
@Override
public boolean shouldOverrideUrlLoading(
@NonNull WebView view, @NonNull WebResourceRequest request) {
flutterApi.requestLoading(this, view, request, reply -> {});
if (!request.isForMainFrame()) {
// The client is only allowed to stop navigations that target the main frame because
// overridden URLs are passed to `loadUrl` and `loadUrl` cannot load a subframe.
return false;
}

// Since we cannot call loadUrl for a subframe, we currently only allow the delegate to stop
// navigations that target the main frame, if the request is not for the main frame
// we just return false to allow the navigation.
//
// For more details see: https://github.com/flutter/flutter/issues/25329#issuecomment-464863209
return request.isForMainFrame() && returnValueForShouldOverrideUrlLoading;
flutterApi.requestLoading(this, view, request, reply -> {});
return returnValueForShouldOverrideUrlLoading;
}

// Legacy codepath for < Lollipop; newer versions use the variant above.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,210 @@

package io.flutter.plugins.webviewflutter;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

import android.net.Uri;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCompatImpl;
import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCreator;
import java.util.HashMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

public class WebViewClientCompatImplTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

@Mock public WebViewClientFlutterApiImpl mockFlutterApi;

@Mock public WebView mockWebView;

@Mock public WebViewClientCompatImpl mockWebViewClient;

InstanceManager instanceManager;
WebViewClientHostApiImpl hostApiImpl;
WebViewClientCompatImpl webViewClient;

@Before
public void setUp() {
instanceManager = InstanceManager.create(identifier -> {});

final WebViewClientCreator webViewClientCreator =
new WebViewClientCreator() {
@Override
@NonNull
public WebViewClient createWebViewClient(
@NonNull WebViewClientFlutterApiImpl flutterApi) {
webViewClient = new WebViewClientCompatImpl(flutterApi);
return webViewClient;
}
};

hostApiImpl =
new WebViewClientHostApiImpl(instanceManager, webViewClientCreator, mockFlutterApi);
hostApiImpl.create(1L);
}

@After
public void tearDown() {
instanceManager.stopFinalizationListener();
}

@Test
public void onPageStarted() {
webViewClient.onPageStarted(mockWebView, "https://www.google.com", null);
verify(mockFlutterApi)
.onPageStarted(eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), any());
}

@Test
public void onReceivedError() {
webViewClient.onReceivedError(mockWebView, 32, "description", "https://www.google.com");
verify(mockFlutterApi)
.onReceivedError(
eq(webViewClient),
eq(mockWebView),
eq(32L),
eq("description"),
eq("https://www.google.com"),
any());
}

@Test
public void urlLoading() {
webViewClient.shouldOverrideUrlLoading(mockWebView, "https://www.google.com");
verify(mockFlutterApi)
.urlLoading(eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), any());
}

@Test
public void urlLoadingForMainFrame() {
webViewClient.setReturnValueForShouldOverrideUrlLoading(false);

final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.isForMainFrame()).thenReturn(true);

assertFalse(webViewClient.shouldOverrideUrlLoading(mockWebView, mockRequest));
verify(mockFlutterApi)
.requestLoading(eq(webViewClient), eq(mockWebView), eq(mockRequest), any());
}

public class WebViewClientCompatImplTest extends WebViewClientTest {
@Test
public void urlLoadingForMainFrameWithOverride() {
webViewClient.setReturnValueForShouldOverrideUrlLoading(true);

@Override
WebViewClient createInstance(WebViewClientFlutterApiImpl flutterApi) {
return new WebViewClientHostApiImpl.WebViewClientCompatImpl(flutterApi);
final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.isForMainFrame()).thenReturn(true);

assertTrue(webViewClient.shouldOverrideUrlLoading(mockWebView, mockRequest));
verify(mockFlutterApi)
.requestLoading(eq(webViewClient), eq(mockWebView), eq(mockRequest), any());
}

@Test
public void urlLoadingNotForMainFrame() {
webViewClient.setReturnValueForShouldOverrideUrlLoading(false);

final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.isForMainFrame()).thenReturn(false);

assertFalse(webViewClient.shouldOverrideUrlLoading(mockWebView, mockRequest));
verifyNoInteractions(mockFlutterApi);
}

@Test
public void urlLoadingNotForMainFrameWithOverride() {
webViewClient.setReturnValueForShouldOverrideUrlLoading(true);

final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.isForMainFrame()).thenReturn(false);

assertFalse(webViewClient.shouldOverrideUrlLoading(mockWebView, mockRequest));
verifyNoInteractions(mockFlutterApi);
}

@Test
public void convertWebResourceRequestWithNullHeaders() {
final Uri mockUri = mock(Uri.class);
when(mockUri.toString()).thenReturn("");

final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.getMethod()).thenReturn("method");
when(mockRequest.getUrl()).thenReturn(mockUri);
when(mockRequest.isForMainFrame()).thenReturn(true);
when(mockRequest.getRequestHeaders()).thenReturn(null);

final GeneratedAndroidWebView.WebResourceRequestData data =
WebViewClientFlutterApiImpl.createWebResourceRequestData(mockRequest);
assertEquals(data.getRequestHeaders(), new HashMap<String, String>());
}

@Test
public void setReturnValueForShouldOverrideUrlLoading() {
final WebViewClientHostApiImpl webViewClientHostApi =
new WebViewClientHostApiImpl(
instanceManager,
new WebViewClientCreator() {
@NonNull
@Override
public WebViewClient createWebViewClient(
@NonNull WebViewClientFlutterApiImpl flutterApi) {
return mockWebViewClient;
}
},
mockFlutterApi);

instanceManager.addDartCreatedInstance(mockWebViewClient, 2);
webViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading(2L, false);

verify(mockWebViewClient).setReturnValueForShouldOverrideUrlLoading(false);
}

@Override
void setReturnValueForShouldOverrideUrlLoading(WebViewClient client, boolean value) {
((WebViewClientHostApiImpl.WebViewClientCompatImpl) client)
.setReturnValueForShouldOverrideUrlLoading(value);
@Test
public void doUpdateVisitedHistory() {
webViewClient.doUpdateVisitedHistory(mockWebView, "https://www.google.com", true);
verify(mockFlutterApi)
.doUpdateVisitedHistory(
eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), eq(true), any());
}

@Test
public void onReceivedHttpError() {
final Uri mockUri = mock(Uri.class);
when(mockUri.toString()).thenReturn("");

final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.getMethod()).thenReturn("method");
when(mockRequest.getUrl()).thenReturn(mockUri);
when(mockRequest.isForMainFrame()).thenReturn(true);
when(mockRequest.getRequestHeaders()).thenReturn(null);

final WebResourceResponse mockResponse = mock(WebResourceResponse.class);
when(mockResponse.getStatusCode()).thenReturn(404);

webViewClient.onReceivedHttpError(mockWebView, mockRequest, mockResponse);
verify(mockFlutterApi)
.onReceivedHttpError(
eq(webViewClient),
eq(mockWebView),
any(WebResourceRequest.class),
any(WebResourceResponse.class),
any());
}
}
Loading

0 comments on commit 7ffecb4

Please sign in to comment.