diff options
28 files changed, 1118 insertions, 742 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 1310d46..cfece24 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -3920,18 +3920,6 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_EXTENSION_WEB_STORE_TITLE" desc="Text for the Chrome Web Store"> Web Store </message> - <message name="IDS_APPS_PROMO_HEADER" desc="Header for the apps promo"> - New! Discover a world of apps & games - </message> - <message name="IDS_APPS_PROMO_TEXT_1" desc="First paragraph of text on the apps promo"> - Visit the <a> Chrome Web Store</a> to add great apps and games to this area of the New Tab page. - </message> - <message name="IDS_APPS_PROMO_TEXT_2" desc="Second paragraph of text on the apps promo"> - What's an app? Try one of these: - </message> - <message name="IDS_APPS_PROMO_HIDE" desc="Text on the button that hides the apps promo"> - No thanks, hide this - </message> <!-- Plugins --> <message name="IDS_PLUGINS_TITLE" desc="Title for the chrome://plugins page."> diff --git a/chrome/browser/extensions/apps_promo.cc b/chrome/browser/extensions/apps_promo.cc new file mode 100644 index 0000000..ec0d9dd --- /dev/null +++ b/chrome/browser/extensions/apps_promo.cc @@ -0,0 +1,221 @@ +// 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/extensions/apps_promo.h" + +#include "base/command_line.h" +#include "base/metrics/histogram.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/ui/webui/shown_sections_handler.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_names.h" + +const int AppsPromo::kDefaultAppsCounterMax = 10; + +// static +void AppsPromo::RegisterPrefs(PrefService* local_state) { + std::string empty; + local_state->RegisterStringPref(prefs::kNTPWebStorePromoId, empty); + local_state->RegisterStringPref(prefs::kNTPWebStorePromoHeader, empty); + local_state->RegisterStringPref(prefs::kNTPWebStorePromoButton, empty); + local_state->RegisterStringPref(prefs::kNTPWebStorePromoLink, empty); + local_state->RegisterStringPref(prefs::kNTPWebStorePromoExpire, empty); +} + +// static +void AppsPromo::RegisterUserPrefs(PrefService* prefs) { + // Set the default value for the counter to max+1 since we don't install + // default apps for new users. + prefs->RegisterIntegerPref( + prefs::kAppsPromoCounter, kDefaultAppsCounterMax + 1); + prefs->RegisterBooleanPref(prefs::kDefaultAppsInstalled, false); + prefs->RegisterStringPref(prefs::kNTPWebStorePromoLastId, std::string()); +} + +// static +void AppsPromo::ClearPromo() { + PrefService* local_state = g_browser_process->local_state(); + local_state->ClearPref(prefs::kNTPWebStorePromoId); + local_state->ClearPref(prefs::kNTPWebStorePromoHeader); + local_state->ClearPref(prefs::kNTPWebStorePromoButton); + local_state->ClearPref(prefs::kNTPWebStorePromoLink); + local_state->ClearPref(prefs::kNTPWebStorePromoExpire); +} + +// static +std::string AppsPromo::GetPromoButtonText() { + PrefService* local_state = g_browser_process->local_state(); + return local_state->GetString(prefs::kNTPWebStorePromoButton); +} + +// static +std::string AppsPromo::GetPromoId() { + PrefService* local_state = g_browser_process->local_state(); + return local_state->GetString(prefs::kNTPWebStorePromoId); +} + +// static +std::string AppsPromo::GetPromoHeaderText() { + PrefService* local_state = g_browser_process->local_state(); + return local_state->GetString(prefs::kNTPWebStorePromoHeader); +} + +// static +GURL AppsPromo::GetPromoLink() { + PrefService* local_state = g_browser_process->local_state(); + return GURL(local_state->GetString(prefs::kNTPWebStorePromoLink)); +} + +// static +std::string AppsPromo::GetPromoExpireText() { + PrefService* local_state = g_browser_process->local_state(); + return local_state->GetString(prefs::kNTPWebStorePromoExpire); +} + +// static +void AppsPromo::SetPromo(const std::string& id, + const std::string& header_text, + const std::string& button_text, + const GURL& link, + const std::string& expire_text) { + PrefService* local_state = g_browser_process->local_state(); + local_state->SetString(prefs::kNTPWebStorePromoId, id); + local_state->SetString(prefs::kNTPWebStorePromoButton, button_text); + local_state->SetString(prefs::kNTPWebStorePromoHeader, header_text); + local_state->SetString(prefs::kNTPWebStorePromoLink, link.spec()); + local_state->SetString(prefs::kNTPWebStorePromoExpire, expire_text); +} + +// static +bool AppsPromo::IsPromoSupportedForLocale() { + PrefService* local_state = g_browser_process->local_state(); + // PromoResourceService will clear the promo data if the current locale is + // not supported. + return local_state->HasPrefPath(prefs::kNTPWebStorePromoId) && + local_state->HasPrefPath(prefs::kNTPWebStorePromoHeader) && + local_state->HasPrefPath(prefs::kNTPWebStorePromoButton) && + local_state->HasPrefPath(prefs::kNTPWebStorePromoLink) && + local_state->HasPrefPath(prefs::kNTPWebStorePromoExpire); +} + +AppsPromo::AppsPromo(PrefService* prefs) + : prefs_(prefs) { + // Poppit, Entanglement + old_default_app_ids_.insert("mcbkbpnkkkipelfledbfocopglifcfmi"); + old_default_app_ids_.insert("aciahcmjmecflokailenpkdchphgkefd"); +} + +AppsPromo::~AppsPromo() {} + +bool AppsPromo::ShouldShowPromo(const ExtensionIdSet& installed_ids, + bool* just_expired) { + *just_expired = false; + + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kForceAppsPromoVisible)) { + return true; + } + + // Don't show the promo if one wasn't served to this locale. + if (!IsPromoSupportedForLocale()) + return false; + + int promo_counter = GetPromoCounter(); + if (GetDefaultAppsInstalled() && promo_counter <= kDefaultAppsCounterMax) { + // If the default apps were installed from a previous Chrome version, we + // should still show the promo. If we don't have the exact set of default + // apps, this means that the user manually installed or uninstalled one. + // We no longer keep track of the default apps once others have been + // installed, so expire them immediately. + if (old_default_app_ids_ != installed_ids) { + ExpireDefaultApps(); + return false; + } + + if (promo_counter == kDefaultAppsCounterMax) { + *just_expired = true; + + // The default apps have expired due to inaction, so ping PROMO_EXPIRE. + UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, + extension_misc::PROMO_EXPIRE, + extension_misc::PROMO_BUCKET_BOUNDARY); + + ExpireDefaultApps(); + return true; + } else { + SetPromoCounter(++promo_counter); + return true; + } + } else if (installed_ids.empty()) { + return true; + } + + return false; +} + +bool AppsPromo::ShouldShowAppLauncher(const ExtensionIdSet& installed_ids) { + // On Chrome OS the default apps are installed via a separate mechanism that + // is always enabled. Therefore we always show the launcher. +#if defined(OS_CHROME) + return true; +#else + + // Always show the app launcher if an app is installed. + if (!installed_ids.empty()) + return true; + + // Otherwise, only show the app launcher if there's a promo for this locale. + return IsPromoSupportedForLocale(); +#endif +} + +void AppsPromo::ExpireDefaultApps() { + SetPromoCounter(kDefaultAppsCounterMax + 1); +} + +void AppsPromo::MaximizeAppsIfFirstView() { + std::string promo_id = GetPromoId(); + + // Maximize the apps section of the NTP if this is the first time viewing the + // specific promo. + if (GetLastPromoId() != promo_id) { + prefs_->SetString(prefs::kNTPWebStorePromoLastId, promo_id); + ShownSectionsHandler::SetShownSection(prefs_, APPS); + } +} + +void AppsPromo::HidePromo() { + UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, + extension_misc::PROMO_CLOSE, + extension_misc::PROMO_BUCKET_BOUNDARY); + + // Put the apps section into menu mode, and maximize the recent section. + ShownSectionsHandler::SetShownSection(prefs_, MENU_APPS); + ShownSectionsHandler::SetShownSection(prefs_, THUMB); + + ExpireDefaultApps(); +} + +std::string AppsPromo::GetLastPromoId() { + return prefs_->GetString(prefs::kNTPWebStorePromoLastId); +} + +void AppsPromo::SetLastPromoId(const std::string& id) { + prefs_->SetString(prefs::kNTPWebStorePromoLastId, id); +} + +int AppsPromo::GetPromoCounter() const { + return prefs_->GetInteger(prefs::kAppsPromoCounter); +} + +void AppsPromo::SetPromoCounter(int val) { + prefs_->SetInteger(prefs::kAppsPromoCounter, val); + prefs_->ScheduleSavePersistentPrefs(); +} + +bool AppsPromo::GetDefaultAppsInstalled() const { + return prefs_->GetBoolean(prefs::kDefaultAppsInstalled); +} diff --git a/chrome/browser/extensions/apps_promo.h b/chrome/browser/extensions/apps_promo.h new file mode 100644 index 0000000..772df00 --- /dev/null +++ b/chrome/browser/extensions/apps_promo.h @@ -0,0 +1,113 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_APPS_PROMO_H_ +#define CHROME_BROWSER_EXTENSIONS_APPS_PROMO_H_ +#pragma once + +#include <set> +#include <string> + +#include "base/gtest_prod_util.h" +#include "chrome/common/extensions/extension.h" + +class PrefService; + +// This encapsulates business logic for: +// - Whether to show the apps promo in the launcher +// - Whether to expire existing default apps +class AppsPromo { + public: + // Register our preferences. Parts of the promo content are stored in Local + // State since they're independent of the user profile. + static void RegisterPrefs(PrefService* local_state); + static void RegisterUserPrefs(PrefService* prefs); + + // Removes the current promo data. + static void ClearPromo(); + + // Gets the ID of the current promo. + static std::string GetPromoId(); + + // Gets the text for the promo button. + static std::string GetPromoButtonText(); + + // Gets the text for the promo header. + static std::string GetPromoHeaderText(); + + // Gets the promo link. + static GURL GetPromoLink(); + + // Gets the text for the promo "hide this" link. + static std::string GetPromoExpireText(); + + // Called to set the current promo data. + static void SetPromo(const std::string& id, + const std::string& header_text, + const std::string& button_text, + const GURL& link, + const std::string& expire_text); + + explicit AppsPromo(PrefService* prefs); + ~AppsPromo(); + + // Gets the set of old default apps that may have been installed by previous + // versions of Chrome. + const ExtensionIdSet& old_default_apps() const { + return old_default_app_ids_; + } + + // Halts the special treatment of the default apps. The default apps may be + // removed by the caller after calling this method. If the apps remain + // installed, AppsPromo will no longer consider the apps "default". + void ExpireDefaultApps(); + + // Called to hide the promo from the apps section. + void HidePromo(); + + // Maximizes the apps section the first time this is called for a given promo. + void MaximizeAppsIfFirstView(); + + // Returns true if the app launcher should be displayed on the NTP. + bool ShouldShowAppLauncher(const ExtensionIdSet& installed_ids); + + // Returns true if the apps promo should be displayed in the launcher. + bool ShouldShowPromo(const ExtensionIdSet& installed_ids, + bool* just_expired); + + private: + FRIEND_TEST_ALL_PREFIXES(ExtensionAppsPromo, HappyPath); + FRIEND_TEST_ALL_PREFIXES(ExtensionAppsPromo, PromoPrefs); + FRIEND_TEST_ALL_PREFIXES(ExtensionAppsPromo, UpdatePromoFocus); + + // The maximum number of times to show the apps promo. The promo counter + // actually goes up to this number + 1 because we need to differentiate + // between the first time we overflow and subsequent times. + static const int kDefaultAppsCounterMax; + + // Returns true if a promo is available for the current locale. + static bool IsPromoSupportedForLocale(); + + bool GetDefaultAppsInstalled() const; + + // Gets/sets the ID of the last promo shown. + std::string GetLastPromoId(); + void SetLastPromoId(const std::string& id); + + // Gets/sets the number of times the promo has been viewed. Promo views are + // only counted when the default apps are installed. + int GetPromoCounter() const; + void SetPromoCounter(int val); + + // Our permanent state is stored in this PrefService instance. + PrefService* prefs_; + + // The set of default extensions. Initialized to a static list in the + // constructor. + ExtensionIdSet old_default_app_ids_; + + DISALLOW_COPY_AND_ASSIGN(AppsPromo); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_APPS_PROMO_H_ diff --git a/chrome/browser/extensions/apps_promo_unittest.cc b/chrome/browser/extensions/apps_promo_unittest.cc new file mode 100644 index 0000000..3816b78 --- /dev/null +++ b/chrome/browser/extensions/apps_promo_unittest.cc @@ -0,0 +1,183 @@ +// 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 "base/logging.h" +#include "chrome/browser/extensions/apps_promo.h" +#include "chrome/browser/prefs/browser_prefs.h" +#include "chrome/browser/ui/webui/shown_sections_handler.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_browser_process.h" +#include "chrome/test/testing_pref_service.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kPromoId[] = "23123123"; +const char kPromoHeader[] = "Get great apps!"; +const char kPromoButton[] = "Click for apps!"; +const char kPromoLink[] = "http://apps.com"; +const char kPromoExpire[] = "No thanks."; + +} // namespace + +class ExtensionAppsPromo : public testing::Test { + public: + TestingPrefService* local_state() { return &local_state_; } + TestingPrefService* prefs() { return &prefs_; } + AppsPromo* apps_promo() { return apps_promo_; } + + protected: + explicit ExtensionAppsPromo(); + virtual ~ExtensionAppsPromo(); + + // testing::Test + virtual void SetUp(); + virtual void TearDown(); + + private: + TestingPrefService local_state_; + TestingPrefService prefs_; + AppsPromo* apps_promo_; +}; + +ExtensionAppsPromo::ExtensionAppsPromo() : + apps_promo_(new AppsPromo(&prefs_)) { +} + +ExtensionAppsPromo::~ExtensionAppsPromo() { + delete apps_promo_; +} + +void ExtensionAppsPromo::SetUp() { + browser::RegisterLocalState(&local_state_); + browser::RegisterUserPrefs(&prefs_); + + TestingBrowserProcess* testing_browser_process = + static_cast<TestingBrowserProcess*>(g_browser_process); + testing_browser_process->SetPrefService(&local_state_); +} + +void ExtensionAppsPromo::TearDown() { + TestingBrowserProcess* testing_browser_process = + static_cast<TestingBrowserProcess*>(g_browser_process); + testing_browser_process->SetPrefService(NULL); +} + +// TODO(dpolukhin): On Chrome OS all apps are installed via external extensions, +// and the web store promo is never shown. +#if !defined(OS_CHROMEOS) +TEST_F(ExtensionAppsPromo, HappyPath) { + const ExtensionIdSet& default_app_ids = apps_promo()->old_default_apps(); + + EXPECT_GT(default_app_ids.size(), 0u); + + // The promo counter should default to the max, since we only use the counter + // if they were installed from older versions of Chrome. + EXPECT_EQ(AppsPromo::kDefaultAppsCounterMax + 1, + apps_promo()->GetPromoCounter()); + + // The app launcher and promo should not be shown if there are no extensions + // installed and no promo is set. + ExtensionIdSet installed_ids; + bool promo_just_expired = false; + EXPECT_FALSE(AppsPromo::IsPromoSupportedForLocale()); + EXPECT_FALSE(apps_promo()->ShouldShowAppLauncher(installed_ids)); + EXPECT_FALSE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + EXPECT_FALSE(promo_just_expired); + + // Once the promo is set, we show both the promo and app launcher. + AppsPromo::SetPromo( + kPromoId, kPromoHeader, kPromoButton, GURL(kPromoLink), kPromoExpire); + + EXPECT_TRUE(AppsPromo::IsPromoSupportedForLocale()); + EXPECT_TRUE(apps_promo()->ShouldShowAppLauncher(installed_ids)); + EXPECT_TRUE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + EXPECT_FALSE(promo_just_expired); + + + // Now install an app and the promo should not be shown. + installed_ids.insert(*(default_app_ids.begin())); + EXPECT_TRUE(AppsPromo::IsPromoSupportedForLocale()); + EXPECT_TRUE(apps_promo()->ShouldShowAppLauncher(installed_ids)); + EXPECT_FALSE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + EXPECT_FALSE(promo_just_expired); + + // Even if the user installs the exact set of default apps, we still don't + // show the promo. + installed_ids = default_app_ids; + EXPECT_TRUE(AppsPromo::IsPromoSupportedForLocale()); + EXPECT_TRUE(apps_promo()->ShouldShowAppLauncher(installed_ids)); + EXPECT_FALSE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + EXPECT_FALSE(promo_just_expired); + + // If the user then uninstalls the apps, the app launcher should remain + // and the promo should return. + installed_ids.clear(); + EXPECT_TRUE(AppsPromo::IsPromoSupportedForLocale()); + EXPECT_TRUE(apps_promo()->ShouldShowAppLauncher(installed_ids)); + EXPECT_TRUE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + EXPECT_FALSE(promo_just_expired); +} + +// Tests get and set of promo content. +TEST_F(ExtensionAppsPromo, PromoPrefs) { + // Store a promo.... + AppsPromo::SetPromo( + kPromoId, kPromoHeader, kPromoButton, GURL(kPromoLink), kPromoExpire); + + // ... then make sure AppsPromo can access it. + EXPECT_EQ(kPromoId, AppsPromo::GetPromoId()); + EXPECT_EQ(kPromoHeader, AppsPromo::GetPromoHeaderText()); + EXPECT_EQ(kPromoButton, AppsPromo::GetPromoButtonText()); + EXPECT_EQ(GURL(kPromoLink), AppsPromo::GetPromoLink()); + EXPECT_EQ(kPromoExpire, AppsPromo::GetPromoExpireText()); + EXPECT_TRUE(AppsPromo::IsPromoSupportedForLocale()); + + AppsPromo::ClearPromo(); + EXPECT_EQ("", AppsPromo::GetPromoId()); + EXPECT_EQ("", AppsPromo::GetPromoHeaderText()); + EXPECT_EQ("", AppsPromo::GetPromoButtonText()); + EXPECT_EQ(GURL(""), AppsPromo::GetPromoLink()); + EXPECT_EQ("", AppsPromo::GetPromoExpireText()); + EXPECT_FALSE(AppsPromo::IsPromoSupportedForLocale()); +} + +// Tests that the apps section is maxmized when showing a promo for the first +// time. +TEST_F(ExtensionAppsPromo, UpdatePromoFocus) { + ExtensionIdSet installed_ids; + + bool promo_just_expired = false; + EXPECT_FALSE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + EXPECT_FALSE(promo_just_expired); + + // Set the promo content. + AppsPromo::SetPromo( + kPromoId, kPromoHeader, kPromoButton, GURL(kPromoLink), kPromoExpire); + + // After asking if we should show the promo, the + EXPECT_TRUE(AppsPromo::IsPromoSupportedForLocale()); + EXPECT_TRUE(apps_promo()->ShouldShowPromo(installed_ids, + &promo_just_expired)); + apps_promo()->MaximizeAppsIfFirstView(); + + EXPECT_TRUE( + (ShownSectionsHandler::GetShownSections(prefs()) & APPS) != 0); + EXPECT_FALSE( + (ShownSectionsHandler::GetShownSections(prefs()) & THUMB) != 0); + + apps_promo()->HidePromo(); + + EXPECT_TRUE((ShownSectionsHandler::GetShownSections(prefs()) & + (MENU_APPS | THUMB)) != 0); +} +#endif // OS_CHROMEOS diff --git a/chrome/browser/extensions/default_apps.cc b/chrome/browser/extensions/default_apps.cc deleted file mode 100644 index bf8aa9a..0000000 --- a/chrome/browser/extensions/default_apps.cc +++ /dev/null @@ -1,181 +0,0 @@ -// 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 <algorithm> - -#include "chrome/browser/extensions/default_apps.h" - -#include "base/command_line.h" -#include "base/metrics/histogram.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" - -const int DefaultApps::kAppsPromoCounterMax = 10; - -// static -void DefaultApps::RegisterUserPrefs(PrefService* prefs) { - prefs->RegisterBooleanPref(prefs::kDefaultAppsInstalled, false); - prefs->RegisterIntegerPref(prefs::kAppsPromoCounter, 0); -} - -DefaultApps::DefaultApps(PrefService* prefs, - const std::string& application_locale) - : prefs_(prefs), application_locale_(application_locale) { - // Poppit, Entanglement - ids_.insert("mcbkbpnkkkipelfledbfocopglifcfmi"); - ids_.insert("aciahcmjmecflokailenpkdchphgkefd"); -} - -DefaultApps::~DefaultApps() {} - -const ExtensionIdSet& DefaultApps::default_apps() const { - return ids_; -} - -bool DefaultApps::DefaultAppSupported() { - // On Chrome OS the default apps are installed via a different mechanism. -#if defined(OS_CHROMEOS) - return false; -#else - return DefaultAppsSupportedForLanguage(); -#endif -} - -bool DefaultApps::DefaultAppsSupportedForLanguage() { - return application_locale_ == "en-US"; -} - -bool DefaultApps::ShouldInstallDefaultApps( - const ExtensionIdSet& installed_ids) { - if (!DefaultAppSupported()) - return false; - - if (GetDefaultAppsInstalled()) - return false; - - // If there are any non-default apps installed, we should never try to install - // the default apps again, even if the non-default apps are later removed. - if (NonDefaultAppIsInstalled(installed_ids)) { - SetDefaultAppsInstalled(true); - return false; - } - - return true; -} - -bool DefaultApps::ShouldShowAppLauncher(const ExtensionIdSet& installed_ids) { - // On Chrome OS the default apps are installed via a separate mechanism that - // is always enabled. Therefore we always show the launcher. -#if defined(OS_CHROME) - return true; -#else - // The app store only supports en-us at the moment, so we don't show the apps - // section by default for users in other locales. But we do show it if a user - // from a non-supported locale somehow installs an app (eg by navigating - // directly to the store). - if (!DefaultAppsSupportedForLanguage()) - return !installed_ids.empty(); - - // For supported locales, we need to wait for all the default apps to be - // installed before showing the apps section. We also show it if any - // non-default app is installed (eg because the user installed the app in a - // previous version of Chrome). - if (GetDefaultAppsInstalled() || NonDefaultAppIsInstalled(installed_ids)) - return true; - else - return false; -#endif -} - -bool DefaultApps::ShouldShowPromo(const ExtensionIdSet& installed_ids, - bool* just_expired) { - *just_expired = false; - - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceAppsPromoVisible)) { - return true; - } - - if (!DefaultAppSupported()) - return false; - - if (!GetDefaultAppsInstalled()) - return false; - - int promo_counter = GetPromoCounter(); - if (promo_counter <= kAppsPromoCounterMax) { - // If we have the exact set of default apps, show the promo. If we don't - // have the exact set of default apps, this means that the user manually - // installed or uninstalled one. The promo doesn't make sense if it shows - // apps the user manually installed, so expire it immediately in that - // situation. - if (ids_ != installed_ids) { - SetPromoHidden(); - return false; - } - - if (promo_counter == kAppsPromoCounterMax) { - *just_expired = true; - UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, - extension_misc::PROMO_EXPIRE, - extension_misc::PROMO_BUCKET_BOUNDARY); - SetPromoCounter(++promo_counter); - return false; - } else { - UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, - extension_misc::PROMO_SEEN, - extension_misc::PROMO_BUCKET_BOUNDARY); - SetPromoCounter(++promo_counter); - return true; - } - } - - return false; -} - -void DefaultApps::DidInstallApp(const ExtensionIdSet& installed_ids) { - // If all the default apps have been installed, stop trying to install them. - // Note that we use std::includes here instead of == because apps might have - // been manually installed while the the default apps were installing and we - // wouldn't want to keep trying to install them in that case. - if (!GetDefaultAppsInstalled() && - std::includes(installed_ids.begin(), installed_ids.end(), - ids_.begin(), ids_.end())) { - SetDefaultAppsInstalled(true); - } -} - -bool DefaultApps::NonDefaultAppIsInstalled( - const ExtensionIdSet& installed_ids) const { - for (ExtensionIdSet::const_iterator iter = installed_ids.begin(); - iter != installed_ids.end(); ++iter) { - if (ids_.find(*iter) == ids_.end()) - return true; - } - - return false; -} - -void DefaultApps::SetPromoHidden() { - SetPromoCounter(kAppsPromoCounterMax + 1); -} - -int DefaultApps::GetPromoCounter() const { - return prefs_->GetInteger(prefs::kAppsPromoCounter); -} - -void DefaultApps::SetPromoCounter(int val) { - prefs_->SetInteger(prefs::kAppsPromoCounter, val); - prefs_->ScheduleSavePersistentPrefs(); -} - -bool DefaultApps::GetDefaultAppsInstalled() const { - return prefs_->GetBoolean(prefs::kDefaultAppsInstalled); -} - -void DefaultApps::SetDefaultAppsInstalled(bool val) { - prefs_->SetBoolean(prefs::kDefaultAppsInstalled, val); - prefs_->ScheduleSavePersistentPrefs(); -} diff --git a/chrome/browser/extensions/default_apps.h b/chrome/browser/extensions/default_apps.h deleted file mode 100644 index 2ff8801..0000000 --- a/chrome/browser/extensions/default_apps.h +++ /dev/null @@ -1,88 +0,0 @@ -// 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. - -#ifndef CHROME_BROWSER_EXTENSIONS_DEFAULT_APPS_H_ -#define CHROME_BROWSER_EXTENSIONS_DEFAULT_APPS_H_ -#pragma once - -#include <set> -#include <string> -#include "chrome/common/extensions/extension.h" -#include "base/gtest_prod_util.h" - -class PrefService; - -// Encapsulates business logic for: -// - Whether to install default apps on Chrome startup -// - Whether to show the app launcher -// - Whether to show the apps promo in the launcher -class DefaultApps { - public: - // The maximum number of times to show the apps promo. The promo counter - // actually goes up to this number + 1 because we need to differentiate - // between the first time we overflow and subsequent times. - static const int kAppsPromoCounterMax; - - // Register our preferences. - static void RegisterUserPrefs(PrefService* prefs); - - explicit DefaultApps(PrefService* prefs, - const std::string& application_locale); - ~DefaultApps(); - - // Gets the set of default apps. - const ExtensionIdSet& default_apps() const; - - // Returns true if the default apps should be installed. - bool ShouldInstallDefaultApps(const ExtensionIdSet& installed_ids); - - // Returns true if the app launcher in the NTP should be shown. - bool ShouldShowAppLauncher(const ExtensionIdSet& installed_ids); - - // Returns true if the apps promo should be displayed in the launcher. - // - // NOTE: If the default apps have been installed, but |installed_ids| is - // different than GetDefaultApps(), this will permanently expire the promo. - bool ShouldShowPromo(const ExtensionIdSet& installed_ids, bool* just_expired); - - // Should be called after each app is installed. Once installed_ids contains - // all the default apps, GetAppsToInstall() will start returning NULL. - void DidInstallApp(const ExtensionIdSet& installed_ids); - - // Force the promo to be hidden. - void SetPromoHidden(); - - private: - FRIEND_TEST_ALL_PREFIXES(ExtensionDefaultApps, HappyPath); - FRIEND_TEST_ALL_PREFIXES(ExtensionDefaultApps, UnsupportedLocale); - FRIEND_TEST_ALL_PREFIXES(ExtensionDefaultApps, HidePromo); - FRIEND_TEST_ALL_PREFIXES(ExtensionDefaultApps, InstallingAnAppHidesPromo); - FRIEND_TEST_ALL_PREFIXES(ExtensionDefaultApps, - ManualAppInstalledWhileInstallingDefaultApps); - - bool DefaultAppSupported(); - bool DefaultAppsSupportedForLanguage(); - - bool NonDefaultAppIsInstalled(const ExtensionIdSet& installed_ids) const; - - bool GetDefaultAppsInstalled() const; - void SetDefaultAppsInstalled(bool val); - - int GetPromoCounter() const; - void SetPromoCounter(int val); - - // Our permanent state is stored in this PrefService instance. - PrefService* prefs_; - - // The locale the browser is currently in. - std::string application_locale_; - - // The set of default extensions. Initialized to a static list in the - // constructor. - ExtensionIdSet ids_; - - DISALLOW_COPY_AND_ASSIGN(DefaultApps); -}; - -#endif // CHROME_BROWSER_EXTENSIONS_DEFAULT_APPS_H_ diff --git a/chrome/browser/extensions/default_apps_unittest.cc b/chrome/browser/extensions/default_apps_unittest.cc deleted file mode 100644 index 316568d..0000000 --- a/chrome/browser/extensions/default_apps_unittest.cc +++ /dev/null @@ -1,236 +0,0 @@ -// 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 "base/logging.h" -#include "chrome/browser/extensions/default_apps.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/test/testing_pref_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -// TODO(dpolukhin): On Chrome OS all apps are installed via external extensions, -// and the web store promo is never shown. -#if !defined(OS_CHROMEOS) -TEST(ExtensionDefaultApps, HappyPath) { - TestingPrefService pref_service; - DefaultApps::RegisterUserPrefs(&pref_service); - DefaultApps default_apps(&pref_service, "en-US"); - - const ExtensionIdSet& default_app_ids = default_apps.default_apps(); - ASSERT_GT(default_app_ids.size(), 0u); - EXPECT_FALSE(default_apps.GetDefaultAppsInstalled()); - EXPECT_EQ(0, default_apps.GetPromoCounter()); - - // If no apps are installed, the default apps should be installed. - ExtensionIdSet installed_app_ids; - EXPECT_TRUE(default_apps.ShouldInstallDefaultApps(installed_app_ids)); - - // The launcher should not be shown until the default apps have been - // installed. - EXPECT_FALSE(default_apps.ShouldShowAppLauncher(installed_app_ids)); - - // The promo should not be shown until the default apps have been installed. - bool promo_just_expired = false; - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - - // Simulate installing the apps one by one and notifying default_apps after - // each intallation. Nothing should change until we have installed all the - // default apps. - for (size_t i = 0; i < default_app_ids.size() - 1; ++i) { - ExtensionIdSet::const_iterator iter = default_app_ids.begin(); - for (size_t j = 0; j <= i; ++j) - ++iter; - installed_app_ids.insert(*iter); - default_apps.DidInstallApp(installed_app_ids); - EXPECT_FALSE(default_apps.GetDefaultAppsInstalled()); - EXPECT_TRUE(default_apps.ShouldInstallDefaultApps(installed_app_ids)); - EXPECT_FALSE(default_apps.ShouldShowAppLauncher(installed_app_ids)); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - } - - // Simulate all the default apps being installed. Now we should stop getting - // default apps to install. - installed_app_ids = default_app_ids; - default_apps.DidInstallApp(installed_app_ids); - EXPECT_TRUE(default_apps.GetDefaultAppsInstalled()); - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_app_ids)); - - // And the promo and launcher should become available. - EXPECT_TRUE(default_apps.ShouldShowAppLauncher(installed_app_ids)); - EXPECT_TRUE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - - // The promo should be available up to the max allowed times, then stop. - // We start counting at 1 because of the call to ShouldShowPromo() above. - for (int i = 1; i < DefaultApps::kAppsPromoCounterMax; ++i) { - EXPECT_TRUE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(i + 1, default_apps.GetPromoCounter()); - } - - // The first time, should_show_promo should flip to true, then back to false. - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_TRUE(promo_just_expired); - EXPECT_EQ(DefaultApps::kAppsPromoCounterMax + 1, - default_apps.GetPromoCounter()); - - // Even if all the apps are subsequently removed, the apps section should - // remain. - installed_app_ids.clear(); - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_app_ids)); - EXPECT_TRUE(default_apps.ShouldShowAppLauncher(installed_app_ids)); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(DefaultApps::kAppsPromoCounterMax + 1, - default_apps.GetPromoCounter()); -} - -TEST(ExtensionDefaultApps, UnsupportedLocale) { - TestingPrefService pref_service; - DefaultApps::RegisterUserPrefs(&pref_service); - DefaultApps default_apps(&pref_service, "fr"); - - const ExtensionIdSet& default_app_ids = default_apps.default_apps(); - EXPECT_GT(default_app_ids.size(), 0u); - - // Since the store only supports en-US at the moment, we don't install default - // apps or promote the store. - ExtensionIdSet installed_ids; - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_ids)); - EXPECT_FALSE(default_apps.ShouldShowAppLauncher(installed_ids)); - - bool promo_just_expired = false; - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - - // If the user installs an app manually, then we show the apps section, but - // no promotion or default apps. - installed_ids.insert(*(default_app_ids.begin())); - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_ids)); - EXPECT_TRUE(default_apps.ShouldShowAppLauncher(installed_ids)); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - - // Even if the user installs the exact set of default apps, we don't show the - // promo. - installed_ids = default_app_ids; - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_ids)); - EXPECT_TRUE(default_apps.ShouldShowAppLauncher(installed_ids)); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - - // If the user uninstalls the apps again, we go back to not showing the - // apps section. - installed_ids.clear(); - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_ids)); - EXPECT_FALSE(default_apps.ShouldShowAppLauncher(installed_ids)); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); -} - -TEST(ExtensionDefaultApps, HidePromo) { - TestingPrefService pref_service; - DefaultApps::RegisterUserPrefs(&pref_service); - DefaultApps default_apps(&pref_service, "en-US"); - - const ExtensionIdSet& default_app_ids = default_apps.default_apps(); - default_apps.DidInstallApp(default_app_ids); - - bool promo_just_expired = false; - EXPECT_TRUE(default_apps.ShouldShowPromo(default_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(1, default_apps.GetPromoCounter()); - - default_apps.SetPromoHidden(); - EXPECT_FALSE(default_apps.ShouldShowPromo(default_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(DefaultApps::kAppsPromoCounterMax + 1, - default_apps.GetPromoCounter()); -} - -TEST(ExtensionDefaultApps, InstallingAnAppHidesPromo) { - TestingPrefService pref_service; - DefaultApps::RegisterUserPrefs(&pref_service); - DefaultApps default_apps(&pref_service, "en-US"); - - const ExtensionIdSet& default_app_ids = default_apps.default_apps(); - ExtensionIdSet installed_app_ids = default_app_ids; - default_apps.DidInstallApp(installed_app_ids); - - bool promo_just_expired = false; - EXPECT_TRUE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(1, default_apps.GetPromoCounter()); - - // Now simulate a new extension being installed. This should cause the promo - // to be hidden. - installed_app_ids.insert("foo"); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_app_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(DefaultApps::kAppsPromoCounterMax + 1, - default_apps.GetPromoCounter()); -} - -TEST(ExtensionDefaultApps, ManualAppInstalledWhileInstallingDefaultApps) { - // It is possible to have apps manually installed while the default apps are - // being installed. The network or server might be down, causing the default - // app installation to fail. The updater might take awhile to get around to - // updating, giving the user a chance to manually intall. - // - // In these cases, we should keep trying to install default apps until we have - // them all, and then stop, even if at that point, we have more apps than just - // the default ones. - TestingPrefService pref_service; - DefaultApps::RegisterUserPrefs(&pref_service); - DefaultApps default_apps(&pref_service, "en-US"); - - // Simulate an app getting installed before the complete set of default apps. - // This should stop the default apps from trying to be installed. The launcher - // should also immediately show up. - ExtensionIdSet installed_ids; - installed_ids.insert("foo"); - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_ids)); - EXPECT_TRUE(default_apps.GetDefaultAppsInstalled()); - EXPECT_TRUE(default_apps.ShouldShowAppLauncher(installed_ids)); - - // The promo shouldn't turn on though, because it would look weird with the - // user's extra, manually installed extensions. - bool promo_just_expired = false; - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - EXPECT_EQ(DefaultApps::kAppsPromoCounterMax + 1, - default_apps.GetPromoCounter()); - - // Going back to a subset of the default apps shouldn't allow the default app - // install to continue. - installed_ids.clear(); - EXPECT_FALSE(default_apps.ShouldInstallDefaultApps(installed_ids)); - EXPECT_TRUE(default_apps.GetDefaultAppsInstalled()); - EXPECT_TRUE(default_apps.ShouldShowAppLauncher(installed_ids)); - EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids, - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); - - // Going to the exact set of default apps shouldn't show the promo. - EXPECT_FALSE(default_apps.ShouldShowPromo(default_apps.default_apps(), - &promo_just_expired)); - EXPECT_FALSE(promo_just_expired); -} -#endif // OS_CHROMEOS diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 78c3ff0..8bf7764 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -24,7 +24,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/extensions/crx_installer.h" -#include "chrome/browser/extensions/default_apps.h" +#include "chrome/browser/extensions/apps_promo.h" #include "chrome/browser/extensions/extension_accessibility_api.h" #include "chrome/browser/extensions/extension_bookmarks_module.h" #include "chrome/browser/extensions/extension_browser_event_router.h" @@ -410,8 +410,7 @@ ExtensionService::ExtensionService(Profile* profile, show_extensions_prompts_(true), ready_(false), ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)), - default_apps_(profile->GetPrefs(), - g_browser_process->GetApplicationLocale()), + apps_promo_(profile->GetPrefs()), event_routers_initialized_(false) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -1671,12 +1670,6 @@ void ExtensionService::OnExtensionInstalled(const Extension* extension) { Details<const Extension>(extension)); } - if (extension->is_app()) { - ExtensionIdSet installed_ids = GetAppIds(); - installed_ids.insert(id); - default_apps_.DidInstallApp(installed_ids); - } - // Transfer ownership of |extension| to AddExtension. AddExtension(scoped_extension); } diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index ee486f7..6c6ad49 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -19,7 +19,7 @@ #include "base/task.h" #include "base/time.h" #include "base/tuple.h" -#include "chrome/browser/extensions/default_apps.h" +#include "chrome/browser/extensions/apps_promo.h" #include "chrome/browser/extensions/extension_icon_manager.h" #include "chrome/browser/extensions/extension_menu_manager.h" #include "chrome/browser/extensions/extension_prefs.h" @@ -163,7 +163,7 @@ class ExtensionService const FilePath& install_directory() const { return install_directory_; } - DefaultApps* default_apps() { return &default_apps_; } + AppsPromo* apps_promo() { return &apps_promo_; } // Whether this extension can run in an incognito window. virtual bool IsIncognitoEnabled(const std::string& extension_id) const; @@ -555,9 +555,8 @@ class ExtensionService typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions; RegisteredComponentExtensions component_extension_manifests_; - // Manages the installation of default apps and the promotion of them in the - // app launcher. - DefaultApps default_apps_; + // Manages the promotion of the web store. + AppsPromo apps_promo_; // Flag to make sure event routers are only initialized once. bool event_routers_initialized_; diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 68f7dab..2a88964 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -15,6 +15,7 @@ #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/download/download_prefs.h" +#include "chrome/browser/extensions/apps_promo.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_web_ui.h" #include "chrome/browser/extensions/extensions_ui.h" @@ -53,6 +54,7 @@ #include "chrome/browser/ui/webui/new_tab_ui.h" #include "chrome/browser/ui/webui/plugins_ui.h" #include "chrome/browser/upgrade_detector.h" +#include "chrome/browser/web_resource/promo_resource_service.h" #include "chrome/common/pref_names.h" #include "content/browser/host_zoom_map.h" @@ -80,6 +82,7 @@ namespace browser { void RegisterLocalState(PrefService* local_state) { // Prefs in Local State + AppsPromo::RegisterPrefs(local_state); Browser::RegisterPrefs(local_state); FlagsUI::RegisterPrefs(local_state); WebCacheManager::RegisterPrefs(local_state); @@ -89,6 +92,7 @@ void RegisterLocalState(PrefService* local_state) { KeywordEditorController::RegisterPrefs(local_state); MetricsLog::RegisterPrefs(local_state); MetricsService::RegisterPrefs(local_state); + PromoResourceService::RegisterPrefs(local_state); SafeBrowsingService::RegisterPrefs(local_state); browser_shutdown::RegisterPrefs(local_state); #if defined(TOOLKIT_VIEWS) @@ -115,6 +119,7 @@ void RegisterLocalState(PrefService* local_state) { void RegisterUserPrefs(PrefService* user_prefs) { // User prefs + AppsPromo::RegisterUserPrefs(user_prefs); AutofillManager::RegisterUserPrefs(user_prefs); SessionStartupPref::RegisterUserPrefs(user_prefs); Browser::RegisterUserPrefs(user_prefs); @@ -129,6 +134,7 @@ void RegisterUserPrefs(PrefService* user_prefs) { NewTabUI::RegisterUserPrefs(user_prefs); PluginsUI::RegisterUserPrefs(user_prefs); ProfileImpl::RegisterUserPrefs(user_prefs); + PromoResourceService::RegisterUserPrefs(user_prefs); HostContentSettingsMap::RegisterUserPrefs(user_prefs); HostZoomMap::RegisterUserPrefs(user_prefs); DevToolsManager::RegisterUserPrefs(user_prefs); diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 5666a77..1f7be8e 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -25,7 +25,6 @@ #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/defaults.h" #include "chrome/browser/download/download_manager.h" -#include "chrome/browser/extensions/default_apps.h" #include "chrome/browser/extensions/extension_devtools_manager.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_event_router.h" @@ -238,7 +237,6 @@ Profile* Profile::CreateProfile(const FilePath& path) { void ProfileImpl::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kSavingBrowserHistoryDisabled, false); prefs->RegisterBooleanPref(prefs::kClearSiteDataOnExit, false); - DefaultApps::RegisterUserPrefs(prefs); } ProfileImpl::ProfileImpl(const FilePath& path) @@ -385,7 +383,6 @@ void ProfileImpl::InitExtensions() { RegisterComponentExtensions(); extensions_service_->Init(); - InstallDefaultApps(); // Load any extensions specified with --load-extension. if (command_line->HasSwitch(switches::kLoadExtension)) { @@ -483,22 +480,6 @@ void ProfileImpl::RegisterComponentExtensions() { #endif } -void ProfileImpl::InstallDefaultApps() { - ExtensionService* extension_service = GetExtensionService(); - DefaultApps* default_apps = extension_service->default_apps(); - - if (!default_apps->ShouldInstallDefaultApps(extension_service->GetAppIds())) - return; - - const ExtensionIdSet& app_ids = default_apps->default_apps(); - PendingExtensionManager* pending_extension_manager = - extension_service->pending_extension_manager(); - for (ExtensionIdSet::const_iterator iter = app_ids.begin(); - iter != app_ids.end(); ++iter) { - pending_extension_manager->AddFromDefaultAppList(*iter); - } -} - void ProfileImpl::InitPromoResources() { if (promo_resource_service_) return; diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index f62d6d9..dcb240f 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -171,7 +171,6 @@ class ProfileImpl : public Profile, } void RegisterComponentExtensions(); - void InstallDefaultApps(); ExtensionPrefValueMap* GetExtensionPrefValueMap(); diff --git a/chrome/browser/resources/new_new_tab.html b/chrome/browser/resources/new_new_tab.html index 59c1bca..ea3dd24 100644 --- a/chrome/browser/resources/new_new_tab.html +++ b/chrome/browser/resources/new_new_tab.html @@ -146,10 +146,13 @@ if ('mode' in hashParams) { <div class="maxiview" id="apps-maxiview"> <div id="apps-promo"> - <button id="apps-promo-hide" i18n-content="appspromohide"></button> - <h3 i18n-content="appspromoheader"></h3> - <p id="apps-promo-text1" i18n-values=".innerHTML:appspromotext1"></p> - <p id="apps-promo-text2" i18n-content="appspromotext2"></p> + <p id="apps-promo-heading"></p> + <div class="g-button-basic"> + <div><span><span> + <a id="apps-promo-link" href=""></a> + </span></span></div> + </div> + <button id="apps-promo-hide"></button> </div> <div id="apps-content"></div> </div> diff --git a/chrome/browser/resources/new_new_tab.js b/chrome/browser/resources/new_new_tab.js index 14dc2dc..dd4c5a0 100644 --- a/chrome/browser/resources/new_new_tab.js +++ b/chrome/browser/resources/new_new_tab.js @@ -87,7 +87,7 @@ function addClosedMenuEntry(menu, url, title, imageUrl, opt_pingUrl) { a.textContent = title; a.style.backgroundImage = 'url(' + imageUrl + ')'; if (opt_pingUrl) - a.setAttribute('ping', opt_pingUrl); + a.ping = opt_pingUrl; addClosedMenuEntryWithLink(menu, a); } @@ -287,8 +287,8 @@ function createRecentItem(data) { el = document.createElement('a'); el.className = 'item'; el.href = data.url; - el.setAttribute('ping', - getAppPingUrl('PING_BY_URL', data.url, 'NTP_RECENTLY_CLOSED')); + el.ping = getAppPingUrl( + 'PING_BY_URL', data.url, 'NTP_RECENTLY_CLOSED'); el.style.backgroundImage = url('chrome://favicon/' + data.url); el.dir = data.direction; el.textContent = data.title; @@ -310,8 +310,8 @@ function addRecentMenuItem(menu, data) { a.href = ''; // To make underline show up. } else { a.href = data.url; - a.setAttribute('ping', - getAppPingUrl('PING_BY_URL', data.url, 'NTP_RECENTLY_CLOSED')); + a.ping = getAppPingUrl( + 'PING_BY_URL', data.url, 'NTP_RECENTLY_CLOSED'); a.style.backgroundImage = 'url(chrome://favicon/' + data.url + ')'; a.textContent = data.title; } @@ -1076,13 +1076,6 @@ function hideNotification() { } } -function showFirstRunNotification() { - showNotification(localStrings.getString('firstrunnotification'), - null, null, 30000); - var notificationElement = $('notification'); - notification.classList.add('first-run'); -} - function showPromoNotification() { showNotification(parseHtmlSubset(localStrings.getString('serverpromo')), localStrings.getString('syncpromotext'), @@ -1450,10 +1443,7 @@ function mostVisitedPages(data, firstRun, hasBlacklistedUrls) { maybeDoneLoading(); }, 1); - // Only show the first run notification if first run. - if (firstRun) { - showFirstRunNotification(); - } else if (localStrings.getString('serverpromo')) { + if (localStrings.getString('serverpromo')) { showPromoNotification(); } } @@ -1467,12 +1457,10 @@ function isDoneLoading() { return !document.body.classList.contains('loading'); } -// Initialize the apps promo. +// Initialize the listener for the "hide this" link on the apps promo. We do +// this outside of getAppsCallback because it only needs to be done once per +// NTP load. document.addEventListener('DOMContentLoaded', function() { - var promoLink = document.querySelector('#apps-promo-text1 a'); - promoLink.id = 'apps-promo-link'; - promoLink.href = localStrings.getString('web_store_url'); - $('apps-promo-hide').addEventListener('click', function() { chrome.send('hideAppsPromo', []); document.documentElement.classList.remove('apps-promo-visible'); diff --git a/chrome/browser/resources/ntp/apps.css b/chrome/browser/resources/ntp/apps.css index bbd5b38..1cbfdce 100644 --- a/chrome/browser/resources/ntp/apps.css +++ b/chrome/browser/resources/ntp/apps.css @@ -142,21 +142,21 @@ menu > button.default { } html.apps-promo-visible #apps-promo { - display: block; + background: url('chrome://theme/IDR_WEBSTORE_ICON') no-repeat; + height: 125px; + -webkit-padding-start: 125px; + display: table-cell; + vertical-align: text-bottom; } -#apps-promo > h3 { - font-size: 16px; - margin-top: 1em; - margin-bottom: 0.25em; -} - -#apps-promo-text1 { - margin-top: 0; +#apps-promo-heading { + font-size: 115%; + font-weight: bold; + margin-bottom: 5px; + -webkit-margin-start: 3px; } #apps-promo-hide { - float: right; -webkit-appearance: none; -webkit-transition: opacity .15s; background-color: transparent; @@ -165,27 +165,13 @@ html.apps-promo-visible #apps-promo { font-family: inherit; font-size: 90%; text-decoration: underline; + margin-top: 2px; } html[dir=rtl] #apps-promo-hide { float: left; } -html.apps-promo-visible .app.web-store-entry { - position: absolute; - left: 100%; - top: 0; - -webkit-margin-start: 22px; -} - -html.apps-promo-visible[dir=rtl] .app.web-store-entry { - right: 100%; -} - -html.apps-promo-visible .app.web-store-entry a { - font-weight: bold; -} - /* We position the web store entry all by its lonesome in the top of the rightmost column when there is at least one full row of apps. Note that this is similar, @@ -201,3 +187,59 @@ never set .loner while the promo is running. html[dir=rtl] .app.web-store-entry.loner { right: 100%; } + +/* g-button CSS styles (see go/buttons) */ +@media screen, projection { + g-button-basic * { + margin: 0; + padding: 0; + } + + .g-button-basic { + direction: ltr; + line-height: 1.2; + width: 20em; + max-width: 795px; + background-color: #cadef4; + border: 1px solid #ccc; + padding: 15px; + text-align: center; + overflow: visible; + } + + .g-button-basic div { + font-size: 1.3em; + background: url('g-button-chocobo-basic-1.png') no-repeat; + background-color: #5679a5; + } + + .g-button-basic div span span a { + display: block; + color: #fff!important; + background: url('g-button-chocobo-basic-2.png') no-repeat right bottom; + padding: 8px 18px 13px 13px; + text-decoration: none; + font-weight: bold; + } + + .g-button-basic div span { + display: block; + background: url('g-button-chocobo-basic-1.png') no-repeat right -400px; + height: 1%; + } + + .g-button-basic p { + text-align: center; + margin: 10px 0 0; + } + + .g-button-basic { + padding: 0; + background: none; + border: 0; + } + + .g-button-basic div span span { + background: url('g-button-chocobo-basic-1.png') no-repeat left bottom; + } +} diff --git a/chrome/browser/resources/ntp/apps.js b/chrome/browser/resources/ntp/apps.js index 89f61dc..a9ea308 100644 --- a/chrome/browser/resources/ntp/apps.js +++ b/chrome/browser/resources/ntp/apps.js @@ -18,6 +18,7 @@ function getAppsCallback(data) { var appsSectionContent = $('apps-content'); var appsMiniview = appsSection.getElementsByClassName('miniview')[0]; var appsPromo = $('apps-promo'); + var appsPromoLink = $('apps-promo-link'); var appsPromoPing = APP_LAUNCH_URL.PING_WEBSTORE + '+' + apps.showPromo; var webStoreEntry, webStoreMiniEntry; @@ -48,8 +49,6 @@ function getAppsCallback(data) { markNewApps(data.apps); apps.data = data.apps; - if (!apps.detachWebstoreEntry) - apps.data.push('web-store-entry'); clearClosedMenu(apps.menu); @@ -62,9 +61,33 @@ function getAppsCallback(data) { appsSectionContent.appendChild(apps.createElement(app)); }); - webStoreEntry = apps.createWebStoreElement(); - webStoreEntry.querySelector('a').setAttribute('ping', appsPromoPing); - appsSectionContent.appendChild(webStoreEntry); + if (data.showPromo) { + // Add the promo content... + $('apps-promo-heading').textContent = data.promoHeader; + appsPromoLink.href = data.promoLink; + appsPromoLink.textContent = data.promoButton; + appsPromoLink.ping = appsPromoPing; + $('apps-promo-hide').textContent = data.promoExpire; + + // ... then display the promo. + document.documentElement.classList.add('apps-promo-visible'); + } else { + document.documentElement.classList.remove('apps-promo-visible'); + } + + // Only show the web store entry if there are apps installed, since the promo + // is sufficient otherwise. + if (data.apps.length > 0) { + webStoreEntry = apps.createWebStoreElement(); + webStoreEntry.querySelector('a').ping = appsPromoPing; + appsSectionContent.appendChild(webStoreEntry); + if (apps.detachWebstoreEntry) { + webStoreEntry.classList.add('loner'); + } else { + webStoreEntry.classList.remove('loner'); + apps.data.push('web-store-entry'); + } + } data.apps.slice(0, MAX_MINIVIEW_ITEMS).forEach(function(app) { appsMiniview.appendChild(apps.createMiniviewElement(app)); @@ -72,7 +95,7 @@ function getAppsCallback(data) { }); if (data.apps.length < MAX_MINIVIEW_ITEMS) { webStoreMiniEntry = apps.createWebStoreMiniElement(); - webStoreEntry.querySelector('a').setAttribute('ping', appsPromoPing); + webStoreMiniEntry.querySelector('a').ping = appsPromoPing; appsMiniview.appendChild(webStoreMiniEntry); addClosedMenuEntryWithLink(apps.menu, apps.createWebStoreClosedMenuElement()); @@ -86,24 +109,14 @@ function getAppsCallback(data) { addClosedMenuFooter(apps.menu, 'apps', MENU_APPS, Section.APPS); apps.loaded = true; - if (apps.showPromo) - document.documentElement.classList.add('apps-promo-visible'); - else - document.documentElement.classList.remove('apps-promo-visible'); - var appsPromoLink = $('apps-promo-link'); if (appsPromoLink) - appsPromoLink.setAttribute('ping', appsPromoPing); + appsPromoLink.ping = appsPromoPing; maybeDoneLoading(); // Disable the animations when the app launcher is being (re)initailized. apps.layout({disableAnimations:true}); - if (apps.detachWebstoreEntry) - webStoreEntry.classList.add('loner'); - else - webStoreEntry.classList.remove('loner'); - if (isDoneLoading()) { updateMiniviewClipping(appsMiniview); layoutSections(); @@ -175,6 +188,11 @@ var apps = (function() { chrome.send('launchApp', args); } + function isAppSectionMaximized() { + return getAppLaunchType() == APP_LAUNCH.NTP_APPS_MAXIMIZED && + !$('apps').classList.contains('disabled'); + } + function isAppsMenu(node) { return node.id == 'apps-menu'; } @@ -216,6 +234,7 @@ var apps = (function() { }; var currentApp; + var promoHasBeenSeen = false; function addContextMenu(el, app) { el.addEventListener('contextmenu', cr.ui.contextMenuHandler); @@ -347,6 +366,14 @@ var apps = (function() { this.invalidate_(); }, + maybePingPromoSeen_: function() { + if (promoHasBeenSeen || !this.showPromo || !isAppSectionMaximized()) + return; + + promoHasBeenSeen = true; + chrome.send('promoSeen', []); + }, + // DragAndDropDelegate dragContainer: $('apps-content'), @@ -564,10 +591,14 @@ var apps = (function() { }, layoutImpl_: function() { - var apps = this.data; + var apps = this.data || []; var rects = this.getLayoutRects_(apps.length); var appsContent = this.dragContainer; + // Ping the PROMO_SEEN histogram only when the promo is maximized, and + // maximum once per NTP load. + this.maybePingPromoSeen_(); + if (!this.visible) return; @@ -651,8 +682,8 @@ var apps = (function() { var a = div.firstChild; a.onclick = handleClick; - a.setAttribute('ping', - getAppPingUrl('PING_BY_ID', this.showPromo, 'NTP_APPS_MAXIMIZED')); + a.ping = getAppPingUrl( + 'PING_BY_ID', this.showPromo, 'NTP_APPS_MAXIMIZED'); a.style.backgroundImage = url(app['icon_big']); if (app.isNew) { div.setAttribute('new', 'new'); @@ -691,8 +722,8 @@ var apps = (function() { a.textContent = app['name']; a.href = app['launch_url']; a.onclick = handleClick; - a.setAttribute('ping', - getAppPingUrl('PING_BY_ID', this.showPromo, 'NTP_APPS_COLLAPSED')); + a.ping = getAppPingUrl( + 'PING_BY_ID', this.showPromo, 'NTP_APPS_COLLAPSED'); a.style.backgroundImage = url(app['icon_small']); a.className = 'item'; span.appendChild(a); @@ -708,8 +739,8 @@ var apps = (function() { a.textContent = app['name']; a.href = app['launch_url']; a.onclick = handleClick; - a.setAttribute('ping', - getAppPingUrl('PING_BY_ID', this.showPromo, 'NTP_APPS_MENU')); + a.ping = getAppPingUrl( + 'PING_BY_ID', this.showPromo, 'NTP_APPS_MENU'); a.style.backgroundImage = url(app['icon_small']); a.className = 'item'; diff --git a/chrome/browser/ui/webui/app_launcher_handler.cc b/chrome/browser/ui/webui/app_launcher_handler.cc index b71e113..67ee56c 100644 --- a/chrome/browser/ui/webui/app_launcher_handler.cc +++ b/chrome/browser/ui/webui/app_launcher_handler.cc @@ -13,7 +13,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" -#include "chrome/browser/extensions/default_apps.h" +#include "chrome/browser/extensions/apps_promo.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/platform_util.h" @@ -145,7 +145,7 @@ bool AppLauncherHandler::HandlePing(Profile* profile, const std::string& path) { // promo if its still displayed. if (is_promo_active) { DCHECK(profile->GetExtensionService()); - profile->GetExtensionService()->default_apps()->SetPromoHidden(); + profile->GetExtensionService()->apps_promo()->ExpireDefaultApps(); } if (is_web_store_ping) { @@ -180,6 +180,8 @@ void AppLauncherHandler::RegisterMessages() { NewCallback(this, &AppLauncherHandler::HandleReorderApps)); web_ui_->RegisterMessageCallback("setPageIndex", NewCallback(this, &AppLauncherHandler::HandleSetPageIndex)); + web_ui_->RegisterMessageCallback("promoSeen", + NewCallback(this, &AppLauncherHandler::HandlePromoSeen)); } void AppLauncherHandler::Observe(NotificationType type, @@ -192,6 +194,9 @@ void AppLauncherHandler::Observe(NotificationType type, case NotificationType::EXTENSION_LOADED: case NotificationType::EXTENSION_UNLOADED: case NotificationType::EXTENSION_LAUNCHER_REORDERED: + // The promo may not load until a couple seconds after the first NTP view, + // so we listen for the load notification and notify the NTP when ready. + case NotificationType::WEB_STORE_PROMO_LOADED: if (web_ui_->tab_contents()) HandleGetApps(NULL); break; @@ -248,10 +253,17 @@ void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { dictionary->SetBoolean( "showLauncher", - extensions_service_->default_apps()->ShouldShowAppLauncher( + extensions_service_->apps_promo()->ShouldShowAppLauncher( extensions_service_->GetAppIds())); } +void AppLauncherHandler::FillPromoDictionary(DictionaryValue* dictionary) { + dictionary->SetString("promoHeader", AppsPromo::GetPromoHeaderText()); + dictionary->SetString("promoButton", AppsPromo::GetPromoButtonText()); + dictionary->SetString("promoLink", AppsPromo::GetPromoLink().spec()); + dictionary->SetString("promoExpire", AppsPromo::GetPromoExpireText()); +} + void AppLauncherHandler::HandleGetApps(const ListValue* args) { DictionaryValue dictionary; @@ -263,24 +275,30 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) { // expired. // b) Conceptually, it doesn't really make sense to count a // prefchange-triggered refresh as a promo 'view'. - DefaultApps* default_apps = extensions_service_->default_apps(); - bool promo_just_expired = false; - if (default_apps->ShouldShowPromo(extensions_service_->GetAppIds(), - &promo_just_expired)) { + AppsPromo* apps_promo = extensions_service_->apps_promo(); + PrefService* prefs = web_ui_->GetProfile()->GetPrefs(); + bool apps_promo_just_expired = false; + if (apps_promo->ShouldShowPromo(extensions_service_->GetAppIds(), + &apps_promo_just_expired)) { + // Maximize the apps section on the first promo view. + apps_promo->MaximizeAppsIfFirstView(); dictionary.SetBoolean("showPromo", true); + FillPromoDictionary(&dictionary); promo_active_ = true; } else { - if (promo_just_expired) { - ignore_changes_ = true; - UninstallDefaultApps(); - ignore_changes_ = false; - ShownSectionsHandler::SetShownSection(web_ui_->GetProfile()->GetPrefs(), - THUMB); - } dictionary.SetBoolean("showPromo", false); promo_active_ = false; } + // If the default apps have just expired (user viewed them too many times with + // no interaction), then we uninstall them and focus the recent sites section. + if (apps_promo_just_expired) { + ignore_changes_ = true; + UninstallDefaultApps(); + ignore_changes_ = false; + ShownSectionsHandler::SetShownSection(prefs, THUMB); + } + FillAppDictionary(&dictionary); web_ui_->CallJavascriptFunction("getAppsCallback", dictionary); @@ -293,6 +311,8 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) { NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_LAUNCHER_REORDERED, NotificationService::AllSources()); + registrar_.Add(this, NotificationType::WEB_STORE_PROMO_LOADED, + NotificationService::AllSources()); } if (pref_change_registrar_.IsEmpty()) { pref_change_registrar_.Init( @@ -346,7 +366,7 @@ void AppLauncherHandler::HandleLaunchApp(const ListValue* args) { if (extension_id != extension_misc::kWebStoreAppId) { RecordAppLaunchByID(promo_active_, launch_bucket); - extensions_service_->default_apps()->SetPromoHidden(); + extensions_service_->apps_promo()->ExpireDefaultApps(); } if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { @@ -420,15 +440,9 @@ void AppLauncherHandler::HandleHideAppsPromo(const ListValue* args) { // If the user has intentionally hidden the promotion, we'll uninstall all the // default apps (we know the user hasn't installed any apps on their own at // this point, or the promotion wouldn't have been shown). - UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, - extension_misc::PROMO_CLOSE, - extension_misc::PROMO_BUCKET_BOUNDARY); - - ShownSectionsHandler::SetShownSection(web_ui_->GetProfile()->GetPrefs(), - THUMB); ignore_changes_ = true; UninstallDefaultApps(); - extensions_service_->default_apps()->SetPromoHidden(); + extensions_service_->apps_promo()->HidePromo(); ignore_changes_ = false; HandleGetApps(NULL); } @@ -480,6 +494,12 @@ void AppLauncherHandler::HandleSetPageIndex(const ListValue* args) { static_cast<int>(page_index)); } +void AppLauncherHandler::HandlePromoSeen(const ListValue* args) { + UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, + extension_misc::PROMO_SEEN, + extension_misc::PROMO_BUCKET_BOUNDARY); +} + // static void AppLauncherHandler::RecordWebStoreLaunch(bool promo_active) { UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, @@ -612,8 +632,8 @@ ExtensionInstallUI* AppLauncherHandler::GetExtensionInstallUI() { } void AppLauncherHandler::UninstallDefaultApps() { - DefaultApps* default_apps = extensions_service_->default_apps(); - const ExtensionIdSet& app_ids = default_apps->default_apps(); + AppsPromo* apps_promo = extensions_service_->apps_promo(); + const ExtensionIdSet& app_ids = apps_promo->old_default_apps(); for (ExtensionIdSet::const_iterator iter = app_ids.begin(); iter != app_ids.end(); ++iter) { if (extensions_service_->GetExtensionById(*iter, true)) diff --git a/chrome/browser/ui/webui/app_launcher_handler.h b/chrome/browser/ui/webui/app_launcher_handler.h index f25b555..2d0e328 100644 --- a/chrome/browser/ui/webui/app_launcher_handler.h +++ b/chrome/browser/ui/webui/app_launcher_handler.h @@ -55,6 +55,9 @@ class AppLauncherHandler : public WebUIMessageHandler, // Populate the given dictionary with all installed app info. void FillAppDictionary(DictionaryValue* value); + // Populate the given dictionary with the web store promo content. + void FillPromoDictionary(DictionaryValue* value); + // Callback for the "getApps" message. void HandleGetApps(const ListValue* args); @@ -79,6 +82,9 @@ class AppLauncherHandler : public WebUIMessageHandler, // Callback for the "setPageIndex" message. void HandleSetPageIndex(const ListValue* args); + // Callback for the "promoSeen" message. + void HandlePromoSeen(const ListValue* args); + private: // Records a web store launch in the appropriate histograms. |promo_active| // specifies if the web store promotion was active. diff --git a/chrome/browser/ui/webui/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp_resource_cache.cc index d93acb1..b3910ed 100644 --- a/chrome/browser/ui/webui/ntp_resource_cache.cc +++ b/chrome/browser/ui/webui/ntp_resource_cache.cc @@ -301,8 +301,6 @@ void NTPResourceCache::CreateNewTabHTML() { 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("firstrunnotification", - l10n_util::GetStringUTF16(IDS_NEW_TAB_FIRST_RUN_NOTIFICATION)); localized_strings.SetString("closefirstrunnotification", l10n_util::GetStringUTF16(IDS_NEW_TAB_CLOSE_FIRST_RUN_NOTIFICATION)); localized_strings.SetString("close", l10n_util::GetStringUTF16(IDS_CLOSE)); @@ -334,14 +332,6 @@ void NTPResourceCache::CreateNewTabHTML() { l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)); localized_strings.SetString("web_store_url", GetUrlWithLang(GURL(Extension::ChromeStoreLaunchURL()))); - localized_strings.SetString("appspromohide", - l10n_util::GetStringUTF16(IDS_APPS_PROMO_HIDE)); - localized_strings.SetString("appspromoheader", - l10n_util::GetStringUTF16(IDS_APPS_PROMO_HEADER)); - localized_strings.SetString("appspromotext1", - l10n_util::GetStringUTF16(IDS_APPS_PROMO_TEXT_1)); - localized_strings.SetString("appspromotext2", - l10n_util::GetStringUTF16(IDS_APPS_PROMO_TEXT_2)); localized_strings.SetString("syncpromotext", l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL)); #if defined(OS_CHROMEOS) diff --git a/chrome/browser/web_resource/promo_resource_service.cc b/chrome/browser/web_resource/promo_resource_service.cc index 947fe95..fb8749e 100644 --- a/chrome/browser/web_resource/promo_resource_service.cc +++ b/chrome/browser/web_resource/promo_resource_service.cc @@ -9,6 +9,7 @@ #include "base/time.h" #include "base/values.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/apps_promo.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" @@ -17,6 +18,7 @@ #include "content/browser/browser_thread.h" #include "content/common/notification_service.h" #include "content/common/notification_type.h" +#include "googleurl/src/gurl.h" namespace { @@ -34,13 +36,16 @@ static const int kNTPPromoGroupSize = 16; // Maximum number of hours for each time slice (4 weeks). static const int kMaxTimeSliceHours = 24 * 7 * 4; -// Used to determine which build type should be shown a given promo. -enum BuildType { - NO_BUILD = 0, - DEV_BUILD = 1, - BETA_BUILD = 1 << 1, - STABLE_BUILD = 1 << 2, -}; +// The version of the service (used to expire the cache when upgrading Chrome +// to versions with different types of promos). +static const int kPromoServiceVersion = 1; + +// Properties used by the server. +static const char kAnswerIdProperty[] = "answer_id"; +static const char kWebStoreHeaderProperty[] = "question"; +static const char kWebStoreButtonProperty[] = "inproduct_target"; +static const char kWebStoreLinkProperty[] = "inproduct"; +static const char kWebStoreExpireProperty[] = "tooltip"; } // namespace @@ -49,6 +54,44 @@ enum BuildType { const char* PromoResourceService::kDefaultPromoResourceServer = "https://www.google.com/support/chrome/bin/topic/1142433/inproduct?hl="; +// static +void PromoResourceService::RegisterPrefs(PrefService* local_state) { + local_state->RegisterIntegerPref(prefs::kNTPPromoVersion, 0); + local_state->RegisterStringPref(prefs::kNTPPromoLocale, std::string()); +} + +// static +void PromoResourceService::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0); + prefs->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0); + prefs->RegisterDoublePref(prefs::kNTPPromoStart, 0); + prefs->RegisterDoublePref(prefs::kNTPPromoEnd, 0); + prefs->RegisterStringPref(prefs::kNTPPromoLine, std::string()); + prefs->RegisterBooleanPref(prefs::kNTPPromoClosed, false); + prefs->RegisterIntegerPref(prefs::kNTPPromoGroup, -1); + prefs->RegisterIntegerPref(prefs::kNTPPromoBuild, + CANARY_BUILD | DEV_BUILD | BETA_BUILD | STABLE_BUILD); + prefs->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0); +} + +// static +bool PromoResourceService::IsBuildTargeted(const std::string& channel, + int builds_allowed) { + if (builds_allowed == NO_BUILD) + return false; + if (channel == "canary" || channel == "canary-m") { + return (CANARY_BUILD & builds_allowed) != 0; + } else if (channel == "dev" || channel == "dev-m") { + return (DEV_BUILD & builds_allowed) != 0; + } else if (channel == "beta" || channel == "beta-m") { + return (BETA_BUILD & builds_allowed) != 0; + } else if (channel == "" || channel == "m") { + return (STABLE_BUILD & builds_allowed) != 0; + } else { + return false; + } +} + PromoResourceService::PromoResourceService(Profile* profile) : WebResourceService(profile, profile->GetPrefs(), @@ -58,34 +101,30 @@ PromoResourceService::PromoResourceService(Profile* profile) prefs::kNTPPromoResourceCacheUpdate, kStartResourceFetchDelay, kCacheUpdateDelay), - web_resource_cache_(NULL) { + web_resource_cache_(NULL), + channel_(NULL) { Init(); } PromoResourceService::~PromoResourceService() { } void PromoResourceService::Init() { - prefs_->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0); - prefs_->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0); - prefs_->RegisterDoublePref(prefs::kNTPPromoStart, 0); - prefs_->RegisterDoublePref(prefs::kNTPPromoEnd, 0); - prefs_->RegisterStringPref(prefs::kNTPPromoLine, std::string()); - prefs_->RegisterBooleanPref(prefs::kNTPPromoClosed, false); - prefs_->RegisterIntegerPref(prefs::kNTPPromoGroup, -1); - prefs_->RegisterIntegerPref(prefs::kNTPPromoBuild, - DEV_BUILD | BETA_BUILD | STABLE_BUILD); - prefs_->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0); - - // If the promo start is in the future, set a notification task to invalidate - // the NTP cache at the time of the promo start. - double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart); - double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd); - ScheduleNotification(promo_start, promo_end); + ScheduleNotificationOnInit(); +} + +bool PromoResourceService::IsThisBuildTargeted(int builds_targeted) { + if (channel_ == NULL) { + base::ThreadRestrictions::ScopedAllowIO allow_io; + channel_ = platform_util::GetVersionStringModifier().c_str(); + } + + return IsBuildTargeted(channel_, builds_targeted); } void PromoResourceService::Unpack(const DictionaryValue& parsed_json) { UnpackLogoSignal(parsed_json); UnpackPromoSignal(parsed_json); + UnpackWebStoreSignal(parsed_json); } void PromoResourceService::ScheduleNotification(double promo_start, @@ -109,6 +148,37 @@ void PromoResourceService::ScheduleNotification(double promo_start, } } +void PromoResourceService::ScheduleNotificationOnInit() { + std::string locale = g_browser_process->GetApplicationLocale(); + if ((GetPromoServiceVersion() != kPromoServiceVersion) || + (GetPromoLocale() != locale)) { + // If the promo service has been upgraded or Chrome switched locales, + // refresh the promos. + PrefService* local_state = g_browser_process->local_state(); + local_state->SetInteger(prefs::kNTPPromoVersion, kPromoServiceVersion); + local_state->SetString(prefs::kNTPPromoLocale, locale); + prefs_->ClearPref(prefs::kNTPPromoResourceCacheUpdate); + AppsPromo::ClearPromo(); + PostNotification(0); + } else { + // If the promo start is in the future, set a notification task to + // invalidate the NTP cache at the time of the promo start. + double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart); + double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd); + ScheduleNotification(promo_start, promo_end); + } +} + +int PromoResourceService::GetPromoServiceVersion() { + PrefService* local_state = g_browser_process->local_state(); + return local_state->GetInteger(prefs::kNTPPromoVersion); +} + +std::string PromoResourceService::GetPromoLocale() { + PrefService* local_state = g_browser_process->local_state(); + return local_state->GetString(prefs::kNTPPromoLocale); +} + void PromoResourceService::UnpackPromoSignal( const DictionaryValue& parsed_json) { DictionaryValue* topic_dict; @@ -134,12 +204,12 @@ void PromoResourceService::UnpackPromoSignal( std::string promo_build = ""; int promo_build_type = 0; int time_slice_hrs = 0; - for (ListValue::const_iterator tip_iter = answer_list->begin(); - tip_iter != answer_list->end(); ++tip_iter) { - if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY)) + for (ListValue::const_iterator answer_iter = answer_list->begin(); + answer_iter != answer_list->end(); ++answer_iter) { + if (!(*answer_iter)->IsType(Value::TYPE_DICTIONARY)) continue; DictionaryValue* a_dic = - static_cast<DictionaryValue*>(*tip_iter); + static_cast<DictionaryValue*>(*answer_iter); std::string promo_signal; if (a_dic->GetString("name", &promo_signal)) { if (promo_signal == "promo_start") { @@ -208,6 +278,72 @@ void PromoResourceService::UnpackPromoSignal( } } +void PromoResourceService::UnpackWebStoreSignal( + const DictionaryValue& parsed_json) { + DictionaryValue* topic_dict; + ListValue* answer_list; + + bool signal_found = false; + std::string promo_id = ""; + std::string promo_header = ""; + std::string promo_button = ""; + std::string promo_link = ""; + std::string promo_expire = ""; + int target_builds = 0; + + if (!parsed_json.GetDictionary("topic", &topic_dict) || + !topic_dict->GetList("answers", &answer_list)) + return; + + for (ListValue::const_iterator answer_iter = answer_list->begin(); + answer_iter != answer_list->end(); ++answer_iter) { + if (!(*answer_iter)->IsType(Value::TYPE_DICTIONARY)) + continue; + DictionaryValue* a_dic = + static_cast<DictionaryValue*>(*answer_iter); + std::string name; + if (!a_dic->GetString("name", &name)) + continue; + + size_t split = name.find(":"); + if (split == std::string::npos) + continue; + + std::string promo_signal = name.substr(0, split); + + if (promo_signal != "webstore_promo" || + !base::StringToInt(name.substr(split+1), &target_builds)) + continue; + + if (!a_dic->GetString(kAnswerIdProperty, &promo_id) || + !a_dic->GetString(kWebStoreHeaderProperty, &promo_header) || + !a_dic->GetString(kWebStoreButtonProperty, &promo_button) || + !a_dic->GetString(kWebStoreLinkProperty, &promo_link) || + !a_dic->GetString(kWebStoreExpireProperty, &promo_expire)) + continue; + + if (IsThisBuildTargeted(target_builds)) { + // Store the first web store promo that targets the current build. + AppsPromo::SetPromo( + promo_id, promo_header, promo_button, GURL(promo_link), promo_expire); + signal_found = true; + break; + } + } + + if (!signal_found) { + // If no web store promos target this build, then clear all the prefs. + AppsPromo::ClearPromo(); + } + + NotificationService::current()->Notify( + NotificationType::WEB_STORE_PROMO_LOADED, + Source<PromoResourceService>(this), + NotificationService::NoDetails()); + + return; +} + void PromoResourceService::UnpackLogoSignal( const DictionaryValue& parsed_json) { DictionaryValue* topic_dict; @@ -229,12 +365,12 @@ void PromoResourceService::UnpackLogoSignal( if (topic_dict->GetList("answers", &answer_list)) { std::string logo_start_string = ""; std::string logo_end_string = ""; - for (ListValue::const_iterator tip_iter = answer_list->begin(); - tip_iter != answer_list->end(); ++tip_iter) { - if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY)) + for (ListValue::const_iterator answer_iter = answer_list->begin(); + answer_iter != answer_list->end(); ++answer_iter) { + if (!(*answer_iter)->IsType(Value::TYPE_DICTIONARY)) continue; DictionaryValue* a_dic = - static_cast<DictionaryValue*>(*tip_iter); + static_cast<DictionaryValue*>(*answer_iter); std::string logo_signal; if (a_dic->GetString("name", &logo_signal)) { if (logo_signal == "custom_logo_start") { @@ -290,27 +426,16 @@ bool CanShowPromo(Profile* profile) { sync_ui_util::GetStatus( profile->GetProfileSyncService()) == sync_ui_util::SYNCED); - // GetVersionStringModifier hits the registry. See http://crbug.com/70898. - base::ThreadRestrictions::ScopedAllowIO allow_io; - const std::string channel = platform_util::GetVersionStringModifier(); bool is_promo_build = false; if (prefs->HasPrefPath(prefs::kNTPPromoBuild)) { - int builds_allowed = prefs->GetInteger(prefs::kNTPPromoBuild); - if (builds_allowed == NO_BUILD) - return false; - if (channel == "dev" || channel == "dev-m") { - is_promo_build = (DEV_BUILD & builds_allowed) != 0; - } else if (channel == "beta" || channel == "beta-m") { - is_promo_build = (BETA_BUILD & builds_allowed) != 0; - } else if (channel == "" || channel == "m") { - is_promo_build = (STABLE_BUILD & builds_allowed) != 0; - } else { - is_promo_build = false; - } + // GetVersionStringModifier hits the registry. See http://crbug.com/70898. + base::ThreadRestrictions::ScopedAllowIO allow_io; + const std::string channel = platform_util::GetVersionStringModifier(); + is_promo_build = PromoResourceService::IsBuildTargeted( + channel, prefs->GetInteger(prefs::kNTPPromoBuild)); } return !promo_closed && !is_synced && is_promo_build; } } // namespace PromoResourceServiceUtil - diff --git a/chrome/browser/web_resource/promo_resource_service.h b/chrome/browser/web_resource/promo_resource_service.h index 733e6ae..2149890a 100644 --- a/chrome/browser/web_resource/promo_resource_service.h +++ b/chrome/browser/web_resource/promo_resource_service.h @@ -16,6 +16,8 @@ bool CanShowPromo(Profile* profile); } // namespace PromoResourceServiceUtil +class PrefService; + // A PromoResourceService fetches data from a web resource server to be used to // dynamically change the appearance of the New Tab Page. For example, it has // been used to fetch "tips" to be displayed on the NTP, or to display @@ -27,8 +29,61 @@ bool CanShowPromo(Profile* profile); class PromoResourceService : public WebResourceService { public: + static bool IsBuildTargeted(const std::string& channel, int builds_targeted); + + static void RegisterPrefs(PrefService* local_state); + + static void RegisterUserPrefs(PrefService* prefs); + explicit PromoResourceService(Profile* profile); + // Default server of dynamically loaded NTP HTML elements. + static const char* kDefaultPromoResourceServer; + + private: + FRIEND_TEST_ALL_PREFIXES(PromoResourceServiceTest, UnpackLogoSignal); + FRIEND_TEST_ALL_PREFIXES(PromoResourceServiceTest, UnpackPromoSignal); + FRIEND_TEST_ALL_PREFIXES(PromoResourceServiceTest, UnpackWebStoreSignal); + + // Identifies types of Chrome builds for promo targeting. + enum BuildType { + NO_BUILD = 0, + DEV_BUILD = 1, + BETA_BUILD = 1 << 1, + STABLE_BUILD = 1 << 2, + CANARY_BUILD = 1 << 3, + }; + + virtual ~PromoResourceService(); + + int GetPromoServiceVersion(); + + // Gets the locale of the last promos fetched from the server. This is saved + // so we can fetch new data if the locale changes. + std::string GetPromoLocale(); + + void Init(); + + // Returns true if |builds_targeted| includes the release channel Chrome + // belongs to. For testing purposes, you can override the current channel + // with set_channel. + bool IsThisBuildTargeted(int builds_targeted); + + // Schedule a notification that a web resource is either going to become + // available or be no longer valid. + void ScheduleNotification(double ms_start_time, double ms_end_time); + + // Schedules the initial notification for when the web resource is going + // to become available or no longer valid. This performs a few additional + // checks than ScheduleNotification, namely it schedules updates immediately + // if the promo service or Chrome locale has changed. + void ScheduleNotificationOnInit(); + + // Overrides the current Chrome release channel for testing purposes. + void set_channel(const char* channel) { channel_ = channel; } + + virtual void Unpack(const DictionaryValue& parsed_json); + // Unpack the web resource as a custom promo signal. Expects a start and end // signal, with the promo to be shown in the tooltip of the start signal // field. Delivery will be in json in the form of: @@ -69,7 +124,6 @@ class PromoResourceService // For example, "7:24" would indicate that all builds should see the promo, // and each group should see it for 24 hours. // - // Public for unit testing. void UnpackPromoSignal(const DictionaryValue& parsed_json); // Unpack the promo resource as a custom logo signal. Expects a start and end @@ -98,29 +152,44 @@ class PromoResourceService // } // } // - // Public for unit testing. void UnpackLogoSignal(const DictionaryValue& parsed_json); - // Default server of dynamically loaded NTP HTML elements. - static const char* kDefaultPromoResourceServer; - - private: - virtual ~PromoResourceService(); - - virtual void Unpack(const DictionaryValue& parsed_json); - - void Init(); - - // Schedule a notification that a web resource is either going to become - // available or be no longer valid. - void ScheduleNotification(double ms_start_time, double ms_end_time); + // Unpack the web store promo. Expects JSON delivery in the following format: + // { + // "topic": { + // "answers": [ + // { + // "answer_id": "1143011", + // "name": "webstore_promo:15", + // "question": "Browse thousands of apps and games for Chrome.", + // "inproduct_target": "Visit the Chrome Web Store", + // "inproduct": "https://chrome.google.com/webstore?hl=en", + // "tooltip": "No thanks, hide this" + // }, + // ... + // ] + // } + // } + // The properties are defined as follows: + // inproduct: the release channels targeted (bitwise or of BuildTypes) + // question: the promo header text + // inproduct_target: the promo button text + // inproduct: the promo button link + // tooltip: the text for the "hide this" link on the promo + // name: starts with "webstore_promo" to identify the signal. the second + // part contains the release channels targeted (bitwise or of + // BuildTypes) + // answer_id: the promo's id + void UnpackWebStoreSignal(const DictionaryValue& parsed_json); // Gets mutable dictionary attached to user's preferences, so that we // can write resource data back to user's pref file. DictionaryValue* web_resource_cache_; + // Overrides the current Chrome release channel for testing purposes. + const char* channel_; + DISALLOW_COPY_AND_ASSIGN(PromoResourceService); }; #endif // CHROME_BROWSER_WEB_RESOURCE_PROMO_RESOURCE_SERVICE_H_ - diff --git a/chrome/browser/web_resource/promo_resource_service_unittest.cc b/chrome/browser/web_resource/promo_resource_service_unittest.cc index e84e43b..6e1f714 100644 --- a/chrome/browser/web_resource/promo_resource_service_unittest.cc +++ b/chrome/browser/web_resource/promo_resource_service_unittest.cc @@ -6,30 +6,29 @@ #include "base/time.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/browser/extensions/apps_promo.h" +#include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/web_resource/promo_resource_service.h" #include "chrome/common/pref_names.h" +#include "chrome/test/testing_browser_process.h" +#include "chrome/test/testing_pref_service.h" #include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" typedef testing::Test PromoResourceServiceTest; -namespace { - -// From promo_resource_service.cc -enum BuildType { - DEV_BUILD = 1, - BETA_BUILD = 1 << 1, - STABLE_BUILD = 1 << 2, -}; - -} // namespace - // Verifies that custom dates read from a web resource server are written to // the preferences file. TEST_F(PromoResourceServiceTest, UnpackLogoSignal) { // Set up a testing profile and create a promo resource service. TestingProfile profile; + TestingPrefService local_state; + TestingBrowserProcess* testing_browser_process = + static_cast<TestingBrowserProcess*>(g_browser_process); + testing_browser_process->SetPrefService(&local_state); + browser::RegisterLocalState(&local_state); + scoped_refptr<PromoResourceService> web_resource_service( new PromoResourceService(&profile)); @@ -112,6 +111,12 @@ TEST_F(PromoResourceServiceTest, UnpackLogoSignal) { TEST_F(PromoResourceServiceTest, UnpackPromoSignal) { // Set up a testing profile and create a promo resource service. TestingProfile profile; + TestingPrefService local_state; + TestingBrowserProcess* testing_browser_process = + static_cast<TestingBrowserProcess*>(g_browser_process); + testing_browser_process->SetPrefService(&local_state); + browser::RegisterLocalState(&local_state); + scoped_refptr<PromoResourceService> web_resource_service( new PromoResourceService(&profile)); @@ -152,9 +157,11 @@ TEST_F(PromoResourceServiceTest, UnpackPromoSignal) { EXPECT_LT(promo_group, 16); int promo_build_type = prefs->GetInteger(prefs::kNTPPromoBuild); - EXPECT_EQ(promo_build_type & DEV_BUILD, DEV_BUILD); - EXPECT_EQ(promo_build_type & BETA_BUILD, BETA_BUILD); - EXPECT_EQ(promo_build_type & STABLE_BUILD, 0); + EXPECT_EQ(promo_build_type & PromoResourceService::DEV_BUILD, + PromoResourceService::DEV_BUILD); + EXPECT_EQ(promo_build_type & PromoResourceService::BETA_BUILD, + PromoResourceService::BETA_BUILD); + EXPECT_EQ(promo_build_type & PromoResourceService::STABLE_BUILD, 0); int promo_time_slice = prefs->GetInteger(prefs::kNTPPromoGroupTimeSlice); EXPECT_EQ(promo_time_slice, 2); @@ -170,4 +177,86 @@ TEST_F(PromoResourceServiceTest, UnpackPromoSignal) { EXPECT_EQ(promo_end, 1327971600); // unix epoch for Jan 31 2012 0100 GMT. } +TEST_F(PromoResourceServiceTest, UnpackWebStoreSignal) { + // Set up a testing profile and create a promo resource service. + TestingProfile profile; + TestingPrefService local_state; + TestingBrowserProcess* testing_browser_process = + static_cast<TestingBrowserProcess*>(g_browser_process); + testing_browser_process->SetPrefService(&local_state); + + browser::RegisterLocalState(&local_state); + + scoped_refptr<PromoResourceService> web_resource_service( + new PromoResourceService(&profile)); + + web_resource_service->set_channel("dev"); + + // Set up start and end dates and promo line in a Dictionary as if parsed + // from the service. + std::string json = "{ " + " \"topic\": {" + " \"answers\": [" + " {" + " \"answer_id\": \"341252\"," + " \"name\": \"webstore_promo:15\"," + " \"question\": \"The header!\"," + " \"inproduct_target\": \"The button label!\"," + " \"inproduct\": \"http://link.com\"," + " \"tooltip\": \"No thanks, hide this.\"" + " }" + " ]" + " }" + "}"; + scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>( + base::JSONReader::Read(json, false))); + + // Initialize a message loop for this to run on. + MessageLoop loop; + + // Check that prefs are set correctly. + web_resource_service->UnpackWebStoreSignal(*(test_json.get())); + PrefService* prefs = profile.GetPrefs(); + ASSERT_TRUE(prefs != NULL); + + EXPECT_EQ("341252", AppsPromo::GetPromoId()); + EXPECT_EQ("The header!", AppsPromo::GetPromoHeaderText()); + EXPECT_EQ("The button label!", AppsPromo::GetPromoButtonText()); + EXPECT_EQ(GURL("http://link.com"), AppsPromo::GetPromoLink()); + EXPECT_EQ("No thanks, hide this.", AppsPromo::GetPromoExpireText()); +} + +TEST_F(PromoResourceServiceTest, IsBuildTargeted) { + // canary + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 1)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 3)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 7)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 15)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 8)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 11)); + + // dev + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 1)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 3)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 7)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 15)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("dev", 8)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 11)); + // beta + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("beta", 1)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 3)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 7)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 15)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("beta", 8)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 11)); + + // stable + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 1)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 3)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 7)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 15)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 8)); + EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 11)); + EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 12)); +} diff --git a/chrome/browser/web_resource/web_resource_service.h b/chrome/browser/web_resource/web_resource_service.h index 1a1074d..32f31e4 100644 --- a/chrome/browser/web_resource/web_resource_service.h +++ b/chrome/browser/web_resource/web_resource_service.h @@ -52,6 +52,8 @@ class WebResourceService // and get proper install directory. PrefService* prefs_; + Profile* profile_; + private: class WebResourceFetcher; friend class WebResourceFetcher; @@ -67,8 +69,6 @@ class WebResourceService // Notify listeners that the state of a web resource has changed. void WebResourceStateChange(); - Profile* profile_; - scoped_ptr<WebResourceFetcher> web_resource_fetcher_; ResourceDispatcherHost* resource_dispatcher_host_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 74434c0..0fa756d 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -835,14 +835,14 @@ 'browser/download/save_types.h', 'browser/enumerate_modules_model_win.cc', 'browser/enumerate_modules_model_win.h', + 'browser/extensions/apps_promo.cc', + 'browser/extensions/apps_promo.h', 'browser/extensions/convert_user_script.cc', 'browser/extensions/convert_user_script.h', 'browser/extensions/convert_web_app.cc', 'browser/extensions/convert_web_app.h', 'browser/extensions/crx_installer.cc', 'browser/extensions/crx_installer.h', - 'browser/extensions/default_apps.cc', - 'browser/extensions/default_apps.h', 'browser/extensions/execute_code_in_tab_function.cc', 'browser/extensions/execute_code_in_tab_function.h', 'browser/extensions/extension_accessibility_api.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 02111f6..d4fb492 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1318,9 +1318,9 @@ 'browser/download/mock_download_manager.h', 'browser/download/save_package_unittest.cc', 'browser/enumerate_modules_model_unittest_win.cc', + 'browser/extensions/apps_promo_unittest.cc', 'browser/extensions/convert_user_script_unittest.cc', 'browser/extensions/convert_web_app_unittest.cc', - 'browser/extensions/default_apps_unittest.cc', 'browser/extensions/extension_event_router_forwarder_unittest.cc', 'browser/extensions/extension_icon_manager_unittest.cc', 'browser/extensions/extension_info_map_unittest.cc', diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 73d5a26..13837a8 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1081,6 +1081,12 @@ const char kNTPPrefVersion[] = "ntp.pref_version"; const char kNTPCustomLogoStart[] = "ntp.alt_logo_start"; const char kNTPCustomLogoEnd[] = "ntp.alt_logo_end"; +// The promo resource service version number. +const char kNTPPromoVersion[] = "ntp.promo_version"; + +// The last locale the promo was fetched for. +const char kNTPPromoLocale[] = "ntp.promo_locale"; + // Whether promo should be shown to Dev builds, Beta and Dev, or all builds. const char kNTPPromoBuild[] = "ntp.promo_build"; @@ -1103,6 +1109,24 @@ const char kNTPPromoLine[] = "ntp.promo_line"; const char kNTPPromoStart[] = "ntp.promo_start"; const char kNTPPromoEnd[] = "ntp.promo_end"; +// The id of the last web store promo actually displayed on the NTP. +const char kNTPWebStorePromoLastId[] = "ntp.webstore_last_promo_id"; + +// The id of the current web store promo. +const char kNTPWebStorePromoId[] = "ntp.webstorepromo.id"; + +// The header line for the NTP web store promo. +const char kNTPWebStorePromoHeader[] = "ntp.webstorepromo.header"; + +// The button text for the NTP web store promo. +const char kNTPWebStorePromoButton[] = "ntp.webstorepromo.button"; + +// The button link for the NTP web store promo. +const char kNTPWebStorePromoLink[] = "ntp.webstorepromo.link"; + +// The "hide this" link text for the NTP web store promo. +const char kNTPWebStorePromoExpire[] = "ntp.webstorepromo.expire"; + // The most up-to-date GPU blacklist downloaded from the web, which replaces // the one that's installed with chrome. const char kGpuBlacklist[] = "gpu_blacklist"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 3608cd2..d4b742d 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -380,6 +380,8 @@ extern const char kNTPShownSections[]; extern const char kNTPPrefVersion[]; extern const char kNTPCustomLogoStart[]; extern const char kNTPCustomLogoEnd[]; +extern const char kNTPPromoVersion[]; +extern const char kNTPPromoLocale[]; extern const char kNTPPromoStart[]; extern const char kNTPPromoEnd[]; extern const char kNTPPromoLine[]; @@ -387,6 +389,12 @@ extern const char kNTPPromoClosed[]; extern const char kNTPPromoGroup[]; extern const char kNTPPromoGroupTimeSlice[]; extern const char kNTPPromoBuild[]; +extern const char kNTPWebStorePromoLastId[]; +extern const char kNTPWebStorePromoId[]; +extern const char kNTPWebStorePromoHeader[]; +extern const char kNTPWebStorePromoButton[]; +extern const char kNTPWebStorePromoLink[]; +extern const char kNTPWebStorePromoExpire[]; extern const char kGpuBlacklist[]; extern const char kGpuBlacklistUpdate[]; diff --git a/content/common/notification_type.h b/content/common/notification_type.h index a3161f1..b105e5e 100644 --- a/content/common/notification_type.h +++ b/content/common/notification_type.h @@ -1317,6 +1317,9 @@ class NotificationType { // process/route id pair for the RenderViewHost. There are no details. PRERENDER_CONTENTS_USED, + // Sent when a new web store promo has been loaded. + WEB_STORE_PROMO_LOADED, + // Count (must be last) ---------------------------------------------------- // Used to determine the number of notification types. Not valid as // a type parameter when registering for or posting notifications. |