普通文本  |  570行  |  23.68 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ntp_resource_cache.h"

#include <algorithm>
#include <vector>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/browser/ui/webui/shown_sections_handler.h"
#include "chrome/browser/web_resource/promo_resource_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "ui/base/animation/animation.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/color_utils.h"

#if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
#elif defined(OS_MACOSX)
#include "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
#elif defined(OS_POSIX)
#include "chrome/browser/ui/gtk/bookmarks/bookmark_bar_gtk.h"
#endif

using base::Time;

namespace {

// The URL for the the Learn More page shown on incognito new tab.
const char kLearnMoreIncognitoUrl[] =
#if defined(OS_CHROMEOS)
    "https://www.google.com/support/chromeos/bin/answer.py?answer=95464";
#else
    "https://www.google.com/support/chrome/bin/answer.py?answer=95464";
#endif

// The URL for the Learn More page shown on guest session new tab.
const char kLearnMoreGuestSessionUrl[] =
    "https://www.google.com/support/chromeos/bin/answer.py?answer=1057090";

// The URL for bookmark sync service help.
const char kSyncServiceHelpUrl[] =
    "https://www.google.com/support/chrome/bin/answer.py?answer=165139";

// The URL to be loaded to display Help.
const char kHelpContentUrl[] =
    "https://www.google.com/support/chrome/";

string16 GetUrlWithLang(const GURL& url) {
  return ASCIIToUTF16(google_util::AppendGoogleLocaleParam(url).spec());
}

std::string SkColorToRGBAString(SkColor color) {
  // We convert the alpha using DoubleToString because StringPrintf will use
  // locale specific formatters (e.g., use , instead of . in German).
  return StringPrintf("rgba(%d,%d,%d,%s)", SkColorGetR(color),
      SkColorGetG(color), SkColorGetB(color),
      base::DoubleToString(SkColorGetA(color) / 255.0).c_str());
}

// Get the CSS string for the background position on the new tab page for the
// states when the bar is attached or detached.
std::string GetNewTabBackgroundCSS(const ui::ThemeProvider* theme_provider,
                                   bool bar_attached) {
  int alignment;
  theme_provider->GetDisplayProperty(
      ThemeService::NTP_BACKGROUND_ALIGNMENT, &alignment);

  // TODO(glen): This is a quick workaround to hide the notused.png image when
  // no image is provided - we don't have time right now to figure out why
  // this is painting as white.
  // http://crbug.com/17593
  if (!theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
    return "-64px";
  }

  if (bar_attached)
    return ThemeService::AlignmentToString(alignment);

  // The bar is detached, so we must offset the background by the bar size
  // if it's a top-aligned bar.
#if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
  int offset = BookmarkBarView::kNewtabBarHeight;
#elif defined(OS_MACOSX)
  int offset = bookmarks::kNTPBookmarkBarHeight;
#elif defined(OS_POSIX)
  int offset = BookmarkBarGtk::kBookmarkBarNTPHeight;
#else
  int offset = 0;
#endif

  if (alignment & ThemeService::ALIGN_TOP) {
    if (alignment & ThemeService::ALIGN_LEFT)
      return "0% " + base::IntToString(-offset) + "px";
    else if (alignment & ThemeService::ALIGN_RIGHT)
      return "100% " + base::IntToString(-offset) + "px";
    return "center " + base::IntToString(-offset) + "px";
  }
  return ThemeService::AlignmentToString(alignment);
}

// How the background image on the new tab page should be tiled (see tiling
// masks in theme_service.h).
std::string GetNewTabBackgroundTilingCSS(
    const ui::ThemeProvider* theme_provider) {
  int repeat_mode;
  theme_provider->GetDisplayProperty(
      ThemeService::NTP_BACKGROUND_TILING, &repeat_mode);
  return ThemeService::TilingToString(repeat_mode);
}

// Is the current time within a given date range?
bool InDateRange(double begin, double end) {
  Time start_time = Time::FromDoubleT(begin);
  Time end_time = Time::FromDoubleT(end);
  return start_time < Time::Now() && end_time > Time::Now();
}

}  // namespace

NTPResourceCache::NTPResourceCache(Profile* profile) : profile_(profile) {
  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
                 NotificationService::AllSources());
  registrar_.Add(this, NotificationType::PROMO_RESOURCE_STATE_CHANGED,
                 NotificationService::AllSources());

  // Watch for pref changes that cause us to need to invalidate the HTML cache.
  pref_change_registrar_.Init(profile_->GetPrefs());
  pref_change_registrar_.Add(prefs::kShowBookmarkBar, this);
  pref_change_registrar_.Add(prefs::kEnableBookmarkBar, this);
  pref_change_registrar_.Add(prefs::kNTPShownSections, this);
}

