Home | History | Annotate | Download | only in webui
      1 // Copyright (c) 2011 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 "build/build_config.h"
      6 
      7 #include "chrome/browser/ui/webui/new_tab_ui.h"
      8 
      9 #include <set>
     10 
     11 #include "base/callback.h"
     12 #include "base/command_line.h"
     13 #include "base/i18n/rtl.h"
     14 #include "base/memory/singleton.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/string_number_conversions.h"
     17 #include "base/threading/thread.h"
     18 #include "base/utf_string_conversions.h"
     19 #include "chrome/browser/metrics/user_metrics.h"
     20 #include "chrome/browser/prefs/pref_service.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/sessions/session_types.h"
     23 #include "chrome/browser/sessions/tab_restore_service.h"
     24 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
     25 #include "chrome/browser/sessions/tab_restore_service_observer.h"
     26 #include "chrome/browser/sync/profile_sync_service.h"
     27 #include "chrome/browser/themes/theme_service.h"
     28 #include "chrome/browser/themes/theme_service_factory.h"
     29 #include "chrome/browser/ui/browser.h"
     30 #include "chrome/browser/ui/webui/app_launcher_handler.h"
     31 #include "chrome/browser/ui/webui/foreign_session_handler.h"
     32 #include "chrome/browser/ui/webui/most_visited_handler.h"
     33 #include "chrome/browser/ui/webui/new_tab_page_sync_handler.h"
     34 #include "chrome/browser/ui/webui/ntp_login_handler.h"
     35 #include "chrome/browser/ui/webui/ntp_resource_cache.h"
     36 #include "chrome/browser/ui/webui/shown_sections_handler.h"
     37 #include "chrome/browser/ui/webui/theme_source.h"
     38 #include "chrome/browser/ui/webui/value_helper.h"
     39 #include "chrome/common/chrome_switches.h"
     40 #include "chrome/common/extensions/extension.h"
     41 #include "chrome/common/pref_names.h"
     42 #include "chrome/common/url_constants.h"
     43 #include "content/browser/browser_thread.h"
     44 #include "content/browser/renderer_host/render_view_host.h"
     45 #include "content/browser/tab_contents/tab_contents.h"
     46 #include "content/common/notification_service.h"
     47 #include "grit/generated_resources.h"
     48 #include "grit/theme_resources.h"
     49 #include "ui/base/l10n/l10n_util.h"
     50 
     51 namespace {
     52 
     53 // The number of recent bookmarks we show.
     54 const int kRecentBookmarks = 9;
     55 
     56 // The number of search URLs to show.
     57 const int kSearchURLs = 3;
     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 kDefaultHtmlTextDirection[] = "ltr";
     67 
     68 ///////////////////////////////////////////////////////////////////////////////
     69 // RecentlyClosedTabsHandler
     70 
     71 class RecentlyClosedTabsHandler : public WebUIMessageHandler,
     72                                   public TabRestoreServiceObserver {
     73  public:
     74   RecentlyClosedTabsHandler() : tab_restore_service_(NULL) {}
     75   virtual ~RecentlyClosedTabsHandler();
     76 
     77   // WebUIMessageHandler implementation.
     78   virtual void RegisterMessages();
     79 
     80   // Callback for the "reopenTab" message. Rewrites the history of the
     81   // currently displayed tab to be the one in TabRestoreService with a
     82   // history of a session passed in through the content pointer.
     83   void HandleReopenTab(const ListValue* args);
     84 
     85   // Callback for the "getRecentlyClosedTabs" message.
     86   void HandleGetRecentlyClosedTabs(const ListValue* args);
     87 
     88   // Observer callback for TabRestoreServiceObserver. Sends data on
     89   // recently closed tabs to the javascript side of this page to
     90   // display to the user.
     91   virtual void TabRestoreServiceChanged(TabRestoreService* service);
     92 
     93   // Observer callback to notice when our associated TabRestoreService
     94   // is destroyed.
     95   virtual void TabRestoreServiceDestroyed(TabRestoreService* service);
     96 
     97  private:
     98   // TabRestoreService that we are observing.
     99   TabRestoreService* tab_restore_service_;
    100 
    101   DISALLOW_COPY_AND_ASSIGN(RecentlyClosedTabsHandler);
    102 };
    103 
    104 void RecentlyClosedTabsHandler::RegisterMessages() {
    105   web_ui_->RegisterMessageCallback("getRecentlyClosedTabs",
    106       NewCallback(this,
    107                   &RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs));
    108   web_ui_->RegisterMessageCallback("reopenTab",
    109       NewCallback(this, &RecentlyClosedTabsHandler::HandleReopenTab));
    110 }
    111 
    112 RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() {
    113   if (tab_restore_service_)
    114     tab_restore_service_->RemoveObserver(this);
    115 }
    116 
    117 void RecentlyClosedTabsHandler::HandleReopenTab(const ListValue* args) {
    118   TabRestoreServiceDelegate* delegate =
    119       TabRestoreServiceDelegate::FindDelegateForController(
    120       &web_ui_->tab_contents()->controller(), NULL);
    121   if (!delegate)
    122     return;
    123 
    124   int session_to_restore;
    125   if (ExtractIntegerValue(args, &session_to_restore))
    126     tab_restore_service_->RestoreEntryById(delegate, session_to_restore, true);
    127   // The current tab has been nuked at this point; don't touch any member
    128   // variables.
    129 }
    130 
    131 void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs(
    132     const ListValue* args) {
    133   if (!tab_restore_service_) {
    134     tab_restore_service_ = web_ui_->GetProfile()->GetTabRestoreService();
    135 
    136     // GetTabRestoreService() can return NULL (i.e., when in Off the
    137     // Record mode)
    138     if (tab_restore_service_) {
    139       // This does nothing if the tabs have already been loaded or they
    140       // shouldn't be loaded.
    141       tab_restore_service_->LoadTabsFromLastSession();
    142 
    143       tab_restore_service_->AddObserver(this);
    144     }
    145   }
    146 
    147   if (tab_restore_service_)
    148     TabRestoreServiceChanged(tab_restore_service_);
    149 }
    150 
    151 void RecentlyClosedTabsHandler::TabRestoreServiceChanged(
    152     TabRestoreService* service) {
    153   ListValue list_value;
    154   NewTabUI::AddRecentlyClosedEntries(service->entries(), &list_value);
    155 
    156   web_ui_->CallJavascriptFunction("recentlyClosedTabs", list_value);
    157 }
    158 
    159 void RecentlyClosedTabsHandler::TabRestoreServiceDestroyed(
    160     TabRestoreService* service) {
    161   tab_restore_service_ = NULL;
    162 }
    163 
    164 ///////////////////////////////////////////////////////////////////////////////
    165 // MetricsHandler
    166 
    167 // Let the page contents record UMA actions. Only use when you can't do it from
    168 // C++. For example, we currently use it to let the NTP log the postion of the
    169 // Most Visited or Bookmark the user clicked on, as we don't get that
    170 // information through RequestOpenURL. You will need to update the metrics
    171 // dashboard with the action names you use, as our processor won't catch that
    172 // information (treat it as RecordComputedMetrics)
    173 class MetricsHandler : public WebUIMessageHandler {
    174  public:
    175   MetricsHandler() {}
    176   virtual ~MetricsHandler() {}
    177 
    178   // WebUIMessageHandler implementation.
    179   virtual void RegisterMessages();
    180 
    181   // Callback which records a user action.
    182   void HandleMetrics(const ListValue* args);
    183 
    184   // Callback for the "logEventTime" message.
    185   void HandleLogEventTime(const ListValue* args);
    186 
    187  private:
    188 
    189   DISALLOW_COPY_AND_ASSIGN(MetricsHandler);
    190 };
    191 
    192 void MetricsHandler::RegisterMessages() {
    193   web_ui_->RegisterMessageCallback("metrics",
    194       NewCallback(this, &MetricsHandler::HandleMetrics));
    195 
    196   web_ui_->RegisterMessageCallback("logEventTime",
    197       NewCallback(this, &MetricsHandler::HandleLogEventTime));
    198 }
    199 
    200 void MetricsHandler::HandleMetrics(const ListValue* args) {
    201   std::string string_action = UTF16ToUTF8(ExtractStringValue(args));
    202   UserMetrics::RecordComputedAction(string_action, web_ui_->GetProfile());
    203 }
    204 
    205 void MetricsHandler::HandleLogEventTime(const ListValue* args) {
    206   std::string event_name = UTF16ToUTF8(ExtractStringValue(args));
    207   web_ui_->tab_contents()->LogNewTabTime(event_name);
    208 }
    209 
    210 ///////////////////////////////////////////////////////////////////////////////
    211 // NewTabPageSetHomePageHandler
    212 
    213 // Sets the new tab page as home page when user clicks on "make this my home
    214 // page" link.
    215 class NewTabPageSetHomePageHandler : public WebUIMessageHandler {
    216  public:
    217   NewTabPageSetHomePageHandler() {}
    218   virtual ~NewTabPageSetHomePageHandler() {}
    219 
    220   // WebUIMessageHandler implementation.
    221   virtual void RegisterMessages();
    222 
    223   // Callback for "setHomePage".
    224   void HandleSetHomePage(const ListValue* args);
    225 
    226  private:
    227 
    228   DISALLOW_COPY_AND_ASSIGN(NewTabPageSetHomePageHandler);
    229 };
    230 
    231 void NewTabPageSetHomePageHandler::RegisterMessages() {
    232   web_ui_->RegisterMessageCallback("setHomePage", NewCallback(
    233       this, &NewTabPageSetHomePageHandler::HandleSetHomePage));
    234 }
    235 
    236 void NewTabPageSetHomePageHandler::HandleSetHomePage(
    237     const ListValue* args) {
    238   web_ui_->GetProfile()->GetPrefs()->SetBoolean(prefs::kHomePageIsNewTabPage,
    239                                                 true);
    240   ListValue list_value;
    241   list_value.Append(new StringValue(
    242       l10n_util::GetStringUTF16(IDS_NEW_TAB_HOME_PAGE_SET_NOTIFICATION)));
    243   list_value.Append(new StringValue(
    244       l10n_util::GetStringUTF16(IDS_NEW_TAB_HOME_PAGE_HIDE_NOTIFICATION)));
    245   web_ui_->CallJavascriptFunction("onHomePageSet", list_value);
    246 }
    247 
    248 ///////////////////////////////////////////////////////////////////////////////
    249 // NewTabPageClosePromoHandler
    250 
    251 // Turns off the promo line permanently when it has been explicitly closed by
    252 // the user.
    253 class NewTabPageClosePromoHandler : public WebUIMessageHandler {
    254  public:
    255   NewTabPageClosePromoHandler() {}
    256   virtual ~NewTabPageClosePromoHandler() {}
    257 
    258   // WebUIMessageHandler implementation.
    259   virtual void RegisterMessages();
    260 
    261   // Callback for "closePromo".
    262   void HandleClosePromo(const ListValue* args);
    263 
    264  private:
    265 
    266   DISALLOW_COPY_AND_ASSIGN(NewTabPageClosePromoHandler);
    267 };
    268 
    269 void NewTabPageClosePromoHandler::RegisterMessages() {
    270   web_ui_->RegisterMessageCallback("closePromo", NewCallback(
    271       this, &NewTabPageClosePromoHandler::HandleClosePromo));
    272 }
    273 
    274 void NewTabPageClosePromoHandler::HandleClosePromo(
    275     const ListValue* args) {
    276   web_ui_->GetProfile()->GetPrefs()->SetBoolean(prefs::kNTPPromoClosed, true);
    277   NotificationService* service = NotificationService::current();
    278   service->Notify(NotificationType::PROMO_RESOURCE_STATE_CHANGED,
    279                   Source<NewTabPageClosePromoHandler>(this),
    280                   NotificationService::NoDetails());
    281 }
    282 
    283 }  // namespace
    284 
    285 ///////////////////////////////////////////////////////////////////////////////
    286 // NewTabUI
    287 
    288 NewTabUI::NewTabUI(TabContents* contents)
    289     : WebUI(contents) {
    290   // Override some options on the Web UI.
    291   hide_favicon_ = true;
    292 
    293   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4))
    294     force_bookmark_bar_visible_ = true;
    295 
    296   focus_location_bar_by_default_ = true;
    297   should_hide_url_ = true;
    298   overridden_title_ = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
    299 
    300   // We count all link clicks as AUTO_BOOKMARK, so that site can be ranked more
    301   // highly. Note this means we're including clicks on not only most visited
    302   // thumbnails, but also clicks on recently bookmarked.
    303   link_transition_type_ = PageTransition::AUTO_BOOKMARK;
    304 
    305   if (NewTabUI::FirstRunDisabled())
    306     NewTabHTMLSource::set_first_run(false);
    307 
    308   static bool first_view = true;
    309   if (first_view) {
    310     first_view = false;
    311   }
    312 
    313   if (!GetProfile()->IsOffTheRecord()) {
    314     PrefService* pref_service = GetProfile()->GetPrefs();
    315     AddMessageHandler((new NTPLoginHandler())->Attach(this));
    316     AddMessageHandler((new ShownSectionsHandler(pref_service))->Attach(this));
    317     AddMessageHandler((new browser_sync::ForeignSessionHandler())->
    318       Attach(this));
    319     AddMessageHandler((new MostVisitedHandler())->Attach(this));
    320     AddMessageHandler((new RecentlyClosedTabsHandler())->Attach(this));
    321     AddMessageHandler((new MetricsHandler())->Attach(this));
    322     if (GetProfile()->IsSyncAccessible())
    323       AddMessageHandler((new NewTabPageSyncHandler())->Attach(this));
    324     ExtensionService* service = GetProfile()->GetExtensionService();
    325     // We might not have an ExtensionService (on ChromeOS when not logged in
    326     // for example).
    327     if (service)
    328       AddMessageHandler((new AppLauncherHandler(service))->Attach(this));
    329 
    330     AddMessageHandler((new NewTabPageSetHomePageHandler())->Attach(this));
    331     AddMessageHandler((new NewTabPageClosePromoHandler())->Attach(this));
    332   }
    333 
    334   // Initializing the CSS and HTML can require some CPU, so do it after
    335   // we've hooked up the most visited handler.  This allows the DB query
    336   // for the new tab thumbs to happen earlier.
    337   InitializeCSSCaches();
    338   NewTabHTMLSource* html_source =
    339       new NewTabHTMLSource(GetProfile()->GetOriginalProfile());
    340   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
    341 
    342   // Listen for theme installation.
    343   registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
    344                  NotificationService::AllSources());
    345   // Listen for bookmark bar visibility changes.
    346   registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
    347                  NotificationService::AllSources());
    348 }
    349 
    350 NewTabUI::~NewTabUI() {
    351 }
    352 
    353 // The timer callback.  If enough time has elapsed since the last paint
    354 // message, we say we're done painting; otherwise, we keep waiting.
    355 void NewTabUI::PaintTimeout() {
    356   // The amount of time there must be no painting for us to consider painting
    357   // finished.  Observed times are in the ~1200ms range on Windows.
    358   base::TimeTicks now = base::TimeTicks::Now();
    359   if ((now - last_paint_) >= base::TimeDelta::FromMilliseconds(kTimeoutMs)) {
    360     // Painting has quieted down.  Log this as the full time to run.
    361     base::TimeDelta load_time = last_paint_ - start_;
    362     int load_time_ms = static_cast<int>(load_time.InMilliseconds());
    363     NotificationService::current()->Notify(
    364         NotificationType::INITIAL_NEW_TAB_UI_LOAD,
    365         NotificationService::AllSources(),
    366         Details<int>(&load_time_ms));
    367     UMA_HISTOGRAM_TIMES("NewTabUI load", load_time);
    368   } else {
    369     // Not enough quiet time has elapsed.
    370     // Some more paints must've occurred since we set the timeout.
    371     // Wait some more.
    372     timer_.Start(base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
    373                  &NewTabUI::PaintTimeout);
    374   }
    375 }
    376 
    377 void NewTabUI::StartTimingPaint(RenderViewHost* render_view_host) {
    378   start_ = base::TimeTicks::Now();
    379   last_paint_ = start_;
    380   registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DID_PAINT,
    381       Source<RenderWidgetHost>(render_view_host));
    382   timer_.Start(base::TimeDelta::FromMilliseconds(kTimeoutMs), this,
    383                &NewTabUI::PaintTimeout);
    384 
    385 }
    386 void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) {
    387   StartTimingPaint(render_view_host);
    388 }
    389 
    390 void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) {
    391   StartTimingPaint(render_view_host);
    392 }
    393 
    394 void NewTabUI::Observe(NotificationType type,
    395                        const NotificationSource& source,
    396                        const NotificationDetails& details) {
    397   switch (type.value) {
    398     case NotificationType::BROWSER_THEME_CHANGED: {
    399       InitializeCSSCaches();
    400       ListValue args;
    401       args.Append(Value::CreateStringValue(
    402           ThemeServiceFactory::GetForProfile(GetProfile())->HasCustomImage(
    403               IDR_THEME_NTP_ATTRIBUTION) ?
    404           "true" : "false"));
    405       CallJavascriptFunction("themeChanged", args);
    406       break;
    407     }
    408     case NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED: {
    409       if (GetProfile()->GetPrefs()->IsManagedPreference(
    410               prefs::kEnableBookmarkBar)) {
    411         break;
    412       }
    413       if (GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar))
    414         CallJavascriptFunction("bookmarkBarAttached");
    415       else
    416         CallJavascriptFunction("bookmarkBarDetached");
    417       break;
    418     }
    419     case NotificationType::RENDER_WIDGET_HOST_DID_PAINT: {
    420       last_paint_ = base::TimeTicks::Now();
    421       break;
    422     }
    423     default:
    424       CHECK(false) << "Unexpected notification: " << type.value;
    425   }
    426 }
    427 
    428 void NewTabUI::InitializeCSSCaches() {
    429   Profile* profile = GetProfile();
    430   ThemeSource* theme = new ThemeSource(profile);
    431   profile->GetChromeURLDataManager()->AddDataSource(theme);
    432 }
    433 
    434 // static
    435 void NewTabUI::RegisterUserPrefs(PrefService* prefs) {
    436   prefs->RegisterIntegerPref(prefs::kNTPPrefVersion, 0);
    437 
    438   MostVisitedHandler::RegisterUserPrefs(prefs);
    439   ShownSectionsHandler::RegisterUserPrefs(prefs);
    440 
    441   UpdateUserPrefsVersion(prefs);
    442 }
    443 
    444 // static
    445 bool NewTabUI::UpdateUserPrefsVersion(PrefService* prefs) {
    446   const int old_pref_version = prefs->GetInteger(prefs::kNTPPrefVersion);
    447   if (old_pref_version != current_pref_version()) {
    448     MigrateUserPrefs(prefs, old_pref_version, current_pref_version());
    449     prefs->SetInteger(prefs::kNTPPrefVersion, current_pref_version());
    450     return true;
    451   }
    452   return false;
    453 }
    454 
    455 // static
    456 void NewTabUI::MigrateUserPrefs(PrefService* prefs, int old_pref_version,
    457                                 int new_pref_version) {
    458   ShownSectionsHandler::MigrateUserPrefs(prefs, old_pref_version,
    459                                          current_pref_version());
    460 }
    461 
    462 // static
    463 bool NewTabUI::FirstRunDisabled() {
    464   const CommandLine* command_line = CommandLine::ForCurrentProcess();
    465   return command_line->HasSwitch(switches::kDisableNewTabFirstRun);
    466 }
    467 
    468 // static
    469 void NewTabUI::SetURLTitleAndDirection(DictionaryValue* dictionary,
    470                                        const string16& title,
    471                                        const GURL& gurl) {
    472   dictionary->SetString("url", gurl.spec());
    473 
    474   bool using_url_as_the_title = false;
    475   string16 title_to_set(title);
    476   if (title_to_set.empty()) {
    477     using_url_as_the_title = true;
    478     title_to_set = UTF8ToUTF16(gurl.spec());
    479   }
    480 
    481   // We set the "dir" attribute of the title, so that in RTL locales, a LTR
    482   // title is rendered left-to-right and truncated from the right. For example,
    483   // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN:
    484   // Microsoft developer network". In RTL locales, in the [New Tab] page, if
    485   // the "dir" of this title is not specified, it takes Chrome UI's
    486   // directionality. So the title will be truncated as "soft developer
    487   // network". Setting the "dir" attribute as "ltr" renders the truncated title
    488   // as "MSDN: Microsoft D...". As another example, the title of
    489   // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the
    490   // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
    491   // "ltr".
    492   //
    493   // Since the title can contain BiDi text, we need to mark the text as either
    494   // RTL or LTR, depending on the characters in the string. If we use the URL
    495   // as the title, we mark the title as LTR since URLs are always treated as
    496   // left to right strings. Simply setting the title's "dir" attribute works
    497   // fine for rendering and truncating the title. However, it does not work for
    498   // entire title within a tooltip when the mouse is over the title link.. For
    499   // example, without LRE-PDF pair, the title "Yahoo!" will be rendered as
    500   // "!Yahoo" within the tooltip when the mouse is over the title link.
    501   std::string direction = kDefaultHtmlTextDirection;
    502   if (base::i18n::IsRTL()) {
    503     if (using_url_as_the_title) {
    504       base::i18n::WrapStringWithLTRFormatting(&title_to_set);
    505     } else {
    506       if (base::i18n::StringContainsStrongRTLChars(title)) {
    507         base::i18n::WrapStringWithRTLFormatting(&title_to_set);
    508         direction = kRTLHtmlTextDirection;
    509       } else {
    510         base::i18n::WrapStringWithLTRFormatting(&title_to_set);
    511       }
    512     }
    513   }
    514   dictionary->SetString("title", title_to_set);
    515   dictionary->SetString("direction", direction);
    516 }
    517 
    518 namespace {
    519 
    520 bool IsTabUnique(const DictionaryValue* tab,
    521                  std::set<std::string>* unique_items) {
    522   DCHECK(unique_items);
    523   std::string title;
    524   std::string url;
    525   if (tab->GetString("title", &title) &&
    526       tab->GetString("url", &url)) {
    527     // TODO(viettrungluu): this isn't obviously reliable, since different
    528     // combinations of titles/urls may conceivably yield the same string.
    529     std::string unique_key = title + url;
    530     if (unique_items->find(unique_key) != unique_items->end())
    531       return false;
    532     else
    533       unique_items->insert(unique_key);
    534   }
    535   return true;
    536 }
    537 
    538 }  // namespace
    539 
    540 // static
    541 void NewTabUI::AddRecentlyClosedEntries(
    542     const TabRestoreService::Entries& entries, ListValue* entry_list_value) {
    543   const int max_count = 10;
    544   int added_count = 0;
    545   std::set<std::string> unique_items;
    546   // We filter the list of recently closed to only show 'interesting' entries,
    547   // where an interesting entry is either a closed window or a closed tab
    548   // whose selected navigation is not the new tab ui.
    549   for (TabRestoreService::Entries::const_iterator it = entries.begin();
    550        it != entries.end() && added_count < max_count; ++it) {
    551     TabRestoreService::Entry* entry = *it;
    552     scoped_ptr<DictionaryValue> entry_dict(new DictionaryValue());
    553     if ((entry->type == TabRestoreService::TAB &&
    554          ValueHelper::TabToValue(
    555              *static_cast<TabRestoreService::Tab*>(entry),
    556              entry_dict.get()) &&
    557          IsTabUnique(entry_dict.get(), &unique_items)) ||
    558         (entry->type == TabRestoreService::WINDOW &&
    559          ValueHelper::WindowToValue(
    560              *static_cast<TabRestoreService::Window*>(entry),
    561              entry_dict.get()))) {
    562       entry_dict->SetInteger("sessionId", entry->id);
    563       entry_list_value->Append(entry_dict.release());
    564       added_count++;
    565     }
    566   }
    567 }
    568 
    569 ///////////////////////////////////////////////////////////////////////////////
    570 // NewTabHTMLSource
    571 
    572 bool NewTabUI::NewTabHTMLSource::first_run_ = true;
    573 
    574 NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile)
    575     : DataSource(chrome::kChromeUINewTabHost, MessageLoop::current()),
    576       profile_(profile) {
    577 }
    578 
    579 void NewTabUI::NewTabHTMLSource::StartDataRequest(const std::string& path,
    580                                                   bool is_incognito,
    581                                                   int request_id) {
    582   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    583 
    584   if (AppLauncherHandler::HandlePing(profile_, path)) {
    585     return;
    586   } else if (!path.empty() && path[0] != '#') {
    587     // A path under new-tab was requested; it's likely a bad relative
    588     // URL from the new tab page, but in any case it's an error.
    589     NOTREACHED();
    590     return;
    591   }
    592 
    593   scoped_refptr<RefCountedBytes> html_bytes(
    594       profile_->GetNTPResourceCache()->GetNewTabHTML(is_incognito));
    595 
    596   SendResponse(request_id, html_bytes);
    597 }
    598 
    599 std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string&) const {
    600   return "text/html";
    601 }
    602 
    603 bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const {
    604   return false;
    605 }
    606