blob: 424cf4e6d9d1a81e1e115ae6efd084c626f142d0 [file] [log] [blame]
// Copyright 2019 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.
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_set.h"
#include "base/containers/flat_map.h"
#include "base/cxx17_backports.h"
#include "base/files/file_path.h"
#include "base/i18n/rtl.h"
#include "base/json/json_reader.h"
#include "base/json/json_string_value_serializer.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/new_tab_page/promos/promo_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/background/ntp_background_service.h"
#include "chrome/browser/search/background/ntp_background_service_factory.h"
#include "chrome/browser/search/background/ntp_custom_background_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/themes/custom_theme_supplier.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/hats/hats_service.h"
#include "chrome/browser/ui/hats/hats_service_factory.h"
#include "chrome/browser/ui/omnibox/omnibox_theme.h"
#include "chrome/browser/ui/webui/new_tab_page/ntp_pref_names.h"
#include "chrome/browser/ui/webui/realbox/realbox.mojom.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/search/ntp_features.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_provider_logos/logo_service.h"
#include "components/search_provider_logos/switches.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/theme_provider.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/native_theme/native_theme.h"
namespace {
const int64_t kMaxDownloadBytes = 1024 * 1024;
const int64_t kMaxModuleFreImpressions = 8;
// Returns true if the scrim (dark gradient overlay) should be hidden for the
// NTP's background image. This is done to fix specific GWS themes where the
// visual impact of the scrim does not align with the artistic intent of
// partnered artists (see crbug.com/1329556).
// TODO(crbug.com/1320107): Remove the scrim for all GWS and custom background
// image configurations on the NTP.
bool ShouldHideScrim(const ThemeService* theme_service) {
DCHECK(theme_service);
const auto* theme_supplier = theme_service->GetThemeSupplier();
if (!theme_supplier ||
theme_supplier->get_theme_type() != CustomThemeSupplier::EXTENSION) {
return false;
}
static constexpr auto kPrideThemeExtensionIdsNoScrim =
base::MakeFixedFlatSet<base::StringPiece>({
"kkdpcclippggiadgghfmkggpemadbfcj",
"jogkmkalhlbppkpmjdpncmpdcinbkekh",
"gfkcjfbbpmldkajnebkophpelmcimglf",
"mchijkgkaabamaokgcnbmjpfoagkpjfc",
"cdabkdaechplopdfoahhjgkbjgillcme",
"depfhkphmnoonikdokgpejilanmcdonk",
"efiamifmcbajfehbkjemggiafognbljk",
"iclkbhippclhfamkdoigedgnnfbhefpl",
"ckfehdejjppobbllbkjgcpaockgdigen",
"figmdifbokklifinmmjcjdkkopjflhnj",
"nkgiaofmleojhehacfognclpmoolihko",
"npkdokffjmnleabnfihminmikibdhmfa",
"klnkeldihpjnjoopojllmnpepbpljico",
"iffdmpenldeofnlfjmbjcdmafhoekmka",
"mckialangcdpcdcflekinnpamfkmkobo",
});
const std::string& extension_id = theme_supplier->extension_id();
return base::Contains(kPrideThemeExtensionIdsNoScrim, extension_id);
}
// Returns true if we should force dark foreground colors for the Google logo
// and the One Google Bar. This is done to fix specific GWS themes where the
// always-light logo and OGB colors do not sufficiently contrast with lighter
// image backgrounds (see crbug.com/1329552).
// TODO(crbug.com/1328918): Address this in a general way and extend support to
// custom background images, not just CWS themes.
bool ShouldForceDarkForegroundColorsForLogoAndOGB(
const ThemeService* theme_service) {
const auto* theme_supplier = theme_service->GetThemeSupplier();
if (!theme_supplier ||
theme_supplier->get_theme_type() != CustomThemeSupplier::EXTENSION) {
return false;
}
static constexpr auto kPrideThemeExtensionIdsDarkForeground =
base::MakeFixedFlatSet<base::StringPiece>({
"klnkeldihpjnjoopojllmnpepbpljico",
"iffdmpenldeofnlfjmbjcdmafhoekmka",
"mckialangcdpcdcflekinnpamfkmkobo",
});
const std::string& extension_id = theme_supplier->extension_id();
return base::Contains(kPrideThemeExtensionIdsDarkForeground, extension_id);
}
new_tab_page::mojom::ThemePtr MakeTheme(
const ui::ColorProvider& color_provider,
const ui::ThemeProvider* theme_provider,
ThemeService* theme_service,
NtpCustomBackgroundService* ntp_custom_background_service,
content::WebContents* web_contents) {
if (ntp_custom_background_service) {
ntp_custom_background_service->RefreshBackgroundIfNeeded();
}
auto theme = new_tab_page::mojom::Theme::New();
auto most_visited = most_visited::mojom::MostVisitedTheme::New();
auto custom_background =
ntp_custom_background_service
? ntp_custom_background_service->GetCustomBackground()
: absl::nullopt;
theme->is_default = theme_service->UsingDefaultTheme();
theme->background_color = color_provider.GetColor(kColorNewTabPageBackground);
SkColor text_color;
if (custom_background.has_value()) {
text_color = color_provider.GetColor(kColorNewTabPageTextUnthemed);
most_visited->background_color = color_provider.GetColor(
kColorNewTabPageMostVisitedTileBackgroundUnthemed);
theme->logo_color =
color_provider.GetColor(kColorNewTabPageLogoUnthemedLight);
} else {
text_color = color_provider.GetColor(kColorNewTabPageText);
most_visited->background_color =
color_provider.GetColor(kColorNewTabPageMostVisitedTileBackground);
if (theme_provider->GetDisplayProperty(
ThemeProperties::NTP_LOGO_ALTERNATE) == 1) {
theme->logo_color = color_provider.GetColor(kColorNewTabPageLogo);
}
}
most_visited->use_white_tile_icon =
color_utils::IsDark(most_visited->background_color);
most_visited->is_dark = !color_utils::IsDark(text_color);
most_visited->use_title_pill = false;
theme->text_color = text_color;
theme->is_dark = !color_utils::IsDark(text_color);
auto background_image = new_tab_page::mojom::BackgroundImage::New();
if (custom_background.has_value()) {
theme->is_custom_background = true;
background_image->url = custom_background->custom_background_url;
} else if (theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
theme->is_custom_background = false;
most_visited->use_title_pill = true;
auto theme_id = theme_service->GetThemeID();
background_image->url = GURL(base::StrCat(
{"chrome-untrusted://theme/IDR_THEME_NTP_BACKGROUND?", theme_id}));
background_image->url_2x = GURL(base::StrCat(
{"chrome-untrusted://theme/IDR_THEME_NTP_BACKGROUND@2x?", theme_id}));
if (theme_provider->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION)) {
background_image->attribution_url = GURL(base::StrCat(
{"chrome://theme/IDR_THEME_NTP_ATTRIBUTION?", theme_id}));
}
background_image->size = "initial";
switch (theme_provider->GetDisplayProperty(
ThemeProperties::NTP_BACKGROUND_TILING)) {
case ThemeProperties::NO_REPEAT:
background_image->repeat_x = "no-repeat";
background_image->repeat_y = "no-repeat";
break;
case ThemeProperties::REPEAT_X:
background_image->repeat_x = "repeat";
background_image->repeat_y = "no-repeat";
break;
case ThemeProperties::REPEAT_Y:
background_image->repeat_x = "no-repeat";
background_image->repeat_y = "repeat";
break;
case ThemeProperties::REPEAT:
background_image->repeat_x = "repeat";
background_image->repeat_y = "repeat";
break;
}
int alignment = theme_provider->GetDisplayProperty(
ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
if (alignment & ThemeProperties::ALIGN_LEFT) {
background_image->position_x = "left";
} else if (alignment & ThemeProperties::ALIGN_RIGHT) {
background_image->position_x = "right";
} else {
background_image->position_x = "center";
}
if (alignment & ThemeProperties::ALIGN_TOP) {
background_image->position_y = "top";
} else if (alignment & ThemeProperties::ALIGN_BOTTOM) {
background_image->position_y = "bottom";
} else {
background_image->position_y = "center";
}
} else {
background_image = nullptr;
}
// The special case handling that removes the scrim and forces dark foreground
// colors should only be applied when the user does not have a custom
// background selected and has installed a GWS theme with a bundled background
// image. The first condition is necessary as a custom background image can be
// set while a GWS theme with a bundled image is concurrently enabled (see
// crbug.com/1329556 and crbug.com/1329552).
if (base::FeatureList::IsEnabled(ntp_features::kCwsScrimRemoval) &&
!custom_background.has_value() &&
theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
if (ShouldForceDarkForegroundColorsForLogoAndOGB(theme_service)) {
theme->is_dark = false;
theme->logo_color =
color_provider.GetColor(kColorNewTabPageLogoUnthemedDark);
}
if (ShouldHideScrim(theme_service))
background_image->scrim_display = "none";
}
theme->background_image = std::move(background_image);
if (custom_background.has_value()) {
theme->background_image_attribution_1 =
custom_background->custom_background_attribution_line_1;
theme->background_image_attribution_2 =
custom_background->custom_background_attribution_line_2;
theme->background_image_attribution_url =
custom_background->custom_background_attribution_action_url;
theme->daily_refresh_collection_id = custom_background->collection_id;
}
theme->most_visited = std::move(most_visited);
auto search_box = realbox::mojom::SearchBoxTheme::New();
search_box->bg =
GetOmniboxColor(theme_provider, OmniboxPart::LOCATION_BAR_BACKGROUND);
search_box->bg_hovered =
GetOmniboxColor(theme_provider, OmniboxPart::LOCATION_BAR_BACKGROUND,
OmniboxPartState::HOVERED);
search_box->border_color =
webui::GetNativeTheme(web_contents)->UserHasContrastPreference()
? theme_provider->GetColor(ThemeProperties::COLOR_LOCATION_BAR_BORDER)
: SkColorSetRGB(218, 220, 224); // google-grey-300
search_box->icon = GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_ICON);
search_box->icon_selected = GetOmniboxColor(
theme_provider, OmniboxPart::RESULTS_ICON, OmniboxPartState::SELECTED);
search_box->is_dark = !color_utils::IsDark(text_color);
search_box->ntp_bg = color_provider.GetColor(kColorNewTabPageBackground);
search_box->placeholder =
GetOmniboxColor(theme_provider, OmniboxPart::LOCATION_BAR_TEXT_DIMMED);
search_box->results_bg =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_BACKGROUND);
search_box->results_bg_hovered =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_BACKGROUND,
OmniboxPartState::HOVERED);
search_box->results_bg_selected =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_BACKGROUND,
OmniboxPartState::SELECTED);
search_box->results_dim =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_TEXT_DIMMED);
search_box->results_dim_selected =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_TEXT_DIMMED,
OmniboxPartState::SELECTED);
search_box->results_text =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_TEXT_DEFAULT);
search_box->results_text_selected =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_TEXT_DEFAULT,
OmniboxPartState::SELECTED);
search_box->results_url =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_TEXT_URL);
search_box->results_url_selected =
GetOmniboxColor(theme_provider, OmniboxPart::RESULTS_TEXT_URL,
OmniboxPartState::SELECTED);
search_box->text =
GetOmniboxColor(theme_provider, OmniboxPart::LOCATION_BAR_TEXT_DEFAULT);
theme->search_box = std::move(search_box);
return theme;
}
SkColor ParseHexColor(const std::string& color) {
SkColor result;
if (color.size() == 7 && color[0] == '#' &&
base::HexStringToUInt(color.substr(1), &result)) {
return SkColorSetA(result, SK_AlphaOPAQUE);
}
return SK_ColorTRANSPARENT;
}
new_tab_page::mojom::ImageDoodlePtr MakeImageDoodle(
search_provider_logos::LogoType type,
const std::string& data,
const std::string& mime_type,
const GURL& animated_url,
int width_px,
int height_px,
const std::string& background_color,
int share_button_x,
int share_button_y,
const std::string& share_button_icon,
const std::string& share_button_bg,
double share_button_opacity,
GURL log_url,
GURL cta_log_url) {
auto doodle = new_tab_page::mojom::ImageDoodle::New();
std::string base64;
base::Base64Encode(data, &base64);
doodle->image_url = GURL(base::StringPrintf(
"data:%s;base64,%s", mime_type.c_str(), base64.c_str()));
if (type == search_provider_logos::LogoType::ANIMATED) {
doodle->animation_url = animated_url;
}
doodle->width = width_px;
doodle->height = height_px;
doodle->background_color = ParseHexColor(background_color);
if (!share_button_icon.empty()) {
doodle->share_button = new_tab_page::mojom::DoodleShareButton::New();
doodle->share_button->x = share_button_x;
doodle->share_button->y = share_button_y;
doodle->share_button->icon_url = GURL(base::StringPrintf(
"data:image/png;base64,%s", share_button_icon.c_str()));
doodle->share_button->background_color = SkColorSetA(
ParseHexColor(share_button_bg),
base::clamp(share_button_opacity, 0.0, 1.0) * SK_AlphaOPAQUE);
}
if (type == search_provider_logos::LogoType::ANIMATED) {
doodle->image_impression_log_url = cta_log_url;
doodle->animation_impression_log_url = log_url;
} else {
doodle->image_impression_log_url = log_url;
}
return doodle;
}
new_tab_page::mojom::PromoPtr MakePromo(const PromoData& data) {
// |data.middle_slot_json| is safe to be decoded here. The JSON string is part
// of a larger JSON initially decoded using the data decoder utility in the
// PromoService to base::Value. The middle-slot promo part is then reencoded
// from base::Value to a JSON string stored in |data.middle_slot_json|.
auto middle_slot = base::JSONReader::Read(data.middle_slot_json);
if (!middle_slot.has_value() ||
middle_slot.value().FindBoolPath("hidden").value_or(false)) {
return nullptr;
}
auto promo = new_tab_page::mojom::Promo::New();
promo->id = data.promo_id;
if (middle_slot.has_value()) {
auto* parts = middle_slot.value().FindListPath("part");
if (parts) {
std::vector<new_tab_page::mojom::PromoPartPtr> mojom_parts;
for (const base::Value& part : parts->GetListDeprecated()) {
const base::Value::Dict& part_dict = part.GetDict();
if (part_dict.Find("image")) {
auto mojom_image = new_tab_page::mojom::PromoImagePart::New();
auto* image_url = part_dict.FindStringByDottedPath("image.image_url");
if (!image_url || image_url->empty()) {
continue;
}
mojom_image->image_url = GURL(*image_url);
auto* target = part_dict.FindStringByDottedPath("image.target");
if (target && !target->empty()) {
mojom_image->target = GURL(*target);
}
mojom_parts.push_back(
new_tab_page::mojom::PromoPart::NewImage(std::move(mojom_image)));
} else if (part_dict.Find("link")) {
auto mojom_link = new_tab_page::mojom::PromoLinkPart::New();
auto* url = part_dict.FindStringByDottedPath("link.url");
if (!url || url->empty()) {
continue;
}
mojom_link->url = GURL(*url);
auto* text = part_dict.FindStringByDottedPath("link.text");
if (!text || text->empty()) {
continue;
}
mojom_link->text = *text;
auto* color = part_dict.FindStringByDottedPath("link.color");
if (color && !color->empty()) {
mojom_link->color = *color;
}
mojom_parts.push_back(
new_tab_page::mojom::PromoPart::NewLink(std::move(mojom_link)));
} else if (part_dict.Find("text")) {
auto mojom_text = new_tab_page::mojom::PromoTextPart::New();
auto* text = part_dict.FindStringByDottedPath("text.text");
if (!text || text->empty()) {
continue;
}
mojom_text->text = *text;
auto* color = part_dict.FindStringByDottedPath("text.color");
if (color && !color->empty()) {
mojom_text->color = *color;
}
mojom_parts.push_back(
new_tab_page::mojom::PromoPart::NewText(std::move(mojom_text)));
}
}
promo->middle_slot_parts = std::move(mojom_parts);
}
}
promo->log_url = data.promo_log_url;
return promo;
}
} // namespace
// static
const char NewTabPageHandler::kModuleDismissedHistogram[] =
"NewTabPage.Modules.Dismissed";
const char NewTabPageHandler::kModuleRestoredHistogram[] =
"NewTabPage.Modules.Restored";
NewTabPageHandler::NewTabPageHandler(
mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
pending_page_handler,
mojo::PendingRemote<new_tab_page::mojom::Page> pending_page,
Profile* profile,
NtpCustomBackgroundService* ntp_custom_background_service,
ThemeService* theme_service,
search_provider_logos::LogoService* logo_service,
content::WebContents* web_contents,
const base::Time& ntp_navigation_start_time)
: ntp_background_service_(
NtpBackgroundServiceFactory::GetForProfile(profile)),
ntp_custom_background_service_(ntp_custom_background_service),
logo_service_(logo_service),
theme_provider_(webui::GetThemeProvider(web_contents)),
theme_service_(theme_service),
profile_(profile),
web_contents_(web_contents),
ntp_navigation_start_time_(ntp_navigation_start_time),
logger_(profile,
GURL(chrome::kChromeUINewTabPageURL),
ntp_navigation_start_time),
promo_service_(PromoServiceFactory::GetForProfile(profile)),
page_{std::move(pending_page)},
receiver_{this, std::move(pending_page_handler)} {
CHECK(ntp_background_service_);
CHECK(ntp_custom_background_service_);
CHECK(logo_service_);
CHECK(theme_service_);
CHECK(promo_service_);
CHECK(web_contents_);
ntp_background_service_->AddObserver(this);
native_theme_observation_.Observe(ui::NativeTheme::GetInstanceForNativeUi());
theme_service_observation_.Observe(theme_service_.get());
ntp_custom_background_service_observation_.Observe(
ntp_custom_background_service_.get());
promo_service_observation_.Observe(promo_service_.get());
OnThemeChanged();
}
NewTabPageHandler::~NewTabPageHandler() {
ntp_background_service_->RemoveObserver(this);
if (select_file_dialog_) {
select_file_dialog_->ListenerDestroyed();
}
}
// static
void NewTabPageHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kNtpDisabledModules, true);
registry->RegisterListPref(prefs::kNtpModulesOrder, true);
registry->RegisterBooleanPref(prefs::kNtpModulesVisible, true);
registry->RegisterIntegerPref(prefs::kNtpModulesShownCount, 0);
registry->RegisterTimePref(prefs::kNtpModulesFirstShownTime, base::Time());
registry->RegisterBooleanPref(prefs::kNtpModulesFreVisible, true);
}
void NewTabPageHandler::SetMostVisitedSettings(bool custom_links_enabled,
bool visible) {
bool old_visible = IsShortcutsVisible();
if (old_visible != visible) {
profile_->GetPrefs()->SetBoolean(ntp_prefs::kNtpShortcutsVisible, visible);
logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_TOGGLE_VISIBILITY,
base::TimeDelta() /* unused */);
}
bool old_custom_links_enabled = IsCustomLinksEnabled();
if (old_custom_links_enabled != custom_links_enabled) {
profile_->GetPrefs()->SetBoolean(ntp_prefs::kNtpUseMostVisitedTiles,
!custom_links_enabled);
logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_TOGGLE_TYPE,
base::TimeDelta() /* unused */);
}
}
void NewTabPageHandler::GetMostVisitedSettings(
GetMostVisitedSettingsCallback callback) {
bool custom_links_enabled = IsCustomLinksEnabled();
bool visible = IsShortcutsVisible();
std::move(callback).Run(custom_links_enabled, visible);
}
void NewTabPageHandler::SetBackgroundImage(const std::string& attribution_1,
const std::string& attribution_2,
const GURL& attribution_url,
const GURL& image_url) {
// Populating the |collection_id| turns on refresh daily which overrides the
// the selected image.
ntp_custom_background_service_->SetCustomBackgroundInfo(
image_url, attribution_1, attribution_2, attribution_url,
/* collection_id= */ "");
LogEvent(NTP_BACKGROUND_IMAGE_SET);
}
void NewTabPageHandler::SetDailyRefreshCollectionId(
const std::string& collection_id) {
// Populating the |collection_id| turns on refresh daily which overrides the
// the selected image.
ntp_custom_background_service_->SetCustomBackgroundInfo(
/* image_url */ GURL(), /* attribution_line_1= */ "",
/* attribution_line_2= */ "",
/* action_url= */ GURL(), collection_id);
LogEvent(NTP_BACKGROUND_DAILY_REFRESH_ENABLED);
}
void NewTabPageHandler::SetNoBackgroundImage() {
ntp_custom_background_service_->SetCustomBackgroundInfo(
/* image_url */ GURL(), /* attribution_line_1= */ "",
/* attribution_line_2= */ "",
/* action_url= */ GURL(), /* collection_id= */ "");
LogEvent(NTP_BACKGROUND_IMAGE_RESET);
}
void NewTabPageHandler::RevertBackgroundChanges() {
ntp_custom_background_service_->RevertBackgroundChanges();
}
void NewTabPageHandler::ConfirmBackgroundChanges() {
ntp_custom_background_service_->ConfirmBackgroundChanges();
}
void NewTabPageHandler::GetBackgroundCollections(
GetBackgroundCollectionsCallback callback) {
if (!ntp_background_service_ || background_collections_callback_) {
std::move(callback).Run(
std::vector<new_tab_page::mojom::BackgroundCollectionPtr>());
return;
}
background_collections_request_start_time_ = base::TimeTicks::Now();
background_collections_callback_ = std::move(callback);
ntp_background_service_->FetchCollectionInfo();
}
void NewTabPageHandler::GetBackgroundImages(
const std::string& collection_id,
GetBackgroundImagesCallback callback) {
if (background_images_callback_) {
std::move(background_images_callback_)
.Run(std::vector<new_tab_page::mojom::CollectionImagePtr>());
}
if (!ntp_background_service_) {
std::move(callback).Run(
std::vector<new_tab_page::mojom::CollectionImagePtr>());
return;
}
images_request_collection_id_ = collection_id;
background_images_request_start_time_ = base::TimeTicks::Now();
background_images_callback_ = std::move(callback);
ntp_background_service_->FetchCollectionImageInfo(collection_id);
}
void NewTabPageHandler::GetDoodle(GetDoodleCallback callback) {
search_provider_logos::LogoCallbacks callbacks;
callbacks.on_cached_encoded_logo_available =
base::BindOnce(&NewTabPageHandler::OnLogoAvailable,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
// This will trigger re-downloading the doodle and caching it. This means a
// new doodle will be returned on subsequent NTP loads.
logo_service_->GetLogo(std::move(callbacks), /*for_webui_ntp=*/true);
}
void NewTabPageHandler::ChooseLocalCustomBackground(
ChooseLocalCustomBackgroundCallback callback) {
// Early return if the select file dialog is already active.
if (select_file_dialog_)
return;
select_file_dialog_ = ui::SelectFileDialog::Create(
this, std::make_unique<ChromeSelectFilePolicy>(web_contents_));
ui::SelectFileDialog::FileTypeInfo file_types;
file_types.allowed_paths = ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH;
file_types.extensions.resize(1);
file_types.extensions[0].push_back(FILE_PATH_LITERAL("jpg"));
file_types.extensions[0].push_back(FILE_PATH_LITERAL("jpeg"));
file_types.extensions[0].push_back(FILE_PATH_LITERAL("png"));
file_types.extensions[0].push_back(FILE_PATH_LITERAL("gif"));
file_types.extension_description_overrides.push_back(
l10n_util::GetStringUTF16(IDS_UPLOAD_IMAGE_FORMAT));
choose_local_custom_background_callback_ = std::move(callback);
select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_OPEN_FILE, std::u16string(),
profile_->last_selected_directory(), &file_types, 0,
base::FilePath::StringType(), web_contents_->GetTopLevelNativeWindow(),
nullptr);
}
void NewTabPageHandler::GetPromo(GetPromoCallback callback) {
// Replace the promo URL with "command:<id>" if such a command ID is set
// via the feature params.
const std::string command_id = base::GetFieldTrialParamValueByFeature(
features::kPromoBrowserCommands, features::kBrowserCommandIdParam);
if (!command_id.empty()) {
auto promo = new_tab_page::mojom::Promo::New();
std::vector<new_tab_page::mojom::PromoPartPtr> parts;
auto image = new_tab_page::mojom::PromoImagePart::New();
// Warning symbol used as the test image.
image->image_url = GURL(
"data:image/"
"svg+xml;base64,"
"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii01IC"
"01IDU4IDU4IiBmaWxsPSIjZmRkNjMzIj48cGF0aCBkPSJNMiA0Mmg0NEwyNCA0IDIgNDJ6"
"bTI0LTZoLTR2LTRoNHY0em0wLThoLTR2LThoNHY4eiIvPjwvc3ZnPg==");
image->target = GURL("command:" + command_id);
parts.push_back(new_tab_page::mojom::PromoPart::NewImage(std::move(image)));
auto link = new_tab_page::mojom::PromoLinkPart::New();
link->url = GURL("command:" + command_id);
link->text = "Test command: " + command_id;
parts.push_back(new_tab_page::mojom::PromoPart::NewLink(std::move(link)));
promo->middle_slot_parts = std::move(parts);
std::move(callback).Run(std::move(promo));
return;
}
promo_callbacks_.push_back(std::move(callback));
if (promo_service_->promo_data().has_value()) {
OnPromoDataUpdated();
}
promo_load_start_time_ = base::TimeTicks::Now();
promo_service_->Refresh();
}
void NewTabPageHandler::OnDismissModule(const std::string& module_id) {
const std::string histogram_prefix(kModuleDismissedHistogram);
base::UmaHistogramExactLinear(histogram_prefix, 1, 1);
base::UmaHistogramExactLinear(histogram_prefix + "." + module_id, 1, 1);
}
void NewTabPageHandler::OnRestoreModule(const std::string& module_id) {
const std::string histogram_prefix(kModuleRestoredHistogram);
base::UmaHistogramExactLinear(histogram_prefix, 1, 1);
base::UmaHistogramExactLinear(histogram_prefix + "." + module_id, 1, 1);
}
void NewTabPageHandler::SetModulesVisible(bool visible) {
profile_->GetPrefs()->SetBoolean(prefs::kNtpModulesVisible, visible);
UpdateDisabledModules();
}
void NewTabPageHandler::SetModuleDisabled(const std::string& module_id,
bool disabled) {
ListPrefUpdate update(profile_->GetPrefs(), prefs::kNtpDisabledModules);
base::Value module_id_value(module_id);
if (disabled) {
if (!base::Contains(update->GetListDeprecated(), module_id_value))
update->Append(std::move(module_id_value));
} else {
update->EraseListValue(module_id_value);
}
UpdateDisabledModules();
}
void NewTabPageHandler::UpdateDisabledModules() {
std::vector<std::string> module_ids;
// If the module visibility is managed by policy we either disable all modules
// (if invisible) or no modules (if visible).
if (!profile_->GetPrefs()->IsManagedPreference(prefs::kNtpModulesVisible)) {
const auto* module_ids_value =
profile_->GetPrefs()->GetList(prefs::kNtpDisabledModules);
for (const auto& id : module_ids_value->GetListDeprecated()) {
module_ids.push_back(id.GetString());
}
}
page_->SetDisabledModules(
!profile_->GetPrefs()->GetBoolean(prefs::kNtpModulesVisible),
std::move(module_ids));
}
void NewTabPageHandler::OnModulesLoadedWithData() {
HatsService* hats_service =
HatsServiceFactory::GetForProfile(profile_, /*create_if_necessary=*/true);
CHECK(hats_service);
hats_service->LaunchDelayedSurveyForWebContents(kHatsSurveyTriggerNtpModules,
web_contents_, 0);
}
void NewTabPageHandler::SetModulesOrder(
const std::vector<std::string>& module_ids) {
base::Value module_ids_value(base::Value::Type::LIST);
for (const auto& module_id : module_ids) {
module_ids_value.Append(module_id);
}
profile_->GetPrefs()->Set(prefs::kNtpModulesOrder, module_ids_value);
}
void NewTabPageHandler::GetModulesOrder(GetModulesOrderCallback callback) {
std::vector<std::string> module_ids;
// First, apply order as set by the last drag&drop interaction.
if (base::FeatureList::IsEnabled(ntp_features::kNtpModulesDragAndDrop)) {
const auto* module_ids_value =
profile_->GetPrefs()->GetList(prefs::kNtpModulesOrder);
for (const auto& id : module_ids_value->GetListDeprecated()) {
module_ids.push_back(id.GetString());
}
}
// Second, append Finch order for modules _not_ ordered by drag&drop.
std::vector<std::string> finch_module_ids = ntp_features::GetModulesOrder();
std::copy_if(finch_module_ids.begin(), finch_module_ids.end(),
std::back_inserter(module_ids),
[&module_ids](const std::string& id) {
return std::find(module_ids.begin(), module_ids.end(), id) ==
module_ids.end();
});
std::move(callback).Run(std::move(module_ids));
}
void NewTabPageHandler::IncrementModulesShownCount() {
const auto ntp_modules_shown_count =
profile_->GetPrefs()->GetInteger(prefs::kNtpModulesShownCount);
if (ntp_modules_shown_count == 0) {
profile_->GetPrefs()->SetTime(prefs::kNtpModulesFirstShownTime,
base::Time::Now());
}
profile_->GetPrefs()->SetInteger(prefs::kNtpModulesShownCount,
ntp_modules_shown_count + 1);
}
void NewTabPageHandler::SetModulesFreVisible(bool visible) {
profile_->GetPrefs()->SetBoolean(prefs::kNtpModulesFreVisible, visible);
page_->SetModulesFreVisibility(visible);
}
void NewTabPageHandler::UpdateModulesFreVisibility() {
const auto ntp_modules_shown_count =
profile_->GetPrefs()->GetInteger(prefs::kNtpModulesShownCount);
const auto ntp_modules_first_shown_time =
profile_->GetPrefs()->GetTime(prefs::kNtpModulesFirstShownTime);
auto ntp_modules_fre_visible =
profile_->GetPrefs()->GetBoolean(prefs::kNtpModulesFreVisible);
if (ntp_modules_fre_visible &&
(ntp_modules_shown_count == kMaxModuleFreImpressions ||
(!ntp_modules_first_shown_time.is_null() &&
(base::Time::Now() - ntp_modules_first_shown_time) == base::Days(1)))) {
LogModulesFreOptInStatus(new_tab_page::mojom::OptInStatus::kImplicitOptIn);
}
// Hide Modular NTP Desktop v1 First Run Experience after
// |kMaxModuleFreImpressions| impressions or 1 day, whichever comes first.
if (ntp_modules_shown_count >= kMaxModuleFreImpressions ||
(!ntp_modules_first_shown_time.is_null() &&
(base::Time::Now() - ntp_modules_first_shown_time) > base::Days(1))) {
ntp_modules_fre_visible = false;
SetModulesFreVisible(ntp_modules_fre_visible);
} else {
page_->SetModulesFreVisibility(ntp_modules_fre_visible);
}
}
void NewTabPageHandler::LogModulesFreOptInStatus(
new_tab_page::mojom::OptInStatus opt_in_status) {
const auto ntp_modules_shown_count =
profile_->GetPrefs()->GetInteger(prefs::kNtpModulesShownCount);
switch (opt_in_status) {
case new_tab_page::mojom::OptInStatus::kExplicitOptIn:
base::UmaHistogramExactLinear("NewTabPage.Modules.FreExplicitOptIn",
ntp_modules_shown_count,
kMaxModuleFreImpressions);
break;
case new_tab_page::mojom::OptInStatus::kImplicitOptIn:
base::UmaHistogramBoolean("NewTabPage.Modules.FreImplicitOptIn", true);
break;
case new_tab_page::mojom::OptInStatus::kOptOut:
base::UmaHistogramExactLinear("NewTabPage.Modules.FreOptOut",
ntp_modules_shown_count,
kMaxModuleFreImpressions);
}
}
void NewTabPageHandler::OnPromoDataUpdated() {
if (promo_load_start_time_.has_value()) {
base::TimeDelta duration = base::TimeTicks::Now() - *promo_load_start_time_;
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency2", duration);
if (promo_service_->promo_status() == PromoService::Status::OK_WITH_PROMO) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.Promos.RequestLatency2.SuccessWithPromo", duration);
} else if (promo_service_->promo_status() ==
PromoService::Status::OK_BUT_BLOCKED) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.Promos.RequestLatency2.SuccessButBlocked", duration);
} else if (promo_service_->promo_status() ==
PromoService::Status::OK_WITHOUT_PROMO) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.Promos.RequestLatency2.SuccessWithoutPromo", duration);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Promos.RequestLatency2.Failure",
duration);
}
promo_load_start_time_ = absl::nullopt;
}
const auto& data = promo_service_->promo_data();
for (auto& callback : promo_callbacks_) {
if (data.has_value() && !data->promo_html.empty()) {
std::move(callback).Run(MakePromo(data.value()));
} else {
std::move(callback).Run(nullptr);
}
}
promo_callbacks_.clear();
}
void NewTabPageHandler::OnPromoServiceShuttingDown() {
promo_service_observation_.Reset();
promo_service_ = nullptr;
}
void NewTabPageHandler::OnAppRendered(double time) {
logger_.LogEvent(NTP_APP_RENDERED,
base::Time::FromJsTime(time) - ntp_navigation_start_time_);
}
void NewTabPageHandler::OnOneGoogleBarRendered(double time) {
logger_.LogEvent(NTP_ONE_GOOGLE_BAR_SHOWN,
base::Time::FromJsTime(time) - ntp_navigation_start_time_);
}
void NewTabPageHandler::OnPromoRendered(double time,
const absl::optional<GURL>& log_url) {
logger_.LogEvent(NTP_MIDDLE_SLOT_PROMO_SHOWN,
base::Time::FromJsTime(time) - ntp_navigation_start_time_);
if (log_url.has_value() && log_url->is_valid()) {
Fetch(*log_url, base::BindOnce([](bool, std::unique_ptr<std::string>) {}));
}
}
void NewTabPageHandler::OnCustomizeDialogAction(
new_tab_page::mojom::CustomizeDialogAction action) {
NTPLoggingEventType event;
switch (action) {
case new_tab_page::mojom::CustomizeDialogAction::kCancelClicked:
event = NTP_CUSTOMIZATION_MENU_CANCEL;
break;
case new_tab_page::mojom::CustomizeDialogAction::kDoneClicked:
event = NTP_CUSTOMIZATION_MENU_DONE;
break;
case new_tab_page::mojom::CustomizeDialogAction::kOpenClicked:
event = NTP_CUSTOMIZATION_MENU_OPENED;
break;
case new_tab_page::mojom::CustomizeDialogAction::kBackgroundsBackClicked:
event = NTP_BACKGROUND_BACK_CLICK;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kBackgroundsNoBackgroundSelected:
event = NTP_BACKGROUND_DEFAULT_SELECTED;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kBackgroundsCollectionOpened:
event = NTP_BACKGROUND_OPEN_COLLECTION;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kBackgroundsRefreshToggleClicked:
event = NTP_BACKGROUND_REFRESH_TOGGLE_CLICKED;
break;
case new_tab_page::mojom::CustomizeDialogAction::kBackgroundsImageSelected:
event = NTP_BACKGROUND_SELECT_IMAGE;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kBackgroundsUploadFromDeviceClicked:
event = NTP_BACKGROUND_UPLOAD_FROM_DEVICE;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kShortcutsCustomLinksClicked:
event = NTP_CUSTOMIZE_SHORTCUT_CUSTOM_LINKS_CLICKED;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kShortcutsMostVisitedClicked:
event = NTP_CUSTOMIZE_SHORTCUT_MOST_VISITED_CLICKED;
break;
case new_tab_page::mojom::CustomizeDialogAction::
kShortcutsVisibilityToggleClicked:
event = NTP_CUSTOMIZE_SHORTCUT_VISIBILITY_TOGGLE_CLICKED;
break;
default:
NOTREACHED();
}
LogEvent(event);
}
void NewTabPageHandler::OnDoodleImageClicked(
new_tab_page::mojom::DoodleImageType type,
const absl::optional<::GURL>& log_url) {
NTPLoggingEventType event;
switch (type) {
case new_tab_page::mojom::DoodleImageType::kAnimation:
event = NTP_ANIMATED_LOGO_CLICKED;
break;
case new_tab_page::mojom::DoodleImageType::kCta:
event = NTP_CTA_LOGO_CLICKED;
break;
case new_tab_page::mojom::DoodleImageType::kStatic:
event = NTP_STATIC_LOGO_CLICKED;
break;
default:
NOTREACHED();
return;
}
LogEvent(event);
if (type == new_tab_page::mojom::DoodleImageType::kCta &&
log_url.has_value()) {
// We just ping the server to indicate a CTA image has been clicked.
Fetch(*log_url, base::BindOnce([](bool, std::unique_ptr<std::string>) {}));
}
}
void NewTabPageHandler::OnDoodleImageRendered(
new_tab_page::mojom::DoodleImageType type,
double time,
const GURL& log_url,
OnDoodleImageRenderedCallback callback) {
if (type == new_tab_page::mojom::DoodleImageType::kCta ||
type == new_tab_page::mojom::DoodleImageType::kStatic) {
logger_.LogEvent(type == new_tab_page::mojom::DoodleImageType::kCta
? NTP_CTA_LOGO_SHOWN_FROM_CACHE
: NTP_STATIC_LOGO_SHOWN_FROM_CACHE,
base::Time::FromJsTime(time) - ntp_navigation_start_time_);
}
Fetch(log_url,
base::BindOnce(&NewTabPageHandler::OnLogFetchResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void NewTabPageHandler::OnDoodleShared(
new_tab_page::mojom::DoodleShareChannel channel,
const std::string& doodle_id,
const absl::optional<std::string>& share_id) {
int channel_id;
switch (channel) {
case new_tab_page::mojom::DoodleShareChannel::kFacebook:
channel_id = 2;
break;
case new_tab_page::mojom::DoodleShareChannel::kTwitter:
channel_id = 3;
break;
case new_tab_page::mojom::DoodleShareChannel::kEmail:
channel_id = 5;
break;
case new_tab_page::mojom::DoodleShareChannel::kLinkCopy:
channel_id = 6;
break;
default:
NOTREACHED();
break;
}
std::string query =
base::StringPrintf("gen_204?atype=i&ct=doodle&ntp=2&cad=sh,%d,ct:%s",
channel_id, doodle_id.c_str());
if (share_id.has_value()) {
query += "&ei=" + *share_id;
}
auto url = GURL(TemplateURLServiceFactory::GetForProfile(profile_)
->search_terms_data()
.GoogleBaseURLValue())
.Resolve(query);
// We just ping the server to indicate a doodle has been shared.
Fetch(url, base::BindOnce([](bool s, std::unique_ptr<std::string>) {}));
}
void NewTabPageHandler::OnPromoLinkClicked() {
LogEvent(NTP_MIDDLE_SLOT_PROMO_LINK_CLICKED);
}
void NewTabPageHandler::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
OnThemeChanged();
}
void NewTabPageHandler::OnThemeChanged() {
page_->SetTheme(MakeTheme(web_contents_->GetColorProvider(), theme_provider_,
theme_service_, ntp_custom_background_service_,
web_contents_));
}
void NewTabPageHandler::OnCustomBackgroundImageUpdated() {
OnThemeChanged();
}
void NewTabPageHandler::OnNtpCustomBackgroundServiceShuttingDown() {
ntp_custom_background_service_observation_.Reset();
ntp_custom_background_service_ = nullptr;
}
void NewTabPageHandler::OnCollectionInfoAvailable() {
if (!background_collections_callback_) {
return;
}
base::TimeDelta duration =
base::TimeTicks::Now() - background_collections_request_start_time_;
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.BackgroundService.Collections.RequestLatency", duration);
// Any response where no collections are returned is considered a failure.
if (ntp_background_service_->collection_info().empty()) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.BackgroundService.Collections.RequestLatency.Failure",
duration);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.BackgroundService.Collections.RequestLatency.Success",
duration);
}
std::vector<new_tab_page::mojom::BackgroundCollectionPtr> collections;
for (const auto& info : ntp_background_service_->collection_info()) {
auto collection = new_tab_page::mojom::BackgroundCollection::New();
collection->id = info.collection_id;
collection->label = info.collection_name;
collection->preview_image_url = GURL(info.preview_image_url);
collections.push_back(std::move(collection));
}
std::move(background_collections_callback_).Run(std::move(collections));
}
void NewTabPageHandler::OnCollectionImagesAvailable() {
if (!background_images_callback_) {
return;
}
base::TimeDelta duration =
base::TimeTicks::Now() - background_images_request_start_time_;
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.BackgroundService.Images.RequestLatency", duration);
// Any response where no images are returned is considered a failure.
if (ntp_background_service_->collection_images().empty()) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.BackgroundService.Images.RequestLatency.Failure", duration);
} else {
UMA_HISTOGRAM_MEDIUM_TIMES(
"NewTabPage.BackgroundService.Images.RequestLatency.Success", duration);
}
std::vector<new_tab_page::mojom::CollectionImagePtr> images;
if (ntp_background_service_->collection_images().empty()) {
std::move(background_images_callback_).Run(std::move(images));
return;
}
auto collection_id =
ntp_background_service_->collection_images()[0].collection_id;
for (const auto& info : ntp_background_service_->collection_images()) {
DCHECK(info.collection_id == collection_id);
auto image = new_tab_page::mojom::CollectionImage::New();
image->attribution_1 = !info.attribution.empty() ? info.attribution[0] : "";
image->attribution_2 =
info.attribution.size() > 1 ? info.attribution[1] : "";
image->attribution_url = info.attribution_action_url;
image->image_url = info.image_url;
image->preview_image_url = info.thumbnail_image_url;
images.push_back(std::move(image));
}
std::move(background_images_callback_).Run(std::move(images));
}
void NewTabPageHandler::OnNextCollectionImageAvailable() {}
void NewTabPageHandler::OnNtpBackgroundServiceShuttingDown() {
ntp_background_service_->RemoveObserver(this);
ntp_background_service_ = nullptr;
}
void NewTabPageHandler::FileSelected(const base::FilePath& path,
int index,
void* params) {
DCHECK(choose_local_custom_background_callback_);
if (ntp_custom_background_service_) {
profile_->set_last_selected_directory(path.DirName());
ntp_custom_background_service_->SelectLocalBackgroundImage(path);
}
select_file_dialog_ = nullptr;
// File selection can happen at any time after NTP load, and is not logged
// with the event.
LogEvent(NTP_CUSTOMIZE_LOCAL_IMAGE_DONE);
LogEvent(NTP_BACKGROUND_UPLOAD_DONE);
if (choose_local_custom_background_callback_)
std::move(choose_local_custom_background_callback_).Run(true);
}
void NewTabPageHandler::FileSelectionCanceled(void* params) {
DCHECK(choose_local_custom_background_callback_);
select_file_dialog_ = nullptr;
// File selection can happen at any time after NTP load, and is not logged
// with the event.
LogEvent(NTP_CUSTOMIZE_LOCAL_IMAGE_CANCEL);
LogEvent(NTP_BACKGROUND_UPLOAD_CANCEL);
if (choose_local_custom_background_callback_)
std::move(choose_local_custom_background_callback_).Run(false);
}
void NewTabPageHandler::OnLogoAvailable(
GetDoodleCallback callback,
search_provider_logos::LogoCallbackReason type,
const absl::optional<search_provider_logos::EncodedLogo>& logo) {
if (!logo) {
std::move(callback).Run(nullptr);
return;
}
auto doodle = new_tab_page::mojom::Doodle::New();
if (logo->metadata.type == search_provider_logos::LogoType::SIMPLE ||
logo->metadata.type == search_provider_logos::LogoType::ANIMATED) {
if (!logo->encoded_image) {
std::move(callback).Run(nullptr);
return;
}
auto image_doodle = new_tab_page::mojom::AllModeImageDoodle::New();
image_doodle->light = MakeImageDoodle(
logo->metadata.type, logo->encoded_image->data(),
logo->metadata.mime_type, logo->metadata.animated_url,
logo->metadata.width_px, logo->metadata.height_px, "#ffffff",
logo->metadata.share_button_x, logo->metadata.share_button_y,
logo->metadata.share_button_icon, logo->metadata.share_button_bg,
logo->metadata.share_button_opacity, logo->metadata.log_url,
logo->metadata.cta_log_url);
if (logo->dark_encoded_image) {
image_doodle->dark = MakeImageDoodle(
logo->metadata.type, logo->dark_encoded_image->data(),
logo->metadata.dark_mime_type, logo->metadata.dark_animated_url,
logo->metadata.dark_width_px, logo->metadata.dark_height_px,
logo->metadata.dark_background_color,
logo->metadata.dark_share_button_x,
logo->metadata.dark_share_button_y,
logo->metadata.dark_share_button_icon,
logo->metadata.dark_share_button_bg,
logo->metadata.dark_share_button_opacity, logo->metadata.dark_log_url,
logo->metadata.dark_cta_log_url);
}
if (logo->metadata.on_click_url.is_valid()) {
image_doodle->on_click_url = logo->metadata.on_click_url;
}
image_doodle->share_url = logo->metadata.short_link;
doodle->image = std::move(image_doodle);
} else if (logo->metadata.type ==
search_provider_logos::LogoType::INTERACTIVE) {
auto interactive_doodle = new_tab_page::mojom::InteractiveDoodle::New();
interactive_doodle->url = logo->metadata.full_page_url;
interactive_doodle->width = logo->metadata.iframe_width_px;
interactive_doodle->height = logo->metadata.iframe_height_px;
doodle->interactive = std::move(interactive_doodle);
} else {
std::move(callback).Run(nullptr);
return;
}
doodle->description = logo->metadata.alt_text;
std::move(callback).Run(std::move(doodle));
}
void NewTabPageHandler::LogEvent(NTPLoggingEventType event) {
logger_.LogEvent(event, base::TimeDelta() /* unused */);
}
void NewTabPageHandler::Fetch(const GURL& url,
OnFetchResultCallback on_result) {
auto traffic_annotation =
net::DefineNetworkTrafficAnnotation("new_tab_page_handler", R"(
semantics {
sender: "New Tab Page"
description: "Logs impression and interaction with doodle or promo."
trigger:
"Showing or clicking on the doodle or promo on the New Tab Page. "
"Desktop only."
data:
"String identifiying todays doodle or promo and token identifying "
"a single interaction session. Data does not contain PII."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"Users can control this feature via selecting a non-Google default "
"search engine in Chrome settings under 'Search Engine'."
chrome_policy {
DefaultSearchProviderEnabled {
policy_options {mode: MANDATORY}
DefaultSearchProviderEnabled: false
}
}
})");
auto url_loader_factory = profile_->GetURLLoaderFactory();
auto request = std::make_unique<network::ResourceRequest>();
request->url = url;
auto loader =
network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
loader->DownloadToString(url_loader_factory.get(),
base::BindOnce(&NewTabPageHandler::OnFetchResult,
weak_ptr_factory_.GetWeakPtr(),
loader.get(), std::move(on_result)),
kMaxDownloadBytes);
loader_map_.insert({loader.get(), std::move(loader)});
}
void NewTabPageHandler::OnFetchResult(const network::SimpleURLLoader* loader,
OnFetchResultCallback on_result,
std::unique_ptr<std::string> body) {
bool success = loader->NetError() == net::OK && loader->ResponseInfo() &&
loader->ResponseInfo()->headers &&
loader->ResponseInfo()->headers->response_code() >= 200 &&
loader->ResponseInfo()->headers->response_code() <= 299 &&
body;
std::move(on_result).Run(success, std::move(body));
loader_map_.erase(loader);
}
void NewTabPageHandler::OnLogFetchResult(OnDoodleImageRenderedCallback callback,
bool success,
std::unique_ptr<std::string> body) {
if (!success || body->size() < 4 || body->substr(0, 4) != ")]}'") {
std::move(callback).Run("", absl::nullopt, "");
return;
}
auto value = base::JSONReader::Read(body->substr(4));
if (!value.has_value()) {
std::move(callback).Run("", absl::nullopt, "");
return;
}
auto* target_url_params_value = value->FindPath("ddllog.target_url_params");
auto target_url_params =
target_url_params_value && target_url_params_value->is_string()
? target_url_params_value->GetString()
: "";
auto* interaction_log_url_value =
value->FindPath("ddllog.interaction_log_url");
auto interaction_log_url =
interaction_log_url_value && interaction_log_url_value->is_string()
? absl::optional<GURL>(
GURL(TemplateURLServiceFactory::GetForProfile(profile_)
->search_terms_data()
.GoogleBaseURLValue())
.Resolve(interaction_log_url_value->GetString()))
: absl::nullopt;
auto* encoded_ei_value = value->FindPath("ddllog.encoded_ei");
auto encoded_ei = encoded_ei_value && encoded_ei_value->is_string()
? encoded_ei_value->GetString()
: "";
std::move(callback).Run(target_url_params, interaction_log_url, encoded_ei);
}
bool NewTabPageHandler::IsCustomLinksEnabled() const {
return !profile_->GetPrefs()->GetBoolean(ntp_prefs::kNtpUseMostVisitedTiles);
}
bool NewTabPageHandler::IsShortcutsVisible() const {
return profile_->GetPrefs()->GetBoolean(ntp_prefs::kNtpShortcutsVisible);
}