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 51namespace { 52 53// The number of recent bookmarks we show. 54const int kRecentBookmarks = 9; 55 56// The number of search URLs to show. 57const 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. 61const 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. 65const char kRTLHtmlTextDirection[] = "rtl"; 66const char kDefaultHtmlTextDirection[] = "ltr"; 67 68/////////////////////////////////////////////////////////////////////////////// 69// RecentlyClosedTabsHandler 70 71class 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 104void 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 112RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() { 113 if (tab_restore_service_) 114 tab_restore_service_->RemoveObserver(this); 115} 116 117void 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 131void 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 151void 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 159void 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) 173class 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 192void 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 200void MetricsHandler::HandleMetrics(const ListValue* args) { 201 std::string string_action = UTF16ToUTF8(ExtractStringValue(args)); 202 UserMetrics::RecordComputedAction(string_action, web_ui_->GetProfile()); 203} 204 205void 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. 215class 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 231void NewTabPageSetHomePageHandler::RegisterMessages() { 232 web_ui_->RegisterMessageCallback("setHomePage", NewCallback( 233 this, &NewTabPageSetHomePageHandler::HandleSetHomePage)); 234} 235 236void 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. 253class 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 269void NewTabPageClosePromoHandler::RegisterMessages() { 270 web_ui_->RegisterMessageCallback("closePromo", NewCallback( 271 this, &NewTabPageClosePromoHandler::HandleClosePromo)); 272} 273 274void 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 288NewTabUI::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 350NewTabUI::~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. 355void 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 377void 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} 386void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) { 387 StartTimingPaint(render_view_host); 388} 389 390void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) { 391 StartTimingPaint(render_view_host); 392} 393 394void 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 428void NewTabUI::InitializeCSSCaches() { 429 Profile* profile = GetProfile(); 430 ThemeSource* theme = new ThemeSource(profile); 431 profile->GetChromeURLDataManager()->AddDataSource(theme); 432} 433 434// static 435void 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 445bool 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 456void 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 463bool NewTabUI::FirstRunDisabled() { 464 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 465 return command_line->HasSwitch(switches::kDisableNewTabFirstRun); 466} 467 468// static 469void 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 518namespace { 519 520bool 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 541void 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 572bool NewTabUI::NewTabHTMLSource::first_run_ = true; 573 574NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile) 575 : DataSource(chrome::kChromeUINewTabHost, MessageLoop::current()), 576 profile_(profile) { 577} 578 579void 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 599std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string&) const { 600 return "text/html"; 601} 602 603bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const { 604 return false; 605} 606