diff options
author | Ben Murdoch <benm@google.com> | 2010-07-29 17:14:53 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-08-04 14:29:45 +0100 |
commit | c407dc5cd9bdc5668497f21b26b09d988ab439de (patch) | |
tree | 7eaf8707c0309516bdb042ad976feedaf72b0bb1 /chrome/browser/dom_ui/new_tab_ui.cc | |
parent | 0998b1cdac5733f299c12d88bc31ef9c8035b8fa (diff) | |
download | external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.zip external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.gz external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.bz2 |
Merge Chromium src@r53293
Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
Diffstat (limited to 'chrome/browser/dom_ui/new_tab_ui.cc')
-rw-r--r-- | chrome/browser/dom_ui/new_tab_ui.cc | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc new file mode 100644 index 0000000..d367db3 --- /dev/null +++ b/chrome/browser/dom_ui/new_tab_ui.cc @@ -0,0 +1,719 @@ +// Copyright (c) 2010 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 "build/build_config.h" + +#include "chrome/browser/dom_ui/new_tab_ui.h" + +#include <set> + +#include "app/l10n_util.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/histogram.h" +#include "base/i18n/rtl.h" +#include "base/singleton.h" +#include "base/thread.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/dom_ui/app_launcher_handler.h" +#include "chrome/browser/dom_ui/dom_ui_theme_source.h" +#include "chrome/browser/dom_ui/most_visited_handler.h" +#include "chrome/browser/dom_ui/new_tab_page_sync_handler.h" +#include "chrome/browser/dom_ui/ntp_resource_cache.h" +#include "chrome/browser/dom_ui/shown_sections_handler.h" +#include "chrome/browser/dom_ui/tips_handler.h" +#include "chrome/browser/importer/importer_data_types.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/sessions/session_types.h" +#include "chrome/browser/sessions/tab_restore_service.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/generated_resources.h" + +#if defined(OS_WIN) +#include "chrome/browser/views/importer_view.h" +#include "views/window/window.h" +#endif + +namespace { + +// The number of recent bookmarks we show. +const int kRecentBookmarks = 9; + +// The number of search URLs to show. +const int kSearchURLs = 3; + +// Strings sent to the page via jstemplates used to set the direction of the +// HTML document based on locale. +const wchar_t kRTLHtmlTextDirection[] = L"rtl"; +const wchar_t kDefaultHtmlTextDirection[] = L"ltr"; + +//////////////////////////////////////////////////////////////////////////////// +// PaintTimer + +// To measure end-to-end performance of the new tab page, we observe paint +// messages and wait for the page to stop repainting. +class PaintTimer : public RenderWidgetHost::PaintObserver { + public: + PaintTimer() { + Start(); + } + + // Start the benchmarking and the timer. + void Start() { + start_ = base::TimeTicks::Now(); + last_paint_ = start_; + + timer_.Start(base::TimeDelta::FromMilliseconds(kTimeoutMs), this, + &PaintTimer::Timeout); + } + + // A callback that is invoked whenever our RenderWidgetHost paints. + virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) { + last_paint_ = base::TimeTicks::Now(); + } + + // The timer callback. If enough time has elapsed since the last paint + // message, we say we're done painting; otherwise, we keep waiting. + void Timeout() { + base::TimeTicks now = base::TimeTicks::Now(); + if ((now - last_paint_) >= base::TimeDelta::FromMilliseconds(kTimeoutMs)) { + // Painting has quieted down. Log this as the full time to run. + base::TimeDelta load_time = last_paint_ - start_; + int load_time_ms = static_cast<int>(load_time.InMilliseconds()); + NotificationService::current()->Notify( + NotificationType::INITIAL_NEW_TAB_UI_LOAD, + NotificationService::AllSources(), + Details<int>(&load_time_ms)); + UMA_HISTOGRAM_TIMES("NewTabUI load", load_time); + } else { + // Not enough quiet time has elapsed. + // Some more paints must've occurred since we set the timeout. + // Wait some more. + timer_.Start(base::TimeDelta::FromMilliseconds(kTimeoutMs), this, + &PaintTimer::Timeout); + } + } + + private: + // The amount of time there must be no painting for us to consider painting + // finished. Observed times are in the ~1200ms range on Windows. + static const int kTimeoutMs = 2000; + // The time when we started benchmarking. + base::TimeTicks start_; + // The last time we got a paint notification. + base::TimeTicks last_paint_; + // Scoping so we can be sure our timeouts don't outlive us. + base::OneShotTimer<PaintTimer> timer_; + + DISALLOW_COPY_AND_ASSIGN(PaintTimer); +}; + +/////////////////////////////////////////////////////////////////////////////// +// RecentlyClosedTabsHandler + +class RecentlyClosedTabsHandler : public DOMMessageHandler, + public TabRestoreService::Observer { + public: + RecentlyClosedTabsHandler() : tab_restore_service_(NULL) {} + virtual ~RecentlyClosedTabsHandler(); + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for the "reopenTab" message. Rewrites the history of the + // currently displayed tab to be the one in TabRestoreService with a + // history of a session passed in through the content pointer. + void HandleReopenTab(const Value* content); + + // Callback for the "getRecentlyClosedTabs" message. + void HandleGetRecentlyClosedTabs(const Value* content); + + // Observer callback for TabRestoreService::Observer. Sends data on + // recently closed tabs to the javascript side of this page to + // display to the user. + virtual void TabRestoreServiceChanged(TabRestoreService* service); + + // Observer callback to notice when our associated TabRestoreService + // is destroyed. + virtual void TabRestoreServiceDestroyed(TabRestoreService* service); + + private: + // Converts a closed tab to the value sent down to the NTP. Returns true on + // success, false if the value shouldn't be sent down. + bool TabToValue(const TabRestoreService::Tab& tab, + DictionaryValue* dictionary); + + // Converts a closed window to the value sent down to the NTP. Returns true + // on success, false if the value shouldn't be sent down. + bool WindowToValue(const TabRestoreService::Window& window, + DictionaryValue* dictionary); + + // Adds tab to unique_items list if it is not present. Returns false if + // tab was already in the list, true if it was absent. A tab is + // considered unique if no other tab shares both its title and its url. + bool EnsureTabIsUnique(const DictionaryValue* tab, + std::set<std::wstring>& unique_items); + + // TabRestoreService that we are observing. + TabRestoreService* tab_restore_service_; + + DISALLOW_COPY_AND_ASSIGN(RecentlyClosedTabsHandler); +}; + +void RecentlyClosedTabsHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("getRecentlyClosedTabs", + NewCallback(this, + &RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs)); + dom_ui_->RegisterMessageCallback("reopenTab", + NewCallback(this, &RecentlyClosedTabsHandler::HandleReopenTab)); +} + +RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() { + if (tab_restore_service_) + tab_restore_service_->RemoveObserver(this); +} + +void RecentlyClosedTabsHandler::HandleReopenTab(const Value* content) { + Browser* browser = Browser::GetBrowserForController( + &dom_ui_->tab_contents()->controller(), NULL); + if (!browser) + return; + + // Extract the integer value of the tab session to restore from the + // incoming string array. This will be greatly simplified when + // DOMUIBindings::send() is generalized to all data types instead of + // silently failing when passed anything other then an array of + // strings. + if (content->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(content); + Value* list_member; + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + std::wstring wstring_value; + if (string_value->GetAsString(&wstring_value)) { + int session_to_restore = StringToInt(WideToUTF16Hack(wstring_value)); + tab_restore_service_->RestoreEntryById(browser, session_to_restore, + true); + // The current tab has been nuked at this point; don't touch any member + // variables. + } + } + } +} + +void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs( + const Value* content) { + if (!tab_restore_service_) { + tab_restore_service_ = dom_ui_->GetProfile()->GetTabRestoreService(); + + // GetTabRestoreService() can return NULL (i.e., when in Off the + // Record mode) + if (tab_restore_service_) { + // This does nothing if the tabs have already been loaded or they + // shouldn't be loaded. + tab_restore_service_->LoadTabsFromLastSession(); + + tab_restore_service_->AddObserver(this); + } + } + + if (tab_restore_service_) + TabRestoreServiceChanged(tab_restore_service_); +} + +void RecentlyClosedTabsHandler::TabRestoreServiceChanged( + TabRestoreService* service) { + const TabRestoreService::Entries& entries = service->entries(); + ListValue list_value; + std::set<std::wstring> unique_items; + int added_count = 0; + const int max_count = 10; + + // We filter the list of recently closed to only show 'interesting' entries, + // where an interesting entry is either a closed window or a closed tab + // whose selected navigation is not the new tab ui. + for (TabRestoreService::Entries::const_iterator it = entries.begin(); + it != entries.end() && added_count < max_count; ++it) { + TabRestoreService::Entry* entry = *it; + DictionaryValue* value = new DictionaryValue(); + if ((entry->type == TabRestoreService::TAB && + TabToValue(*static_cast<TabRestoreService::Tab*>(entry), value) && + EnsureTabIsUnique(value, unique_items)) || + (entry->type == TabRestoreService::WINDOW && + WindowToValue(*static_cast<TabRestoreService::Window*>(entry), + value))) { + value->SetInteger(L"sessionId", entry->id); + list_value.Append(value); + added_count++; + } else { + delete value; + } + } + dom_ui_->CallJavascriptFunction(L"recentlyClosedTabs", list_value); +} + +void RecentlyClosedTabsHandler::TabRestoreServiceDestroyed( + TabRestoreService* service) { + tab_restore_service_ = NULL; +} + +bool RecentlyClosedTabsHandler::TabToValue( + const TabRestoreService::Tab& tab, + DictionaryValue* dictionary) { + if (tab.navigations.empty()) + return false; + + const TabNavigation& current_navigation = + tab.navigations.at(tab.current_navigation_index); + if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL)) + return false; + + NewTabUI::SetURLTitleAndDirection(dictionary, current_navigation.title(), + current_navigation.virtual_url()); + dictionary->SetString(L"type", L"tab"); + dictionary->SetReal(L"timestamp", tab.timestamp.ToDoubleT()); + return true; +} + +bool RecentlyClosedTabsHandler::WindowToValue( + const TabRestoreService::Window& window, + DictionaryValue* dictionary) { + if (window.tabs.empty()) { + NOTREACHED(); + return false; + } + + ListValue* tab_values = new ListValue(); + for (size_t i = 0; i < window.tabs.size(); ++i) { + DictionaryValue* tab_value = new DictionaryValue(); + if (TabToValue(window.tabs[i], tab_value)) + tab_values->Append(tab_value); + else + delete tab_value; + } + if (tab_values->GetSize() == 0) { + delete tab_values; + return false; + } + + dictionary->SetString(L"type", L"window"); + dictionary->SetReal(L"timestamp", window.timestamp.ToDoubleT()); + dictionary->Set(L"tabs", tab_values); + return true; +} + +bool RecentlyClosedTabsHandler::EnsureTabIsUnique(const DictionaryValue* tab, + std::set<std::wstring>& unique_items) { + std::wstring title; + std::wstring url; + if (tab->GetString(L"title", &title) && + tab->GetString(L"url", &url)) { + std::wstring unique_key = title + url; + if (unique_items.find(unique_key) != unique_items.end()) + return false; + else + unique_items.insert(unique_key); + } + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// MetricsHandler + +// Let the page contents record UMA actions. Only use when you can't do it from +// C++. For example, we currently use it to let the NTP log the postion of the +// Most Visited or Bookmark the user clicked on, as we don't get that +// information through RequestOpenURL. You will need to update the metrics +// dashboard with the action names you use, as our processor won't catch that +// information (treat it as RecordComputedMetrics) +class MetricsHandler : public DOMMessageHandler { + public: + MetricsHandler() {} + virtual ~MetricsHandler() {} + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback which records a user action. + void HandleMetrics(const Value* content); + + // Callback for the "logEventTime" message. + void HandleLogEventTime(const Value* content); + + private: + + DISALLOW_COPY_AND_ASSIGN(MetricsHandler); +}; + +void MetricsHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("metrics", + NewCallback(this, &MetricsHandler::HandleMetrics)); + + dom_ui_->RegisterMessageCallback("logEventTime", + NewCallback(this, &MetricsHandler::HandleLogEventTime)); +} + +void MetricsHandler::HandleMetrics(const Value* content) { + if (content && content->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(content); + Value* list_member; + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + std::wstring wstring_value; + if (string_value->GetAsString(&wstring_value)) { + UserMetrics::RecordComputedAction(WideToASCII(wstring_value), + dom_ui_->GetProfile()); + } + } + } +} + +void MetricsHandler::HandleLogEventTime(const Value* content) { + if (content && content->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(content); + Value* list_member; + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + std::string event_name; + if (list_member->GetAsString(&event_name)) { + dom_ui_->tab_contents()->LogNewTabTime(event_name); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// NewTabPageSetHomePageHandler + +// Sets the new tab page as home page when user clicks on "make this my home +// page" link. +class NewTabPageSetHomePageHandler : public DOMMessageHandler { + public: + NewTabPageSetHomePageHandler() {} + virtual ~NewTabPageSetHomePageHandler() {} + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for "setHomePage". + void HandleSetHomePage(const Value* value); + + private: + + DISALLOW_COPY_AND_ASSIGN(NewTabPageSetHomePageHandler); +}; + +void NewTabPageSetHomePageHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("setHomePage", NewCallback( + this, &NewTabPageSetHomePageHandler::HandleSetHomePage)); +} + +void NewTabPageSetHomePageHandler::HandleSetHomePage( + const Value* value) { + dom_ui_->GetProfile()->GetPrefs()->SetBoolean(prefs::kHomePageIsNewTabPage, + true); + ListValue list_value; + list_value.Append(new StringValue( + l10n_util::GetString(IDS_NEW_TAB_HOME_PAGE_SET_NOTIFICATION))); + list_value.Append(new StringValue( + l10n_util::GetString(IDS_NEW_TAB_HOME_PAGE_HIDE_NOTIFICATION))); + dom_ui_->CallJavascriptFunction(L"onHomePageSet", list_value); +} + +/////////////////////////////////////////////////////////////////////////////// +// NewTabPageImportBookmarksHandler +class NewTabPageImportBookmarksHandler : public DOMMessageHandler { + public: + NewTabPageImportBookmarksHandler() {} + virtual ~NewTabPageImportBookmarksHandler() {} + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for "importBookmarks". + void HandleImportBookmarks(const Value* value); + private: + + DISALLOW_COPY_AND_ASSIGN(NewTabPageImportBookmarksHandler); +}; + +void NewTabPageImportBookmarksHandler::RegisterMessages() { + dom_ui_->RegisterMessageCallback("importBookmarks", NewCallback( + this, &NewTabPageImportBookmarksHandler::HandleImportBookmarks)); +} + +void NewTabPageImportBookmarksHandler::HandleImportBookmarks( + const Value* value) { + Browser* browser = NULL; + TabContentsDelegate* delegate = dom_ui_->tab_contents()->delegate(); + if (delegate) + browser = delegate->GetBrowser(); + DCHECK(browser); +#if defined(OS_WIN) + views::Window::CreateChromeWindow( + browser->window()->GetNativeHandle(), + gfx::Rect(), + new ImporterView(dom_ui_->GetProfile(), importer::FAVORITES))->Show(); +#endif +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// NewTabUI + +NewTabUI::NewTabUI(TabContents* contents) + : DOMUI(contents) { + // Override some options on the DOM UI. + hide_favicon_ = true; + force_bookmark_bar_visible_ = true; + force_extension_shelf_visible_ = true; + focus_location_bar_by_default_ = true; + should_hide_url_ = true; + overridden_title_ = WideToUTF16Hack(l10n_util::GetString(IDS_NEW_TAB_TITLE)); + + // We count all link clicks as AUTO_BOOKMARK, so that site can be ranked more + // highly. Note this means we're including clicks on not only most visited + // thumbnails, but also clicks on recently bookmarked. + link_transition_type_ = PageTransition::AUTO_BOOKMARK; + + if (NewTabUI::FirstRunDisabled()) + NewTabHTMLSource::set_first_run(false); + + static bool first_view = true; + if (first_view) { + Profile* profile = GetProfile(); + profile->GetPrefs()->SetInteger(prefs::kNTPPromoViewsRemaining, + profile->GetPrefs()->GetInteger(prefs::kNTPPromoViewsRemaining) - 1); + profile->GetBookmarkModel()->AddObserver(this); + first_view = false; + } + + if (!GetProfile()->IsOffTheRecord()) { + PrefService* pref_service = GetProfile()->GetPrefs(); + AddMessageHandler((new ShownSectionsHandler(pref_service))->Attach(this)); + AddMessageHandler((new MostVisitedHandler())->Attach(this)); + AddMessageHandler((new RecentlyClosedTabsHandler())->Attach(this)); + AddMessageHandler((new MetricsHandler())->Attach(this)); + if (WebResourcesEnabled()) + AddMessageHandler((new TipsHandler())->Attach(this)); + if (GetProfile()->IsSyncAccessible()) + AddMessageHandler((new NewTabPageSyncHandler())->Attach(this)); + if (Extension::AppsAreEnabled()) { + ExtensionsService* service = GetProfile()->GetExtensionsService(); + // We might not have an ExtensionsService (on ChromeOS when not logged in + // for example). + if (service) + AddMessageHandler((new AppLauncherHandler(service))->Attach(this)); + } + + AddMessageHandler((new NewTabPageSetHomePageHandler())->Attach(this)); + AddMessageHandler((new NewTabPageImportBookmarksHandler())->Attach(this)); + } + + // Initializing the CSS and HTML can require some CPU, so do it after + // we've hooked up the most visited handler. This allows the DB query + // for the new tab thumbs to happen earlier. + InitializeCSSCaches(); + NewTabHTMLSource* html_source = + new NewTabHTMLSource(GetProfile()->GetOriginalProfile()); + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + Singleton<ChromeURLDataManager>::get(), + &ChromeURLDataManager::AddDataSource, + make_scoped_refptr(html_source))); + + // Listen for theme installation. + registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, + NotificationService::AllSources()); + // Listen for bookmark bar visibility changes. + registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, + NotificationService::AllSources()); +} + +NewTabUI::~NewTabUI() { + BookmarkModel* bookmark_model = GetProfile()->GetBookmarkModel(); + if (bookmark_model) + bookmark_model->RemoveObserver(this); +} + +void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) { + render_view_host->set_paint_observer(new PaintTimer); +} + +void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) { + render_view_host->set_paint_observer(new PaintTimer); +} + +void NewTabUI::BookmarkNodeAdded(BookmarkModel* model, + const BookmarkNode* parent, + int index) { + // Stop showing the promo, and no longer observe the bookmark model. + GetProfile()->GetPrefs()->SetInteger(prefs::kNTPPromoViewsRemaining, 0); + GetProfile()->GetBookmarkModel()->RemoveObserver(this); +} + +void NewTabUI::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (NotificationType::BROWSER_THEME_CHANGED == type) { + InitializeCSSCaches(); + CallJavascriptFunction(L"themeChanged"); + } else if (NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED) { + if (GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar)) + CallJavascriptFunction(L"bookmarkBarAttached"); + else + CallJavascriptFunction(L"bookmarkBarDetached"); + } +} + +void NewTabUI::InitializeCSSCaches() { + DOMUIThemeSource* theme = new DOMUIThemeSource(GetProfile()); + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + Singleton<ChromeURLDataManager>::get(), + &ChromeURLDataManager::AddDataSource, + make_scoped_refptr(theme))); +} + +// static +void NewTabUI::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterIntegerPref(prefs::kNTPPrefVersion, 0); + + MostVisitedHandler::RegisterUserPrefs(prefs); + ShownSectionsHandler::RegisterUserPrefs(prefs); + if (NewTabUI::WebResourcesEnabled()) + TipsHandler::RegisterUserPrefs(prefs); + + UpdateUserPrefsVersion(prefs); +} + +// static +bool NewTabUI::UpdateUserPrefsVersion(PrefService* prefs) { + const int old_pref_version = prefs->GetInteger(prefs::kNTPPrefVersion); + if (old_pref_version != current_pref_version()) { + MigrateUserPrefs(prefs, old_pref_version, current_pref_version()); + prefs->SetInteger(prefs::kNTPPrefVersion, current_pref_version()); + return true; + } + return false; +} + +// static +void NewTabUI::MigrateUserPrefs(PrefService* prefs, int old_pref_version, + int new_pref_version) { + ShownSectionsHandler::MigrateUserPrefs(prefs, old_pref_version, + current_pref_version()); +} + +// static +bool NewTabUI::WebResourcesEnabled() { + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + return !command_line->HasSwitch(switches::kDisableWebResources); +} + +// static +bool NewTabUI::FirstRunDisabled() { + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(switches::kDisableNewTabFirstRun); +} + +// static +void NewTabUI::SetURLTitleAndDirection(DictionaryValue* dictionary, + const string16& title, + const GURL& gurl) { + std::wstring wstring_url = UTF8ToWide(gurl.spec()); + dictionary->SetString(L"url", wstring_url); + + std::wstring wstring_title = UTF16ToWide(title); + + bool using_url_as_the_title = false; + std::wstring title_to_set(wstring_title); + if (title_to_set.empty()) { + using_url_as_the_title = true; + title_to_set = wstring_url; + } + + // We set the "dir" attribute of the title, so that in RTL locales, a LTR + // title is rendered left-to-right and truncated from the right. For example, + // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN: + // Microsoft developer network". In RTL locales, in the [New Tab] page, if + // the "dir" of this title is not specified, it takes Chrome UI's + // directionality. So the title will be truncated as "soft developer + // network". Setting the "dir" attribute as "ltr" renders the truncated title + // as "MSDN: Microsoft D...". As another example, the title of + // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the + // title will be rendered as "!Yahoo" if its "dir" attribute is not set to + // "ltr". + // + // Since the title can contain BiDi text, we need to mark the text as either + // RTL or LTR, depending on the characters in the string. If we use the URL + // as the title, we mark the title as LTR since URLs are always treated as + // left to right strings. Simply setting the title's "dir" attribute works + // fine for rendering and truncating the title. However, it does not work for + // entire title within a tooltip when the mouse is over the title link.. For + // example, without LRE-PDF pair, the title "Yahoo!" will be rendered as + // "!Yahoo" within the tooltip when the mouse is over the title link. + std::wstring direction = kDefaultHtmlTextDirection; + if (base::i18n::IsRTL()) { + if (using_url_as_the_title) { + base::i18n::WrapStringWithLTRFormatting(&title_to_set); + } else { + if (base::i18n::StringContainsStrongRTLChars(wstring_title)) { + base::i18n::WrapStringWithRTLFormatting(&title_to_set); + direction = kRTLHtmlTextDirection; + } else { + base::i18n::WrapStringWithLTRFormatting(&title_to_set); + } + } + } + dictionary->SetString(L"title", title_to_set); + dictionary->SetString(L"direction", direction); +} + +/////////////////////////////////////////////////////////////////////////////// +// NewTabHTMLSource + +bool NewTabUI::NewTabHTMLSource::first_run_ = true; + +NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile) + : DataSource(chrome::kChromeUINewTabHost, MessageLoop::current()), + profile_(profile) { +} + +void NewTabUI::NewTabHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + if (!path.empty() && path[0] != '#') { + // A path under new-tab was requested; it's likely a bad relative + // URL from the new tab page, but in any case it's an error. + NOTREACHED(); + return; + } + + scoped_refptr<RefCountedBytes> html_bytes = + profile_->GetNTPResourceCache()->GetNewTabHTML(is_off_the_record); + + SendResponse(request_id, html_bytes); +} |