Commit 5a2b62ff authored by Esmael El-Moslimany's avatar Esmael El-Moslimany Committed by Commit Bot

WebUI NTP: choose local image file as background

Implements that select local image file as NTP background feature in the
customize dialog. In the UI it says "Upload from device" which seems
fine since it matches chrome-search://local-ntp.

Bug: 1032328
Change-Id: Ib211b74769e86da5566787b601830b12c8fbfa41
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2152001
Commit-Queue: Esmael Elmoslimany <aee@chromium.org>
Reviewed-by: default avatarAlex Gough <ajgo@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759827}
parent 3111672d
...@@ -319,15 +319,33 @@ class AppElement extends PolymerElement { ...@@ -319,15 +319,33 @@ class AppElement extends PolymerElement {
*/ */
updateBackgroundImagePath_() { updateBackgroundImagePath_() {
// The |backgroundSelection_| is retained after the dialog commits the // The |backgroundSelection_| is retained after the dialog commits the
// change to the theme. Since |backgroundSelection_| has precendence over // change to the theme. Since |backgroundSelection_| has precedence over
// the theme background, the |backgroundSelection_| needs to be reset when // the theme background, the |backgroundSelection_| needs to be reset when
// the theme is updated. This is only necessary when the dialog is closed. // the theme is updated. This is only necessary when the dialog is closed.
// If the dialog is open, it will either commit the |backgroundSelection_| // If the dialog is open, it will either commit the |backgroundSelection_|
// or reset |backgroundSelection_| on cancel. // or reset |backgroundSelection_| on cancel.
//
// Update after background image path is updated so the image is not shown
// before the path is updated.
if (!this.showCustomizeDialog_ && if (!this.showCustomizeDialog_ &&
this.backgroundSelection_.type !== this.backgroundSelection_.type !==
BackgroundSelectionType.NO_SELECTION) { BackgroundSelectionType.NO_SELECTION) {
this.backgroundSelection_ = {type: BackgroundSelectionType.NO_SELECTION}; // Wait when local image is selected, then no background is previewed
// followed by selecting a new local image. This avoids a flicker. The
// iframe with the old image is shown briefly before it navigates to a new
// iframe location, then fetches and renders the new local image.
if (this.backgroundSelection_.type ===
BackgroundSelectionType.NO_BACKGROUND) {
setTimeout(() => {
this.backgroundSelection_ = {
type: BackgroundSelectionType.NO_SELECTION
};
}, 100);
} else {
this.backgroundSelection_ = {
type: BackgroundSelectionType.NO_SELECTION
};
}
} }
let path; let path;
switch (this.backgroundSelection_.type) { switch (this.backgroundSelection_.type) {
......
...@@ -40,9 +40,13 @@ ...@@ -40,9 +40,13 @@
position: relative; position: relative;
} }
.selected .image { .selected .image,
.selected #uploadFromDevice {
box-shadow: 0 1px 3px 0 rgba(var(--google-grey-800-rgb), .3), box-shadow: 0 1px 3px 0 rgba(var(--google-grey-800-rgb), .3),
0 4px 8px 3px rgba(var(--google-grey-800-rgb), .15); 0 4px 8px 3px rgba(var(--google-grey-800-rgb), .15);
}
.selected .image {
transform: scale(.8); transform: scale(.8);
} }
...@@ -88,34 +92,77 @@ ...@@ -88,34 +92,77 @@
display: block; display: block;
} }
#noBackground .image,
#uploadFromDevice .image {
background: var(--ntp-background-override-color);
border: 1px solid var(--ntp-border-color);
}
#uploadFromDevice {
position: relative;
}
#uploadFromDeviceImage {
position: absolute;
top: 0;
width: 100%;
}
#uploadFromDeviceImage .label {
text-align: center;
}
#uploadIcon {
-webkit-mask-image: url(icons/upload.svg);
-webkit-mask-repeat: no-repeat;
-webkit-mask-size: 100%;
background-color: var(--google-grey-refresh-700);
height: 32px;
margin: 61px auto 8px;
width: 32px;
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.selected .image { .selected .image,
.selected #uploadFromDevice {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .3), box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .3),
0 4px 8px 3px rgba(0, 0, 0, .15); 0 4px 8px 3px rgba(0, 0, 0, .15);
} }
}
#noBackground .image { #uploadIcon {
background: var(--ntp-background-override-color); background-color: var(--google-grey-refresh-500);
border: 1px solid var(--ntp-border-color); }
} }
</style> </style>
<ntp-grid id="collections" columns="3" hidden="[[selectedCollection]]"> <ntp-grid id="collections" columns="3" hidden="[[selectedCollection]]">
<div id="uploadFromDevice" class="tile" role="button"
on-click="onUploadFromDeviceClick_">
<div class$="[[getCustomBackgroundClass_(theme, backgroundSelection)]]">
<div class="image">
</div>
<div id="uploadFromDeviceImage">
<div id="uploadIcon"></div>
<div class="label">$i18n{uploadFromDevice}</div>
</div>
<div class="selected-circle"></div>
<div class="selected-check"></div>
</div>
<div class="label"></div>
</div>
<div id="noBackground" class="tile" role="button" on-click="onDefaultClick_"> <div id="noBackground" class="tile" role="button" on-click="onDefaultClick_">
<div <div class$="[[getNoBackgroundClass_(theme, backgroundSelection)]]">
class$="[[getNoBackgroundClass_(theme, backgroundSelection)]]">
<div class="image"> <div class="image">
<ntp-mini-page></ntp-mini-page> <ntp-mini-page></ntp-mini-page>
</div> </div>
<div class="selected-circle"></div> <div class="selected-circle"></div>
<div class="selected-check"></div> <div class="selected-check"></div>
</div> </div>
<div class="label">No background</div> <div class="label">$i18n{noBackground}</div>
</div> </div>
<dom-repeat id="collectionsRepeat" items="[[collections_]]"> <dom-repeat id="collectionsRepeat" items="[[collections_]]">
<template> <template>
<div class="tile" tabindex="0" title="[[item.label]]" role="button" <div class="tile" tabindex="0" title="[[item.label]]" role="button"
on-click="onCollectionClick_"> on-click="onCollectionClick_">
<ntp-untrusted-iframe class="image" <ntp-untrusted-iframe class="image"
path="background_image?[[item.previewImageUrl.url]]"> path="background_image?[[item.previewImageUrl.url]]">
</ntp-untrusted-iframe> </ntp-untrusted-iframe>
......
...@@ -57,6 +57,23 @@ class CustomizeBackgroundsElement extends PolymerElement { ...@@ -57,6 +57,23 @@ class CustomizeBackgroundsElement extends PolymerElement {
}); });
} }
/**
* @return {string}
* @private
*/
getCustomBackgroundClass_() {
switch (this.backgroundSelection.type) {
case BackgroundSelectionType.NO_SELECTION:
return this.theme && this.theme.backgroundImageUrl &&
this.theme.backgroundImageUrl.url.startsWith(
'chrome-untrusted://new-tab-page/background.jpg') ?
'selected' :
'';
default:
return '';
}
}
/** /**
* @return {string} * @return {string}
* @private * @private
...@@ -110,6 +127,16 @@ class CustomizeBackgroundsElement extends PolymerElement { ...@@ -110,6 +127,16 @@ class CustomizeBackgroundsElement extends PolymerElement {
this.selectedCollection = this.$.collectionsRepeat.itemForElement(e.target); this.selectedCollection = this.$.collectionsRepeat.itemForElement(e.target);
} }
/** @private */
async onUploadFromDeviceClick_() {
const {success} = await this.pageHandler_.chooseLocalCustomBackground();
if (success) {
// The theme update is asynchronous. Close the dialog and allow ntp-app
// to update the |backgroundSelection|.
this.dispatchEvent(new Event('close', {bubbles: true, composed: true}));
}
}
/** @private */ /** @private */
onDefaultClick_() { onDefaultClick_() {
this.backgroundSelection = {type: BackgroundSelectionType.NO_BACKGROUND}; this.backgroundSelection = {type: BackgroundSelectionType.NO_BACKGROUND};
......
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M3 12v2h10v-2z"/><path d="M3 6.846h2.99V11h3.996V6.846H13L8 2z"/></g></svg>
\ No newline at end of file
...@@ -84,6 +84,8 @@ ...@@ -84,6 +84,8 @@
file="icons/twitter.svg" type="BINDATA" compress="gzip" /> file="icons/twitter.svg" type="BINDATA" compress="gzip" />
<include name="IDR_NEW_TAB_PAGE_MAIL_SVG" <include name="IDR_NEW_TAB_PAGE_MAIL_SVG"
file="icons/mail.svg" type="BINDATA" compress="gzip" /> file="icons/mail.svg" type="BINDATA" compress="gzip" />
<include name="IDR_NEW_TAB_PAGE_UPLOAD_SVG" file="icons/upload.svg"
type="BINDATA" compress="gzip" />
<include name="IDR_NEW_TAB_PAGE_MINI_PAGE_JS" <include name="IDR_NEW_TAB_PAGE_MINI_PAGE_JS"
file="${root_gen_dir}/chrome/browser/resources/new_tab_page/mini_page.js" file="${root_gen_dir}/chrome/browser/resources/new_tab_page/mini_page.js"
use_base_dir="false" type="BINDATA" compress="gzip" /> use_base_dir="false" type="BINDATA" compress="gzip" />
......
...@@ -220,6 +220,8 @@ interface PageHandler { ...@@ -220,6 +220,8 @@ interface PageHandler {
PasteIntoOmnibox(string text); PasteIntoOmnibox(string text);
// Gets current doodle if there is one. // Gets current doodle if there is one.
GetDoodle() => (Doodle? doodle); GetDoodle() => (Doodle? doodle);
// Choose custom background from local file system.
ChooseLocalCustomBackground() => (bool success);
}; };
// WebUI-side handler for requests from the browser. // WebUI-side handler for requests from the browser.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/base64.h" #include "base/base64.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/i18n/rtl.h" #include "base/i18n/rtl.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/background/ntp_background_service.h" #include "chrome/browser/search/background/ntp_background_service.h"
...@@ -16,6 +17,8 @@ ...@@ -16,6 +17,8 @@
#include "chrome/browser/search/instant_service.h" #include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h" #include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search_provider_logos/logo_service_factory.h" #include "chrome/browser/search_provider_logos/logo_service_factory.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/browser/ui/search/ntp_user_data_logger.h"
#include "chrome/browser/ui/search/omnibox_utils.h" #include "chrome/browser/ui/search/omnibox_utils.h"
#include "chrome/common/search/generated_colors_info.h" #include "chrome/common/search/generated_colors_info.h"
#include "chrome/common/search/instant_types.h" #include "chrome/common/search/instant_types.h"
...@@ -63,7 +66,15 @@ new_tab_page::mojom::ThemePtr MakeTheme(const NtpTheme& ntp_theme) { ...@@ -63,7 +66,15 @@ new_tab_page::mojom::ThemePtr MakeTheme(const NtpTheme& ntp_theme) {
theme->logo_color = ntp_theme.logo_color; theme->logo_color = ntp_theme.logo_color;
} }
if (!ntp_theme.custom_background_url.is_empty()) { if (!ntp_theme.custom_background_url.is_empty()) {
theme->background_image_url = ntp_theme.custom_background_url; base::StringPiece url = ntp_theme.custom_background_url.spec();
// TODO(crbug.com/1041125): Clean up when chrome-search://local-ntp removed.
if (url.starts_with("chrome-search://local-ntp/")) {
theme->background_image_url =
GURL("chrome-untrusted://new-tab-page/" +
url.substr(strlen("chrome-search://local-ntp/")).as_string());
} else {
theme->background_image_url = ntp_theme.custom_background_url;
}
} }
if (!ntp_theme.custom_background_attribution_line_1.empty()) { if (!ntp_theme.custom_background_attribution_line_1.empty()) {
theme->background_image_attribution_1 = theme->background_image_attribution_1 =
...@@ -98,6 +109,7 @@ NewTabPageHandler::NewTabPageHandler( ...@@ -98,6 +109,7 @@ NewTabPageHandler::NewTabPageHandler(
NtpBackgroundServiceFactory::GetForProfile(profile)), NtpBackgroundServiceFactory::GetForProfile(profile)),
logo_service_(LogoServiceFactory::GetForProfile(profile)), logo_service_(LogoServiceFactory::GetForProfile(profile)),
page_{std::move(pending_page)}, page_{std::move(pending_page)},
profile_(profile),
receiver_{this, std::move(pending_page_handler)}, receiver_{this, std::move(pending_page_handler)},
web_contents_(web_contents) { web_contents_(web_contents) {
CHECK(instant_service_); CHECK(instant_service_);
...@@ -320,6 +332,26 @@ void NewTabPageHandler::GetDoodle(GetDoodleCallback callback) { ...@@ -320,6 +332,26 @@ void NewTabPageHandler::GetDoodle(GetDoodleCallback callback) {
logo_service_->GetLogo(std::move(callbacks), /*for_webui_ntp=*/true); logo_service_->GetLogo(std::move(callbacks), /*for_webui_ntp=*/true);
} }
void NewTabPageHandler::ChooseLocalCustomBackground(
ChooseLocalCustomBackgroundCallback callback) {
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.extension_description_overrides.push_back(
l10n_util::GetStringUTF16(IDS_UPLOAD_IMAGE_FORMAT));
select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(),
profile_->last_selected_directory(), &file_types, 0,
base::FilePath::StringType(), web_contents_->GetTopLevelNativeWindow(),
nullptr);
choose_local_custom_background_callback_ = std::move(callback);
}
void NewTabPageHandler::NtpThemeChanged(const NtpTheme& ntp_theme) { void NewTabPageHandler::NtpThemeChanged(const NtpTheme& ntp_theme) {
page_->SetTheme(MakeTheme(ntp_theme)); page_->SetTheme(MakeTheme(ntp_theme));
} }
...@@ -409,6 +441,38 @@ void NewTabPageHandler::OnOmniboxFocusChanged(OmniboxFocusState state, ...@@ -409,6 +441,38 @@ void NewTabPageHandler::OnOmniboxFocusChanged(OmniboxFocusState state,
} }
} }
void NewTabPageHandler::FileSelected(const base::FilePath& path,
int index,
void* params) {
if (instant_service_) {
profile_->set_last_selected_directory(path.DirName());
instant_service_->SelectLocalBackgroundImage(path);
}
select_file_dialog_ = nullptr;
// File selection can happen at any time after NTP load, and is not logged
// with the event.
NTPUserDataLogger::GetOrCreateFromWebContents(web_contents_)
->LogEvent(NTP_CUSTOMIZE_LOCAL_IMAGE_DONE,
base::TimeDelta::FromSeconds(0));
NTPUserDataLogger::GetOrCreateFromWebContents(web_contents_)
->LogEvent(NTP_BACKGROUND_UPLOAD_DONE, base::TimeDelta::FromSeconds(0));
std::move(choose_local_custom_background_callback_).Run(true);
}
void NewTabPageHandler::FileSelectionCanceled(void* params) {
select_file_dialog_ = nullptr;
// File selection can happen at any time after NTP load, and is not logged
// with the event.
NTPUserDataLogger::GetOrCreateFromWebContents(web_contents_)
->LogEvent(NTP_CUSTOMIZE_LOCAL_IMAGE_CANCEL,
base::TimeDelta::FromSeconds(0));
NTPUserDataLogger::GetOrCreateFromWebContents(web_contents_)
->LogEvent(NTP_BACKGROUND_UPLOAD_CANCEL, base::TimeDelta::FromSeconds(0));
std::move(choose_local_custom_background_callback_).Run(false);
}
void NewTabPageHandler::OnLogoAvailable( void NewTabPageHandler::OnLogoAvailable(
GetDoodleCallback callback, GetDoodleCallback callback,
search_provider_logos::LogoCallbackReason type, search_provider_logos::LogoCallbackReason type,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "ui/shell_dialogs/select_file_dialog.h"
class GURL; class GURL;
class InstantService; class InstantService;
...@@ -39,7 +40,8 @@ class LogoService; ...@@ -39,7 +40,8 @@ class LogoService;
class NewTabPageHandler : public new_tab_page::mojom::PageHandler, class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
public InstantServiceObserver, public InstantServiceObserver,
public NtpBackgroundServiceObserver, public NtpBackgroundServiceObserver,
public OmniboxTabHelper::Observer { public OmniboxTabHelper::Observer,
public ui::SelectFileDialog::Listener {
public: public:
NewTabPageHandler(mojo::PendingReceiver<new_tab_page::mojom::PageHandler> NewTabPageHandler(mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
pending_page_handler, pending_page_handler,
...@@ -81,6 +83,8 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler, ...@@ -81,6 +83,8 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
void FocusOmnibox() override; void FocusOmnibox() override;
void PasteIntoOmnibox(const std::string& text) override; void PasteIntoOmnibox(const std::string& text) override;
void GetDoodle(GetDoodleCallback callback) override; void GetDoodle(GetDoodleCallback callback) override;
void ChooseLocalCustomBackground(
ChooseLocalCustomBackgroundCallback callback) override;
private: private:
// InstantServiceObserver: // InstantServiceObserver:
...@@ -98,11 +102,18 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler, ...@@ -98,11 +102,18 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
void OnOmniboxFocusChanged(OmniboxFocusState state, void OnOmniboxFocusChanged(OmniboxFocusState state,
OmniboxFocusChangeReason reason) override; OmniboxFocusChangeReason reason) override;
// SelectFileDialog::Listener:
void FileSelected(const base::FilePath& path,
int index,
void* params) override;
void FileSelectionCanceled(void* params) override;
void OnLogoAvailable( void OnLogoAvailable(
GetDoodleCallback callback, GetDoodleCallback callback,
search_provider_logos::LogoCallbackReason type, search_provider_logos::LogoCallbackReason type,
const base::Optional<search_provider_logos::EncodedLogo>& logo); const base::Optional<search_provider_logos::EncodedLogo>& logo);
ChooseLocalCustomBackgroundCallback choose_local_custom_background_callback_;
chrome_colors::ChromeColorsService* chrome_colors_service_; chrome_colors::ChromeColorsService* chrome_colors_service_;
InstantService* instant_service_; InstantService* instant_service_;
NtpBackgroundService* ntp_background_service_; NtpBackgroundService* ntp_background_service_;
...@@ -112,7 +123,9 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler, ...@@ -112,7 +123,9 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
std::string images_request_collection_id_; std::string images_request_collection_id_;
GetBackgroundImagesCallback background_images_callback_; GetBackgroundImagesCallback background_images_callback_;
mojo::Remote<new_tab_page::mojom::Page> page_; mojo::Remote<new_tab_page::mojom::Page> page_;
Profile* profile_;
mojo::Receiver<new_tab_page::mojom::PageHandler> receiver_; mojo::Receiver<new_tab_page::mojom::PageHandler> receiver_;
scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
content::WebContents* web_contents_; content::WebContents* web_contents_;
base::WeakPtrFactory<NewTabPageHandler> weak_ptr_factory_{this}; base::WeakPtrFactory<NewTabPageHandler> weak_ptr_factory_{this};
......
...@@ -85,6 +85,7 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) { ...@@ -85,6 +85,7 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
{"hideShortcutsDesc", IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC}, {"hideShortcutsDesc", IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC},
{"mostVisited", IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL}, {"mostVisited", IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL},
{"myShortcuts", IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL}, {"myShortcuts", IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL},
{"noBackground", IDS_NTP_CUSTOMIZE_NO_BACKGROUND_LABEL},
{"refreshDaily", IDS_NTP_CUSTOM_BG_DAILY_REFRESH}, {"refreshDaily", IDS_NTP_CUSTOM_BG_DAILY_REFRESH},
{"shortcutsCurated", IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC}, {"shortcutsCurated", IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC},
{"shortcutsMenuItem", IDS_NTP_CUSTOMIZE_MENU_SHORTCUTS_LABEL}, {"shortcutsMenuItem", IDS_NTP_CUSTOMIZE_MENU_SHORTCUTS_LABEL},
...@@ -93,6 +94,7 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) { ...@@ -93,6 +94,7 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
{"themesMenuItem", IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL}, {"themesMenuItem", IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL},
{"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC}, {"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
{"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL}, {"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
{"uploadFromDevice", IDS_NTP_CUSTOMIZE_UPLOAD_FROM_DEVICE_LABEL},
// Voice search. // Voice search.
{"audioError", IDS_NEW_TAB_VOICE_AUDIO_ERROR}, {"audioError", IDS_NEW_TAB_VOICE_AUDIO_ERROR},
......
...@@ -7,10 +7,13 @@ ...@@ -7,10 +7,13 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include "base/files/file_util.h"
#include "base/memory/ref_counted_memory.h" #include "base/memory/ref_counted_memory.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/one_google_bar/one_google_bar_data.h" #include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
#include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h" #include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
...@@ -33,11 +36,24 @@ std::string FormatTemplate(int resource_id, ...@@ -33,11 +36,24 @@ std::string FormatTemplate(int resource_id,
return ui::ReplaceTemplateExpressions(string_piece, replacements); return ui::ReplaceTemplateExpressions(string_piece, replacements);
} }
std::string ReadBackgroundImageData(const base::FilePath& profile_path) {
std::string data_string;
base::ReadFileToString(profile_path.AppendASCII("background.jpg"),
&data_string);
return data_string;
}
void ServeBackgroundImageData(content::URLDataSource::GotDataCallback callback,
std::string data_string) {
std::move(callback).Run(base::RefCountedString::TakeString(&data_string));
}
} // namespace } // namespace
UntrustedSource::UntrustedSource(Profile* profile) UntrustedSource::UntrustedSource(Profile* profile)
: one_google_bar_service_( : one_google_bar_service_(
OneGoogleBarServiceFactory::GetForProfile(profile)), OneGoogleBarServiceFactory::GetForProfile(profile)),
profile_(profile),
promo_service_(PromoServiceFactory::GetForProfile(profile)) { promo_service_(PromoServiceFactory::GetForProfile(profile)) {
// |promo_service_| is null in incognito, or when the feature is // |promo_service_| is null in incognito, or when the feature is
// disabled. // disabled.
...@@ -99,7 +115,9 @@ void UntrustedSource::StartDataRequest( ...@@ -99,7 +115,9 @@ void UntrustedSource::StartDataRequest(
return; return;
} }
if ((path == "image" || path == "background_image" || path == "iframe") && if ((path == "image" || path == "background_image" || path == "iframe") &&
url_param.is_valid() && url_param.SchemeIs(url::kHttpsScheme)) { url_param.is_valid() &&
(url_param.SchemeIs(url::kHttpsScheme) ||
url_param.SchemeIs(content::kChromeUIUntrustedScheme))) {
ui::TemplateReplacements replacements; ui::TemplateReplacements replacements;
replacements["url"] = url_param.spec(); replacements["url"] = url_param.spec();
int resource_id = int resource_id =
...@@ -112,12 +130,24 @@ void UntrustedSource::StartDataRequest( ...@@ -112,12 +130,24 @@ void UntrustedSource::StartDataRequest(
std::move(callback).Run(base::RefCountedString::TakeString(&html)); std::move(callback).Run(base::RefCountedString::TakeString(&html));
return; return;
} }
if (path == "background.jpg") {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::BindOnce(&ReadBackgroundImageData, profile_->GetPath()),
base::BindOnce(&ServeBackgroundImageData, std::move(callback)));
return;
}
std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>()); std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>());
} }
std::string UntrustedSource::GetMimeType(const std::string& path) { std::string UntrustedSource::GetMimeType(const std::string& path) {
if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII)) const std::string stripped_path = path.substr(0, path.find("?"));
if (base::EndsWith(stripped_path, ".js",
base::CompareCase::INSENSITIVE_ASCII))
return "application/javascript"; return "application/javascript";
if (base::EndsWith(stripped_path, ".jpg",
base::CompareCase::INSENSITIVE_ASCII))
return "image/jpg";
return "text/html"; return "text/html";
} }
...@@ -145,7 +175,8 @@ bool UntrustedSource::ShouldServiceRequest( ...@@ -145,7 +175,8 @@ bool UntrustedSource::ShouldServiceRequest(
const std::string path = url.path().substr(1); const std::string path = url.path().substr(1);
return path == "one-google-bar" || path == "one_google_bar.js" || return path == "one-google-bar" || path == "one_google_bar.js" ||
path == "promo" || path == "promo.js" || path == "image" || path == "promo" || path == "promo.js" || path == "image" ||
path == "background_image" || path == "iframe"; path == "background_image" || path == "iframe" ||
path == "background.jpg";
} }
void UntrustedSource::OnOneGoogleBarDataUpdated() { void UntrustedSource::OnOneGoogleBarDataUpdated() {
......
...@@ -31,7 +31,7 @@ class Profile; ...@@ -31,7 +31,7 @@ class Profile;
// will cover the entire element. // will cover the entire element.
// * chrome-untrusted://new-tab-page/iframe?<url>: Behaves like an iframe with // * chrome-untrusted://new-tab-page/iframe?<url>: Behaves like an iframe with
// src set to <url>. // src set to <url>.
// Each of those helpers only accept HTTPS URLs. // Each of those helpers only accept URLs with HTTPS or chrome-untrusted:.
class UntrustedSource : public content::URLDataSource, class UntrustedSource : public content::URLDataSource,
public OneGoogleBarServiceObserver, public OneGoogleBarServiceObserver,
public PromoServiceObserver { public PromoServiceObserver {
...@@ -71,6 +71,7 @@ class UntrustedSource : public content::URLDataSource, ...@@ -71,6 +71,7 @@ class UntrustedSource : public content::URLDataSource,
OneGoogleBarService* one_google_bar_service_; OneGoogleBarService* one_google_bar_service_;
ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver> ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver>
one_google_bar_service_observer_{this}; one_google_bar_service_observer_{this};
Profile* profile_;
std::vector<content::URLDataSource::GotDataCallback> promo_callbacks_; std::vector<content::URLDataSource::GotDataCallback> promo_callbacks_;
PromoService* promo_service_; PromoService* promo_service_;
ScopedObserver<PromoService, PromoServiceObserver> promo_service_observer_{ ScopedObserver<PromoService, PromoServiceObserver> promo_service_observer_{
......
...@@ -7,7 +7,7 @@ import 'chrome://new-tab-page/customize_backgrounds.js'; ...@@ -7,7 +7,7 @@ import 'chrome://new-tab-page/customize_backgrounds.js';
import {BrowserProxy} from 'chrome://new-tab-page/browser_proxy.js'; import {BrowserProxy} from 'chrome://new-tab-page/browser_proxy.js';
import {BackgroundSelectionType} from 'chrome://new-tab-page/customize_dialog.js'; import {BackgroundSelectionType} from 'chrome://new-tab-page/customize_dialog.js';
import {assertNotStyle, assertStyle, createTestProxy} from 'chrome://test/new_tab_page/test_support.js'; import {assertNotStyle, assertStyle, createTestProxy} from 'chrome://test/new_tab_page/test_support.js';
import {flushTasks, isVisible} from 'chrome://test/test_util.m.js'; import {eventToPromise, flushTasks, isVisible} from 'chrome://test/test_util.m.js';
function createCollection(id = 0, label = '', url = '') { function createCollection(id = 0, label = '', url = '') {
return {id: id, label: label, previewImageUrl: {url: url}}; return {id: id, label: label, previewImageUrl: {url: url}};
...@@ -56,11 +56,11 @@ suite('NewTabPageCustomizeBackgroundsTest', () => { ...@@ -56,11 +56,11 @@ suite('NewTabPageCustomizeBackgroundsTest', () => {
assertStyle(customizeBackgrounds.$.images, 'display', 'none'); assertStyle(customizeBackgrounds.$.images, 'display', 'none');
const tiles = const tiles =
customizeBackgrounds.shadowRoot.querySelectorAll('#collections .tile'); customizeBackgrounds.shadowRoot.querySelectorAll('#collections .tile');
assertEquals(2, tiles.length); assertEquals(3, tiles.length);
assertEquals('col_0', tiles[1].getAttribute('title')); assertEquals('col_0', tiles[2].getAttribute('title'));
assertEquals( assertEquals(
'background_image?https://col_0.jpg', 'background_image?https://col_0.jpg',
tiles[1].querySelector('.image').path); tiles[2].querySelector('.image').path);
}); });
test('clicking collection selects collection', async function() { test('clicking collection selects collection', async function() {
...@@ -73,7 +73,7 @@ suite('NewTabPageCustomizeBackgroundsTest', () => { ...@@ -73,7 +73,7 @@ suite('NewTabPageCustomizeBackgroundsTest', () => {
// Act. // Act.
customizeBackgrounds.shadowRoot customizeBackgrounds.shadowRoot
.querySelector('#collections .tile:nth-child(2)') .querySelector('#collections .tile:nth-child(3)')
.click(); .click();
// Assert. // Assert.
...@@ -214,6 +214,16 @@ suite('NewTabPageCustomizeBackgroundsTest', () => { ...@@ -214,6 +214,16 @@ suite('NewTabPageCustomizeBackgroundsTest', () => {
assertFalse(element.classList.contains('selected')); assertFalse(element.classList.contains('selected'));
}); });
test('choosing local dispatches cancel', async () => {
const customizeBackgrounds = await createCustomizeBackgrounds();
handler.setResultFor(
'chooseLocalCustomBackground', Promise.resolve({success: true}));
const waitForClose = eventToPromise('close', customizeBackgrounds);
customizeBackgrounds.$.uploadFromDevice.click();
await handler.whenCalled('chooseLocalCustomBackground');
await waitForClose;
});
suite('no background', () => { suite('no background', () => {
let customizeBackgrounds; let customizeBackgrounds;
......
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