NTPResourceCache::~NTPResourceCache() {}

RefCountedBytes* NTPResourceCache::GetNewTabHTML(bool is_incognito) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (is_incognito) {
    if (!new_tab_incognito_html_.get())
      CreateNewTabIncognitoHTML();
  } else {
    if (!new_tab_html_.get())
      CreateNewTabHTML();
  }
  return is_incognito ? new_tab_incognito_html_.get()
                      : new_tab_html_.get();
}

RefCountedBytes* NTPResourceCache::GetNewTabCSS(bool is_incognito) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (is_incognito) {
    if (!new_tab_incognito_css_.get())
      CreateNewTabIncognitoCSS();
  } else {
    if (!new_tab_css_.get())
      CreateNewTabCSS();
  }
  return is_incognito ? new_tab_incognito_css_.get()
                      : new_tab_css_.get();
}

void NTPResourceCache::Observe(NotificationType type,
    const NotificationSource& source, const NotificationDetails& details) {
  // Invalidate the cache.
  if (NotificationType::BROWSER_THEME_CHANGED == type ||
      NotificationType::PROMO_RESOURCE_STATE_CHANGED == type) {
    new_tab_incognito_html_ = NULL;
    new_tab_html_ = NULL;
    new_tab_incognito_css_ = NULL;
    new_tab_css_ = NULL;
  } else if (NotificationType::PREF_CHANGED == type) {
    std::string* pref_name = Details<std::string>(details).ptr();
    if (*pref_name == prefs::kShowBookmarkBar ||
        *pref_name == prefs::kEnableBookmarkBar ||
        *pref_name == prefs::kHomePageIsNewTabPage ||
        *pref_name == prefs::kNTPShownSections) {
      new_tab_incognito_html_ = NULL;
      new_tab_html_ = NULL;
    } else {
      NOTREACHED();
    }
  } else {
    NOTREACHED();
  }
}

void NTPResourceCache::CreateNewTabIncognitoHTML() {
  DictionaryValue localized_strings;
  localized_strings.SetString("title",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
  int new_tab_message_ids = IDS_NEW_TAB_OTR_MESSAGE;
  int new_tab_html_idr = IDR_INCOGNITO_TAB_HTML;
  const char* new_tab_link = kLearnMoreIncognitoUrl;
  // TODO(altimofeev): consider implementation without 'if def' usage.
#if defined(OS_CHROMEOS)
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession)) {
    new_tab_message_ids = IDS_NEW_TAB_GUEST_SESSION_MESSAGE;
    new_tab_html_idr = IDR_GUEST_SESSION_TAB_HTML;
    new_tab_link = kLearnMoreGuestSessionUrl;
  }
#endif
  localized_strings.SetString("content",
      l10n_util::GetStringFUTF16(new_tab_message_ids,
                                 GetUrlWithLang(GURL(new_tab_link))));
  localized_strings.SetString("extensionsmessage",
      l10n_util::GetStringFUTF16(IDS_NEW_TAB_OTR_EXTENSIONS_MESSAGE,
                                 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
                                 ASCIIToUTF16(chrome::kChromeUIExtensionsURL)));
  bool bookmark_bar_attached = profile_->GetPrefs()->GetBoolean(
      prefs::kShowBookmarkBar);
  localized_strings.SetString("bookmarkbarattached",
      bookmark_bar_attached ? "true" : "false");

  ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings);

  static const base::StringPiece incognito_tab_html(
      ResourceBundle::GetSharedInstance().GetRawDataResource(
          new_tab_html_idr));

  std::string full_html = jstemplate_builder::GetI18nTemplateHtml(
      incognito_tab_html, &localized_strings);

  new_tab_incognito_html_ = new RefCountedBytes;
  new_tab_incognito_html_->data.resize(full_html.size());
  std::copy(full_html.begin(), full_html.end(),
            new_tab_incognito_html_->data.begin());
}

