1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/search/instant_service.h"
12 #include "chrome/browser/search/instant_service_factory.h"
13 #include "chrome/browser/search_engines/template_url_service_factory.h"
14 #include "chrome/browser/ui/webui/favicon_source.h"
15 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
16 #include "chrome/browser/ui/webui/new_tab_page/untrusted_source.h"
17 #include "chrome/browser/ui/webui/webui_util.h"
18 #include "chrome/common/url_constants.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "chrome/grit/new_tab_page_resources.h"
21 #include "chrome/grit/new_tab_page_resources_map.h"
22 #include "components/favicon_base/favicon_url_parser.h"
23 #include "components/strings/grit/components_strings.h"
24 #include "content/public/browser/url_data_source.h"
25 #include "content/public/browser/web_ui_data_source.h"
26 #include "ui/base/accelerators/accelerator.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/webui/web_ui_util.h"
29
30 using content::BrowserContext;
31 using content::WebContents;
32
33 namespace {
34
35 constexpr char kGeneratedPath[] =
36 "@out_folder@/gen/chrome/browser/resources/new_tab_page/";
37
CreateNewTabPageUiHtmlSource(Profile * profile)38 content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
39 content::WebUIDataSource* source =
40 content::WebUIDataSource::Create(chrome::kChromeUINewTabPageHost);
41 source->OverrideContentSecurityPolicyChildSrc(base::StringPrintf(
42 "frame-src %s;", chrome::kChromeUIUntrustedNewTabPageUrl));
43
44 ui::Accelerator undo_accelerator(ui::VKEY_Z, ui::EF_PLATFORM_ACCELERATOR);
45 source->AddString("undoDescription", l10n_util::GetStringFUTF16(
46 IDS_UNDO_DESCRIPTION,
47 undo_accelerator.GetShortcutText()));
48 source->AddString("googleBaseUrl",
49 GURL(TemplateURLServiceFactory::GetForProfile(profile)
50 ->search_terms_data()
51 .GoogleBaseURLValue())
52 .spec());
53
54 static constexpr webui::LocalizedString kStrings[] = {
55 {"title", IDS_NEW_TAB_TITLE},
56 {"undo", IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE},
57
58 // Custom Links
59 {"addLinkTitle", IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TITLE},
60 {"editLinkTitle", IDS_NTP_CUSTOM_LINKS_EDIT_SHORTCUT},
61 {"invalidUrl", IDS_NTP_CUSTOM_LINKS_INVALID_URL},
62 {"linkAddedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_ADDED},
63 {"linkCancel", IDS_NTP_CUSTOM_LINKS_CANCEL},
64 {"linkCantCreate", IDS_NTP_CUSTOM_LINKS_CANT_CREATE},
65 {"linkCantEdit", IDS_NTP_CUSTOM_LINKS_CANT_EDIT},
66 {"linkDone", IDS_NTP_CUSTOM_LINKS_DONE},
67 {"linkEditedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_EDITED},
68 {"linkRemove", IDS_NTP_CUSTOM_LINKS_REMOVE},
69 {"linkRemovedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_REMOVED},
70 {"nameField", IDS_NTP_CUSTOM_LINKS_NAME},
71 {"restoreDefaultLinks", IDS_NTP_CONFIRM_MSG_RESTORE_DEFAULTS},
72 {"restoreThumbnailsShort", IDS_NEW_TAB_RESTORE_THUMBNAILS_SHORT_LINK},
73 {"urlField", IDS_NTP_CUSTOM_LINKS_URL},
74
75 // Customize button and dialog.
76 {"backButton", IDS_ACCNAME_BACK},
77 {"backgroundsMenuItem", IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL},
78 {"cancelButton", IDS_CANCEL},
79 {"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
80 {"customizeButton", IDS_NTP_CUSTOMIZE_BUTTON_LABEL},
81 {"customizeThisPage", IDS_NTP_CUSTOM_BG_CUSTOMIZE_NTP_LABEL},
82 {"defaultThemeLabel", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
83 {"doneButton", IDS_DONE},
84 {"hideShortcuts", IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_LABEL},
85 {"hideShortcutsDesc", IDS_NTP_CUSTOMIZE_HIDE_SHORTCUTS_DESC},
86 {"mostVisited", IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL},
87 {"myShortcuts", IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_LABEL},
88 {"shortcutsCurated", IDS_NTP_CUSTOMIZE_MY_SHORTCUTS_DESC},
89 {"shortcutsMenuItem", IDS_NTP_CUSTOMIZE_MENU_SHORTCUTS_LABEL},
90 {"shortcutsOption", IDS_NTP_CUSTOMIZE_MENU_SHORTCUTS_LABEL},
91 {"shortcutsSuggested", IDS_NTP_CUSTOMIZE_MOST_VISITED_DESC},
92 {"themesMenuItem", IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL},
93 {"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
94 {"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
95
96 // Voice search.
97 {"audioError", IDS_NEW_TAB_VOICE_AUDIO_ERROR},
98 {"close", IDS_NEW_TAB_VOICE_CLOSE_TOOLTIP},
99 {"details", IDS_NEW_TAB_VOICE_DETAILS},
100 {"languageError", IDS_NEW_TAB_VOICE_LANGUAGE_ERROR},
101 {"learnMore", IDS_LEARN_MORE},
102 {"listening", IDS_NEW_TAB_VOICE_LISTENING},
103 {"networkError", IDS_NEW_TAB_VOICE_NETWORK_ERROR},
104 {"noTranslation", IDS_NEW_TAB_VOICE_NO_TRANSLATION},
105 {"noVoice", IDS_NEW_TAB_VOICE_NO_VOICE},
106 {"otherError", IDS_NEW_TAB_VOICE_OTHER_ERROR},
107 {"permissionError", IDS_NEW_TAB_VOICE_PERMISSION_ERROR},
108 {"speak", IDS_NEW_TAB_VOICE_READY},
109 {"tryAgain", IDS_NEW_TAB_VOICE_TRY_AGAIN},
110 {"voiceSearchButtonLabel", IDS_TOOLTIP_MIC_SEARCH},
111 {"waiting", IDS_NEW_TAB_VOICE_WAITING},
112
113 // Search box.
114 {"searchBoxHint", IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MD},
115 };
116 AddLocalizedStringsBulk(source, kStrings);
117
118 source->AddResourcePath("skcolor.mojom-lite.js",
119 IDR_NEW_TAB_PAGE_SKCOLOR_MOJO_LITE_JS);
120 source->AddResourcePath("new_tab_page.mojom-lite.js",
121 IDR_NEW_TAB_PAGE_MOJO_LITE_JS);
122 webui::SetupWebUIDataSource(
123 source, base::make_span(kNewTabPageResources, kNewTabPageResourcesSize),
124 kGeneratedPath, IDR_NEW_TAB_PAGE_NEW_TAB_PAGE_HTML);
125
126 return source;
127 }
128
129 } // namespace
130
NewTabPageUI(content::WebUI * web_ui)131 NewTabPageUI::NewTabPageUI(content::WebUI* web_ui)
132 : ui::MojoWebUIController(web_ui, true),
133 page_factory_receiver_(this),
134 profile_(Profile::FromWebUI(web_ui)),
135 instant_service_(InstantServiceFactory::GetForProfile(profile_)),
136 web_contents_(web_ui->GetWebContents()) {
137 content::WebUIDataSource::Add(profile_,
138 CreateNewTabPageUiHtmlSource(profile_));
139
140 content::URLDataSource::Add(
141 profile_, std::make_unique<FaviconSource>(
142 profile_, chrome::FaviconUrlFormat::kFavicon2));
143 content::URLDataSource::Add(profile_,
144 std::make_unique<UntrustedSource>(profile_));
145
146 web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
147
148 UpdateBackgroundColor(*instant_service_->GetInitializedNtpTheme());
149 instant_service_->AddObserver(this);
150 }
151
WEB_UI_CONTROLLER_TYPE_IMPL(NewTabPageUI)152 WEB_UI_CONTROLLER_TYPE_IMPL(NewTabPageUI)
153
154 NewTabPageUI::~NewTabPageUI() {
155 instant_service_->RemoveObserver(this);
156 }
157
158 // static
IsNewTabPageOrigin(const GURL & url)159 bool NewTabPageUI::IsNewTabPageOrigin(const GURL& url) {
160 return url.GetOrigin() == GURL(chrome::kChromeUINewTabPageURL).GetOrigin();
161 }
162
BindInterface(mojo::PendingReceiver<new_tab_page::mojom::PageHandlerFactory> pending_receiver)163 void NewTabPageUI::BindInterface(
164 mojo::PendingReceiver<new_tab_page::mojom::PageHandlerFactory>
165 pending_receiver) {
166 if (page_factory_receiver_.is_bound()) {
167 page_factory_receiver_.reset();
168 }
169
170 page_factory_receiver_.Bind(std::move(pending_receiver));
171 }
172
CreatePageHandler(mojo::PendingRemote<new_tab_page::mojom::Page> pending_page,mojo::PendingReceiver<new_tab_page::mojom::PageHandler> pending_page_handler)173 void NewTabPageUI::CreatePageHandler(
174 mojo::PendingRemote<new_tab_page::mojom::Page> pending_page,
175 mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
176 pending_page_handler) {
177 DCHECK(pending_page.is_valid());
178 page_handler_ = std::make_unique<NewTabPageHandler>(
179 std::move(pending_page_handler), std::move(pending_page), profile_,
180 web_contents_);
181 }
182
NtpThemeChanged(const NtpTheme & theme)183 void NewTabPageUI::NtpThemeChanged(const NtpTheme& theme) {
184 // Load time data is cached across page reloads. Update the background color
185 // here to prevent a white flicker on page reload.
186 UpdateBackgroundColor(theme);
187 }
188
MostVisitedInfoChanged(const InstantMostVisitedInfo & info)189 void NewTabPageUI::MostVisitedInfoChanged(const InstantMostVisitedInfo& info) {}
190
UpdateBackgroundColor(const NtpTheme & theme)191 void NewTabPageUI::UpdateBackgroundColor(const NtpTheme& theme) {
192 std::unique_ptr<base::DictionaryValue> update(new base::DictionaryValue);
193 auto background_color = theme.background_color;
194 update->SetString(
195 "backgroundColor",
196 base::StringPrintf("#%02X%02X%02X", SkColorGetR(background_color),
197 SkColorGetG(background_color),
198 SkColorGetB(background_color)));
199 content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
200 std::move(update));
201 }
202