Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / webui / ntp / new_tab_ui.cc
blob2392516fed5f725c62667bb1fec28b0e48cdc928
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
7 #include <set>
9 #include "base/i18n/rtl.h"
10 #include "base/lazy_instance.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/webui/metrics_handler.h"
18 #include "chrome/browser/ui/webui/ntp/favicon_webui_handler.h"
19 #include "chrome/browser/ui/webui/ntp/foreign_session_handler.h"
20 #include "chrome/browser/ui/webui/ntp/most_visited_handler.h"
21 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
22 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h"
23 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
24 #include "chrome/browser/ui/webui/ntp/recently_closed_tabs_handler.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/url_constants.h"
27 #include "components/user_prefs/pref_registry_syncable.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/url_data_source.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_ui.h"
35 #include "grit/browser_resources.h"
36 #include "grit/generated_resources.h"
37 #include "ui/base/l10n/l10n_util.h"
39 #if !defined(OS_ANDROID)
40 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
41 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
42 #include "chrome/browser/ui/webui/ntp/new_tab_page_handler.h"
43 #include "chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.h"
44 #include "chrome/browser/ui/webui/ntp/ntp_login_handler.h"
45 #include "chrome/browser/ui/webui/ntp/suggestions_page_handler.h"
46 #else
47 #include "chrome/browser/ui/webui/ntp/android/bookmarks_handler.h"
48 #include "chrome/browser/ui/webui/ntp/android/context_menu_handler.h"
49 #include "chrome/browser/ui/webui/ntp/android/navigation_handler.h"
50 #include "chrome/browser/ui/webui/ntp/android/new_tab_page_ready_handler.h"
51 #include "chrome/browser/ui/webui/ntp/android/promo_handler.h"
52 #endif
54 #if defined(ENABLE_THEMES)
55 #include "chrome/browser/ui/webui/theme_handler.h"
56 #endif
58 #if defined(USE_ASH)
59 #include "chrome/browser/ui/host_desktop.h"
60 #endif
62 using content::BrowserThread;
63 using content::RenderViewHost;
64 using content::WebUIController;
66 namespace {
68 // The amount of time there must be no painting for us to consider painting
69 // finished. Observed times are in the ~1200ms range on Windows.
70 const int kTimeoutMs = 2000;
72 // Strings sent to the page via jstemplates used to set the direction of the
73 // HTML document based on locale.
74 const char kRTLHtmlTextDirection[] = "rtl";
75 const char kLTRHtmlTextDirection[] = "ltr";
77 static base::LazyInstance<std::set<const WebUIController*> > g_live_new_tabs;
79 const char* GetHtmlTextDirection(const base::string16& text) {
80 if (base::i18n::IsRTL() && base::i18n::StringContainsStrongRTLChars(text))
81 return kRTLHtmlTextDirection;
82 else
83 return kLTRHtmlTextDirection;
86 } // namespace
88 ///////////////////////////////////////////////////////////////////////////////
89 // NewTabUI
91 NewTabUI::NewTabUI(content::WebUI* web_ui)
92 : WebUIController(web_ui),
93 WebContentsObserver(web_ui->GetWebContents()),
94 showing_sync_bubble_(false) {
95 g_live_new_tabs.Pointer()->insert(this);
96 web_ui->OverrideTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
98 // We count all link clicks as AUTO_BOOKMARK, so that site can be ranked more
99 // highly. Note this means we're including clicks on not only most visited
100 // thumbnails, but also clicks on recently bookmarked.
101 web_ui->SetLinkTransitionType(content::PAGE_TRANSITION_AUTO_BOOKMARK);
103 if (!GetProfile()->IsOffTheRecord()) {
104 web_ui->AddMessageHandler(new browser_sync::ForeignSessionHandler());
105 web_ui->AddMessageHandler(new MetricsHandler());
106 web_ui->AddMessageHandler(new MostVisitedHandler());
107 web_ui->AddMessageHandler(new RecentlyClosedTabsHandler());
108 #if !defined(OS_ANDROID)
109 web_ui->AddMessageHandler(new FaviconWebUIHandler());
110 web_ui->AddMessageHandler(new NewTabPageHandler());
111 web_ui->AddMessageHandler(new CoreAppLauncherHandler());
112 if (NewTabUI::IsDiscoveryInNTPEnabled())
113 web_ui->AddMessageHandler(new SuggestionsHandler());
114 // Android doesn't have a sync promo/username on NTP.
115 web_ui->AddMessageHandler(new NewTabPageSyncHandler());
117 if (MightShowApps()) {
118 ExtensionService* service = GetProfile()->GetExtensionService();
119 // We might not have an ExtensionService (on ChromeOS when not logged in
120 // for example).
121 if (service)
122 web_ui->AddMessageHandler(new AppLauncherHandler(service));
124 #endif
127 #if defined(OS_ANDROID)
128 // These handlers are specific to the Android NTP page.
129 web_ui->AddMessageHandler(new BookmarksHandler());
130 web_ui->AddMessageHandler(new ContextMenuHandler());
131 web_ui->AddMessageHandler(new FaviconWebUIHandler());
132 web_ui->AddMessageHandler(new NavigationHandler());
133 web_ui->AddMessageHandler(new NewTabPageReadyHandler());
134 if (!GetProfile()->IsOffTheRecord())
135 web_ui->AddMessageHandler(new PromoHandler());
136 #else
137 // Android uses native UI for sync setup.
138 if (NTPLoginHandler::ShouldShow(GetProfile()))
139 web_ui->AddMessageHandler(new NTPLoginHandler());
140 #endif
142 #if defined(ENABLE_THEMES)
143 // The theme handler can require some CPU, so do it after hooking up the most
144 // visited handler. This allows the DB query for the new tab thumbs to happen
145 // earlier.
146 web_ui->AddMessageHandler(new ThemeHandler());
147 #endif
149 scoped_ptr<NewTabHTMLSource> html_source(new NewTabHTMLSource(
150 GetProfile()->GetOriginalProfile()));
152 // These two resources should be loaded only if suggestions NTP is enabled.
153 html_source->AddResource("suggestions_page.css", "text/css",
154 NewTabUI::IsDiscoveryInNTPEnabled() ? IDR_SUGGESTIONS_PAGE_CSS : 0);
155 if (NewTabUI::IsDiscoveryInNTPEnabled()) {
156 html_source->AddResource("suggestions_page.js", "application/javascript",
157 IDR_SUGGESTIONS_PAGE_JS);
159 // content::URLDataSource assumes the ownership of the html_source.
160 content::URLDataSource::Add(GetProfile(), html_source.release());
162 pref_change_registrar_.Init(GetProfile()->GetPrefs());
163 pref_change_registrar_.Add(prefs::kShowBookmarkBar,
164 base::Bind(&NewTabUI::OnShowBookmarkBarChanged,
165 base::Unretained(this)));
168 NewTabUI::~NewTabUI() {
169 g_live_new_tabs.Pointer()->erase(this);
172 // The timer callback. If enough time has elapsed since the last paint
173 // message, we say we're done painting; otherwise, we keep waiting.
174 void NewTabUI::PaintTimeout() {
175 // The amount of time there must be no painting for us to consider painting
176 // finished. Observed times are in the ~1200ms range on Windows.
177 base::TimeTicks now = base::TimeTicks::Now();
178 if ((now - last_paint_) >= base::TimeDelta::FromMilliseconds(kTimeoutMs)) {
179 // Painting has quieted down. Log this as the full time to run.
180 base::TimeDelta load_time = last_paint_ - start_;
181 UMA_HISTOGRAM_TIMES("NewTabUI load", load_time);
182 } else {
183 // Not enough quiet time has elapsed.
184 // Some more paints must've occurred since we set the timeout.
185 // Wait some more.
186 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
187 &NewTabUI::PaintTimeout);
191 void NewTabUI::StartTimingPaint(RenderViewHost* render_view_host) {
192 start_ = base::TimeTicks::Now();
193 last_paint_ = start_;
195 content::NotificationSource source =
196 content::Source<content::RenderWidgetHost>(render_view_host);
197 if (!registrar_.IsRegistered(this,
198 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
199 source)) {
200 registrar_.Add(
201 this,
202 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
203 source);
206 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
207 &NewTabUI::PaintTimeout);
210 void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) {
211 StartTimingPaint(render_view_host);
214 void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) {
215 StartTimingPaint(render_view_host);
218 void NewTabUI::WasHidden() {
219 EmitNtpStatistics();
222 void NewTabUI::Observe(int type,
223 const content::NotificationSource& source,
224 const content::NotificationDetails& details) {
225 switch (type) {
226 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: {
227 last_paint_ = base::TimeTicks::Now();
228 break;
230 default:
231 CHECK(false) << "Unexpected notification: " << type;
235 void NewTabUI::EmitNtpStatistics() {
236 NTPUserDataLogger::GetOrCreateFromWebContents(
237 web_contents())->EmitNtpStatistics();
240 void NewTabUI::OnShowBookmarkBarChanged() {
241 base::StringValue attached(
242 GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) ?
243 "true" : "false");
244 web_ui()->CallJavascriptFunction("ntp.setBookmarkBarAttached", attached);
247 // static
248 void NewTabUI::RegisterProfilePrefs(
249 user_prefs::PrefRegistrySyncable* registry) {
250 #if !defined(OS_ANDROID)
251 CoreAppLauncherHandler::RegisterProfilePrefs(registry);
252 NewTabPageHandler::RegisterProfilePrefs(registry);
253 if (NewTabUI::IsDiscoveryInNTPEnabled())
254 SuggestionsHandler::RegisterProfilePrefs(registry);
255 #endif
256 MostVisitedHandler::RegisterProfilePrefs(registry);
257 browser_sync::ForeignSessionHandler::RegisterProfilePrefs(registry);
260 // static
261 bool NewTabUI::MightShowApps() {
262 // Android does not have apps.
263 #if defined(OS_ANDROID)
264 return false;
265 #else
266 return true;
267 #endif
270 // static
271 bool NewTabUI::ShouldShowApps() {
272 // Ash shows apps in app list thus should not show apps page in NTP4.
273 // Android does not have apps.
274 #if defined(OS_ANDROID)
275 return false;
276 #elif defined(USE_ASH)
277 return chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH;
278 #else
279 return true;
280 #endif
283 // static
284 bool NewTabUI::IsDiscoveryInNTPEnabled() {
285 // TODO(beaudoin): The flag was removed during a clean-up pass. We leave that
286 // here to easily enable it back when we will explore this option again.
287 return false;
290 // static
291 void NewTabUI::SetUrlTitleAndDirection(base::DictionaryValue* dictionary,
292 const base::string16& title,
293 const GURL& gurl) {
294 dictionary->SetString("url", gurl.spec());
296 bool using_url_as_the_title = false;
297 base::string16 title_to_set(title);
298 if (title_to_set.empty()) {
299 using_url_as_the_title = true;
300 title_to_set = base::UTF8ToUTF16(gurl.spec());
303 // We set the "dir" attribute of the title, so that in RTL locales, a LTR
304 // title is rendered left-to-right and truncated from the right. For example,
305 // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN:
306 // Microsoft developer network". In RTL locales, in the [New Tab] page, if
307 // the "dir" of this title is not specified, it takes Chrome UI's
308 // directionality. So the title will be truncated as "soft developer
309 // network". Setting the "dir" attribute as "ltr" renders the truncated title
310 // as "MSDN: Microsoft D...". As another example, the title of
311 // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the
312 // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
313 // "ltr".
314 std::string direction;
315 if (using_url_as_the_title)
316 direction = kLTRHtmlTextDirection;
317 else
318 direction = GetHtmlTextDirection(title);
320 dictionary->SetString("title", title_to_set);
321 dictionary->SetString("direction", direction);
324 // static
325 void NewTabUI::SetFullNameAndDirection(const base::string16& full_name,
326 base::DictionaryValue* dictionary) {
327 dictionary->SetString("full_name", full_name);
328 dictionary->SetString("full_name_direction", GetHtmlTextDirection(full_name));
331 // static
332 NewTabUI* NewTabUI::FromWebUIController(WebUIController* ui) {
333 if (!g_live_new_tabs.Pointer()->count(ui))
334 return NULL;
335 return static_cast<NewTabUI*>(ui);
338 Profile* NewTabUI::GetProfile() const {
339 return Profile::FromWebUI(web_ui());
342 ///////////////////////////////////////////////////////////////////////////////
343 // NewTabHTMLSource
345 NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile)
346 : profile_(profile) {
349 std::string NewTabUI::NewTabHTMLSource::GetSource() const {
350 return chrome::kChromeUINewTabHost;
353 void NewTabUI::NewTabHTMLSource::StartDataRequest(
354 const std::string& path,
355 int render_process_id,
356 int render_frame_id,
357 const content::URLDataSource::GotDataCallback& callback) {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360 std::map<std::string, std::pair<std::string, int> >::iterator it =
361 resource_map_.find(path);
362 if (it != resource_map_.end()) {
363 scoped_refptr<base::RefCountedStaticMemory> resource_bytes(
364 it->second.second ?
365 ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
366 it->second.second) :
367 new base::RefCountedStaticMemory);
368 callback.Run(resource_bytes.get());
369 return;
372 if (!path.empty() && path[0] != '#') {
373 // A path under new-tab was requested; it's likely a bad relative
374 // URL from the new tab page, but in any case it's an error.
376 // TODO(dtrainor): Can remove this #if check once we update the
377 // accessibility script to no longer try to access urls like
378 // '?2314124523523'.
379 // See http://crbug.com/150252.
380 #if !defined(OS_ANDROID)
381 NOTREACHED() << path << " should not have been requested on the NTP";
382 #endif
383 callback.Run(NULL);
384 return;
387 content::RenderProcessHost* render_host =
388 content::RenderProcessHost::FromID(render_process_id);
389 NTPResourceCache::WindowType win_type = NTPResourceCache::GetWindowType(
390 profile_, render_host);
391 scoped_refptr<base::RefCountedMemory> html_bytes(
392 NTPResourceCacheFactory::GetForProfile(profile_)->
393 GetNewTabHTML(win_type));
395 callback.Run(html_bytes.get());
398 std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string& resource)
399 const {
400 std::map<std::string, std::pair<std::string, int> >::const_iterator it =
401 resource_map_.find(resource);
402 if (it != resource_map_.end())
403 return it->second.first;
404 return "text/html";
407 bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const {
408 return false;
411 bool NewTabUI::NewTabHTMLSource::ShouldAddContentSecurityPolicy() const {
412 return false;
415 void NewTabUI::NewTabHTMLSource::AddResource(const char* resource,
416 const char* mime_type,
417 int resource_id) {
418 DCHECK(resource);
419 DCHECK(mime_type);
420 resource_map_[std::string(resource)] =
421 std::make_pair(std::string(mime_type), resource_id);
424 NewTabUI::NewTabHTMLSource::~NewTabHTMLSource() {}