void NTPResourceCache::CreateNewTabHTML() {
  // Show the profile name in the title and most visited labels if the current
  // profile is not the default.
  string16 apps = l10n_util::GetStringUTF16(IDS_NEW_TAB_APPS);
  string16 title = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
  string16 most_visited = l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED);
  DictionaryValue localized_strings;
  localized_strings.SetString("bookmarkbarattached",
      profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) ?
      "true" : "false");
  localized_strings.SetString("hasattribution",
      ThemeServiceFactory::GetForProfile(profile_)->HasCustomImage(
          IDR_THEME_NTP_ATTRIBUTION) ?
      "true" : "false");
  localized_strings.SetString("apps", apps);
  localized_strings.SetString("title", title);
  localized_strings.SetString("mostvisited", most_visited);
  localized_strings.SetString("restorethumbnails",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_RESTORE_THUMBNAILS_LINK));
  localized_strings.SetString("recentlyclosed",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_RECENTLY_CLOSED));
  localized_strings.SetString("closedwindowsingle",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE));
  localized_strings.SetString("foreignsessions",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_SESSIONS));
  localized_strings.SetString("closedwindowmultiple",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE));
  localized_strings.SetString("attributionintro",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_ATTRIBUTION_INTRO));
  localized_strings.SetString("thumbnailremovednotification",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_THUMBNAIL_REMOVED_NOTIFICATION));
  localized_strings.SetString("undothumbnailremove",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE));
  localized_strings.SetString("removethumbnailtooltip",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_REMOVE_THUMBNAIL_TOOLTIP));
  localized_strings.SetString("pinthumbnailtooltip",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_PIN_THUMBNAIL_TOOLTIP));
  localized_strings.SetString("unpinthumbnailtooltip",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_UNPIN_THUMBNAIL_TOOLTIP));
  localized_strings.SetString("showhidethumbnailtooltip",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_SHOW_HIDE_THUMBNAIL_TOOLTIP));
  localized_strings.SetString("showhidelisttooltip",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_SHOW_HIDE_LIST_TOOLTIP));
  localized_strings.SetString("pagedisplaytooltip",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_PAGE_DISPLAY_TOOLTIP));
  localized_strings.SetString("closefirstrunnotification",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_CLOSE_FIRST_RUN_NOTIFICATION));
  localized_strings.SetString("close", l10n_util::GetStringUTF16(IDS_CLOSE));
  localized_strings.SetString("history",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_HISTORY));
  localized_strings.SetString("downloads",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_DOWNLOADS));
  localized_strings.SetString("help",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_HELP));
  localized_strings.SetString("helpurl",
      GetUrlWithLang(GURL(kHelpContentUrl)));
  localized_strings.SetString("appsettings",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_SETTINGS));
  localized_strings.SetString("appuninstall",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_UNINSTALL));
  localized_strings.SetString("appoptions",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_OPTIONS));
  localized_strings.SetString("appcreateshortcut",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_CREATE_SHORTCUT));
  localized_strings.SetString("applaunchtypepinned",
      l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
  localized_strings.SetString("applaunchtyperegular",
      l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
  localized_strings.SetString("applaunchtypewindow",
      l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
  localized_strings.SetString("applaunchtypefullscreen",
      l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
  localized_strings.SetString("web_store_title",
      l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
  localized_strings.SetString("web_store_url",
      GetUrlWithLang(GURL(Extension::ChromeStoreLaunchURL())));
  localized_strings.SetString("syncpromotext",
      l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL));
#if defined(OS_CHROMEOS)
  localized_strings.SetString("expandMenu",
      l10n_util::GetStringUTF16(IDS_NEW_TAB_CLOSE_MENU_EXPAND));
#endif

  // Don't initiate the sync related message passing with the page if the sync
  // code is not present.
  if (profile_->GetProfileSyncService())
    localized_strings.SetString("syncispresent", "true");
  else
    localized_strings.SetString("syncispresent", "false");

  ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings);

  // Control fade and resize animations.
  std::string anim =
      ui::Animation::ShouldRenderRichAnimation() ? "true" : "false";
  localized_strings.SetString("anim", anim);

  // Pass the shown_sections pref early so that we can prevent flicker.
  const int shown_sections = ShownSectionsHandler::GetShownSections(
      profile_->GetPrefs());
  localized_strings.SetInteger("shown_sections", shown_sections);

  // If the user has preferences for a start and end time for a custom logo,
  // and the time now is between these two times, show the custom logo.
  if (profile_->GetPrefs()->FindPreference(prefs::kNTPCustomLogoStart) &&
      profile_->GetPrefs()->FindPreference(prefs::kNTPCustomLogoEnd)) {
    localized_strings.SetString("customlogo",
        InDateRange(profile_->GetPrefs()->GetDouble(prefs::kNTPCustomLogoStart),
                    profile_->GetPrefs()->GetDouble(prefs::kNTPCustomLogoEnd)) ?
        "true" : "false");
  } else {
    localized_strings.SetString("customlogo", "false");
  }

  // If the user has preferences for a start and end time for a promo from
  // the server, and this promo string exists, set the localized string.
  if (profile_->GetPrefs()->FindPreference(prefs::kNTPPromoStart) &&
      profile_->GetPrefs()->FindPreference(prefs::kNTPPromoEnd) &&
      profile_->GetPrefs()->FindPreference(prefs::kNTPPromoLine) &&
      PromoResourceServiceUtil::CanShowPromo(profile_)) {
    localized_strings.SetString("serverpromo",
        InDateRange(profile_->GetPrefs()->GetDouble(prefs::kNTPPromoStart),
                    profile_->GetPrefs()->GetDouble(prefs::kNTPPromoEnd)) ?
                    profile_->GetPrefs()->GetString(prefs::kNTPPromoLine) :
                                                    std::string());
    UserMetrics::RecordAction(UserMetricsAction("NTPPromoShown"));
  }

  // Load the new tab page appropriate for this build
  // Note that some builds (eg. TOUCHUI) don't make use of everything we
  // do here (all of the template data, etc.), but we keep the back end
  // consistent across builds, supporting the union of all NTP front-ends
  // for simplicity.
  std::string full_html;
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4)) {
    base::StringPiece new_tab_html(ResourceBundle::GetSharedInstance().
        GetRawDataResource(IDR_NEW_TAB_4_HTML));
    full_html = jstemplate_builder::GetI18nTemplateHtml(new_tab_html,
                                                        &localized_strings);
  } else {
    base::StringPiece new_tab_html(ResourceBundle::GetSharedInstance().
        GetRawDataResource(IDR_NEW_TAB_HTML));

    // Inject the template data into the HTML so that it is available before any
    // layout is needed.
    std::string json_html;
    jstemplate_builder::AppendJsonHtml(&localized_strings, &json_html);

    static const base::StringPiece template_data_placeholder(
        "<!-- template data placeholder -->");
    size_t pos = new_tab_html.find(template_data_placeholder);

    if (pos != base::StringPiece::npos) {
      full_html.assign(new_tab_html.data(), pos);
      full_html.append(json_html);
      size_t after_offset = pos + template_data_placeholder.size();
      full_html.append(new_tab_html.data() + after_offset,
                       new_tab_html.size() - after_offset);
    } else {
      NOTREACHED();
      full_html.assign(new_tab_html.data(), new_tab_html.size());
    }
  }

  new_tab_html_ = new RefCountedBytes;
  new_tab_html_->data.resize(full_html.size());
  std::copy(full_html.begin(), full_html.end(), new_tab_html_->data.begin());
}

