Commit 097652a6 authored by Jeremy Roman's avatar Jeremy Roman Committed by Commit Bot

Portals/Android: Handle HTTP Basic auth triggered from a portal WebContents.

Since this pathway is triggered by tab helpers, they are now registered
in Chrome/Android much as they are already in Chrome/Desktop (by Browser).

A test is included which works mostly the same way as the desktop browser
test equivalent. Some plumbing was required to expose inner web contents to
Java, via JNI.

Bug: 1038584
Change-Id: I3127b0a392774c528f620220934283c4affe6643
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1986341Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarKevin McNee <mcnee@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#733019}
parent f21eba88
......@@ -15,22 +15,30 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.login.ChromeHttpAuthHandler;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.test.util.Coordinates;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.content_public.browser.test.util.TouchCommon;
import org.chromium.net.test.EmbeddedTestServer;
import java.util.List;
/**
* Tests for the chrome/ layer support of the HTML portal element.
*/
......@@ -343,4 +351,70 @@ public class PortalsTest {
WebContents contents = mActivityTestRule.getWebContents();
Assert.assertTrue(Coordinates.createFor(contents).getScrollYPixInt() > 0);
}
private static class AuthHandlerCallbackHelper
extends CallbackHelper implements Callback<ChromeHttpAuthHandler> {
private ChromeHttpAuthHandler mAuthHandler;
public ChromeHttpAuthHandler getAuthHandler() {
return mAuthHandler;
}
@Override
public void onResult(ChromeHttpAuthHandler authHandler) {
mAuthHandler = authHandler;
notifyCalled();
}
}
/**
* Tests that ChromeHttpAuthHandler is triggered within portals.
*
* This is the Android counterpart to PortalBrowserTest.HttpBasicAuthenticationInPortal.
*/
@Test
@MediumTest
@Feature({"Portals"})
public void testHttpBasicAuthenticationInPortal() throws Exception {
mActivityTestRule.startMainActivityWithURL(
mTestServer.getURL("/chrome/test/data/android/about.html"));
Tab tab = mActivityTestRule.getActivity().getActivityTab();
final WebContents contents = tab.getWebContents();
Assert.assertNotNull(contents);
// TODO(jbroman): It would be nicer to augment the underlying code to support waiting on
// promises, rather than needing to use domAutomationController here.
JavaScriptUtils.runJavascriptWithAsyncResult(contents,
"new Promise((resolve, reject) => {\n"
+ " let portal = document.createElement('portal');\n"
+ " portal.src = '/chrome/test/data/android/about.html?2';\n"
+ " portal.onload = () => resolve(true);\n"
+ " document.body.appendChild(portal);\n"
+ "}).then(() => domAutomationController.send(true));");
final WebContents portalContents = TestThreadUtils.runOnUiThreadBlocking(() -> {
List<? extends WebContents> innerContents = contents.getInnerWebContents();
Assert.assertEquals(1, innerContents.size());
return innerContents.get(0);
});
final AuthHandlerCallbackHelper helper = new AuthHandlerCallbackHelper();
TestThreadUtils.runOnUiThreadBlocking(
() -> ChromeHttpAuthHandler.setTestCreationCallback(helper));
try {
JavaScriptUtils.executeJavaScript(
portalContents, "location.href = '/auth-basic?realm=Aperture'");
helper.waitForFirst();
} finally {
TestThreadUtils.runOnUiThreadBlocking(
() -> ChromeHttpAuthHandler.setTestCreationCallback(null));
}
final ChromeHttpAuthHandler authHandler = helper.getAuthHandler();
Assert.assertNotNull(authHandler);
CriteriaHelper.pollUiThread(Criteria.equals(true, authHandler::isShowingAuthDialog));
ThreadUtils.runOnUiThread(() -> authHandler.proceed("basicuser", "secret"));
CriteriaHelper.pollUiThread(Criteria.equals("basicuser/secret", portalContents::getTitle));
}
}
......@@ -22,12 +22,14 @@
#include "chrome/browser/android/chrome_feature_list.h"
#include "chrome/browser/android/feature_utilities.h"
#include "chrome/browser/android/hung_renderer_infobar_delegate.h"
#include "chrome/browser/android/tab_android.h"
#include "chrome/browser/banners/app_banner_manager_android.h"
#include "chrome/browser/content_settings/sound_content_setting_observer.h"
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/media/protected_media_identifier_permission_context.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
......@@ -39,6 +41,7 @@
#include "chrome/browser/ui/android/infobars/framebust_block_infobar.h"
#include "chrome/browser/ui/android/sms/sms_infobar.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "chrome/browser/ui/autofill/chrome_autofill_client.h"
#include "chrome/browser/ui/blocked_content/popup_blocker.h"
#include "chrome/browser/ui/blocked_content/popup_tracker.h"
#include "chrome/browser/ui/browser_navigator_params.h"
......@@ -138,6 +141,23 @@ TabWebContentsDelegateAndroid::TabWebContentsDelegateAndroid(JNIEnv* env,
TabWebContentsDelegateAndroid::~TabWebContentsDelegateAndroid() = default;
void TabWebContentsDelegateAndroid::PortalWebContentsCreated(
content::WebContents* portal_contents) {
WebContentsDelegateAndroid::PortalWebContentsCreated(portal_contents);
// This is a subset of the tab helpers that would be attached by
// TabAndroid::AttachTabHelpers.
//
// TODO(jbroman): This doesn't adequately handle all of the tab helpers that
// might be wanted here, and there are also likely to be issues with tab
// helpers that are unprepared for portal activation to transition them.
autofill::ChromeAutofillClient::CreateForWebContents(portal_contents);
ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
portal_contents,
autofill::ChromeAutofillClient::FromWebContents(portal_contents));
InfoBarService::CreateForWebContents(portal_contents);
}
void TabWebContentsDelegateAndroid::RunFileChooser(
content::RenderFrameHost* render_frame_host,
std::unique_ptr<content::FileSelectListener> listener,
......
......@@ -41,6 +41,7 @@ class TabWebContentsDelegateAndroid
TabWebContentsDelegateAndroid(JNIEnv* env, jobject obj);
~TabWebContentsDelegateAndroid() override;
void PortalWebContentsCreated(content::WebContents* portal_contents) override;
void RunFileChooser(content::RenderFrameHost* render_frame_host,
std::unique_ptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) override;
......
......@@ -47,18 +47,21 @@ class LoginHandlerAndroid : public LoginHandler {
LoginModelData* login_model_data) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::WebContents* contents =
web_contents()->GetResponsibleWebContents();
// Get pointer to TabAndroid
CHECK(web_contents());
CHECK(contents);
ViewAndroidHelper* view_helper =
ViewAndroidHelper::FromWebContents(web_contents());
ViewAndroidHelper::FromWebContents(contents);
if (vr::VrTabHelper::IsUiSuppressedInVr(
web_contents(), vr::UiSuppressedElement::kHttpAuth)) {
contents, vr::UiSuppressedElement::kHttpAuth)) {
CancelAuth();
return;
}
TabAndroid* tab = TabAndroid::FromWebContents(web_contents());
TabAndroid* tab = TabAndroid::FromWebContents(contents);
// Notify WindowAndroid that HTTP authentication is required.
if (tab && view_helper->GetViewAndroid() &&
view_helper->GetViewAndroid()->GetWindowAndroid()) {
......
......@@ -353,6 +353,23 @@ ScopedJavaLocalRef<jobject> WebContentsAndroid::GetRenderWidgetHostView(
return rwhva->GetJavaObject();
}
ScopedJavaLocalRef<jobjectArray> WebContentsAndroid::GetInnerWebContents(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
std::vector<WebContents*> inner_web_contents =
web_contents_->GetInnerWebContents();
jclass clazz =
org_chromium_content_browser_webcontents_WebContentsImpl_clazz(env);
jobjectArray array =
env->NewObjectArray(inner_web_contents.size(), clazz, nullptr);
for (size_t i = 0; i < inner_web_contents.size(); i++) {
ScopedJavaLocalRef<jobject> contents_java =
inner_web_contents[i]->GetJavaWebContents();
env->SetObjectArrayElement(array, i, contents_java.obj());
}
return ScopedJavaLocalRef<jobjectArray>(env, array);
}
RenderWidgetHostViewAndroid*
WebContentsAndroid::GetRenderWidgetHostViewAndroid() {
RenderWidgetHostView* rwhv = NULL;
......
......@@ -250,6 +250,10 @@ class CONTENT_EXPORT WebContentsAndroid {
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
base::android::ScopedJavaLocalRef<jobjectArray> GetInnerWebContents(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid();
class DestructionObserver : public base::CheckedObserver {
......
......@@ -55,6 +55,8 @@ import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.WindowAndroid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
......@@ -389,6 +391,14 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate, Wi
return rwhvi;
}
@Override
public List<WebContentsImpl> getInnerWebContents() {
checkNotDestroyed();
WebContentsImpl[] innerWebContents = WebContentsImplJni.get().getInnerWebContents(
mNativeWebContentsAndroid, WebContentsImpl.this);
return Collections.unmodifiableList(Arrays.asList(innerWebContents));
}
@Override
public String getTitle() {
checkNotDestroyed();
......@@ -1024,6 +1034,8 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate, Wi
RenderFrameHost getFocusedFrame(long nativeWebContentsAndroid, WebContentsImpl caller);
RenderWidgetHostViewImpl getRenderWidgetHostView(
long nativeWebContentsAndroid, WebContentsImpl caller);
WebContentsImpl[] getInnerWebContents(
long nativeWebContentsAndroid, WebContentsImpl caller);
String getTitle(long nativeWebContentsAndroid, WebContentsImpl caller);
String getVisibleURL(long nativeWebContentsAndroid, WebContentsImpl caller);
String getEncoding(long nativeWebContentsAndroid, WebContentsImpl caller);
......
......@@ -17,6 +17,8 @@ import org.chromium.ui.base.EventForwarder;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.WindowAndroid;
import java.util.List;
/**
* The WebContents Java wrapper to allow communicating with the native WebContents object.
*
......@@ -152,6 +154,11 @@ public interface WebContents extends Parcelable {
@Nullable
RenderWidgetHostView getRenderWidgetHostView();
/**
* @return The WebContents that are nested within this one.
*/
List<? extends WebContents> getInnerWebContents();
/**
* @return The title for the current visible page.
*/
......
......@@ -26,6 +26,9 @@ import org.chromium.ui.base.EventForwarder;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.WindowAndroid;
import java.util.Collections;
import java.util.List;
/**
* Mock class for {@link WebContents}.
*/
......@@ -91,6 +94,11 @@ public class MockWebContents implements WebContents {
return null;
}
@Override
public List<? extends WebContents> getInnerWebContents() {
return Collections.emptyList();
}
@Override
public String getTitle() {
return null;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment