Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / webui / ntp / new_tab_ui.cc
blobe92c3f2d20fcfb3e56fab7d38d62e17ce40e588b
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/app_launcher_handler.h"
19 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
20 #include "chrome/browser/ui/webui/ntp/favicon_webui_handler.h"
21 #include "chrome/browser/ui/webui/ntp/new_tab_page_handler.h"
22 #include "chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.h"
23 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
24 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h"
25 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/grit/generated_resources.h"
29 #include "components/pref_registry/pref_registry_syncable.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/render_view_host.h"
34 #include "content/public/browser/url_data_source.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_ui.h"
37 #include "extensions/browser/extension_system.h"
38 #include "grit/browser_resources.h"
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/base/resource/resource_bundle.h"
42 #if defined(ENABLE_THEMES)
43 #include "chrome/browser/ui/webui/theme_handler.h"
44 #endif
46 #if defined(USE_ASH)
47 #include "chrome/browser/ui/host_desktop.h"
48 #endif
50 using content::BrowserThread;
51 using content::RenderViewHost;
52 using content::WebUIController;
54 namespace {
56 // The amount of time there must be no painting for us to consider painting
57 // finished. Observed times are in the ~1200ms range on Windows.
58 const int kTimeoutMs = 2000;
60 // Strings sent to the page via jstemplates used to set the direction of the
61 // HTML document based on locale.
62 const char kRTLHtmlTextDirection[] = "rtl";
63 const char kLTRHtmlTextDirection[] = "ltr";
65 static base::LazyInstance<std::set<const WebUIController*> > g_live_new_tabs;
67 const char* GetHtmlTextDirection(const base::string16& text) {
68 if (base::i18n::IsRTL() && base::i18n::StringContainsStrongRTLChars(text))
69 return kRTLHtmlTextDirection;
70 else
71 return kLTRHtmlTextDirection;
74 } // namespace
76 ///////////////////////////////////////////////////////////////////////////////
77 // NewTabUI
79 NewTabUI::NewTabUI(content::WebUI* web_ui)
80 : WebUIController(web_ui),
81 WebContentsObserver(web_ui->GetWebContents()),
82 showing_sync_bubble_(false) {
83 g_live_new_tabs.Pointer()->insert(this);
84 web_ui->OverrideTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
86 // We count all link clicks as AUTO_BOOKMARK, so that site can be ranked more
87 // highly. Note this means we're including clicks on not only most visited
88 // thumbnails, but also clicks on recently bookmarked.
89 web_ui->SetLinkTransitionType(ui::PAGE_TRANSITION_AUTO_BOOKMARK);
91 Profile* profile = GetProfile();
92 if (!profile->IsOffTheRecord()) {
93 web_ui->AddMessageHandler(new MetricsHandler());
94 web_ui->AddMessageHandler(new FaviconWebUIHandler());
95 web_ui->AddMessageHandler(new NewTabPageHandler());
96 web_ui->AddMessageHandler(new CoreAppLauncherHandler());
97 web_ui->AddMessageHandler(new NewTabPageSyncHandler());
99 ExtensionService* service =
100 extensions::ExtensionSystem::Get(profile)->extension_service();
101 // We might not have an ExtensionService (on ChromeOS when not logged in
102 // for example).
103 if (service)
104 web_ui->AddMessageHandler(new AppLauncherHandler(service));
107 #if defined(ENABLE_THEMES)
108 // The theme handler can require some CPU, so do it after hooking up the most
109 // visited handler. This allows the DB query for the new tab thumbs to happen
110 // earlier.
111 web_ui->AddMessageHandler(new ThemeHandler());
112 #endif
114 scoped_ptr<NewTabHTMLSource> html_source(
115 new NewTabHTMLSource(profile->GetOriginalProfile()));
117 // content::URLDataSource assumes the ownership of the html_source.
118 content::URLDataSource::Add(profile, html_source.release());
120 pref_change_registrar_.Init(profile->GetPrefs());
121 pref_change_registrar_.Add(bookmarks::prefs::kShowBookmarkBar,
122 base::Bind(&NewTabUI::OnShowBookmarkBarChanged,
123 base::Unretained(this)));
126 NewTabUI::~NewTabUI() {
127 g_live_new_tabs.Pointer()->erase(this);
130 // The timer callback. If enough time has elapsed since the last paint
131 // message, we say we're done painting; otherwise, we keep waiting.
132 void NewTabUI::PaintTimeout() {
133 // The amount of time there must be no painting for us to consider painting
134 // finished. Observed times are in the ~1200ms range on Windows.
135 base::TimeTicks now = base::TimeTicks::Now();
136 if ((now - last_paint_) >= base::TimeDelta::FromMilliseconds(kTimeoutMs)) {
137 // Painting has quieted down. Log this as the full time to run.
138 base::TimeDelta load_time = last_paint_ - start_;
139 UMA_HISTOGRAM_TIMES("NewTabUI load", load_time);
140 } else {
141 // Not enough quiet time has elapsed.
142 // Some more paints must've occurred since we set the timeout.
143 // Wait some more.
144 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
145 &NewTabUI::PaintTimeout);
149 void NewTabUI::StartTimingPaint(RenderViewHost* render_view_host) {
150 start_ = base::TimeTicks::Now();
151 last_paint_ = start_;
153 content::NotificationSource source =
154 content::Source<content::RenderWidgetHost>(render_view_host);
155 if (!registrar_.IsRegistered(this,
156 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
157 source)) {
158 registrar_.Add(
159 this,
160 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
161 source);
164 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
165 &NewTabUI::PaintTimeout);
168 void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) {
169 StartTimingPaint(render_view_host);
172 void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) {
173 StartTimingPaint(render_view_host);
176 void NewTabUI::WasHidden() {
177 EmitNtpStatistics();
180 void NewTabUI::Observe(int type,
181 const content::NotificationSource& source,
182 const content::NotificationDetails& details) {
183 switch (type) {
184 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: {
185 last_paint_ = base::TimeTicks::Now();
186 break;
188 default:
189 CHECK(false) << "Unexpected notification: " << type;
193 void NewTabUI::EmitNtpStatistics() {
194 NTPUserDataLogger::GetOrCreateFromWebContents(
195 web_contents())->EmitNtpStatistics();
198 void NewTabUI::OnShowBookmarkBarChanged() {
199 base::StringValue attached(
200 GetProfile()->GetPrefs()->GetBoolean(bookmarks::prefs::kShowBookmarkBar) ?
201 "true" : "false");
202 web_ui()->CallJavascriptFunction("ntp.setBookmarkBarAttached", attached);
205 // static
206 void NewTabUI::RegisterProfilePrefs(
207 user_prefs::PrefRegistrySyncable* registry) {
208 CoreAppLauncherHandler::RegisterProfilePrefs(registry);
209 NewTabPageHandler::RegisterProfilePrefs(registry);
212 // static
213 bool NewTabUI::ShouldShowApps() {
214 // Ash shows apps in app list thus should not show apps page in NTP4.
215 #if defined(USE_ASH)
216 return chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH;
217 #else
218 return true;
219 #endif
222 // static
223 void NewTabUI::SetUrlTitleAndDirection(base::DictionaryValue* dictionary,
224 const base::string16& title,
225 const GURL& gurl) {
226 dictionary->SetString("url", gurl.spec());
228 bool using_url_as_the_title = false;
229 base::string16 title_to_set(title);
230 if (title_to_set.empty()) {
231 using_url_as_the_title = true;
232 title_to_set = base::UTF8ToUTF16(gurl.spec());
235 // We set the "dir" attribute of the title, so that in RTL locales, a LTR
236 // title is rendered left-to-right and truncated from the right. For example,
237 // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN:
238 // Microsoft developer network". In RTL locales, in the [New Tab] page, if
239 // the "dir" of this title is not specified, it takes Chrome UI's
240 // directionality. So the title will be truncated as "soft developer
241 // network". Setting the "dir" attribute as "ltr" renders the truncated title
242 // as "MSDN: Microsoft D...". As another example, the title of
243 // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the
244 // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
245 // "ltr".
246 std::string direction;
247 if (using_url_as_the_title)
248 direction = kLTRHtmlTextDirection;
249 else
250 direction = GetHtmlTextDirection(title);
252 dictionary->SetString("title", title_to_set);
253 dictionary->SetString("direction", direction);
256 // static
257 void NewTabUI::SetFullNameAndDirection(const base::string16& full_name,
258 base::DictionaryValue* dictionary) {
259 dictionary->SetString("full_name", full_name);
260 dictionary->SetString("full_name_direction", GetHtmlTextDirection(full_name));
263 // static
264 NewTabUI* NewTabUI::FromWebUIController(WebUIController* ui) {
265 if (!g_live_new_tabs.Pointer()->count(ui))
266 return NULL;
267 return static_cast<NewTabUI*>(ui);
270 Profile* NewTabUI::GetProfile() const {
271 return Profile::FromWebUI(web_ui());
274 ///////////////////////////////////////////////////////////////////////////////
275 // NewTabHTMLSource
277 NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile)
278 : profile_(profile) {
281 std::string NewTabUI::NewTabHTMLSource::GetSource() const {
282 return chrome::kChromeUINewTabHost;
285 void NewTabUI::NewTabHTMLSource::StartDataRequest(
286 const std::string& path,
287 int render_process_id,
288 int render_frame_id,
289 const content::URLDataSource::GotDataCallback& callback) {
290 DCHECK_CURRENTLY_ON(BrowserThread::UI);
292 std::map<std::string, std::pair<std::string, int> >::iterator it =
293 resource_map_.find(path);
294 if (it != resource_map_.end()) {
295 scoped_refptr<base::RefCountedStaticMemory> resource_bytes(
296 it->second.second ?
297 ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
298 it->second.second) :
299 new base::RefCountedStaticMemory);
300 callback.Run(resource_bytes.get());
301 return;
304 if (!path.empty() && path[0] != '#') {
305 // A path under new-tab was requested; it's likely a bad relative
306 // URL from the new tab page, but in any case it's an error.
307 NOTREACHED() << path << " should not have been requested on the NTP";
308 callback.Run(NULL);
309 return;
312 content::RenderProcessHost* render_host =
313 content::RenderProcessHost::FromID(render_process_id);
314 NTPResourceCache::WindowType win_type = NTPResourceCache::GetWindowType(
315 profile_, render_host);
316 scoped_refptr<base::RefCountedMemory> html_bytes(
317 NTPResourceCacheFactory::GetForProfile(profile_)->
318 GetNewTabHTML(win_type));
320 callback.Run(html_bytes.get());
323 std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string& resource)
324 const {
325 std::map<std::string, std::pair<std::string, int> >::const_iterator it =
326 resource_map_.find(resource);
327 if (it != resource_map_.end())
328 return it->second.first;
329 return "text/html";
332 bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const {
333 return false;
336 bool NewTabUI::NewTabHTMLSource::ShouldAddContentSecurityPolicy() const {
337 return false;
340 void NewTabUI::NewTabHTMLSource::AddResource(const char* resource,
341 const char* mime_type,
342 int resource_id) {
343 DCHECK(resource);
344 DCHECK(mime_type);
345 resource_map_[std::string(resource)] =
346 std::make_pair(std::string(mime_type), resource_id);
349 NewTabUI::NewTabHTMLSource::~NewTabHTMLSource() {}