void NTPResourceCache::CreateNewTabIncognitoCSS() {
  ui::ThemeProvider* tp = ThemeServiceFactory::GetForProfile(profile_);
  DCHECK(tp);

  // Get our theme colors
  SkColor color_background =
      tp->GetColor(ThemeService::COLOR_NTP_BACKGROUND);

  // Generate the replacements.
  std::vector<std::string> subst;

  // Cache-buster for background.
  subst.push_back(
      profile_->GetPrefs()->GetString(prefs::kCurrentThemeID));  // $1

  // Colors.
  subst.push_back(SkColorToRGBAString(color_background));  // $2
  subst.push_back(GetNewTabBackgroundCSS(tp, false));  // $3
  subst.push_back(GetNewTabBackgroundCSS(tp, true));  // $4
  subst.push_back(GetNewTabBackgroundTilingCSS(tp));  // $5

  // Get our template.
  static const base::StringPiece new_tab_theme_css(
      ResourceBundle::GetSharedInstance().GetRawDataResource(
      IDR_NEW_INCOGNITO_TAB_THEME_CSS));

  // Create the string from our template and the replacements.
  std::string full_css = ReplaceStringPlaceholders(
      new_tab_theme_css, subst, NULL);

  new_tab_incognito_css_ = new RefCountedBytes;
  new_tab_incognito_css_->data.resize(full_css.size());
  std::copy(full_css.begin(), full_css.end(),
            new_tab_incognito_css_->data.begin());
}

