Commit ed15ce53 authored by Kyle Milka's avatar Kyle Milka Committed by Commit Bot

[NTP] Initiate server-side resource requests earlier

Previously requests for promos and the OGB were not sent until
the NTP had almost finished loading. Instead start these requests
when the request for the main HTML page is received.

With this change promos and the OBG will appear along with search
suggestions and the most visited tiles if they're available (which
is now fairly likely). Otherwise they will appear once ready.

Also removes a console error when the OGB failed to load.

Bug: 770640
Change-Id: Iff7a29a002325853115678f12c89c29869c076e5
Reviewed-on: https://chromium-review.googlesource.com/c/1476148
Commit-Queue: Kyle Milka <kmilka@chromium.org>
Reviewed-by: default avatarKristi Park <kristipark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636345}
parent 1de47fd5
......@@ -128,6 +128,7 @@ var IDS = {
NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x',
NOTIFICATION_MESSAGE: 'mv-msg',
NTP_CONTENTS: 'ntp-contents',
OGB: 'one-google',
PROMO: 'promo',
RESTORE_ALL_LINK: 'mv-restore',
SUGGESTIONS: 'suggestions',
......@@ -966,32 +967,15 @@ function handlePostMessage(event) {
if (cmd === 'loaded') {
tilesAreLoaded = true;
if (configData.isGooglePage) {
// Show search suggestions if they were previously hidden.
// Show search suggestions, promo, and the OGB if they were previously
// hidden.
if ($(IDS.SUGGESTIONS)) {
$(IDS.SUGGESTIONS).style.visibility = 'visible';
}
if (!$('one-google-loader')) {
// Load the OneGoogleBar script. It'll create a global variable name
// "og" which is a dict corresponding to the native OneGoogleBarData
// type. We do this only after all the tiles have loaded, to avoid
// slowing down the main page load.
var ogScript = document.createElement('script');
ogScript.id = 'one-google-loader';
ogScript.src = 'chrome-search://local-ntp/one-google.js';
document.body.appendChild(ogScript);
ogScript.onload = function() {
injectOneGoogleBar(og);
};
}
if (!$('promo-loader')) {
var promoScript = document.createElement('script');
promoScript.id = 'promo-loader';
promoScript.src = 'chrome-search://local-ntp/promo.js';
document.body.appendChild(promoScript);
promoScript.onload = function() {
injectPromo(promo);
};
if ($(IDS.PROMO)) {
$(IDS.PROMO).style.display = 'block';
}
$(IDS.OGB).classList.remove('hidden');
$(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT)
.classList.toggle(
customBackgrounds.CLASSES.OPTION_DISABLED,
......@@ -1028,11 +1012,15 @@ function handlePostMessage(event) {
}
}
function showSearchSuggestions() {
// Inject search suggestions as early as possible to avoid shifting of other
// elements.
/**
* Request data for search suggestions, promo, and the OGB. Insert it into
* the page once it's available. For search suggestions this should be almost
* immediately as cached data is always used. Promos and the OGB may need
* to wait for the asynchronous network request to complete.
*/
function requestAndInsertGoogleResources() {
if (!$('search-suggestions-loader')) {
var ssScript = document.createElement('script');
let ssScript = document.createElement('script');
ssScript.id = 'search-suggestions-loader';
ssScript.src = 'chrome-search://local-ntp/search-suggestions.js';
ssScript.async = false;
......@@ -1041,6 +1029,26 @@ function showSearchSuggestions() {
injectSearchSuggestions(search_suggestions);
};
}
if (!$('one-google-loader')) {
// Load the OneGoogleBar script. It'll create a global variable |og| which
// is a JSON object corresponding to the native OneGoogleBarData type.
let ogScript = document.createElement('script');
ogScript.id = 'one-google-loader';
ogScript.src = 'chrome-search://local-ntp/one-google.js';
document.body.appendChild(ogScript);
ogScript.onload = function() {
injectOneGoogleBar(og);
};
}
if (!$('promo-loader')) {
let promoScript = document.createElement('script');
promoScript.id = 'promo-loader';
promoScript.src = 'chrome-search://local-ntp/promo.js';
document.body.appendChild(promoScript);
promoScript.onload = function() {
injectPromo(promo);
};
}
}
......@@ -1106,7 +1114,7 @@ function init() {
var searchboxApiHandle = embeddedSearchApiHandle.searchBox;
if (configData.isGooglePage) {
showSearchSuggestions();
requestAndInsertGoogleResources();
enableMDIcons();
ntpApiHandle.onaddcustomlinkdone = onAddCustomLinkDone;
......@@ -1327,6 +1335,7 @@ function injectPromo(promo) {
let promoContainer = document.createElement('div');
promoContainer.id = IDS.PROMO;
promoContainer.innerHTML += promo.promoHtml;
promoContainer.style.display = 'none';
$(IDS.NTP_CONTENTS).appendChild(promoContainer);
if (promo.promoLogUrl) {
......@@ -1363,6 +1372,10 @@ function injectSearchSuggestions(suggestions) {
* doesn't block the main page load.
*/
function injectOneGoogleBar(ogb) {
if (ogb.barHtml == '') {
return;
}
var inHeadStyle = document.createElement('style');
inHeadStyle.type = 'text/css';
inHeadStyle.appendChild(document.createTextNode(ogb.inHeadStyle));
......@@ -1375,9 +1388,8 @@ function injectOneGoogleBar(ogb) {
renderOneGoogleBarTheme();
var ogElem = $('one-google');
var ogElem = $(IDS.OGB);
ogElem.innerHTML = ogb.barHtml;
ogElem.classList.remove('hidden');
var afterBarScript = document.createElement('script');
afterBarScript.type = 'text/javascript';
......
......@@ -405,28 +405,42 @@ base::Value ConvertAlbumPhotosToDict(
return photos;
}
std::unique_ptr<base::DictionaryValue> ConvertOGBDataToDict(
const OneGoogleBarData& og) {
auto result = std::make_unique<base::DictionaryValue>();
result->SetString("barHtml", og.bar_html);
result->SetString("inHeadScript", og.in_head_script);
result->SetString("inHeadStyle", og.in_head_style);
result->SetString("afterBarScript", og.after_bar_script);
result->SetString("endOfBodyHtml", og.end_of_body_html);
result->SetString("endOfBodyScript", og.end_of_body_script);
return result;
scoped_refptr<base::RefCountedString> GetOGBString(
const base::Optional<OneGoogleBarData>& og) {
base::DictionaryValue dict;
if (og.has_value()) {
dict.SetString("barHtml", og->bar_html);
dict.SetString("inHeadScript", og->in_head_script);
dict.SetString("inHeadStyle", og->in_head_style);
dict.SetString("afterBarScript", og->after_bar_script);
dict.SetString("endOfBodyHtml", og->end_of_body_html);
dict.SetString("endOfBodyScript", og->end_of_body_script);
} else {
dict.SetString("barHtml", std::string());
}
std::string js;
base::JSONWriter::Write(dict, &js);
js = "var og = " + js + ";";
return scoped_refptr<base::RefCountedString>(
base::RefCountedString::TakeString(&js));
}
std::unique_ptr<base::DictionaryValue> ConvertPromoDataToDict(
scoped_refptr<base::RefCountedString> GetPromoString(
const base::Optional<PromoData>& promo) {
auto result = std::make_unique<base::DictionaryValue>();
base::DictionaryValue dict;
if (promo.has_value()) {
result->SetString("promoHtml", promo->promo_html);
result->SetString("promoLogUrl", promo->promo_log_url.spec());
dict.SetString("promoHtml", promo->promo_html);
dict.SetString("promoLogUrl", promo->promo_log_url.spec());
} else {
result->SetString("promoHtml", std::string());
dict.SetString("promoHtml", std::string());
}
return result;
std::string js;
base::JSONWriter::Write(dict, &js);
js = "var promo = " + js + ";";
return scoped_refptr<base::RefCountedString>(
base::RefCountedString::TakeString(&js));
}
std::unique_ptr<base::DictionaryValue> ConvertSearchSuggestDataToDict(
......@@ -571,6 +585,10 @@ class LocalNtpSource::SearchConfigurationProvider
UpdateConfigData();
}
bool DefaultSearchProviderIsGoogle() {
return search::DefaultSearchProviderIsGoogle(service_);
}
~SearchConfigurationProvider() override {
if (service_)
service_->RemoveObserver(this);
......@@ -889,40 +907,32 @@ void LocalNtpSource::StartDataRequest(
if (stripped_path == kOneGoogleBarScriptFilename) {
if (!one_google_bar_service_) {
callback.Run(nullptr);
return;
} else {
ServeOneGoogleBarWhenAvailable(callback);
}
one_google_bar_requests_.emplace_back(base::TimeTicks::Now(), callback);
one_google_bar_service_->Refresh();
return;
}
if (stripped_path == kPromoScriptFilename) {
if (!promo_service_) {
callback.Run(nullptr);
return;
} else {
ServePromoWhenAvailable(callback);
}
// TODO(crbug/909931): There's no need to fetch the promo on each load,
// we can sometimes use cached data.
promo_requests_.emplace_back(base::TimeTicks::Now(), callback);
promo_service_->Refresh();
return;
}
// Search suggestions always used a cached value, so there is no need to
// refresh the data until the old data is used.
if (stripped_path == kSearchSuggestionsScriptFilename) {
if (!search_suggest_service_) {
callback.Run(nullptr);
return;
}
MaybeServeSearchSuggestions(callback);
search_suggest_requests_.emplace_back(base::TimeTicks::Now());
search_suggest_service_->Refresh();
} else {
ServeSearchSuggestionsIfAvailable(callback);
pending_search_suggest_request_ = base::TimeTicks::Now();
search_suggest_service_->Refresh();
}
return;
}
......@@ -958,6 +968,10 @@ void LocalNtpSource::StartDataRequest(
#endif // !defined(GOOGLE_CHROME_BUILD)
if (stripped_path == kMainHtmlFilename) {
if (search_config_provider_->DefaultSearchProviderIsGoogle()) {
InitiatePromoAndOGBRequests();
}
std::string html = ui::ResourceBundle::GetSharedInstance()
.GetRawDataResource(IDR_LOCAL_NTP_HTML)
.as_string();
......@@ -1269,37 +1283,40 @@ void LocalNtpSource::OnPromoServiceShuttingDown() {
void LocalNtpSource::OnSearchSuggestDataUpdated() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (pending_search_suggest_request_.has_value()) {
return;
}
SearchSuggestLoader::Status result =
search_suggest_service_->search_suggest_status();
base::TimeTicks now = base::TimeTicks::Now();
for (const auto& request : search_suggest_requests_) {
base::TimeDelta delta = now - request.start_time;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.SearchSuggestions.RequestLatency",
delta);
SearchSuggestionsRequestStatus request_status =
SearchSuggestionsRequestStatus::UNKNOWN_ERROR;
if (result == SearchSuggestLoader::Status::SIGNED_OUT) {
request_status = SearchSuggestionsRequestStatus::SIGNED_OUT;
} else if (result == SearchSuggestLoader::Status::OPTED_OUT) {
request_status = SearchSuggestionsRequestStatus::OPTED_OUT;
} else if (result == SearchSuggestLoader::Status::IMPRESSION_CAP) {
request_status = SearchSuggestionsRequestStatus::IMPRESSION_CAP;
} else if (result == SearchSuggestLoader::Status::REQUESTS_FROZEN) {
request_status = SearchSuggestionsRequestStatus::FROZEN;
} else if (result == SearchSuggestLoader::Status::OK) {
request_status = SearchSuggestionsRequestStatus::SENT;
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.SearchSuggestions.RequestLatency.Success", delta);
} else if (result == SearchSuggestLoader::Status::FATAL_ERROR) {
request_status = SearchSuggestionsRequestStatus::FATAL_ERROR;
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.SearchSuggestions.RequestLatency.Failure", delta);
}
UMA_HISTOGRAM_ENUMERATION("NewTabPage.SearchSuggestions.RequestStatus",
request_status);
base::TimeDelta delta =
base::TimeTicks::Now() - *pending_search_suggest_request_;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.SearchSuggestions.RequestLatency",
delta);
SearchSuggestionsRequestStatus request_status =
SearchSuggestionsRequestStatus::UNKNOWN_ERROR;
if (result == SearchSuggestLoader::Status::SIGNED_OUT) {
request_status = SearchSuggestionsRequestStatus::SIGNED_OUT;
} else if (result == SearchSuggestLoader::Status::OPTED_OUT) {
request_status = SearchSuggestionsRequestStatus::OPTED_OUT;
} else if (result == SearchSuggestLoader::Status::IMPRESSION_CAP) {
request_status = SearchSuggestionsRequestStatus::IMPRESSION_CAP;
} else if (result == SearchSuggestLoader::Status::REQUESTS_FROZEN) {
request_status = SearchSuggestionsRequestStatus::FROZEN;
} else if (result == SearchSuggestLoader::Status::OK) {
request_status = SearchSuggestionsRequestStatus::SENT;
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.SearchSuggestions.RequestLatency.Success", delta);
} else if (result == SearchSuggestLoader::Status::FATAL_ERROR) {
request_status = SearchSuggestionsRequestStatus::FATAL_ERROR;
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.SearchSuggestions.RequestLatency.Failure", delta);
}
search_suggest_requests_.clear();
UMA_HISTOGRAM_ENUMERATION("NewTabPage.SearchSuggestions.RequestStatus",
request_status);
pending_search_suggest_request_ = base::nullopt;
}
void LocalNtpSource::OnSearchSuggestServiceShuttingDown() {
......@@ -1308,7 +1325,8 @@ void LocalNtpSource::OnSearchSuggestServiceShuttingDown() {
search_suggest_service_observer_.RemoveAll();
search_suggest_service_ = nullptr;
}
void LocalNtpSource::MaybeServeSearchSuggestions(
void LocalNtpSource::ServeSearchSuggestionsIfAvailable(
const content::URLDataSource::GotDataCallback& callback) {
base::Optional<SearchSuggestData> data =
search_suggest_service_->search_suggest_data();
......@@ -1328,59 +1346,89 @@ void LocalNtpSource::ServeOneGoogleBar(
const base::Optional<OneGoogleBarData>& data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (one_google_bar_requests_.empty())
if (!pending_one_google_bar_request_.has_value()) {
return;
}
scoped_refptr<base::RefCountedString> result;
if (data.has_value()) {
std::string js;
base::JSONWriter::Write(*ConvertOGBDataToDict(*data), &js);
js = "var og = " + js + ";";
result = base::RefCountedString::TakeString(&js);
result = GetOGBString(data);
}
base::TimeTicks now = base::TimeTicks::Now();
for (const auto& request : one_google_bar_requests_) {
request.callback.Run(result);
base::TimeDelta delta = now - request.start_time;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.OneGoogleBar.RequestLatency", delta);
if (result) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.OneGoogleBar.RequestLatency.Success", delta);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.OneGoogleBar.RequestLatency.Failure", delta);
}
base::TimeDelta delta =
base::TimeTicks::Now() - *pending_one_google_bar_request_;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.OneGoogleBar.RequestLatency", delta);
if (result) {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.OneGoogleBar.RequestLatency.Success",
delta);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.OneGoogleBar.RequestLatency.Failure",
delta);
}
for (const auto& callback : one_google_bar_callbacks_) {
callback.Run(result);
}
pending_one_google_bar_request_ = base::nullopt;
one_google_bar_callbacks_.clear();
}
void LocalNtpSource::ServeOneGoogleBarWhenAvailable(
const content::URLDataSource::GotDataCallback& callback) {
base::Optional<OneGoogleBarData> data =
one_google_bar_service_->one_google_bar_data();
if (!pending_one_google_bar_request_.has_value()) {
callback.Run(GetOGBString(data));
} else {
one_google_bar_callbacks_.emplace_back(callback);
}
one_google_bar_requests_.clear();
}
void LocalNtpSource::ServePromo(const base::Optional<PromoData>& data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (promo_requests_.empty())
if (!pending_promo_request_.has_value()) {
return;
}
scoped_refptr<base::RefCountedString> result;
std::string js;
base::JSONWriter::Write(*ConvertPromoDataToDict(data), &js);
js = "var promo = " + js + ";";
result = base::RefCountedString::TakeString(&js);
scoped_refptr<base::RefCountedString> result = GetPromoString(data);
base::TimeTicks now = base::TimeTicks::Now();
for (const auto& request : promo_requests_) {
request.callback.Run(result);
base::TimeDelta delta = now - request.start_time;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency", delta);
if (result) {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency.Success",
delta);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency.Failure",
delta);
}
base::TimeDelta delta = base::TimeTicks::Now() - *pending_promo_request_;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency", delta);
if (result) {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency.Success",
delta);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency.Failure",
delta);
}
for (const auto& callback : promo_callbacks_) {
callback.Run(result);
}
pending_promo_request_ = base::nullopt;
promo_callbacks_.clear();
}
void LocalNtpSource::ServePromoWhenAvailable(
const content::URLDataSource::GotDataCallback& callback) {
base::Optional<PromoData> data = promo_service_->promo_data();
if (!pending_promo_request_.has_value()) {
callback.Run(GetPromoString(data));
} else {
promo_callbacks_.emplace_back(callback);
}
}
void LocalNtpSource::InitiatePromoAndOGBRequests() {
if (one_google_bar_service_) {
pending_one_google_bar_request_ = base::TimeTicks::Now();
one_google_bar_service_->Refresh();
}
if (promo_service_) {
pending_promo_request_ = base::TimeTicks::Now();
promo_service_->Refresh();
}
promo_requests_.clear();
}
LocalNtpSource::NtpBackgroundRequest::NtpBackgroundRequest(
......@@ -1392,31 +1440,3 @@ LocalNtpSource::NtpBackgroundRequest::NtpBackgroundRequest(
const NtpBackgroundRequest&) = default;
LocalNtpSource::NtpBackgroundRequest::~NtpBackgroundRequest() = default;
LocalNtpSource::OneGoogleBarRequest::OneGoogleBarRequest(
base::TimeTicks start_time,
const content::URLDataSource::GotDataCallback& callback)
: start_time(start_time), callback(callback) {}
LocalNtpSource::OneGoogleBarRequest::OneGoogleBarRequest(
const OneGoogleBarRequest&) = default;
LocalNtpSource::OneGoogleBarRequest::~OneGoogleBarRequest() = default;
LocalNtpSource::PromoRequest::PromoRequest(
base::TimeTicks start_time,
const content::URLDataSource::GotDataCallback& callback)
: start_time(start_time), callback(callback) {}
LocalNtpSource::PromoRequest::PromoRequest(const PromoRequest&) = default;
LocalNtpSource::PromoRequest::~PromoRequest() = default;
LocalNtpSource::SearchSuggestRequest::SearchSuggestRequest(
base::TimeTicks start_time)
: start_time(start_time) {}
LocalNtpSource::SearchSuggestRequest::SearchSuggestRequest(
const SearchSuggestRequest&) = default;
LocalNtpSource::SearchSuggestRequest::~SearchSuggestRequest() = default;
......@@ -69,35 +69,6 @@ class LocalNtpSource : public content::URLDataSource,
content::URLDataSource::GotDataCallback callback;
};
struct OneGoogleBarRequest {
OneGoogleBarRequest(
base::TimeTicks start_time,
const content::URLDataSource::GotDataCallback& callback);
OneGoogleBarRequest(const OneGoogleBarRequest&);
~OneGoogleBarRequest();
base::TimeTicks start_time;
content::URLDataSource::GotDataCallback callback;
};
struct PromoRequest {
PromoRequest(base::TimeTicks start_time,
const content::URLDataSource::GotDataCallback& callback);
PromoRequest(const PromoRequest&);
~PromoRequest();
base::TimeTicks start_time;
content::URLDataSource::GotDataCallback callback;
};
struct SearchSuggestRequest {
explicit SearchSuggestRequest(base::TimeTicks start_time);
explicit SearchSuggestRequest(const SearchSuggestRequest&);
~SearchSuggestRequest();
base::TimeTicks start_time;
};
// Overridden from content::URLDataSource:
std::string GetSource() const override;
void StartDataRequest(
......@@ -133,13 +104,32 @@ class LocalNtpSource : public content::URLDataSource,
void OnSearchSuggestDataUpdated() override;
void OnSearchSuggestServiceShuttingDown() override;
// Called when the OGB data is available and serves |data| to any pending
// request from the NTP.
void ServeOneGoogleBar(const base::Optional<OneGoogleBarData>& data);
// Called when the page requests OGB data. If the data is available it
// is returned immediately, otherwise it is returned when it becomes available
// in ServeOneGoogleBar.
void ServeOneGoogleBarWhenAvailable(
const content::URLDataSource::GotDataCallback& callback);
// Called when the promo data is available and serves |data| to any pending
// requests from the NTP.
void ServePromo(const base::Optional<PromoData>& data);
// Called when the page requests promo data. If the data is available it
// is returned immediately, otherwise it is returned when it becomes
// available in ServePromo.
void ServePromoWhenAvailable(
const content::URLDataSource::GotDataCallback& callback);
void MaybeServeSearchSuggestions(
// If suggestion data is available return it immediately, otherwise no search
// suggestions will be shown on this NTP load.
void ServeSearchSuggestionsIfAvailable(
const content::URLDataSource::GotDataCallback& callback);
// Start requests for the promo and OGB.
void InitiatePromoAndOGBRequests();
Profile* const profile_;
std::vector<NtpBackgroundRequest> ntp_background_collections_requests_;
......@@ -152,20 +142,23 @@ class LocalNtpSource : public content::URLDataSource,
ScopedObserver<NtpBackgroundService, NtpBackgroundServiceObserver>
ntp_background_service_observer_;
std::vector<OneGoogleBarRequest> one_google_bar_requests_;
base::Optional<base::TimeTicks> pending_one_google_bar_request_;
std::vector<content::URLDataSource::GotDataCallback>
one_google_bar_callbacks_;
OneGoogleBarService* one_google_bar_service_;
ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver>
one_google_bar_service_observer_;
std::vector<PromoRequest> promo_requests_;
base::Optional<base::TimeTicks> pending_promo_request_;
std::vector<content::URLDataSource::GotDataCallback> promo_callbacks_;
PromoService* promo_service_;
ScopedObserver<PromoService, PromoServiceObserver> promo_service_observer_;
std::vector<SearchSuggestRequest> search_suggest_requests_;
base::Optional<base::TimeTicks> pending_search_suggest_request_;
SearchSuggestService* search_suggest_service_;
......
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