• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
6 
7 #include <set>
8 
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/foreign_session_handler.h"
22 #include "chrome/browser/ui/webui/ntp/most_visited_handler.h"
23 #include "chrome/browser/ui/webui/ntp/new_tab_page_handler.h"
24 #include "chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.h"
25 #include "chrome/browser/ui/webui/ntp/ntp_login_handler.h"
26 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
27 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h"
28 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
29 #include "chrome/browser/ui/webui/ntp/recently_closed_tabs_handler.h"
30 #include "chrome/browser/ui/webui/ntp/suggestions_page_handler.h"
31 #include "chrome/common/pref_names.h"
32 #include "chrome/common/url_constants.h"
33 #include "components/pref_registry/pref_registry_syncable.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/notification_service.h"
36 #include "content/public/browser/render_process_host.h"
37 #include "content/public/browser/render_view_host.h"
38 #include "content/public/browser/url_data_source.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_ui.h"
41 #include "grit/browser_resources.h"
42 #include "grit/generated_resources.h"
43 #include "ui/base/l10n/l10n_util.h"
44 
45 #if defined(ENABLE_THEMES)
46 #include "chrome/browser/ui/webui/theme_handler.h"
47 #endif
48 
49 #if defined(USE_ASH)
50 #include "chrome/browser/ui/host_desktop.h"
51 #endif
52 
53 using content::BrowserThread;
54 using content::RenderViewHost;
55 using content::WebUIController;
56 
57 namespace {
58 
59 // The amount of time there must be no painting for us to consider painting
60 // finished.  Observed times are in the ~1200ms range on Windows.
61 const int kTimeoutMs = 2000;
62 
63 // Strings sent to the page via jstemplates used to set the direction of the
64 // HTML document based on locale.
65 const char kRTLHtmlTextDirection[] = "rtl";
66 const char kLTRHtmlTextDirection[] = "ltr";
67 
68 static base::LazyInstance<std::set<const WebUIController*> > g_live_new_tabs;
69 
GetHtmlTextDirection(const base::string16 & text)70 const char* GetHtmlTextDirection(const base::string16& text) {
71   if (base::i18n::IsRTL() && base::i18n::StringContainsStrongRTLChars(text))
72     return kRTLHtmlTextDirection;
73   else
74     return kLTRHtmlTextDirection;
75 }
76 
77 }  // namespace
78 
79 ///////////////////////////////////////////////////////////////////////////////
80 // NewTabUI
81 
NewTabUI(content::WebUI * web_ui)82 NewTabUI::NewTabUI(content::WebUI* web_ui)
83     : WebUIController(web_ui),
84       WebContentsObserver(web_ui->GetWebContents()),
85       showing_sync_bubble_(false) {
86   g_live_new_tabs.Pointer()->insert(this);
87   web_ui->OverrideTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
88 
89   // We count all link clicks as AUTO_BOOKMARK, so that site can be ranked more
90   // highly. Note this means we're including clicks on not only most visited
91   // thumbnails, but also clicks on recently bookmarked.
92   web_ui->SetLinkTransitionType(content::PAGE_TRANSITION_AUTO_BOOKMARK);
93 
94   if (!GetProfile()->IsOffTheRecord()) {
95     web_ui->AddMessageHandler(new browser_sync::ForeignSessionHandler());
96     web_ui->AddMessageHandler(new MetricsHandler());
97     web_ui->AddMessageHandler(new MostVisitedHandler());
98     web_ui->AddMessageHandler(new RecentlyClosedTabsHandler());
99     web_ui->AddMessageHandler(new FaviconWebUIHandler());
100     web_ui->AddMessageHandler(new NewTabPageHandler());
101     web_ui->AddMessageHandler(new CoreAppLauncherHandler());
102     if (NewTabUI::IsDiscoveryInNTPEnabled())
103       web_ui->AddMessageHandler(new SuggestionsHandler());
104     web_ui->AddMessageHandler(new NewTabPageSyncHandler());
105 
106     ExtensionService* service = GetProfile()->GetExtensionService();
107     // We might not have an ExtensionService (on ChromeOS when not logged in
108     // for example).
109     if (service)
110       web_ui->AddMessageHandler(new AppLauncherHandler(service));
111   }
112 
113   if (NTPLoginHandler::ShouldShow(GetProfile()))
114     web_ui->AddMessageHandler(new NTPLoginHandler());
115 
116 #if defined(ENABLE_THEMES)
117   // The theme handler can require some CPU, so do it after hooking up the most
118   // visited handler. This allows the DB query for the new tab thumbs to happen
119   // earlier.
120   web_ui->AddMessageHandler(new ThemeHandler());
121 #endif
122 
123   scoped_ptr<NewTabHTMLSource> html_source(new NewTabHTMLSource(
124       GetProfile()->GetOriginalProfile()));
125 
126   // These two resources should be loaded only if suggestions NTP is enabled.
127   html_source->AddResource("suggestions_page.css", "text/css",
128       NewTabUI::IsDiscoveryInNTPEnabled() ? IDR_SUGGESTIONS_PAGE_CSS : 0);
129   if (NewTabUI::IsDiscoveryInNTPEnabled()) {
130     html_source->AddResource("suggestions_page.js", "application/javascript",
131         IDR_SUGGESTIONS_PAGE_JS);
132   }
133   // content::URLDataSource assumes the ownership of the html_source.
134   content::URLDataSource::Add(GetProfile(), html_source.release());
135 
136   pref_change_registrar_.Init(GetProfile()->GetPrefs());
137   pref_change_registrar_.Add(prefs::kShowBookmarkBar,
138                              base::Bind(&NewTabUI::OnShowBookmarkBarChanged,
139                                         base::Unretained(this)));
140 }
141 
~NewTabUI()142 NewTabUI::~NewTabUI() {
143   g_live_new_tabs.Pointer()->erase(this);
144 }
145 
146 // The timer callback.  If enough time has elapsed since the last paint
147 // message, we say we're done painting; otherwise, we keep waiting.
PaintTimeout()148 void NewTabUI::PaintTimeout() {
149   // The amount of time there must be no painting for us to consider painting
150   // finished.  Observed times are in the ~1200ms range on Windows.
151   base::TimeTicks now = base::TimeTicks::Now();
152   if ((now - last_paint_) >= base::TimeDelta::FromMilliseconds(kTimeoutMs)) {
153     // Painting has quieted down.  Log this as the full time to run.
154     base::TimeDelta load_time = last_paint_ - start_;
155     UMA_HISTOGRAM_TIMES("NewTabUI load", load_time);
156   } else {
157     // Not enough quiet time has elapsed.
158     // Some more paints must've occurred since we set the timeout.
159     // Wait some more.
160     timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
161                  &NewTabUI::PaintTimeout);
162   }
163 }
164 
StartTimingPaint(RenderViewHost * render_view_host)165 void NewTabUI::StartTimingPaint(RenderViewHost* render_view_host) {
166   start_ = base::TimeTicks::Now();
167   last_paint_ = start_;
168 
169   content::NotificationSource source =
170       content::Source<content::RenderWidgetHost>(render_view_host);
171   if (!registrar_.IsRegistered(this,
172           content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
173           source)) {
174     registrar_.Add(
175         this,
176         content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
177         source);
178   }
179 
180   timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
181                &NewTabUI::PaintTimeout);
182 }
183 
RenderViewCreated(RenderViewHost * render_view_host)184 void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) {
185   StartTimingPaint(render_view_host);
186 }
187 
RenderViewReused(RenderViewHost * render_view_host)188 void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) {
189   StartTimingPaint(render_view_host);
190 }
191 
WasHidden()192 void NewTabUI::WasHidden() {
193   EmitNtpStatistics();
194 }
195 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)196 void NewTabUI::Observe(int type,
197                        const content::NotificationSource& source,
198                        const content::NotificationDetails& details) {
199   switch (type) {
200     case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: {
201       last_paint_ = base::TimeTicks::Now();
202       break;
203     }
204     default:
205       CHECK(false) << "Unexpected notification: " << type;
206   }
207 }
208 
EmitNtpStatistics()209 void NewTabUI::EmitNtpStatistics() {
210   NTPUserDataLogger::GetOrCreateFromWebContents(
211       web_contents())->EmitNtpStatistics();
212 }
213 
OnShowBookmarkBarChanged()214 void NewTabUI::OnShowBookmarkBarChanged() {
215   base::StringValue attached(
216       GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) ?
217           "true" : "false");
218   web_ui()->CallJavascriptFunction("ntp.setBookmarkBarAttached", attached);
219 }
220 
221 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)222 void NewTabUI::RegisterProfilePrefs(
223     user_prefs::PrefRegistrySyncable* registry) {
224   CoreAppLauncherHandler::RegisterProfilePrefs(registry);
225   NewTabPageHandler::RegisterProfilePrefs(registry);
226   if (NewTabUI::IsDiscoveryInNTPEnabled())
227     SuggestionsHandler::RegisterProfilePrefs(registry);
228   MostVisitedHandler::RegisterProfilePrefs(registry);
229   browser_sync::ForeignSessionHandler::RegisterProfilePrefs(registry);
230 }
231 
232 // static
ShouldShowApps()233 bool NewTabUI::ShouldShowApps() {
234 // Ash shows apps in app list thus should not show apps page in NTP4.
235 #if defined(USE_ASH)
236   return chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH;
237 #else
238   return true;
239 #endif
240 }
241 
242 // static
IsDiscoveryInNTPEnabled()243 bool NewTabUI::IsDiscoveryInNTPEnabled() {
244   // TODO(beaudoin): The flag was removed during a clean-up pass. We leave that
245   // here to easily enable it back when we will explore this option again.
246   return false;
247 }
248 
249 // static
SetUrlTitleAndDirection(base::DictionaryValue * dictionary,const base::string16 & title,const GURL & gurl)250 void NewTabUI::SetUrlTitleAndDirection(base::DictionaryValue* dictionary,
251                                        const base::string16& title,
252                                        const GURL& gurl) {
253   dictionary->SetString("url", gurl.spec());
254 
255   bool using_url_as_the_title = false;
256   base::string16 title_to_set(title);
257   if (title_to_set.empty()) {
258     using_url_as_the_title = true;
259     title_to_set = base::UTF8ToUTF16(gurl.spec());
260   }
261 
262   // We set the "dir" attribute of the title, so that in RTL locales, a LTR
263   // title is rendered left-to-right and truncated from the right. For example,
264   // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN:
265   // Microsoft developer network". In RTL locales, in the [New Tab] page, if
266   // the "dir" of this title is not specified, it takes Chrome UI's
267   // directionality. So the title will be truncated as "soft developer
268   // network". Setting the "dir" attribute as "ltr" renders the truncated title
269   // as "MSDN: Microsoft D...". As another example, the title of
270   // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the
271   // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
272   // "ltr".
273   std::string direction;
274   if (using_url_as_the_title)
275     direction = kLTRHtmlTextDirection;
276   else
277     direction = GetHtmlTextDirection(title);
278 
279   dictionary->SetString("title", title_to_set);
280   dictionary->SetString("direction", direction);
281 }
282 
283 // static
SetFullNameAndDirection(const base::string16 & full_name,base::DictionaryValue * dictionary)284 void NewTabUI::SetFullNameAndDirection(const base::string16& full_name,
285                                        base::DictionaryValue* dictionary) {
286   dictionary->SetString("full_name", full_name);
287   dictionary->SetString("full_name_direction", GetHtmlTextDirection(full_name));
288 }
289 
290 // static
FromWebUIController(WebUIController * ui)291 NewTabUI* NewTabUI::FromWebUIController(WebUIController* ui) {
292   if (!g_live_new_tabs.Pointer()->count(ui))
293     return NULL;
294   return static_cast<NewTabUI*>(ui);
295 }
296 
GetProfile() const297 Profile* NewTabUI::GetProfile() const {
298   return Profile::FromWebUI(web_ui());
299 }
300 
301 ///////////////////////////////////////////////////////////////////////////////
302 // NewTabHTMLSource
303 
NewTabHTMLSource(Profile * profile)304 NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile)
305     : profile_(profile) {
306 }
307 
GetSource() const308 std::string NewTabUI::NewTabHTMLSource::GetSource() const {
309   return chrome::kChromeUINewTabHost;
310 }
311 
StartDataRequest(const std::string & path,int render_process_id,int render_frame_id,const content::URLDataSource::GotDataCallback & callback)312 void NewTabUI::NewTabHTMLSource::StartDataRequest(
313     const std::string& path,
314     int render_process_id,
315     int render_frame_id,
316     const content::URLDataSource::GotDataCallback& callback) {
317   DCHECK_CURRENTLY_ON(BrowserThread::UI);
318 
319   std::map<std::string, std::pair<std::string, int> >::iterator it =
320     resource_map_.find(path);
321   if (it != resource_map_.end()) {
322     scoped_refptr<base::RefCountedStaticMemory> resource_bytes(
323         it->second.second ?
324             ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
325                 it->second.second) :
326             new base::RefCountedStaticMemory);
327     callback.Run(resource_bytes.get());
328     return;
329   }
330 
331   if (!path.empty() && path[0] != '#') {
332     // A path under new-tab was requested; it's likely a bad relative
333     // URL from the new tab page, but in any case it's an error.
334     NOTREACHED() << path << " should not have been requested on the NTP";
335     callback.Run(NULL);
336     return;
337   }
338 
339   content::RenderProcessHost* render_host =
340       content::RenderProcessHost::FromID(render_process_id);
341   NTPResourceCache::WindowType win_type = NTPResourceCache::GetWindowType(
342       profile_, render_host);
343   scoped_refptr<base::RefCountedMemory> html_bytes(
344       NTPResourceCacheFactory::GetForProfile(profile_)->
345       GetNewTabHTML(win_type));
346 
347   callback.Run(html_bytes.get());
348 }
349 
GetMimeType(const std::string & resource) const350 std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string& resource)
351     const {
352   std::map<std::string, std::pair<std::string, int> >::const_iterator it =
353       resource_map_.find(resource);
354   if (it != resource_map_.end())
355     return it->second.first;
356   return "text/html";
357 }
358 
ShouldReplaceExistingSource() const359 bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const {
360   return false;
361 }
362 
ShouldAddContentSecurityPolicy() const363 bool NewTabUI::NewTabHTMLSource::ShouldAddContentSecurityPolicy() const {
364   return false;
365 }
366 
AddResource(const char * resource,const char * mime_type,int resource_id)367 void NewTabUI::NewTabHTMLSource::AddResource(const char* resource,
368                                              const char* mime_type,
369                                              int resource_id) {
370   DCHECK(resource);
371   DCHECK(mime_type);
372   resource_map_[std::string(resource)] =
373       std::make_pair(std::string(mime_type), resource_id);
374 }
375 
~NewTabHTMLSource()376 NewTabUI::NewTabHTMLSource::~NewTabHTMLSource() {}
377