Commit 5a088038 authored by rdevlin.cronin's avatar rdevlin.cronin Committed by Commit bot

[Extensions Page] Fix the flicker of "No extensions" before extension data loads

There was a flicker caused because we remove the 'loading'
class from the page before the extensions list finishes
updating itself (asynchronously).

BUG=469053
BUG=469332

Review URL: https://codereview.chromium.org/1025703003

Cr-Commit-Position: refs/heads/master@{#321869}
parent 717f2c07
......@@ -140,11 +140,16 @@ cr.define('extensions', function() {
/**
* Creates a new list of extensions.
* @param {Object=} opt_propertyBag Optional properties.
* @constructor
* @extends {HTMLDivElement}
*/
var ExtensionList = cr.ui.define('div');
function ExtensionList() {
var div = document.createElement('div');
div.__proto__ = ExtensionList.prototype;
/** @private {!Array<ExtensionInfo>} */
div.extensions_ = [];
return div;
}
/**
* @type {Object<string, number>} A map from extension id to last reloaded
......@@ -188,29 +193,61 @@ cr.define('extensions', function() {
*/
butterbarShown_: false,
decorate: function() {
chrome.developerPrivate.getExtensionsInfo(
{includeDisabled: true, includeTerminated: true},
function(extensions) {
// Sort in order of unpacked vs. packed, followed by name, followed by
// id.
extensions.sort(function(a, b) {
function compare(x, y) {
return x < y ? -1 : (x > y ? 1 : 0);
}
function compareLocation(x, y) {
return x.location == chrome.developerPrivate.Location.UNPACKED ?
-1 : (x.location == y.location ? 0 : 1);
}
return compareLocation(a, b) ||
compare(a.name.toLowerCase(), b.name.toLowerCase()) ||
compare(a.id, b.id);
});
this.extensions_ = extensions;
this.showExtensionNodes_();
/**
* Whether or not incognito mode is available.
* @private {boolean}
*/
incognitoAvailable_: false,
/**
* Whether or not the app info dialog is enabled.
* @private {boolean}
*/
enableAppInfoDialog_: false,
/**
* Updates the extensions on the page.
* @param {boolean} incognitoAvailable Whether or not incognito is allowed.
* @param {boolean} enableAppInfoDialog Whether or not the app info dialog
* is enabled.
* @return {Promise} A promise that is resolved once the extensions data is
* fully updated.
*/
updateExtensionsData: function(incognitoAvailable, enableAppInfoDialog) {
// If we start to need more information about the extension configuration,
// consider passing in the full object from the ExtensionSettings.
this.incognitoAvailable_ = incognitoAvailable;
this.enableAppInfoDialog_ = enableAppInfoDialog;
return new Promise(function(resolve, reject) {
chrome.developerPrivate.getExtensionsInfo(
{includeDisabled: true, includeTerminated: true},
function(extensions) {
// Sort in order of unpacked vs. packed, followed by name, followed by
// id.
extensions.sort(function(a, b) {
function compare(x, y) {
return x < y ? -1 : (x > y ? 1 : 0);
}
function compareLocation(x, y) {
return x.location == chrome.developerPrivate.Location.UNPACKED ?
-1 : (x.location == y.location ? 0 : 1);
}
return compareLocation(a, b) ||
compare(a.name.toLowerCase(), b.name.toLowerCase()) ||
compare(a.id, b.id);
});
this.extensions_ = extensions;
this.showExtensionNodes_();
resolve();
}.bind(this));
}.bind(this));
},
/** @return {number} The number of extensions being displayed. */
getNumExtensions: function() {
return this.extensions_.length;
},
getIdQueryParam_: function() {
return parseQueryParams(document.location)['id'];
},
......@@ -268,9 +305,6 @@ cr.define('extensions', function() {
var idToOpenOptions = this.getOptionsQueryParam_();
if (idToOpenOptions && $(idToOpenOptions))
this.showEmbeddedExtensionOptions_(idToOpenOptions, true);
var noExtensions = this.extensions_.length == 0;
this.classList.toggle('empty-extension-list', noExtensions);
},
/** Updates each row's focusable elements without rebuilding the grid. */
......@@ -545,7 +579,7 @@ cr.define('extensions', function() {
// The 'allow in incognito' checkbox.
this.updateVisibility_(row, '.incognito-control',
isActive && this.data_.incognitoAvailable,
isActive && this.incognitoAvailable_,
function(item) {
var incognito = item.querySelector('input');
incognito.disabled = !extension.incognitoAccess.isEnabled;
......@@ -594,7 +628,7 @@ cr.define('extensions', function() {
// The 'View in Web Store/View Web Site' link.
var siteLinkEnabled = !!extension.homepageUrl &&
!this.data_.enableAppInfoDialog;
!this.enableAppInfoDialog_;
this.updateVisibility_(row, '.site-link', siteLinkEnabled,
function(item) {
item.href = extension.homepageUrl;
......@@ -721,7 +755,7 @@ cr.define('extensions', function() {
depNode.querySelector('.dep-extension-id').textContent =
dependentExtension.id;
dependentList.appendChild(depNode);
});
}, this);
});
// The active views.
......
......@@ -141,6 +141,10 @@ html:not(.focus-outline-visible)
font-weight: bold;
}
#no-extensions {
margin-top: 3em;
}
#suggest-gallery {
-webkit-padding-start: 10px;
}
......@@ -160,17 +164,6 @@ html[dir=rtl] #footer-section {
line-height: 32px;
}
.empty-extension-list {
height: 3em;
}
.loading #no-extensions,
.loading #footer-section,
#extension-settings-list:not(.empty-extension-list) ~ #no-extensions,
.empty-extension-list ~ #footer-section {
display: none;
}
.extension-list-item-wrapper {
margin: 23px 0;
}
......
<!doctype html>
<html i18n-values="dir:textdirection;lang:language" class="loading">
<html i18n-values="dir:textdirection;lang:language">
<head>
<meta charset="utf-8">
......@@ -114,21 +114,22 @@
</div>
</div>
<include src="extension_load_error.html">
<div id="extension-settings-list" class="empty-extension-list"></div>
<div id="no-extensions">
<div id="extension-list-wrapper" hidden>
<div id="footer-section">
<a target="_blank" class="more-extensions-link"
i18n-values="href:extensionSettingsGetMoreExtensionsUrl"
i18n-content="extensionSettingsGetMoreExtensions"></a>
<a is="action-link" class="extension-commands-config"
i18n-content="extensionSettingsCommandsLink" hidden></a>
</div>
</div>
<div id="no-extensions" hidden>
<span id="no-extensions-message"
i18n-content="extensionSettingsNoExtensions"></span>
<span id="suggest-gallery" class="more-extensions-link"
i18n-values=".innerHTML:extensionSettingsSuggestGallery">
</span>
</div>
<div id="footer-section">
<a target="_blank" class="more-extensions-link"
i18n-values="href:extensionSettingsGetMoreExtensionsUrl"
i18n-content="extensionSettingsGetMoreExtensions"></a>
<a is="action-link" class="extension-commands-config"
i18n-content="extensionSettingsCommandsLink" hidden></a>
</div>
</div>
<span id="font-measuring-div"></span>
......
......@@ -133,6 +133,11 @@ cr.define('extensions', function() {
// Set the title.
uber.setTitle(loadTimeData.getString('extensionSettings'));
var extensionList = new ExtensionList();
extensionList.id = 'extension-settings-list';
var wrapper = $('extension-list-wrapper');
wrapper.insertBefore(extensionList, wrapper.firstChild);
// This will request the data to show on the page and will get a response
// back in returnExtensionsData.
chrome.send('extensionSettingsRequestExtensionsData');
......@@ -141,7 +146,7 @@ cr.define('extensions', function() {
$('toggle-dev-on').addEventListener('change', function(e) {
this.updateDevControlsVisibility_(true);
$('extension-settings-list').updateFocusableElements();
extensionList.updateFocusableElements();
chrome.send('extensionSettingsToggleDeveloperMode',
[$('toggle-dev-on').checked]);
}.bind(this));
......@@ -298,15 +303,6 @@ cr.define('extensions', function() {
* @param {ExtensionDataResponse} extensionsData
*/
ExtensionSettings.returnExtensionsData = function(extensionsData) {
// We can get called many times in short order, thus we need to
// be careful to remove the 'finished loading' timeout.
if (this.loadingTimeout_)
window.clearTimeout(this.loadingTimeout_);
document.documentElement.classList.add('loading');
this.loadingTimeout_ = window.setTimeout(function() {
document.documentElement.classList.remove('loading');
}, 0);
webuiResponded = true;
var supervised = extensionsData.profileIsSupervised;
......@@ -324,9 +320,23 @@ cr.define('extensions', function() {
instance.updateDevControlsVisibility_(false);
$('load-unpacked').disabled = extensionsData.loadUnpackedDisabled;
ExtensionList.prototype.data_ = extensionsData;
ExtensionList.decorate($('extension-settings-list'));
var extensionList = $('extension-settings-list');
extensionList.updateExtensionsData(
extensionsData.incognitoAvailable,
extensionsData.enableAppInfoDialog).then(function() {
// We can get called many times in short order, thus we need to
// be careful to remove the 'finished loading' timeout.
if (this.loadingTimeout_)
window.clearTimeout(this.loadingTimeout_);
document.documentElement.classList.add('loading');
this.loadingTimeout_ = window.setTimeout(function() {
document.documentElement.classList.remove('loading');
}, 0);
var hasExtensions = extensionList.getNumExtensions() != 0;
$('no-extensions').hidden = hasExtensions;
$('extension-list-wrapper').hidden = !hasExtensions;
}.bind(this));
};
/**
......
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