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