Commit 2e4bec79 authored by Anastasiia N's avatar Anastasiia N Committed by Commit Bot

Use Polymer in inline login flow

- Migrate inline login flow to use Polymer.
- Remove unused method: "handleOAuth2TokenFailure"
- Remove "show-back-button" call, and use <if expr="chromeos"> to show
the button on ChromeOS only.

Bug: 1096007
Test: browser_tests --gtest_filter="*InlineLoginBrowserTest*"
Change-Id: If146b080bb0fef65f8e931ac35809a8dfa8aa66b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2279795Reviewed-by: default avatarYusuf Sengul <yusufsn@google.com>
Reviewed-by: default avatarKush Sinha <sinhak@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarJames MacLean <wjmaclean@chromium.org>
Reviewed-by: default avatarRebekah Potter <rbpotter@chromium.org>
Commit-Queue: Anastasiia N <anastasiian@chromium.org>
Cr-Commit-Position: refs/heads/master@{#791531}
parent 3f28e399
......@@ -5851,6 +5851,7 @@ grit("resources") {
deps += [
"//chrome/browser/resources/discards:discards_resources_gen",
"//chrome/browser/resources/gaia_auth_host:modulize",
"//chrome/browser/resources/inline_login:web_components",
"//chrome/browser/resources/management:web_components",
"//chrome/browser/resources/signin:web_components",
"//chrome/browser/ui/webui/discards:mojo_bindings_js",
......
......@@ -4088,7 +4088,8 @@ class ChromeSignInWebViewTest : public WebViewTest {
"var count = 10;"
"var interval;"
"interval = setInterval(function(){"
" if (document.getElementsByTagName('webview').length) {"
" if (document.querySelector('inline-login-app').shadowRoot"
" .querySelector('webview')) {"
" document.title = 'success';"
" console.log('FOUND webview');"
" clearInterval(interval);"
......
......@@ -132,17 +132,15 @@
<if expr="not is_android">
<!-- Page not available for guest. -->
<include name="IDR_PAGE_NOT_AVAILABLE_FOR_GUEST_APP_HTML" file="resources\page_not_available_for_guest\app.html" type="BINDATA" />
<include name="IDR_INLINE_LOGIN_HTML" file="resources\inline_login\inline_login.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_INLINE_LOGIN_APP_JS" file="${root_gen_dir}\chrome\browser\resources\inline_login\inline_login_app.js" use_base_dir="false" type ="BINDATA" preprocess="true" />
<include name="IDR_INLINE_LOGIN_BROWSER_PROXY_JS" file="resources\inline_login\inline_login_browser_proxy.js" type ="BINDATA"/>
</if>
<include name="IDR_CHROME_URLS_DISABLED_PAGE_HTML" file="resources\chrome_urls_disabled_page\app.html" type="BINDATA" />
<include name="IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST" file="resources\identity_scope_approval_dialog\manifest.json" type="BINDATA" />
<include name="IDR_INLINE_LOGIN_HTML" file="resources\inline_login\inline_login.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" preprocess="true" />
<include name="IDR_INLINE_LOGIN_CSS" file="resources\inline_login\inline_login.css" flattenhtml="true" type="BINDATA" preprocess="true" />
<include name="IDR_INLINE_LOGIN_JS" file="resources\inline_login\inline_login.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_GAIA_AUTH_AUTHENTICATOR_JS" file="resources\gaia_auth_host\authenticator.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_INSPECT_CSS" file="resources\inspect\inspect.css" flattenhtml="true" type="BINDATA" />
<include name="IDR_INSPECT_HTML" file="resources\inspect\inspect.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_INSPECT_JS" file="resources\inspect\inspect.js" flattenhtml="true" type="BINDATA" />
......
......@@ -32,6 +32,7 @@ if (enable_js_type_check) {
"downloads:closure_compile",
"gaia_auth_host:closure_compile",
"history:closure_compile",
"inline_login:closure_compile",
"local_ntp:closure_compile",
"local_state:closure_compile",
"management:closure_compile",
......
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
import("//tools/polymer/html_to_js.gni")
js_type_check("closure_compile") {
is_polymer3 = true
closure_flags =
default_closure_args + [
"js_module_root=../../chrome/browser/resources/gaia_auth_host/",
"js_module_root=./gen/chrome/browser/resources/gaia_auth_host/",
]
deps = [
":inline_login_app",
":inline_login_browser_proxy",
]
}
js_library("inline_login_app") {
deps = [
":inline_login_browser_proxy",
"//chrome/browser/resources/gaia_auth_host:authenticator.m",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
"//ui/webui/resources/js:assert.m",
"//ui/webui/resources/js:web_ui_listener_behavior.m",
]
externs_list = [ "$externs_path/webview_tag.js" ]
}
js_library("inline_login_browser_proxy") {
deps = [
"//chrome/browser/resources/gaia_auth_host:authenticator.m",
"//ui/webui/resources/js:cr.m",
]
externs_list = [ "$externs_path/chrome_send.js" ]
}
html_to_js("web_components") {
js_files = [ "inline_login_app.js" ]
}
/* Copyright 2013 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
html,
body,
#contents,
#signin-frame {
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
width: 100%;
}
#signin-frame,
#spinner-container {
background-color: var(--cr-card-background-color);
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
#spinner-container {
align-items: center;
display: flex;
justify-content: center;
}
#contents:not(.loading) #spinner-container {
display: none;
}
#navigation-button {
background: white;
bottom: 32px;
left: 32px;
padding-inline-start: 10px;
position: absolute;
visibility: hidden;
}
:host-context([dir='rtl']) #navigation-button {
left: auto;
right: 32px;
}
#navigation-button.enabled {
visibility: visible;
}
/* On Chrome OS, we need to shift the webview a bit to accommodate the Back
* button at the bottom.
*/
<if expr="chromeos">
#signin-frame {
top: -35px; /*dialog top border size*/
}
#contents,
#signin-frame {
height: calc(100% - 96px);
}
@media (max-width: 767px) {
#contents,
#signin-frame {
height: calc(100% - 80px);
}
#navigation-button {
bottom: 24px;
left: 24px;
}
:host-context([dir='rtl']) #navigation-button {
left: auto;
right: 24px;
}
}
</if>
<!doctype html>
<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<title>$i18n{title}</title>
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="stylesheet" href="chrome://resources/css/spinner.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
<link rel="stylesheet" href="chrome://chrome-signin/inline_login.css">
<script src="chrome://resources/js/cr.js"></script>
<script src="chrome://resources/js/cr/event_target.js"></script>
<script src="chrome://resources/js/load_time_data.js"></script>
<script src="chrome://resources/js/util.js"></script>
<script src="chrome://chrome-signin/gaia_auth_host.js"></script>
<script src="chrome://chrome-signin/inline_login.js"></script>
<script src="chrome://chrome-signin/strings.js"></script>
<meta charset="utf-8">
<title>$i18n{title}</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
</head>
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
<body>
<div id="contents" class="loading">
<webview id="signin-frame" name="signin-frame" allowscaling></webview>
<div id="spinner-container">
<div class="spinner"></div>
</div>
</div>
<cr-button
id="navigation-button"
aria-label="$i18n{accessibleBackButtonLabel}">
<iron-icon id="navigation-icon"></iron-icon>
$i18n{accessibleBackButtonLabel}
</cr-button>
<inline-login-app></inline-login-app>
<script type="module" src="inline_login_app.js"></script>
</body>
</html>
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Inline login UI.
*/
cr.define('inline.login', function() {
'use strict';
/**
* The auth extension host instance.
* @type {cr.login.Authenticator}
*/
let authExtHost;
/**
* Whether the auth ready event has been fired, for testing purpose.
*/
let authReadyFired;
/**
* Whether the login UI is loaded for signing in primary account.
*/
let isLoginPrimaryAccount;
function onResize(e) {
chrome.send('switchToFullTab', [e.detail]);
}
function onAuthReady(e) {
$('contents').classList.toggle('loading', false);
authReadyFired = true;
if (isLoginPrimaryAccount) {
chrome.send('metricsHandler:recordAction', ['Signin_SigninPage_Shown']);
}
chrome.send('authExtensionReady');
}
function onDropLink(e) {
// Navigate to the dropped link.
window.location.href = e.detail;
}
function onNewWindow(e) {
window.open(e.detail.targetUrl, '_blank');
e.detail.window.discard();
}
function onAuthCompleted(e) {
completeLogin(e.detail);
}
function completeLogin(credentials) {
chrome.send('completeLogin', [credentials]);
$('contents').classList.toggle('loading', true);
}
function onShowIncognito() {
chrome.send('showIncognito');
}
/**
* Initialize the UI.
*/
function initialize() {
$('navigation-button').addEventListener('click', navigateBackInWebview);
cr.addWebUIListener('show-back-button', showBackButton);
cr.addWebUIListener('load-auth-extension', loadAuthExtension);
cr.addWebUIListener('close-dialog', closeDialog);
cr.addWebUIListener('send-lst-fetch-results', sendLSTFetchResults);
authExtHost = new cr.login.Authenticator('signin-frame');
authExtHost.addEventListener('dropLink', onDropLink);
authExtHost.addEventListener('ready', onAuthReady);
authExtHost.addEventListener('newWindow', onNewWindow);
authExtHost.addEventListener('resize', onResize);
authExtHost.addEventListener('authCompleted', onAuthCompleted);
authExtHost.addEventListener('showIncognito', onShowIncognito);
chrome.send('initialize');
}
/**
* Loads auth extension.
* @param {Object} data Parameters for auth extension.
*/
function loadAuthExtension(data) {
// TODO(rogerta): in when using webview, the |completeLogin| argument
// is ignored. See addEventListener() call above.
authExtHost.load(data.authMode, data, completeLogin);
$('contents')
.classList.toggle(
'loading',
data.authMode != cr.login.Authenticator.AuthMode.DESKTOP ||
data.constrained == '1');
isLoginPrimaryAccount = data.isLoginPrimaryAccount;
}
/**
* Closes the inline login dialog.
*/
function closeDialog() {
chrome.send('dialogClose', ['']);
}
/**
* Sends a message 'lstFetchResults'. This is a specific message sent when
* the inline signin is loaded with reason REASON_FETCH_LST_ONLY. Handlers of
* this message would expect a single argument a base::Dictionary value that
* contains the values fetched from the gaia sign in endpoint.
* @param {string} arg The string representation of the json data returned by
* the sign in dialog after it has finished the sign in process.
*/
function sendLSTFetchResults(arg) {
chrome.send('lstFetchResults', [arg]);
}
/**
* Invoked when failed to get oauth2 refresh token.
*/
function handleOAuth2TokenFailure() {
// TODO(xiyuan): Show an error UI.
authExtHost.reload();
$('contents').classList.toggle('loading', true);
}
/**
* Returns the auth host instance, for testing purpose.
*/
function getAuthExtHost() {
return authExtHost;
}
/**
* Returns whether the auth UI is ready, for testing purpose.
*/
function isAuthReady() {
return authReadyFired;
}
function showBackButton() {
$('navigation-icon').icon =
isRTL() ? 'cr:chevron-right' : 'cr:chevron-left';
$('navigation-button').classList.add('enabled');
}
function navigateBackInWebview() {
if ($('signin-frame').canGoBack()) {
$('signin-frame').back();
$('signin-frame').focus();
} else {
closeDialog();
}
}
return {
closeDialog: closeDialog,
getAuthExtHost: getAuthExtHost,
handleOAuth2TokenFailure: handleOAuth2TokenFailure,
initialize: initialize,
isAuthReady: isAuthReady,
loadAuthExtension: loadAuthExtension,
showBackButton: showBackButton,
};
});
document.addEventListener('DOMContentLoaded', inline.login.initialize);
<style>
:host {
--dialog-top-border-size: 35px;
display: flex;
flex-direction: column;
height: 100%;
}
.signin-frame {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}
.container {
align-items: center;
display: flex;
flex-grow: 1;
justify-content: center;
}
<if expr="chromeos">
.container {
margin-top: calc(-1 * var(--dialog-top-border-size));
}
</if>
.signin-frame {
background-color: white;
overflow: hidden;
}
.buttons {
padding: 0 32px 32px;
}
paper-spinner-lite {
--spinner-size: 24px;
display: none;
height: var(--spinner-size);
width: var(--spinner-size);
}
paper-spinner-lite[active] {
display: inline-block;
}
</style>
<div class="container">
<webview id="signinFrame" name="signin-frame" class="signin-frame"
hidden$="[[loading_]]" allowscaling></webview>
<paper-spinner-lite active="[[loading_]]">
</paper-spinner-lite>
</div>
<if expr="chromeos">
<div class="buttons">
<cr-button class="back-button"
aria-label="$i18n{accessibleBackButtonLabel}"
on-tap="handleGoBack_">
<iron-icon icon="[[getBackButtonIcon_()]]"></iron-icon>
$i18n{accessibleBackButtonLabel}
</cr-button>
</div>
</if>
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/icons.m.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import {isRTL} from 'chrome://resources/js/util.m.js';
import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {AuthCompletedCredentials, Authenticator, AuthParams} from '../gaia_auth_host/authenticator.m.js';
import {InlineLoginBrowserProxy, InlineLoginBrowserProxyImpl} from './inline_login_browser_proxy.js';
/**
* @fileoverview Inline login WebUI in various signin flows for ChromeOS and
* Chrome desktop (Windows only).
*/
Polymer({
is: 'inline-login-app',
_template: html`{__html_template__}`,
behaviors: [WebUIListenerBehavior],
properties: {
/**
* Indicates whether the page is loading.
* @private {boolean}
*/
loading_: {
type: Boolean,
value: true,
},
},
/**
* The auth extension host instance.
* @private {?Authenticator}
*/
authExtHost_: null,
/** @private {?InlineLoginBrowserProxy} */
browserProxy_: null,
/**
* Whether the login UI is loaded for signing in primary account.
* @private {boolean}
*/
isLoginPrimaryAccount_: false,
/** @override */
created() {
this.browserProxy_ = InlineLoginBrowserProxyImpl.getInstance();
},
/** @override */
ready() {
this.authExtHost_ = new Authenticator(
/** @type {!WebView} */ (this.$.signinFrame));
this.addAuthExtHostListeners_();
this.browserProxy_.initialize();
},
/** @override */
attached() {
this.addWebUIListener(
'load-auth-extension', data => this.loadAuthExtension_(data));
this.addWebUIListener(
'send-lst-fetch-results', arg => this.sendLSTFetchResults_(arg));
this.addWebUIListener('close-dialog', () => this.closeDialog_());
},
/** @private */
addAuthExtHostListeners_() {
this.authExtHost_.addEventListener(
'dropLink',
e => this.onDropLink_(
/** @type {!CustomEvent<string>} */ (e)));
this.authExtHost_.addEventListener(
'newWindow',
e => this.onNewWindow_(
/** @type {!CustomEvent<NewWindowProperties>} */ (e)));
this.authExtHost_.addEventListener('ready', () => this.onAuthReady_());
this.authExtHost_.addEventListener(
'resize',
e => this.onResize_(
/** @type {!CustomEvent<string>} */ (e)));
this.authExtHost_.addEventListener(
'authCompleted',
e => this.onAuthCompleted_(
/** @type {!CustomEvent<!AuthCompletedCredentials>} */ (e)));
this.authExtHost_.addEventListener(
'showIncognito', () => this.onShowIncognito_());
},
/**
* @param {!CustomEvent<string>} e
* @private
*/
onDropLink_(e) {
// Navigate to the dropped link.
window.location.href = e.detail;
},
/**
* @param {!CustomEvent<NewWindowProperties>} e
* @private
*/
onNewWindow_(e) {
window.open(e.detail.targetUrl, '_blank');
e.detail.window.discard();
},
/** @private */
onAuthReady_() {
this.loading_ = false;
if (this.isLoginPrimaryAccount_) {
this.browserProxy_.recordAction('Signin_SigninPage_Shown');
}
this.browserProxy_.authExtensionReady();
},
/**
* @param {!CustomEvent<string>} e
* @private
*/
onResize_(e) {
this.browserProxy_.switchToFullTab(e.detail);
},
/**
* @param {!CustomEvent<!AuthCompletedCredentials>} e
* @private
*/
onAuthCompleted_(e) {
this.loading_ = true;
this.browserProxy_.completeLogin(e.detail);
},
/** @private */
onShowIncognito_() {
this.browserProxy_.showIncognito();
},
/**
* Loads auth extension.
* @param {!AuthParams} data Parameters for auth extension.
* @private
*/
loadAuthExtension_(data) {
this.authExtHost_.load(data.authMode, data);
this.loading_ = true;
this.isLoginPrimaryAccount_ = data.isLoginPrimaryAccount;
},
/**
* Sends a message 'lstFetchResults'. This is a specific message sent when
* the inline signin is loaded with reason REASON_FETCH_LST_ONLY. Handlers of
* this message would expect a single argument a base::Dictionary value that
* contains the values fetched from the gaia sign in endpoint.
* @param {string} arg The string representation of the json data returned by
* the sign in dialog after it has finished the sign in process.
* @private
*/
sendLSTFetchResults_(arg) {
this.browserProxy_.lstFetchResults(arg);
},
/**
* Closes the login dialog.
* @private
*/
closeDialog_() {
this.browserProxy_.dialogClose();
},
/**
* Navigates back in the web view if possible. Otherwise closes the dialog.
* @private
*/
handleGoBack_() {
if (this.$.signinFrame.canGoBack()) {
this.$.signinFrame.back();
this.$.signinFrame.focus();
} else {
this.closeDialog_();
}
},
/**
* @return {string}
* @private
*/
getBackButtonIcon_() {
return isRTL() ? 'cr:chevron-right' : 'cr:chevron-left';
},
/** @param {!Authenticator} authExtHost */
setAuthExtHostForTest(authExtHost) {
this.authExtHost_ = authExtHost;
this.addAuthExtHostListeners_();
},
});
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
import {AuthCompletedCredentials} from '../gaia_auth_host/authenticator.m.js';
/** @interface */
export class InlineLoginBrowserProxy {
/** Send 'initialize' message to prepare for starting auth. */
initialize() {}
/**
* Send 'authExtensionReady' message to handle tasks after auth extension
* loads.
*/
authExtensionReady() {}
/**
* Send 'switchToFullTab' message to switch the UI from a constrained dialog
* to a full tab.
* @param {!string} url
*/
switchToFullTab(url) {}
/**
* Send 'completeLogin' message to complete login.
* @param {!AuthCompletedCredentials} credentials
*/
completeLogin(credentials) {}
/**
* Send 'lstFetchResults' message.
* @param {string} arg The string representation of the json data returned by
* the sign in dialog after it has finished the sign in process.
*/
lstFetchResults(arg) {}
/**
* Send 'metricsHandler:recordAction' message.
* @param {string} metricsAction The action to be recorded.
*/
recordAction(metricsAction) {}
/** Send 'showIncognito' message to the handler */
showIncognito() {}
/** Send 'dialogClose' message to close the login dialog. */
dialogClose() {}
}
/** @implements {InlineLoginBrowserProxy} */
export class InlineLoginBrowserProxyImpl {
/** @override */
initialize() {
chrome.send('initialize');
}
/** @override */
authExtensionReady() {
chrome.send('authExtensionReady');
}
/** @override */
switchToFullTab(url) {
chrome.send('switchToFullTab', [url]);
}
/** @override */
completeLogin(credentials) {
chrome.send('completeLogin', [credentials]);
}
/** @override */
lstFetchResults(arg) {
chrome.send('lstFetchResults', [arg]);
}
/** @override */
recordAction(metricsAction) {
chrome.send('metricsHandler:recordAction', [metricsAction]);
}
/** @override */
showIncognito() {
chrome.send('showIncognito');
}
/** @override */
dialogClose() {
chrome.send('dialogClose');
}
}
addSingletonGetter(InlineLoginBrowserProxyImpl);
......@@ -37,7 +37,8 @@ namespace {
bool g_enable_gcpw_signin_during_tests = false;
#endif // BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
// This message must match the one sent in inline_login.js: sendLSTFetchResults.
// This message must match the one sent in inline_login_app.js:
// sendLSTFetchResults.
constexpr char kLSTFetchResultsMessage[] = "lstFetchResults";
void WriteResultToHandle(const base::Value& result) {
......
......@@ -271,12 +271,6 @@ void InlineLoginHandlerChromeOS::SetExtraInitParams(
params.SetBoolean("ignoreCrOSIdpSetting", true);
}
void InlineLoginHandlerChromeOS::HandleAuthExtensionReadyMessage(
const base::ListValue* args) {
AllowJavascript();
FireWebUIListener("show-back-button");
}
void InlineLoginHandlerChromeOS::CompleteLogin(const std::string& email,
const std::string& password,
const std::string& gaia_id,
......@@ -339,7 +333,6 @@ void InlineLoginHandlerChromeOS::CompleteLogin(const std::string& email,
void InlineLoginHandlerChromeOS::HandleDialogClose(
const base::ListValue* args) {
AllowJavascript();
close_dialog_closure_.Run();
}
......
......@@ -23,7 +23,6 @@ class InlineLoginHandlerChromeOS : public InlineLoginHandler {
// InlineLoginHandler overrides.
void RegisterMessages() override;
void SetExtraInitParams(base::DictionaryValue& params) override;
void HandleAuthExtensionReadyMessage(const base::ListValue* args) override;
void CompleteLogin(const std::string& email,
const std::string& password,
const std::string& gaia_id,
......
......@@ -22,20 +22,20 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/gaia_auth_host_resources.h"
#include "chrome/grit/gaia_auth_host_resources_map.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/content_switches.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/resources/grit/webui_resources.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/webui/chromeos/edu_account_login_handler_chromeos.h"
#include "chrome/browser/ui/webui/signin/inline_login_handler_chromeos.h"
#include "chrome/grit/gaia_auth_host_resources.h"
#include "chrome/grit/gaia_auth_host_resources_map.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/resources/grit/webui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#else
#include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
......@@ -43,10 +43,10 @@
namespace {
#if defined(OS_CHROMEOS)
constexpr char kResourcesGeneratedPath[] =
"@out_folder@/gen/chrome/browser/resources/";
#if defined(OS_CHROMEOS)
void AddEduStrings(content::WebUIDataSource* source,
const base::string16& username) {
source->AddLocalizedString("okButton", IDS_APP_OK);
......@@ -94,11 +94,10 @@ void AddEduStrings(content::WebUIDataSource* source,
content::WebUIDataSource* CreateWebUIDataSource() {
content::WebUIDataSource* source =
content::WebUIDataSource::Create(chrome::kChromeUIChromeSigninHost);
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ObjectSrc, "object-src chrome:;");
source->UseStringsJs();
source->SetDefaultResource(IDR_INLINE_LOGIN_HTML);
webui::SetupWebUIDataSource(
source,
base::make_span(kGaiaAuthHostResources, kGaiaAuthHostResourcesSize),
kResourcesGeneratedPath, IDR_INLINE_LOGIN_HTML);
// Only add a filter when runing as test.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
......@@ -110,9 +109,8 @@ content::WebUIDataSource* CreateWebUIDataSource() {
}
static constexpr webui::ResourcePath kResources[] = {
{"inline_login.css", IDR_INLINE_LOGIN_CSS},
{"inline_login.js", IDR_INLINE_LOGIN_JS},
{"gaia_auth_host.js", IDR_GAIA_AUTH_AUTHENTICATOR_JS},
{"inline_login_app.js", IDR_INLINE_LOGIN_APP_JS},
{"inline_login_browser_proxy.js", IDR_INLINE_LOGIN_BROWSER_PROXY_JS},
#if defined(OS_CHROMEOS)
{"error_screen.js", IDR_ACCOUNT_MANAGER_COMPONENTS_ERROR_SCREEN_JS},
{"edu", IDR_EDU_LOGIN_EDU_LOGIN_HTML},
......@@ -130,8 +128,6 @@ content::WebUIDataSource* CreateWebUIDataSource() {
{"edu_login_parent_info.js", IDR_EDU_LOGIN_EDU_LOGIN_PARENT_INFO_JS},
{"edu_login_signin.js", IDR_EDU_LOGIN_EDU_LOGIN_SIGNIN_JS},
{"edu_login_error.js", IDR_EDU_LOGIN_EDU_LOGIN_ERROR_JS},
{"test_loader.js", IDR_WEBUI_JS_TEST_LOADER},
{"test_loader.html", IDR_WEBUI_HTML_TEST_LOADER},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
{"googleg.svg", IDR_ACCOUNT_MANAGER_WELCOME_GOOGLE_LOGO_SVG},
#endif
......@@ -140,16 +136,6 @@ content::WebUIDataSource* CreateWebUIDataSource() {
};
webui::AddResourcePathsBulk(source, kResources);
#if defined(OS_CHROMEOS)
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ScriptSrc,
"script-src chrome://resources chrome://test 'self';");
webui::SetupWebUIDataSource(
source,
base::make_span(kGaiaAuthHostResources, kGaiaAuthHostResourcesSize),
kResourcesGeneratedPath, IDR_INLINE_LOGIN_HTML);
#endif // defined(OS_CHROMEOS)
source->AddLocalizedString("title", IDS_CHROME_SIGNIN_TITLE);
source->AddLocalizedString(
"accessibleCloseButtonLabel", IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON);
......
......@@ -366,15 +366,14 @@ void WaitUntilUIReady(Browser* browser) {
std::string message;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
browser->tab_strip_model()->GetActiveWebContents(),
"if (!inline.login.getAuthExtHost())"
" inline.login.initialize();"
"var handler = function() {"
" window.domAutomationController.send('ready');"
"};"
"if (inline.login.isAuthReady())"
"if (!document.querySelector('inline-login-app').loading_)"
" handler();"
"else"
" inline.login.getAuthExtHost().addEventListener('ready', handler);",
" document.querySelector('inline-login-app').authExtHost_"
" .addEventListener('ready', handler);",
&message));
ASSERT_EQ("ready", message);
}
......
......@@ -122,6 +122,10 @@ if (include_js_tests) {
"//chrome/browser/resources/signin/sync_confirmation/sync_confirmation_browser_proxy.js",
]
if (is_chromeos || is_win) {
sources += [ "inline_login/inline_login_browsertest.js" ]
}
if (is_chromeos) {
sources += [
"../../../browser/resources/chromeos/login/security_token_pin_browsertest.js",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for the Inline login flow ('Add account' flow) on
* ChromeOS and desktop Chrome.
*/
// Polymer BrowserTest fixture.
GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
GEN('#include "content/public/test/browser_test.h"');
// eslint-disable-next-line no-var
var InlineLoginBrowserTest = class extends PolymerTest {
/** @override */
get browsePreload() {
// See Reason enum in components/signin/public/base/signin_metrics.h.
return 'chrome://chrome-signin/test_loader.html?module=inline_login/inline_login_test.js&reason=5';
}
get suiteName() {
return inline_login_test.suiteName;
}
/** @override */
get extraLibraries() {
return [
'//third_party/mocha/mocha.js',
'//chrome/test/data/webui/mocha_adapter.js',
];
}
/** @param {string} testName The name of the test to run. */
runMochaTest(testName) {
runMochaTest(this.suiteName, testName);
}
};
TEST_F('InlineLoginBrowserTest', 'Initialize', function() {
this.runMochaTest(inline_login_test.TestNames.Initialize);
});
TEST_F('InlineLoginBrowserTest', 'WebUICallbacks', function() {
this.runMochaTest(inline_login_test.TestNames.WebUICallbacks);
});
TEST_F('InlineLoginBrowserTest', 'AuthExtHostCallbacks', function() {
this.runMochaTest(inline_login_test.TestNames.AuthExtHostCallbacks);
});
TEST_F('InlineLoginBrowserTest', 'BackButton', function() {
this.runMochaTest(inline_login_test.TestNames.BackButton);
});
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://chrome-signin/inline_login_app.js';
import {InlineLoginBrowserProxyImpl} from 'chrome://chrome-signin/inline_login_browser_proxy.js';
import {assert} from 'chrome://resources/js/assert.m.js';
import {isChromeOS} from 'chrome://resources/js/cr.m.js';
import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
import {TestAuthenticator, TestInlineLoginBrowserProxy} from './inline_login_test_util.js';
window.inline_login_test = {};
inline_login_test.suiteName = 'InlineLoginTest';
/** @enum {string} */
inline_login_test.TestNames = {
Initialize: 'Initialize',
WebUICallbacks: 'WebUICallbacks',
AuthExtHostCallbacks: 'AuthExtHostCallbacks',
BackButton: 'BackButton'
};
suite(inline_login_test.suiteName, () => {
/** @type {InlineLoginAppElement} */
let inlineLoginComponent;
/** @type {TestInlineLoginBrowserProxy} */
let testBrowserProxy;
/** @type {TestAuthenticator} */
let testAuthenticator;
setup(() => {
testBrowserProxy = new TestInlineLoginBrowserProxy();
InlineLoginBrowserProxyImpl.instance_ = testBrowserProxy;
document.body.innerHTML = '';
inlineLoginComponent = document.createElement('inline-login-app');
document.body.appendChild(inlineLoginComponent);
testAuthenticator = new TestAuthenticator();
inlineLoginComponent.setAuthExtHostForTest(testAuthenticator);
flush();
});
test(assert(inline_login_test.TestNames.Initialize), () => {
// The 'loading' spinner should be shown and 'initialize' should be called
// on startup.
assertTrue(inlineLoginComponent.$$('paper-spinner-lite').active);
assertTrue(inlineLoginComponent.$$('#signinFrame').hidden);
assertEquals(1, testBrowserProxy.getCallCount('initialize'));
});
test(assert(inline_login_test.TestNames.WebUICallbacks), () => {
const fakeAuthExtensionData = {
hl: 'hl',
gaiaUrl: 'gaiaUrl',
authMode: 1,
email: 'example@gmail.com',
};
webUIListenerCallback('load-auth-extension', fakeAuthExtensionData);
assertEquals(1, testAuthenticator.loadCalls);
assertEquals(fakeAuthExtensionData, testAuthenticator.data);
assertEquals(fakeAuthExtensionData.authMode, testAuthenticator.authMode);
const fakeLstFetchResults = '{result: "fakeLstFetchResults"}';
webUIListenerCallback('send-lst-fetch-results', fakeLstFetchResults);
assertEquals(
1,
testBrowserProxy.getCallCount('lstFetchResults', fakeLstFetchResults));
webUIListenerCallback('close-dialog');
assertEquals(1, testBrowserProxy.getCallCount('dialogClose'));
});
test(assert(inline_login_test.TestNames.AuthExtHostCallbacks), async () => {
const fakeUrl = 'www.google.com/fake';
assertTrue(inlineLoginComponent.$$('paper-spinner-lite').active);
testAuthenticator.dispatchEvent(new Event('ready'));
assertEquals(1, testBrowserProxy.getCallCount('authExtensionReady'));
assertFalse(inlineLoginComponent.$$('paper-spinner-lite').active);
testAuthenticator.dispatchEvent(
new CustomEvent('resize', {detail: fakeUrl}));
const switchToFullTabUrl =
await testBrowserProxy.whenCalled('switchToFullTab');
assertEquals(fakeUrl, switchToFullTabUrl);
const fakeCredentials = {email: 'example@gmail.com'};
testAuthenticator.dispatchEvent(
new CustomEvent('authCompleted', {detail: fakeCredentials}));
const completeLoginResult =
await testBrowserProxy.whenCalled('completeLogin');
assertTrue(inlineLoginComponent.$$('paper-spinner-lite').active);
assertEquals(fakeCredentials, completeLoginResult);
testAuthenticator.dispatchEvent(new Event('showIncognito'));
assertEquals(1, testBrowserProxy.getCallCount('showIncognito'));
});
test(assert(inline_login_test.TestNames.BackButton), () => {
const backButton = inlineLoginComponent.$$('.back-button');
if (!isChromeOS) {
// Back button should be shown only on ChromeOS.
assertEquals(null, backButton);
return;
}
let backInWebviewCalls = 0;
inlineLoginComponent.$.signinFrame.back = () => backInWebviewCalls++;
// If we cannot go back in the webview - we should close the dialog.
inlineLoginComponent.$.signinFrame.canGoBack = () => false;
backButton.click();
assertEquals(1, testBrowserProxy.getCallCount('dialogClose'));
assertEquals(0, backInWebviewCalls);
testBrowserProxy.reset();
// Go back in the webview if possible.
inlineLoginComponent.$.signinFrame.canGoBack = () => true;
backButton.click();
assertEquals(0, testBrowserProxy.getCallCount('dialogClose'));
assertEquals(1, backInWebviewCalls);
});
});
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {AuthMode, AuthParams} from 'chrome://chrome-signin/gaia_auth_host/authenticator.m.js';
import {InlineLoginBrowserProxy} from 'chrome://chrome-signin/inline_login_browser_proxy.js';
import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
import {TestBrowserProxy} from '../test_browser_proxy.m.js';
export class TestAuthenticator extends EventTarget {
constructor() {
super();
/** @type {?AuthMode} */
this.authMode = null;
/** @type {?AuthParams} */
this.data = null;
/** @type {number} */
this.loadCalls = 0;
}
/**
* @param {AuthMode} authMode Authorization mode.
* @param {AuthParams} data Parameters for the authorization flow.
*/
load(authMode, data) {
this.loadCalls++;
this.authMode = authMode;
this.data = data;
}
}
/** @implements {InlineLoginBrowserProxy} */
export class TestInlineLoginBrowserProxy extends TestBrowserProxy {
constructor() {
super([
'initialize',
'authExtensionReady',
'switchToFullTab',
'completeLogin',
'lstFetchResults',
'metricsHandler:recordAction',
'showIncognito',
'dialogClose',
]);
}
/** @override */
initialize() {
this.methodCalled('initialize');
}
/** @override */
authExtensionReady() {
this.methodCalled('authExtensionReady');
}
/** @override */
switchToFullTab(url) {
this.methodCalled('switchToFullTab', url);
}
/** @override */
completeLogin(credentials) {
this.methodCalled('completeLogin', credentials);
}
/** @override */
lstFetchResults(arg) {
this.methodCalled('lstFetchResults', arg);
}
/** @override */
recordAction(metricsAction) {
this.methodCalled('metricsHandler:recordAction', metricsAction);
}
/** @override */
showIncognito() {
this.methodCalled('showIncognito');
}
/** @override */
dialogClose() {
this.methodCalled('dialogClose');
}
}
......@@ -486,6 +486,20 @@ WebView.prototype.loadstart;
/** @type {!ChromeEvent} */
WebView.prototype.loadstop;
/**
* "newwindow" event object properties.
* @typedef{{
* window: NewWindow,
* targetUrl: string,
* initialWidth: number,
* initialHeight: number,
* name: string,
* windowOpenDisposition: string
* }}
* @see https://developer.chrome.com/apps/tags/webview#event-newwindow
*/
var NewWindowProperties;
/** @type {!ChromeEvent} */
WebView.prototype.newwindow;
......
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