blob: 8fe871589fe08a51553a895578c956a65319087e [file] [log] [blame]
// Copyright 2018 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 Polymer element for displaying material design assistant
* value prop screen.
*
* Event 'loading' will be fired when the page is loading/reloading.
* Event 'error' will be fired when the webview failed to load.
* Event 'loaded' will be fired when the page has been successfully loaded.
*/
/**
* Name of the screen.
* @type {string}
*/
const VALUE_PROP_SCREEN_ID = 'ValuePropScreen';
Polymer({
is: 'assistant-value-prop',
behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
properties: {
/**
* Buttons are disabled when the webview content is loading.
*/
buttonsDisabled: {
type: Boolean,
value: true,
},
/**
* Default url for locale en_us.
*/
defaultUrl: {
type: String,
value() {
return this.urlTemplate_.replace('$', 'en_us');
}
},
/**
* Whether new OOBE layout is enabled.
* @type {boolean}
*/
newLayoutEnabled_: {
type: Boolean,
value() {
return loadTimeData.valueExists('newLayoutEnabled') &&
loadTimeData.getBoolean('newLayoutEnabled');
}
},
/**
* Indicates whether user is minor mode user (e.g. under age of 18).
*/
isMinorMode_: {
type: Boolean,
value() {
return loadTimeData.valueExists('isMinorMode') &&
loadTimeData.getBoolean('isMinorMode');
}
},
/**
* Used to determine which activity control settings should be shown.
*/
currentConsentStep_: {
type: Number,
value: 0,
},
},
setUrlTemplateForTesting(url) {
this.urlTemplate_ = url;
},
/**
* The value prop URL template - loaded from loadTimeData.
* The template is expected to have '$' instead of the locale.
* @private {string}
*/
urlTemplate_:
'https://www.gstatic.com/opa-android/oobe/a02187e41eed9e42/v3_omni_$.html',
/**
* Whether try to reload with the default url when a 404 error occurred.
* @type {boolean}
* @private
*/
reloadWithDefaultUrl_: false,
/**
* Whether an error occurs while the webview is loading.
* @type {boolean}
* @private
*/
loadingError_: false,
/**
* The value prop webview object.
* @type {Object}
* @private
*/
valuePropView_: null,
/**
* Whether the screen has been initialized.
* @type {boolean}
* @private
*/
initialized_: false,
/**
* Whether the response header has been received for the value prop view.
* @type {boolean}
* @private
*/
headerReceived_: false,
/**
* Whether all the setting zippy has been successfully loaded.
* @type {boolean}
* @private
*/
settingZippyLoaded_: false,
/**
* Whether all the consent text strings has been successfully loaded.
* @type {boolean}
* @private
*/
consentStringLoaded_: false,
/**
* Whether the screen has been shown to the user.
* @type {boolean}
* @private
*/
screenShown_: false,
/**
* Sanitizer used to sanitize html snippets.
* @type {HtmlSanitizer}
* @private
*/
sanitizer_: new HtmlSanitizer(),
/** @private {?assistant.BrowserProxy} */
browserProxy_: null,
/**
* On-tap event handler for skip button.
*
* @private
*/
onSkipTap_() {
if (this.buttonsDisabled) {
return;
}
this.buttonsDisabled = true;
this.browserProxy_.userActed(VALUE_PROP_SCREEN_ID, ['skip-pressed']);
},
/**
* On-tap event handler for next button.
*
* @private
*/
onNextTap_() {
if (this.buttonsDisabled) {
return;
}
this.buttonsDisabled = true;
this.browserProxy_.userActed(VALUE_PROP_SCREEN_ID, ['next-pressed']);
},
/** @override */
created() {
this.browserProxy_ = assistant.BrowserProxyImpl.getInstance();
},
/**
* Sets learn more content text and shows it as overlay dialog.
* @param {string} content HTML formatted text to show.
*/
showLearnMoreOverlay(title, additionalInfo) {
this.$['overlay-title-text'].innerHTML =
this.sanitizer_.sanitizeHtml(title);
this.$['overlay-additional-info-text'].innerHTML =
this.sanitizer_.sanitizeHtml(additionalInfo);
this.$['learn-more-overlay'].setTitleAriaLabel(title);
this.$['learn-more-overlay'].showModal();
this.$['overlay-close-button'].focus();
},
/**
* Hides overlay dialog.
*/
hideOverlay() {
this.$['learn-more-overlay'].close();
if (this.lastFocusedElement) {
this.lastFocusedElement.focus();
this.lastFocusedElement = null;
}
},
/**
* Reloads value prop page by fetching setting zippy and consent string.
*/
reloadPage() {
this.fire('loading');
if (this.initialized_) {
this.browserProxy_.userActed(VALUE_PROP_SCREEN_ID, ['reload-requested']);
this.settingZippyLoaded_ = false;
this.consentStringLoaded_ = false;
}
this.buttonsDisabled = true;
this.currentConsentStep_ = 0;
},
/**
* Reloads value prop animation webview.
*/
reloadWebView() {
this.loadingError_ = false;
this.headerReceived_ = false;
let locale = this.locale.replace('-', '_').toLowerCase();
this.valuePropView_.src = this.urlTemplate_.replace('$', locale);
},
/**
* Handles event when value prop webview cannot be loaded.
*/
onWebViewErrorOccurred(details) {
this.fire('error');
this.loadingError_ = true;
},
/**
* Handles event when value prop webview is loaded.
*/
onWebViewContentLoad(details) {
if (details == null) {
return;
}
if (this.loadingError_ || !this.headerReceived_) {
return;
}
if (this.reloadWithDefaultUrl_) {
this.valuePropView_.src = this.defaultUrl;
this.headerReceived_ = false;
this.reloadWithDefaultUrl_ = false;
return;
}
if (this.settingZippyLoaded_ && this.consentStringLoaded_) {
this.onPageLoaded();
}
},
/**
* Handles event when webview request headers received.
*/
onWebViewHeadersReceived(details) {
if (details == null) {
return;
}
this.headerReceived_ = true;
if (details.statusCode == '404') {
if (details.url != this.defaultUrl) {
this.reloadWithDefaultUrl_ = true;
return;
} else {
this.onWebViewErrorOccurred();
}
} else if (details.statusCode != '200') {
this.onWebViewErrorOccurred();
}
},
/**
* Reload the page with the given consent string text data.
*/
reloadContent(data) {
this.$['value-prop-dialog'].setAttribute(
'aria-label', data['valuePropTitle']);
this.$['title-text'].textContent = data['valuePropTitle'];
this.$['intro-text'].textContent = data['valuePropIntro'];
this.$['user-image'].src = data['valuePropUserImage'];
this.$['user-name'].textContent = data['valuePropIdentity'];
this.$['next-button'].labelForAria = data['valuePropNextButton'];
this.$['next-button-text'].textContent = data['valuePropNextButton'];
this.$['skip-button'].labelForAria = data['valuePropSkipButton'];
this.$['skip-button-text'].textContent = data['valuePropSkipButton'];
this.$['footer-text'].innerHTML =
this.sanitizer_.sanitizeHtml(data['valuePropFooter']);
this.consentStringLoaded_ = true;
if (this.settingZippyLoaded_) {
this.reloadWebView();
}
},
/**
* Add a setting zippy with the provided data.
*/
addSettingZippy(zippy_data) {
if (this.settingZippyLoaded_) {
if (this.consentStringLoaded_) {
this.reloadWebView();
}
return;
}
// `zippy_data` contains a list of lists, where each list contains the
// setting zippys that should be shown on the same screen.
for (var i in zippy_data) {
for (var j in zippy_data[i]) {
var data = zippy_data[i][j];
var zippy = document.createElement('setting-zippy');
zippy.setAttribute(
'icon-src',
'data:text/html;charset=utf-8,' +
encodeURIComponent(
zippy.getWrappedIcon(data['iconUri'], data['title'])));
zippy.setAttribute('step', i);
if (!this.newLayoutEnabled_) {
zippy.setAttribute('hide-line', true);
} else if (this.isMinorMode_) {
zippy.setAttribute('hide-line', true);
zippy.setAttribute('card-style', true);
}
var title = document.createElement('div');
title.slot = 'title';
title.innerHTML = this.sanitizer_.sanitizeHtml(data['title']);
zippy.appendChild(title);
var description = document.createElement('div');
description.slot = 'content';
description.innerHTML =
this.sanitizer_.sanitizeHtml(data['description']);
description.innerHTML += ' ';
var learnMoreLink = document.createElement('a');
learnMoreLink.slot = 'content';
learnMoreLink.textContent = data['popupLink'];
learnMoreLink.setAttribute('href', 'javascript:void(0)');
learnMoreLink.onclick = function(title, additionalInfo, focus) {
this.lastFocusedElement = focus;
this.showLearnMoreOverlay(title, additionalInfo);
}.bind(this, data['title'], data['additionalInfo'], learnMoreLink);
description.appendChild(learnMoreLink);
zippy.appendChild(description);
this.$['consents-container'].appendChild(zippy);
}
}
this.showSettingZippyForStep_(this.currentConsentStep_);
this.settingZippyLoaded_ = true;
if (this.consentStringLoaded_) {
this.reloadWebView();
}
},
/**
* Handles event when all the page content has been loaded.
*/
onPageLoaded() {
this.fire('loaded');
this.buttonsDisabled = false;
this.$['next-button'].focus();
if (!this.hidden && !this.screenShown_) {
this.browserProxy_.screenShown(VALUE_PROP_SCREEN_ID);
this.screenShown_ = true;
}
},
/**
* Signal from host to show the screen.
*/
onShow() {
this.$['overlay-close-button'].addEventListener(
'click', this.hideOverlay.bind(this));
Polymer.RenderStatus.afterNextRender(
this, () => this.$['next-button'].focus());
if (!this.initialized_) {
if (this.newLayoutEnabled_) {
this.valuePropView_ = this.$['value-prop-view'];
} else {
this.valuePropView_ = this.$['value-prop-view-old'];
}
this.initializeWebview_(this.valuePropView_);
this.reloadPage();
this.initialized_ = true;
}
},
/**
* Update the screen to show the next setting zippy. This is called only for
* minor users as settings are unbundled.
*/
showNextSettingZippy() {
this.currentConsentStep_ += 1;
this.showSettingZippyForStep_(this.currentConsentStep_);
this.buttonsDisabled = false;
},
/**
* Update visibility of setting zippys for a given step.
* @param {number} step
*/
showSettingZippyForStep_(step) {
for (let zippy of this.$['consents-container'].children) {
zippy.hidden = zippy.getAttribute('step') != step;
}
},
initializeWebview_(webview) {
const requestFilter = {urls: ['<all_urls>'], types: ['main_frame']};
webview.request.onErrorOccurred.addListener(
this.onWebViewErrorOccurred.bind(this), requestFilter);
webview.request.onHeadersReceived.addListener(
this.onWebViewHeadersReceived.bind(this), requestFilter);
webview.addEventListener(
'contentload', this.onWebViewContentLoad.bind(this));
webview.addContentScripts([webviewStripLinksContentScript]);
},
/**
* Returns the webview animation container.
*/
getAnimationContainer() {
return this.$['animation-container'];
},
});