void NTPResourceCache::CreateNewTabCSS() {
  ui::ThemeProvider* tp = ThemeServiceFactory::GetForProfile(profile_);
  DCHECK(tp);

  // Get our theme colors
  SkColor color_background =
      tp->GetColor(ThemeService::COLOR_NTP_BACKGROUND);
  SkColor color_text = tp->GetColor(ThemeService::COLOR_NTP_TEXT);
  SkColor color_link = tp->GetColor(ThemeService::COLOR_NTP_LINK);
  SkColor color_link_underline =
      tp->GetColor(ThemeService::COLOR_NTP_LINK_UNDERLINE);

  SkColor color_section =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION);
  SkColor color_section_text =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_TEXT);
  SkColor color_section_link =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_LINK);
  SkColor color_section_link_underline =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE);
  SkColor color_section_header_text =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_HEADER_TEXT);
  SkColor color_section_header_text_hover =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_HEADER_TEXT_HOVER);
  SkColor color_section_header_rule =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_HEADER_RULE);
  SkColor color_section_header_rule_light =
      tp->GetColor(ThemeService::COLOR_NTP_SECTION_HEADER_RULE_LIGHT);
  SkColor color_text_light =
      tp->GetColor(ThemeService::COLOR_NTP_TEXT_LIGHT);

  SkColor color_header =
      tp->GetColor(ThemeService::COLOR_NTP_HEADER);
  // Generate a lighter color for the header gradients.
  color_utils::HSL header_lighter;
  color_utils::SkColorToHSL(color_header, &header_lighter);
  header_lighter.l += (1 - header_lighter.l) * 0.33;
  SkColor color_header_gradient_light =
      color_utils::HSLToSkColor(header_lighter, SkColorGetA(color_header));

  // Generate section border color from the header color. See
  // BookmarkBarView::Paint for how we do this for the bookmark bar
  // borders.
  SkColor color_section_border =
      SkColorSetARGB(80,
                     SkColorGetR(color_header),
                     SkColorGetG(color_header),
                     SkColorGetB(color_header));

  // Generate the replacements.
  std::vector<std::string> subst;
  // A second list of replacements, each of which must be in $$x format,
  // where x is a digit from 1-9.
  std::vector<std::string> subst2;
  std::vector<std::string> subst3;

  // Cache-buster for background.
  subst.push_back(
      profile_->GetPrefs()->GetString(prefs::kCurrentThemeID));  // $1

  // Colors.
  subst.push_back(SkColorToRGBAString(color_background));  // $2
  subst.push_back(GetNewTabBackgroundCSS(tp, false));  // $3
  subst.push_back(GetNewTabBackgroundCSS(tp, true));  // $4
  subst.push_back(GetNewTabBackgroundTilingCSS(tp));  // $5
  subst.push_back(SkColorToRGBAString(color_header));  // $6
  subst.push_back(SkColorToRGBAString(color_header_gradient_light));  // $7
  subst.push_back(SkColorToRGBAString(color_text));  // $8
  subst.push_back(SkColorToRGBAString(color_link));  // $9

  subst2.push_back(SkColorToRGBAString(color_section));  // $$1
  subst2.push_back(SkColorToRGBAString(color_section_border));  // $$2
  subst2.push_back(SkColorToRGBAString(color_section_text));  // $$3
  subst2.push_back(SkColorToRGBAString(color_section_link));  // $$4
  subst2.push_back(SkColorToRGBAString(color_link_underline));  // $$5
  subst2.push_back(SkColorToRGBAString(color_section_link_underline));  // $$6
  subst2.push_back(SkColorToRGBAString(color_section_header_text)); // $$7
  subst2.push_back(SkColorToRGBAString(
      color_section_header_text_hover)); // $$8
  subst2.push_back(SkColorToRGBAString(color_section_header_rule));  // $$9

  subst3.push_back(SkColorToRGBAString(
      color_section_header_rule_light));  // $$$1
  subst3.push_back(SkColorToRGBAString(
      SkColorSetA(color_section_header_rule, 0)));  // $$$2
  subst3.push_back(SkColorToRGBAString(color_text_light));  // $$$3

  // Get our template.
  int ntp_css_resource_id =
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4) ?
          IDR_NEW_TAB_4_THEME_CSS : IDR_NEW_TAB_THEME_CSS;
  static const base::StringPiece new_tab_theme_css(
      ResourceBundle::GetSharedInstance().GetRawDataResource(
          ntp_css_resource_id));

  // Create the string from our template and the replacements.
  std::string css_string;
  css_string = ReplaceStringPlaceholders(new_tab_theme_css, subst, NULL);
  css_string = ReplaceStringPlaceholders(css_string, subst2, NULL);
  css_string = ReplaceStringPlaceholders(css_string, subst3, NULL);

  new_tab_css_ = new RefCountedBytes;
  new_tab_css_->data.resize(css_string.size());
  std::copy(css_string.begin(), css_string.end(),
            new_tab_css_->data.begin());
}