summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorbattre@chromium.org <battre@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-11 18:53:25 +0000
committerbattre@chromium.org <battre@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-11 18:53:25 +0000
commit7c1490dad18464a5ae6f748cbf645ff7fca4a108 (patch)
tree35bd01dc78a4d167c8e524fb15e55eae558ba82b /chrome
parent4647cbe2346b9db9444634e05198d1f5f84f50b9 (diff)
downloadchromium_src-7c1490dad18464a5ae6f748cbf645ff7fca4a108.zip
chromium_src-7c1490dad18464a5ae6f748cbf645ff7fca4a108.tar.gz
chromium_src-7c1490dad18464a5ae6f748cbf645ff7fca4a108.tar.bz2
Warn user in case extension delays network traffic too much.
This CL adds a badge to the wrench menu and warning messages to chrome://extensions in case an extension delays network traffic too much and thereby causes a bad user experience. BUG=82618 TEST=Install an extension using the webRequest API in a debug build. In debug build the extension should delay the network traffic enough to cause warning messages. You can use https://adblockplus.org/development-builds/experimental-adblock-plus-for-chrome-builds-available-with-better-blocking for example. Review URL: http://codereview.chromium.org/8176001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@104929 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/chrome_command_ids.h1
-rw-r--r--chrome/app/generated_resources.grd11
-rw-r--r--chrome/browser/extensions/extension_global_error_badge.cc75
-rw-r--r--chrome/browser/extensions/extension_global_error_badge.h45
-rw-r--r--chrome/browser/extensions/extension_service.cc15
-rw-r--r--chrome/browser/extensions/extension_service.h8
-rw-r--r--chrome/browser/extensions/extension_ui_unittest.cc2
-rw-r--r--chrome/browser/extensions/extension_warning_set.cc167
-rw-r--r--chrome/browser/extensions/extension_warning_set.h90
-rw-r--r--chrome/browser/extensions/extension_warning_set_unittest.cc142
-rw-r--r--chrome/browser/extensions/extension_webrequest_api.cc3
-rw-r--r--chrome/browser/extensions/extension_webrequest_time_tracker.cc110
-rw-r--r--chrome/browser/extensions/extension_webrequest_time_tracker.h35
-rw-r--r--chrome/browser/extensions/extension_webrequest_time_tracker_unittest.cc78
-rw-r--r--chrome/browser/resources/options/extension_list.js23
-rw-r--r--chrome/browser/resources/options/extension_settings.css16
-rw-r--r--chrome/browser/ui/webui/options/extension_settings_handler.cc31
-rw-r--r--chrome/browser/ui/webui/options/extension_settings_handler.h4
-rw-r--r--chrome/chrome_browser.gypi4
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_notification_types.h5
21 files changed, 846 insertions, 20 deletions
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 21433b3..6bbad9b 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -170,6 +170,7 @@
#define IDC_FILE_MANAGER 40028
#define IDC_BOOKMARKS_MENU 40029
#define IDC_SHOW_SYNC_SETUP 40030
+#define IDC_EXTENSION_ERRORS 40031
// Spell-check
// Insert any additional suggestions before _LAST; these have to be consecutive.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b84aefe..37e0ded 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4115,6 +4115,17 @@ Keep your key file in a safe place. You will need it to create new versions of y
Web Store
</message>
+ <!-- Global error messages for extensions -->
+ <message name="IDS_EXTENSION_WARNINGS_WRENCH_MENU_ITEM" desc="The wrench menu item indicating that extensions caused problems">
+ Misbehaving extension
+ </message>
+ <message name="IDS_EXTENSION_WARNINGS_TITLE" desc="The title of a section in chrome://extensions which contains the warning(s) that relates to one particular extension">
+ Warning:
+ </message>
+ <message name="IDS_EXTENSION_WARNINGS_NETWORK_DELAY" desc="Warning message indicating that an extension caused excessive network delays for web requests">
+ This extension is slowing down <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>. You should disable it to restore <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>'s performance.
+ </message>
+
<!-- Plugins -->
<message name="IDS_PLUGINS_TITLE" desc="Title for the chrome://plugins page.">
Plug-ins
diff --git a/chrome/browser/extensions/extension_global_error_badge.cc b/chrome/browser/extensions/extension_global_error_badge.cc
new file mode 100644
index 0000000..8124dc8
--- /dev/null
+++ b/chrome/browser/extensions/extension_global_error_badge.cc
@@ -0,0 +1,75 @@
+// 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/extension_global_error_badge.h"
+
+#include "base/logging.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+ExtensionGlobalErrorBadge::ExtensionGlobalErrorBadge() {}
+
+ExtensionGlobalErrorBadge::~ExtensionGlobalErrorBadge() {}
+
+bool ExtensionGlobalErrorBadge::HasBadge() {
+ return true;
+}
+
+bool ExtensionGlobalErrorBadge::HasMenuItem() {
+ return true;
+}
+
+int ExtensionGlobalErrorBadge::MenuItemCommandID() {
+ return IDC_EXTENSION_ERRORS;
+}
+
+string16 ExtensionGlobalErrorBadge::MenuItemLabel() {
+ return l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_WRENCH_MENU_ITEM);
+}
+
+void ExtensionGlobalErrorBadge::ExecuteMenuItem(Browser* browser) {
+ ExtensionService* extension_service =
+ browser->GetProfile()->GetExtensionService();
+
+ // Suppress all current warnings in the extension service from triggering
+ // a badge on the wrench menu in the future of this session.
+ extension_service->extension_warnings()->SuppressBadgeForCurrentWarnings();
+
+ browser->ExecuteCommand(IDC_MANAGE_EXTENSIONS);
+}
+
+bool ExtensionGlobalErrorBadge::HasBubbleView() {
+ return false;
+}
+
+string16 ExtensionGlobalErrorBadge::GetBubbleViewTitle() {
+ return string16();
+}
+
+string16 ExtensionGlobalErrorBadge::GetBubbleViewMessage() {
+ return string16();
+}
+
+string16 ExtensionGlobalErrorBadge::GetBubbleViewAcceptButtonLabel() {
+ return string16();
+}
+
+string16 ExtensionGlobalErrorBadge::GetBubbleViewCancelButtonLabel() {
+ return string16();
+}
+
+void ExtensionGlobalErrorBadge::BubbleViewDidClose() {
+}
+
+void ExtensionGlobalErrorBadge::BubbleViewAcceptButtonPressed() {
+ NOTREACHED();
+}
+
+void ExtensionGlobalErrorBadge::BubbleViewCancelButtonPressed() {
+ NOTREACHED();
+}
diff --git a/chrome/browser/extensions/extension_global_error_badge.h b/chrome/browser/extensions/extension_global_error_badge.h
new file mode 100644
index 0000000..f49ce4b
--- /dev/null
+++ b/chrome/browser/extensions/extension_global_error_badge.h
@@ -0,0 +1,45 @@
+// 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_EXTENSION_GLOBAL_ERROR_BADGE_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_GLOBAL_ERROR_BADGE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/ui/global_error.h"
+
+// Non-modal GlobalError implementation that warns the user if extensions
+// created warnings or errors. If the user clicks on the wrench menu, the user
+// is redirected to chrome://extensions to inspect the errors.
+//
+// This class is a candidate to be merged with ExtensionGlobalError once
+// it is finished.
+class ExtensionGlobalErrorBadge : public GlobalError {
+ public:
+ ExtensionGlobalErrorBadge();
+ virtual ~ExtensionGlobalErrorBadge();
+
+ // Implementation for GlobalError:
+ virtual bool HasBadge() OVERRIDE;
+
+ virtual bool HasMenuItem() OVERRIDE;
+ virtual int MenuItemCommandID() OVERRIDE;
+ virtual string16 MenuItemLabel() OVERRIDE;
+ virtual void ExecuteMenuItem(Browser* browser) OVERRIDE;
+
+ virtual bool HasBubbleView() OVERRIDE;
+ virtual string16 GetBubbleViewTitle() OVERRIDE;
+ virtual string16 GetBubbleViewMessage() OVERRIDE;
+ virtual string16 GetBubbleViewAcceptButtonLabel() OVERRIDE;
+ virtual string16 GetBubbleViewCancelButtonLabel() OVERRIDE;
+ virtual void BubbleViewDidClose() OVERRIDE;
+ virtual void BubbleViewAcceptButtonPressed() OVERRIDE;
+ virtual void BubbleViewCancelButtonPressed() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExtensionGlobalErrorBadge);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_GLOBAL_ERROR_BADGE_H_
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 58ec01c..86004d7 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -603,7 +603,8 @@ ExtensionService::ExtensionService(Profile* profile,
app_notification_manager_(new AppNotificationManager(profile)),
permissions_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
apps_promo_(profile->GetPrefs()),
- event_routers_initialized_(false) {
+ event_routers_initialized_(false),
+ extension_warnings_(profile) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Figure out if extension installation should be enabled.
@@ -971,6 +972,12 @@ bool ExtensionService::UninstallExtension(
UserMetrics::RecordAction(
UserMetricsAction("Extensions.ExtensionUninstalled"));
+ // Uninstalling one extension might have solved the problems of others.
+ // Therefore, we clear warnings of this type for all extensions.
+ std::set<ExtensionWarningSet::WarningType> warnings;
+ extension_warnings_.GetWarningsAffectingExtension(extension_id, &warnings);
+ extension_warnings_.ClearWarnings(warnings);
+
return true;
}
@@ -1071,6 +1078,12 @@ void ExtensionService::DisableExtension(const std::string& extension_id) {
NotifyExtensionUnloaded(extension, extension_misc::UNLOAD_REASON_DISABLE);
SyncExtensionChangeIfNeeded(*extension);
+
+ // Deactivating one extension might have solved the problems of others.
+ // Therefore, we clear warnings of this type for all extensions.
+ std::set<ExtensionWarningSet::WarningType> warnings;
+ extension_warnings_.GetWarningsAffectingExtension(extension_id, &warnings);
+ extension_warnings_.ClearWarnings(warnings);
}
void ExtensionService::GrantPermissions(const Extension* extension) {
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index fa0a3cc..f30c480 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -30,6 +30,7 @@
#include "chrome/browser/extensions/extension_settings_frontend.h"
#include "chrome/browser/extensions/extension_sync_data.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
+#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/extensions/extensions_quota_service.h"
#include "chrome/browser/extensions/external_extension_provider_interface.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
@@ -554,6 +555,10 @@ class ExtensionService
}
#endif
+ ExtensionWarningSet* extension_warnings() {
+ return &extension_warnings_;
+ }
+
private:
// Bundle of type (app or extension)-specific sync stuff.
struct SyncBundle {
@@ -817,6 +822,9 @@ class ExtensionService
SyncBundle app_sync_bundle_;
SyncBundle extension_sync_bundle_;
+ // Contains an entry for each warning that shall be currently shown.
+ ExtensionWarningSet extension_warnings_;
+
FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest,
InstallAppsWithUnlimtedStorage);
FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest,
diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc
index 587645e..c92d742 100644
--- a/chrome/browser/extensions/extension_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_ui_unittest.cc
@@ -40,7 +40,7 @@ namespace {
EXPECT_EQ("", error);
return ExtensionSettingsHandler::CreateExtensionDetailValue(
- NULL, extension.get(), pages, true, false);
+ NULL, extension.get(), pages, NULL, true, false);
}
diff --git a/chrome/browser/extensions/extension_warning_set.cc b/chrome/browser/extensions/extension_warning_set.cc
new file mode 100644
index 0000000..c99f18a2
--- /dev/null
+++ b/chrome/browser/extensions/extension_warning_set.cc
@@ -0,0 +1,167 @@
+// 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/extension_warning_set.h"
+
+#include "chrome/browser/extensions/extension_global_error_badge.h"
+#include "chrome/browser/ui/global_error_service.h"
+#include "chrome/browser/ui/global_error_service_factory.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/common/notification_service.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+// This class is used to represent warnings if extensions misbehave.
+class ExtensionWarning {
+ public:
+ // Default constructor for storing ExtensionServiceWarning in STL containers
+ // do not use.
+ ExtensionWarning();
+
+ // Constructs a warning of type |type| for extension |extension_id|. This
+ // could be for example the fact that an extension conflicted with others.
+ ExtensionWarning(ExtensionWarningSet::WarningType type,
+ const std::string& extension_id);
+
+ ~ExtensionWarning();
+
+ // Returns the specific warning type.
+ ExtensionWarningSet::WarningType warning_type() const { return type_; }
+
+ // Returns the id of the extension for which this warning is valid.
+ const std::string& extension_id() const { return extension_id_; }
+
+ private:
+ ExtensionWarningSet::WarningType type_;
+ std::string extension_id_;
+
+ // Allow implicit copy and assign operator.
+};
+
+ExtensionWarning::ExtensionWarning() : type_(ExtensionWarningSet::kInvalid) {
+}
+
+ExtensionWarning::ExtensionWarning(
+ ExtensionWarningSet::WarningType type,
+ const std::string& extension_id)
+ : type_(type), extension_id_(extension_id) {
+}
+
+ExtensionWarning::~ExtensionWarning() {
+}
+
+bool operator<(const ExtensionWarning& a, const ExtensionWarning& b) {
+ if (a.warning_type() == b.warning_type())
+ return a.extension_id() < b.extension_id();
+ return a.warning_type() < b.warning_type();
+}
+
+// Static
+string16 ExtensionWarningSet::GetLocalizedWarning(
+ ExtensionWarningSet::WarningType warning_type) {
+ switch (warning_type) {
+ case ExtensionWarningSet::kInvalid:
+ case ExtensionWarningSet::kMaxWarningType:
+ NOTREACHED();
+ return string16();
+ case ExtensionWarningSet::kNetworkDelay:
+ return l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_WARNINGS_NETWORK_DELAY,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
+ }
+ NOTREACHED(); // Switch statement has no default branch.
+ return string16();
+}
+
+ExtensionWarningSet::ExtensionWarningSet(Profile* profile)
+ : extension_global_error_badge_(NULL), profile_(profile) {
+}
+
+ExtensionWarningSet::~ExtensionWarningSet() {
+}
+
+void ExtensionWarningSet::SetWarning(ExtensionWarningSet::WarningType type,
+ const std::string& extension_id) {
+ ExtensionWarning warning(type, extension_id);
+ bool inserted = warnings_.insert(warning).second;
+ if (inserted) {
+ NotifyWarningsChanged();
+ UpdateWarningBadge();
+ }
+}
+
+void ExtensionWarningSet::ClearWarnings(
+ const std::set<ExtensionWarningSet::WarningType>& types) {
+ bool deleted_anything = false;
+ for (const_iterator i = warnings_.begin(); i != warnings_.end();) {
+ if (types.find(i->warning_type()) != types.end()) {
+ deleted_anything = true;
+ warnings_.erase(i++);
+ } else {
+ ++i;
+ }
+ }
+
+ if (deleted_anything) {
+ NotifyWarningsChanged();
+ UpdateWarningBadge();
+ }
+}
+
+void ExtensionWarningSet::GetWarningsAffectingExtension(
+ const std::string& extension_id,
+ std::set<ExtensionWarningSet::WarningType>* result) const {
+ result->clear();
+ for (const_iterator i = warnings_.begin(); i != warnings_.end(); ++i) {
+ if (i->extension_id() == extension_id)
+ result->insert(i->warning_type());
+ }
+}
+
+void ExtensionWarningSet::SuppressBadgeForCurrentWarnings() {
+ badge_suppressions_.insert(warnings_.begin(), warnings_.end());
+ UpdateWarningBadge();
+}
+
+void ExtensionWarningSet::NotifyWarningsChanged() {
+ NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED,
+ Source<Profile>(profile_),
+ NotificationService::NoDetails());
+}
+
+void ExtensionWarningSet::ActivateBadge() {
+ DCHECK(!extension_global_error_badge_);
+ DCHECK(profile_);
+ extension_global_error_badge_ = new ExtensionGlobalErrorBadge;
+ GlobalErrorServiceFactory::GetForProfile(profile_)->AddGlobalError(
+ extension_global_error_badge_);
+}
+
+void ExtensionWarningSet::DeactivateBadge() {
+ DCHECK(extension_global_error_badge_);
+ DCHECK(profile_);
+ GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError(
+ extension_global_error_badge_);
+ delete extension_global_error_badge_;
+ extension_global_error_badge_ = NULL;
+}
+
+void ExtensionWarningSet::UpdateWarningBadge() {
+ // We need a badge if a warning exists that has not been suppressed.
+ bool need_warning_badge = false;
+ for (const_iterator i = warnings_.begin(); i != warnings_.end(); ++i) {
+ if (badge_suppressions_.find(*i) == badge_suppressions_.end()) {
+ need_warning_badge = true;
+ break;
+ }
+ }
+
+ // Activate or hide the warning badge in case the current state is incorrect.
+ if (extension_global_error_badge_ && !need_warning_badge)
+ DeactivateBadge();
+ else if (!extension_global_error_badge_ && need_warning_badge)
+ ActivateBadge();
+}
diff --git a/chrome/browser/extensions/extension_warning_set.h b/chrome/browser/extensions/extension_warning_set.h
new file mode 100644
index 0000000..7a44517
--- /dev/null
+++ b/chrome/browser/extensions/extension_warning_set.h
@@ -0,0 +1,90 @@
+// 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_EXTENSION_WARNING_SET_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_WARNING_SET_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "base/string16.h"
+
+class ExtensionWarning;
+class ExtensionGlobalErrorBadge;
+class Profile;
+
+// A set of warnings caused by extensions. These warnings (e.g. conflicting
+// modifications of network requests by extensions, slow extensions, etc.)
+// trigger a warning badge in the UI and and provide means to resolve them.
+class ExtensionWarningSet {
+ public:
+ enum WarningType {
+ // Don't use this, it is only intended for the default constructor and
+ // does not have localized warning messages for the UI.
+ kInvalid = 0,
+ // An extension caused excessive network delays.
+ kNetworkDelay,
+ kMaxWarningType
+ };
+
+ // Returns a localized string describing |warning_type|.
+ static string16 GetLocalizedWarning(WarningType warning_type);
+
+ // |profile| may be NULL for testing. In this case, be sure to not insert
+ // any warnings.
+ explicit ExtensionWarningSet(Profile* profile);
+ virtual ~ExtensionWarningSet();
+
+ // Adds a warning and triggers a chrome::NOTIFICATION_EXTENSION_WARNING
+ // message if this warning is is new. If the warning is new and has not
+ // been suppressed, this may activate a badge on the wrench menu.
+ void SetWarning(ExtensionWarningSet::WarningType type,
+ const std::string& extension_id);
+
+ // Clears all warnings of types contained in |types| and triggers a
+ // chrome::NOTIFICATION_EXTENSION_WARNING message if such warnings existed.
+ // If no warning remains that is not suppressed, this may deactivate a
+ // warning badge on the wrench mennu.
+ void ClearWarnings(const std::set<WarningType>& types);
+
+ // Suppresses showing a badge for all currently existing warnings in the
+ // future.
+ void SuppressBadgeForCurrentWarnings();
+
+ // Stores all warnings for extension |extension_id| in |result|. The previous
+ // content of |result| is erased.
+ void GetWarningsAffectingExtension(
+ const std::string& extension_id,
+ std::set<WarningType>* result) const;
+
+ protected:
+ // Virtual for testing.
+ virtual void NotifyWarningsChanged();
+ virtual void ActivateBadge();
+ virtual void DeactivateBadge();
+
+ // Tracks the currently existing ExtensionGlobalErrorBadge that indicates in
+ // the UI that there are |extension_warnings_|. Weak pointer as the object is
+ // owned by the GlobalErrorService. NULL if there is no warning to be
+ // displayed on the wrench menu currently.
+ ExtensionGlobalErrorBadge* extension_global_error_badge_;
+
+ private:
+ typedef std::set<ExtensionWarning>::const_iterator const_iterator;
+
+ // Shows or hides the warning badge on the wrench menu depending on whether
+ // any non-suppressed warnings exist.
+ void UpdateWarningBadge();
+
+ // Currently existing warnings.
+ std::set<ExtensionWarning> warnings_;
+
+ // Warnings that do not trigger a badge on the wrench menu.
+ std::set<ExtensionWarning> badge_suppressions_;
+
+ Profile* profile_;
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_WARNING_SET_H_
diff --git a/chrome/browser/extensions/extension_warning_set_unittest.cc b/chrome/browser/extensions/extension_warning_set_unittest.cc
new file mode 100644
index 0000000..0831e5f
--- /dev/null
+++ b/chrome/browser/extensions/extension_warning_set_unittest.cc
@@ -0,0 +1,142 @@
+// 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/extension_warning_set.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ExtensionglobalError;
+
+namespace {
+
+class MockExtensionWarningSet : public ExtensionWarningSet {
+ public:
+ MockExtensionWarningSet() : ExtensionWarningSet(NULL) {
+ ON_CALL(*this, ActivateBadge()).WillByDefault(
+ testing::Invoke(this, &MockExtensionWarningSet::ActivateBadgeImpl));
+ ON_CALL(*this, DeactivateBadge()).WillByDefault(
+ testing::Invoke(this, &MockExtensionWarningSet::DeactivateBadgeImpl));
+ }
+ virtual ~MockExtensionWarningSet() {}
+
+ MOCK_METHOD0(NotifyWarningsChanged, void());
+ MOCK_METHOD0(ActivateBadge, void());
+ MOCK_METHOD0(DeactivateBadge, void());
+
+ void ActivateBadgeImpl() {
+ // Just fill the value so that we see that something would be there
+ // in a non-mocked execution.
+ extension_global_error_badge_ =
+ reinterpret_cast<ExtensionGlobalErrorBadge*>(1);
+ }
+ void DeactivateBadgeImpl() {
+ extension_global_error_badge_ = NULL;
+ }
+};
+
+const char* ext1_id = "extension1";
+const char* ext2_id = "extension2";
+
+} // namespace
+
+// Check that inserting a warning triggers notifications, whereas inserting
+// the same warning again is silent.
+TEST(ExtensionWarningSet, SetWarning) {
+ MockExtensionWarningSet warnings;
+
+ // Insert warning for the first time.
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ EXPECT_CALL(warnings, ActivateBadge());
+ warnings.SetWarning(ExtensionWarningSet::kNetworkDelay, ext1_id);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Second insertion of same warning does not trigger anything.
+ warnings.SetWarning(ExtensionWarningSet::kNetworkDelay, ext1_id);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+}
+
+// Check that ClearWarnings deletes exactly the specified warnings and
+// triggers notifications where appropriate.
+TEST(ExtensionWarningSet, ClearWarnings) {
+ MockExtensionWarningSet warnings;
+
+ // TODO(battre): Replace kInvalid with something else once we have additional
+ // warning types.
+ ExtensionWarningSet::WarningType type1 = ExtensionWarningSet::kNetworkDelay;
+ ExtensionWarningSet::WarningType type2 = ExtensionWarningSet::kInvalid;
+
+ // Insert two unique warnings.
+ EXPECT_CALL(warnings, NotifyWarningsChanged()).Times(2);
+ EXPECT_CALL(warnings, ActivateBadge()).Times(1);
+ warnings.SetWarning(type1, ext1_id);
+ warnings.SetWarning(type2, ext2_id);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Remove the one warning and check that the badge remains.
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ std::set<ExtensionWarningSet::WarningType> to_clear;
+ to_clear.insert(ExtensionWarningSet::kInvalid);
+ warnings.ClearWarnings(to_clear);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Check that the correct warnings appear in |warnings|.
+ std::set<ExtensionWarningSet::WarningType> existing_warnings;
+ warnings.GetWarningsAffectingExtension(ext1_id, &existing_warnings);
+ EXPECT_EQ(1u, existing_warnings.size());
+ warnings.GetWarningsAffectingExtension(ext2_id, &existing_warnings);
+ EXPECT_EQ(0u, existing_warnings.size());
+
+ // Remove the other one warning and check that badge disappears.
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ EXPECT_CALL(warnings, DeactivateBadge());
+ to_clear.insert(ExtensionWarningSet::kNetworkDelay);
+ warnings.ClearWarnings(to_clear);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Check that not warnings remain.
+ warnings.GetWarningsAffectingExtension(ext1_id, &existing_warnings);
+ EXPECT_EQ(0u, existing_warnings.size());
+ warnings.GetWarningsAffectingExtension(ext2_id, &existing_warnings);
+ EXPECT_EQ(0u, existing_warnings.size());
+}
+
+// Check that no badge appears if it has been suppressed for a specific
+// warning.
+TEST(ExtensionWarningSet, SuppressBadgeForCurrentWarnings) {
+ MockExtensionWarningSet warnings;
+ // TODO(battre): Replace kInvalid with something else once we have additional
+ // warning types.
+ ExtensionWarningSet::WarningType type1 = ExtensionWarningSet::kNetworkDelay;
+ ExtensionWarningSet::WarningType type2 = ExtensionWarningSet::kInvalid;
+
+ // Insert first warning.
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ EXPECT_CALL(warnings, ActivateBadge());
+ warnings.SetWarning(type1, ext1_id);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Suppress first warning.
+ EXPECT_CALL(warnings, DeactivateBadge());
+ warnings.SuppressBadgeForCurrentWarnings();
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Simulate deinstallation of extension.
+ std::set<ExtensionWarningSet::WarningType> to_clear;
+ warnings.GetWarningsAffectingExtension(ext1_id, &to_clear);
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ warnings.ClearWarnings(to_clear);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Set first warning again and verify that not badge is shown this time.
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ warnings.SetWarning(type1, ext1_id);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+
+ // Set second warning and verify that it shows a badge.
+ EXPECT_CALL(warnings, NotifyWarningsChanged());
+ EXPECT_CALL(warnings, ActivateBadge());
+ warnings.SetWarning(type2, ext2_id);
+ testing::Mock::VerifyAndClearExpectations(&warnings);
+}
diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc
index a225112..0874731 100644
--- a/chrome/browser/extensions/extension_webrequest_api.cc
+++ b/chrome/browser/extensions/extension_webrequest_api.cc
@@ -496,7 +496,8 @@ int ExtensionWebRequestEventRouter::OnBeforeRequest(
request_time_tracker_->LogRequestStartTime(request->identifier(),
base::Time::Now(),
- request->url());
+ request->url(),
+ profile);
int extra_info_spec = 0;
std::vector<const EventListener*> listeners =
diff --git a/chrome/browser/extensions/extension_webrequest_time_tracker.cc b/chrome/browser/extensions/extension_webrequest_time_tracker.cc
index 9ccdef7..abae501 100644
--- a/chrome/browser/extensions/extension_webrequest_time_tracker.cc
+++ b/chrome/browser/extensions/extension_webrequest_time_tracker.cc
@@ -4,7 +4,13 @@
#include "chrome/browser/extensions/extension_webrequest_time_tracker.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
#include "base/metrics/histogram.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_warning_set.h"
+#include "chrome/browser/profiles/profile_manager.h"
// TODO(mpcomplete): tweak all these constants.
namespace {
@@ -25,23 +31,86 @@ const double kThresholdExcessiveDelay = 0.50;
// delay, then we will warn the user.
const size_t kNumModerateDelaysBeforeWarning = 50u;
const size_t kNumExcessiveDelaysBeforeWarning = 10u;
+
+// Handles ExtensionWebRequestTimeTrackerDelegate calls on UI thread.
+void NotifyNetworkDelaysOnUI(void* profile,
+ std::set<std::string> extension_ids) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Profile* p = reinterpret_cast<Profile*>(profile);
+ if (!p || !g_browser_process->profile_manager()->IsValidProfile(p))
+ return;
+
+ ExtensionWarningSet* warnings =
+ p->GetExtensionService()->extension_warnings();
+
+ for (std::set<std::string>::const_iterator i = extension_ids.begin();
+ i != extension_ids.end(); ++i) {
+ warnings->SetWarning(ExtensionWarningSet::kNetworkDelay, *i);
+ }
+}
+
+// Default implementation for ExtensionWebRequestTimeTrackerDelegate
+// that sets a warning in the extension service of |profile|.
+class DefaultDelegate : public ExtensionWebRequestTimeTrackerDelegate {
+ public:
+ virtual ~DefaultDelegate() {}
+
+ // Implementation of ExtensionWebRequestTimeTrackerDelegate.
+ virtual void NotifyExcessiveDelays(
+ void* profile,
+ size_t num_delayed_messages,
+ size_t total_num_messages,
+ const std::set<std::string>& extension_ids) OVERRIDE;
+ virtual void NotifyModerateDelays(
+ void* profile,
+ size_t num_delayed_messages,
+ size_t total_num_messages,
+ const std::set<std::string>& extension_ids) OVERRIDE;
+};
+
+void DefaultDelegate::NotifyExcessiveDelays(
+ void* profile,
+ size_t num_delayed_messages,
+ size_t total_num_messages,
+ const std::set<std::string>& extension_ids) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&NotifyNetworkDelaysOnUI, profile, extension_ids));
+}
+
+void DefaultDelegate::NotifyModerateDelays(
+ void* profile,
+ size_t num_delayed_messages,
+ size_t total_num_messages,
+ const std::set<std::string>& extension_ids) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&NotifyNetworkDelaysOnUI, profile, extension_ids));
+}
+
} // namespace
ExtensionWebRequestTimeTracker::RequestTimeLog::RequestTimeLog()
- : completed(false) {
+ : profile(NULL), completed(false) {
}
ExtensionWebRequestTimeTracker::RequestTimeLog::~RequestTimeLog() {
}
-ExtensionWebRequestTimeTracker::ExtensionWebRequestTimeTracker() {
+ExtensionWebRequestTimeTracker::ExtensionWebRequestTimeTracker()
+ : delegate_(new DefaultDelegate) {
}
ExtensionWebRequestTimeTracker::~ExtensionWebRequestTimeTracker() {
}
void ExtensionWebRequestTimeTracker::LogRequestStartTime(
- int64 request_id, const base::Time& start_time, const GURL& url) {
+ int64 request_id,
+ const base::Time& start_time,
+ const GURL& url,
+ void* profile) {
// Trim old completed request logs.
while (request_ids_.size() > kMaxRequestsLogged) {
int64 to_remove = request_ids_.front();
@@ -64,6 +133,7 @@ void ExtensionWebRequestTimeTracker::LogRequestStartTime(
RequestTimeLog& log = request_time_logs_[request_id];
log.request_start_time = start_time;
log.url = url;
+ log.profile = profile;
}
void ExtensionWebRequestTimeTracker::LogRequestEndTime(
@@ -86,6 +156,18 @@ void ExtensionWebRequestTimeTracker::LogRequestEndTime(
Analyze(request_id);
}
+std::set<std::string> ExtensionWebRequestTimeTracker::GetExtensionIds(
+ const RequestTimeLog& log) const {
+ std::set<std::string> result;
+ for (std::map<std::string, base::TimeDelta>::const_iterator i =
+ log.extension_block_durations.begin();
+ i != log.extension_block_durations.end();
+ ++i) {
+ result.insert(i->first);
+ }
+ return result;
+}
+
void ExtensionWebRequestTimeTracker::Analyze(int64 request_id) {
RequestTimeLog& log = request_time_logs_[request_id];
@@ -101,18 +183,31 @@ void ExtensionWebRequestTimeTracker::Analyze(int64 request_id) {
log.block_duration.InMilliseconds() << "/" <<
log.request_duration.InMilliseconds() << " = " << percentage;
- // TODO(mpcomplete): need actual UI for the warning.
// TODO(mpcomplete): blame a specific extension. Maybe go through the list
// of recent requests and find the extension that has caused the most delays.
if (percentage > kThresholdExcessiveDelay) {
excessive_delays_.insert(request_id);
if (excessive_delays_.size() > kNumExcessiveDelaysBeforeWarning) {
LOG(ERROR) << "WR excessive delays:" << excessive_delays_.size();
+ if (delegate_.get()) {
+ delegate_->NotifyExcessiveDelays(log.profile,
+ excessive_delays_.size(),
+ request_ids_.size(),
+ GetExtensionIds(log));
+ }
}
} else if (percentage > kThresholdModerateDelay) {
moderate_delays_.insert(request_id);
- if (moderate_delays_.size() > kNumModerateDelaysBeforeWarning) {
+ if (moderate_delays_.size() + excessive_delays_.size() >
+ kNumModerateDelaysBeforeWarning) {
LOG(ERROR) << "WR moderate delays:" << moderate_delays_.size();
+ if (delegate_.get()) {
+ delegate_->NotifyModerateDelays(
+ log.profile,
+ moderate_delays_.size() + excessive_delays_.size(),
+ request_ids_.size(),
+ GetExtensionIds(log));
+ }
}
}
}
@@ -152,3 +247,8 @@ void ExtensionWebRequestTimeTracker::SetRequestRedirected(int64 request_id) {
// down this request. Just ignore it.
request_time_logs_.erase(request_id);
}
+
+void ExtensionWebRequestTimeTracker::SetDelegate(
+ ExtensionWebRequestTimeTrackerDelegate* delegate) {
+ delegate_.reset(delegate);
+}
diff --git a/chrome/browser/extensions/extension_webrequest_time_tracker.h b/chrome/browser/extensions/extension_webrequest_time_tracker.h
index b9911b1..b114141 100644
--- a/chrome/browser/extensions/extension_webrequest_time_tracker.h
+++ b/chrome/browser/extensions/extension_webrequest_time_tracker.h
@@ -11,14 +11,35 @@
#include <set>
#include <string>
-#include "base/time.h"
#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
#include "googleurl/src/gurl.h"
namespace base {
class Time;
}
+class ExtensionWebRequestTimeTrackerDelegate {
+ public:
+ virtual ~ExtensionWebRequestTimeTrackerDelegate() {}
+
+ // Notifies the delegate that |num_delayed_messages| of the last
+ // |total_num_messages| inspected messages were excessively/moderately
+ // delayed. Every excessively delayed message is also counted as a moderately
+ // delayed message.
+ virtual void NotifyExcessiveDelays(
+ void* profile,
+ size_t num_delayed_messages,
+ size_t total_num_messages,
+ const std::set<std::string>& extension_ids) = 0;
+ virtual void NotifyModerateDelays(
+ void* profile,
+ size_t num_delayed_messages,
+ size_t total_num_messages,
+ const std::set<std::string>& extension_ids) = 0;
+};
+
// This class keeps monitors how much delay extensions add to network requests
// by using the webRequest API. If the delay is sufficient, we will warn the
// user that extensions are slowing down the browser.
@@ -29,7 +50,7 @@ class ExtensionWebRequestTimeTracker {
// Records the time that a request was created.
void LogRequestStartTime(int64 request_id, const base::Time& start_time,
- const GURL& url);
+ const GURL& url, void* profile);
// Records the time that a request either completed or encountered an error.
void LogRequestEndTime(int64 request_id, const base::Time& end_time);
@@ -53,10 +74,14 @@ class ExtensionWebRequestTimeTracker {
// Called when an extension has redirected the given request to another URL.
void SetRequestRedirected(int64 request_id);
+ // Takes ownership of |delegate|.
+ void SetDelegate(ExtensionWebRequestTimeTrackerDelegate* delegate);
+
private:
// Timing information for a single request.
struct RequestTimeLog {
GURL url; // used for debug purposes only
+ void* profile; // profile that created the request
bool completed;
base::Time request_start_time;
base::TimeDelta request_duration;
@@ -70,6 +95,9 @@ class ExtensionWebRequestTimeTracker {
// if necessary.
void Analyze(int64 request_id);
+ // Returns a list of all extension IDs that contributed to delay for |log|.
+ std::set<std::string> GetExtensionIds(const RequestTimeLog& log) const;
+
// A map of recent request IDs to timing info for each request.
std::map<int64, RequestTimeLog> request_time_logs_;
@@ -82,6 +110,9 @@ class ExtensionWebRequestTimeTracker {
std::set<int64> excessive_delays_;
std::set<int64> moderate_delays_;
+ // Defaults to a delegate that sets warnings in the extension service.
+ scoped_ptr<ExtensionWebRequestTimeTrackerDelegate> delegate_;
+
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTimeTrackerTest, Basic);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTimeTrackerTest,
IgnoreFastRequests);
diff --git a/chrome/browser/extensions/extension_webrequest_time_tracker_unittest.cc b/chrome/browser/extensions/extension_webrequest_time_tracker_unittest.cc
index 9439354..522572e 100644
--- a/chrome/browser/extensions/extension_webrequest_time_tracker_unittest.cc
+++ b/chrome/browser/extensions/extension_webrequest_time_tracker_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/extension_webrequest_time_tracker.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -11,6 +12,16 @@ const base::TimeDelta kRequestDelta = base::TimeDelta::FromMilliseconds(100);
const base::TimeDelta kTinyDelay = base::TimeDelta::FromMilliseconds(1);
const base::TimeDelta kModerateDelay = base::TimeDelta::FromMilliseconds(25);
const base::TimeDelta kExcessiveDelay = base::TimeDelta::FromMilliseconds(75);
+
+class ExtensionWebRequestTimeTrackerDelegateMock
+ : public ExtensionWebRequestTimeTrackerDelegate {
+ public:
+ MOCK_METHOD4(NotifyExcessiveDelays,
+ void (void*, size_t, size_t, const std::set<std::string>&));
+ MOCK_METHOD4(NotifyModerateDelays,
+ void (void*, size_t, size_t, const std::set<std::string>&));
+};
+
} // namespace
//class ExtensionWebRequestTimeTrackerTest : public testing::Test {};
@@ -18,8 +29,9 @@ const base::TimeDelta kExcessiveDelay = base::TimeDelta::FromMilliseconds(75);
TEST(ExtensionWebRequestTimeTrackerTest, Basic) {
ExtensionWebRequestTimeTracker tracker;
base::Time start;
+ void* profile = NULL;
- tracker.LogRequestStartTime(42, start, GURL());
+ tracker.LogRequestStartTime(42, start, GURL(), profile);
EXPECT_EQ(1u, tracker.request_time_logs_.size());
ASSERT_EQ(1u, tracker.request_ids_.size());
EXPECT_EQ(42, tracker.request_ids_.front());
@@ -32,14 +44,15 @@ TEST(ExtensionWebRequestTimeTrackerTest, Basic) {
TEST(ExtensionWebRequestTimeTrackerTest, CancelOrRedirect) {
ExtensionWebRequestTimeTracker tracker;
base::Time start;
+ void* profile = NULL;
- tracker.LogRequestStartTime(1, start, GURL());
+ tracker.LogRequestStartTime(1, start, GURL(), profile);
EXPECT_EQ(1u, tracker.request_time_logs_.size());
tracker.SetRequestCanceled(1);
tracker.LogRequestEndTime(1, start + kRequestDelta);
EXPECT_EQ(0u, tracker.request_time_logs_.size());
- tracker.LogRequestStartTime(2, start, GURL());
+ tracker.LogRequestStartTime(2, start, GURL(), profile);
EXPECT_EQ(1u, tracker.request_time_logs_.size());
tracker.SetRequestRedirected(2);
tracker.LogRequestEndTime(2, start + kRequestDelta);
@@ -51,11 +64,12 @@ TEST(ExtensionWebRequestTimeTrackerTest, Delays) {
base::Time start;
std::string extension1_id("1");
std::string extension2_id("2");
+ void* profile = NULL;
// Start 3 requests with different amounts of delay from 2 extensions.
- tracker.LogRequestStartTime(1, start, GURL());
- tracker.LogRequestStartTime(2, start, GURL());
- tracker.LogRequestStartTime(3, start, GURL());
+ tracker.LogRequestStartTime(1, start, GURL(), profile);
+ tracker.LogRequestStartTime(2, start, GURL(), profile);
+ tracker.LogRequestStartTime(3, start, GURL(), profile);
tracker.IncrementExtensionBlockTime(extension1_id, 1, kTinyDelay);
tracker.IncrementExtensionBlockTime(extension1_id, 2, kTinyDelay);
tracker.IncrementExtensionBlockTime(extension1_id, 3, kTinyDelay);
@@ -76,9 +90,59 @@ TEST(ExtensionWebRequestTimeTrackerTest, Delays) {
// Now issue a bunch more requests and ensure that the old delays are
// forgotten.
for (int64 i = 4; i < 500; ++i) {
- tracker.LogRequestStartTime(i, start, GURL());
+ tracker.LogRequestStartTime(i, start, GURL(), profile);
tracker.LogRequestEndTime(i, start + kRequestDelta);
}
EXPECT_EQ(0u, tracker.moderate_delays_.size());
EXPECT_EQ(0u, tracker.excessive_delays_.size());
}
+
+TEST(ExtensionWebRequestTimeTrackerTest, Delegate) {
+ using testing::_;
+ using testing::Mock;
+
+ ExtensionWebRequestTimeTrackerDelegateMock* delegate(
+ new ExtensionWebRequestTimeTrackerDelegateMock);
+ ExtensionWebRequestTimeTracker tracker;
+ tracker.SetDelegate(delegate);
+ base::Time start;
+ std::string extension1_id("1");
+ void* profile = NULL;
+ // Set of all extensions that blocked network requests.
+ std::set<std::string> extensions;
+ extensions.insert(extension1_id);
+
+ const int num_moderate_delays = 51;
+ const int num_excessive_delays = 11;
+ int request_nr = 0;
+
+ // Check that (only) the last moderate delay triggers the delegate callback.
+ for (int64 i = 0; i < num_moderate_delays; ++i) {
+ request_nr++;
+ if (i == num_moderate_delays-1) {
+ EXPECT_CALL(*delegate,
+ NotifyModerateDelays(profile , i+1, request_nr, extensions));
+ }
+ tracker.LogRequestStartTime(request_nr, start, GURL(), profile);
+ tracker.IncrementExtensionBlockTime(extension1_id, request_nr,
+ kModerateDelay);
+ tracker.IncrementTotalBlockTime(request_nr, kModerateDelay);
+ tracker.LogRequestEndTime(request_nr, start + kRequestDelta);
+ Mock::VerifyAndClearExpectations(delegate);
+ }
+
+ // Check that (only) the last excessive delay triggers the delegate callback.
+ for (int64 i = 0; i < num_excessive_delays; ++i) {
+ request_nr++;
+ if (i == num_excessive_delays-1) {
+ EXPECT_CALL(*delegate,
+ NotifyExcessiveDelays(profile, i+1, request_nr, extensions));
+ }
+ tracker.LogRequestStartTime(request_nr, start, GURL(), profile);
+ tracker.IncrementExtensionBlockTime(extension1_id, request_nr,
+ kExcessiveDelay);
+ tracker.IncrementTotalBlockTime(request_nr, kExcessiveDelay);
+ tracker.LogRequestEndTime(request_nr, start + kRequestDelta);
+ Mock::VerifyAndClearExpectations(delegate);
+ }
+}
diff --git a/chrome/browser/resources/options/extension_list.js b/chrome/browser/resources/options/extension_list.js
index 0e852eb..d4c7121 100644
--- a/chrome/browser/resources/options/extension_list.js
+++ b/chrome/browser/resources/options/extension_list.js
@@ -239,6 +239,27 @@ cr.define('options', function() {
vbox.appendChild(link);
}
+ if (extension.warnings.length > 0) {
+ var warningsDiv = this.ownerDocument.createElement('div');
+ warningsDiv.classList.add('extension-warnings');
+
+ var warningsHeader = this.ownerDocument.createElement('span');
+ warningsHeader.classList.add('extension-warnings-title');
+ warningsHeader.textContent =
+ localStrings.getString('extensionSettingsWarningsTitle');
+ warningsDiv.appendChild(warningsHeader);
+
+ var warningList = this.ownerDocument.createElement('ul');
+ for (var j = 0; j < extension.warnings.length; ++j) {
+ var warningEntry = this.ownerDocument.createElement('li');
+ warningEntry.textContent = extension.warnings[j];
+ warningList.appendChild(warningEntry);
+ }
+ warningsDiv.appendChild(warningList);
+
+ vbox.appendChild(warningsDiv);
+ }
+
// And now the details section that is normally hidden.
var details = this.ownerDocument.createElement('div');
details.classList.add('vbox');
@@ -711,4 +732,4 @@ cr.define('options', function() {
return {
ExtensionsList: ExtensionsList
};
-}); \ No newline at end of file
+});
diff --git a/chrome/browser/resources/options/extension_settings.css b/chrome/browser/resources/options/extension_settings.css
index 471d4814..f24a005 100644
--- a/chrome/browser/resources/options/extension_settings.css
+++ b/chrome/browser/resources/options/extension_settings.css
@@ -138,6 +138,22 @@ found in the LICENSE file.
-webkit-user-select: none;
}
+.extension-warnings-title {
+ color: red;
+}
+
+.extension-warnings {
+ margin-top: 6px;
+}
+
+.extension-warnings ul {
+ margin: 0;
+}
+
+.extension-warnings > * {
+ white-space: normal;
+}
+
.informative-text {
color: gray;
}
diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.cc b/chrome/browser/ui/webui/options/extension_settings_handler.cc
index 82234af..ccc5283 100644
--- a/chrome/browser/ui/webui/options/extension_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/extension_settings_handler.cc
@@ -18,6 +18,7 @@
#include "chrome/browser/extensions/extension_disabled_infobar_delegate.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_updater.h"
+#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/background_contents.h"
@@ -143,6 +144,8 @@ void ExtensionSettingsHandler::HandleRequestExtensionsData(
// Add the extensions to the results structure.
ListValue *extensions_list = new ListValue();
+ ExtensionWarningSet* warnings = extension_service_->extension_warnings();
+
const ExtensionList* extensions = extension_service_->extensions();
for (ExtensionList::const_iterator extension = extensions->begin();
extension != extensions->end(); ++extension) {
@@ -151,6 +154,7 @@ void ExtensionSettingsHandler::HandleRequestExtensionsData(
extension_service_,
*extension,
GetActivePagesForExtension(*extension),
+ warnings,
true, false)); // enabled, terminated
}
}
@@ -162,6 +166,7 @@ void ExtensionSettingsHandler::HandleRequestExtensionsData(
extension_service_,
*extension,
GetActivePagesForExtension(*extension),
+ warnings,
false, false)); // enabled, terminated
}
}
@@ -174,6 +179,7 @@ void ExtensionSettingsHandler::HandleRequestExtensionsData(
extension_service_,
*extension,
empty_pages, // Terminated process has no active pages.
+ warnings,
false, true)); // enabled, terminated
}
}
@@ -206,6 +212,8 @@ void ExtensionSettingsHandler::MaybeRegisterForNotifications() {
Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
Source<Profile>(profile));
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED,
+ Source<Profile>(profile));
registrar_.Add(this,
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
NotificationService::AllBrowserContextsAndSources());
@@ -558,6 +566,8 @@ void ExtensionSettingsHandler::GetLocalizedValues(
l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION));
localized_strings->SetString("extensionSettingsInDevelopment",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT));
+ localized_strings->SetString("extensionSettingsWarningsTitle",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE));
}
void ExtensionSettingsHandler::Initialize() {
@@ -625,6 +635,7 @@ void ExtensionSettingsHandler::Observe(int type,
case chrome::NOTIFICATION_EXTENSION_PROCESS_CREATED:
case chrome::NOTIFICATION_EXTENSION_UNLOADED:
case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
+ case chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED:
case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
MaybeUpdateAfterNotification();
break;
@@ -649,7 +660,9 @@ void ExtensionSettingsHandler::MaybeUpdateAfterNotification() {
// Static
DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
ExtensionService* service, const Extension* extension,
- const std::vector<ExtensionPage>& pages, bool enabled, bool terminated) {
+ const std::vector<ExtensionPage>& pages,
+ const ExtensionWarningSet* warnings_set,
+ bool enabled, bool terminated) {
DictionaryValue* extension_data = new DictionaryValue();
GURL icon =
ExtensionIconSource::GetIconURL(extension,
@@ -713,6 +726,22 @@ DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
extension->browser_action() || extension->page_action());
extension_data->SetString("homepageUrl", extension->GetHomepageURL().spec());
+ // Add warnings.
+ ListValue* warnings_list = new ListValue;
+ if (warnings_set) {
+ std::set<ExtensionWarningSet::WarningType> warnings;
+ warnings_set->GetWarningsAffectingExtension(extension->id(), &warnings);
+
+ for (std::set<ExtensionWarningSet::WarningType>::const_iterator iter =
+ warnings.begin();
+ iter != warnings.end();
+ ++iter) {
+ string16 warning_string(ExtensionWarningSet::GetLocalizedWarning(*iter));
+ warnings_list->Append(Value::CreateStringValue(warning_string));
+ }
+ }
+ extension_data->Set("warnings", warnings_list);
+
return extension_data;
}
diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.h b/chrome/browser/ui/webui/options/extension_settings_handler.h
index d326a1d..f0bfdf0 100644
--- a/chrome/browser/ui/webui/options/extension_settings_handler.h
+++ b/chrome/browser/ui/webui/options/extension_settings_handler.h
@@ -11,6 +11,7 @@
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_uninstall_dialog.h"
+#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/ui/shell_dialogs.h"
#include "chrome/browser/ui/webui/options/options_ui.h"
#include "chrome/browser/ui/webui/chrome_web_ui.h"
@@ -57,11 +58,12 @@ class ExtensionSettingsHandler : public OptionsPageUIHandler,
static void RegisterUserPrefs(PrefService* prefs);
// Extension Detail JSON Struct for page. (static for ease of testing).
- // Note: service can be NULL in unit tests.
+ // Note: |service| and |warnings| can be NULL in unit tests.
static base::DictionaryValue* CreateExtensionDetailValue(
ExtensionService* service,
const Extension* extension,
const std::vector<ExtensionPage>& pages,
+ const ExtensionWarningSet* warnings,
bool enabled,
bool terminated);
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 08a77e9..2d5768b 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1002,6 +1002,8 @@
'browser/extensions/extension_function_dispatcher.h',
'browser/extensions/extension_global_error.cc',
'browser/extensions/extension_global_error.h',
+ 'browser/extensions/extension_global_error_badge.cc',
+ 'browser/extensions/extension_global_error_badge.h',
'browser/extensions/extension_history_api.cc',
'browser/extensions/extension_history_api.h',
'browser/extensions/extension_history_api_constants.cc',
@@ -1161,6 +1163,8 @@
'browser/extensions/extension_uninstall_dialog.h',
'browser/extensions/extension_updater.cc',
'browser/extensions/extension_updater.h',
+ 'browser/extensions/extension_warning_set.cc',
+ 'browser/extensions/extension_warning_set.h',
'browser/extensions/extension_web_socket_proxy_private_api.cc',
'browser/extensions/extension_web_socket_proxy_private_api.h',
'browser/extensions/extension_web_ui.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 5de75e1..c35c027 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1299,6 +1299,7 @@
'browser/extensions/extension_sync_data_unittest.cc',
'browser/extensions/extension_ui_unittest.cc',
'browser/extensions/extension_updater_unittest.cc',
+ 'browser/extensions/extension_warning_set_unittest.cc',
'browser/extensions/extension_webnavigation_unittest.cc',
'browser/extensions/extension_webrequest_api_unittest.cc',
'browser/extensions/extension_webrequest_time_tracker_unittest.cc',
diff --git a/chrome/common/chrome_notification_types.h b/chrome/common/chrome_notification_types.h
index 35d3349..0fb69fd 100644
--- a/chrome/common/chrome_notification_types.h
+++ b/chrome/common/chrome_notification_types.h
@@ -515,6 +515,11 @@ enum NotificationType {
// (const std::string).
NOTIFICATION_EXTENSION_UPDATE_FOUND,
+ // Sent when one or more extensions changed their warning status (like
+ // slowing down Chrome or conflicting with each other).
+ // The source is a Profile.
+ NOTIFICATION_EXTENSION_WARNING_CHANGED,
+
// An installed app changed notification state (added or removed
// notifications). The source is a Profile, and the details are a string
// with the extension id of the app.