summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-14 22:32:39 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-14 22:32:39 +0000
commit0c6da50c299be943b4c04a3953e0931734af7eaf (patch)
treeb5fa120b75591c1fa56a07232cf685ac1b2a8eb8 /chrome
parenta7918784c2ac95844ae775be2fef06cd3efb092c (diff)
downloadchromium_src-0c6da50c299be943b4c04a3953e0931734af7eaf.zip
chromium_src-0c6da50c299be943b4c04a3953e0931734af7eaf.tar.gz
chromium_src-0c6da50c299be943b4c04a3953e0931734af7eaf.tar.bz2
Disable an extension when it is upgraded to a version that requires more
permissions then before, and prompt the user to re-enable. Incidentally, this required adding support for disabling extensions. BUG=12140 TEST=covered by unit tests Review URL: http://codereview.chromium.org/165414 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23480 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd12
-rw-r--r--chrome/browser/browser.cc11
-rw-r--r--chrome/browser/browser_resources.grd2
-rw-r--r--chrome/browser/extensions/extension_browsertest.cc51
-rw-r--r--chrome/browser/extensions/extension_browsertest.h8
-rw-r--r--chrome/browser/extensions/extension_browsertests_misc.cc48
-rw-r--r--chrome/browser/extensions/extension_disabled_infobar_delegate.cc92
-rw-r--r--chrome/browser/extensions/extension_disabled_infobar_delegate.h19
-rw-r--r--chrome/browser/extensions/extension_prefs.cc38
-rw-r--r--chrome/browser/extensions/extension_prefs.h13
-rw-r--r--chrome/browser/extensions/extension_ui_unittest.cc5
-rw-r--r--chrome/browser/extensions/extensions_service.cc126
-rw-r--r--chrome/browser/extensions/extensions_service.h27
-rw-r--r--chrome/browser/extensions/extensions_ui.cc25
-rw-r--r--chrome/browser/extensions/extensions_ui.h6
-rw-r--r--chrome/browser/resources/extensions_ui.html22
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--chrome/common/extensions/extension.h4
-rw-r--r--chrome/common/notification_type.h4
-rwxr-xr-xchrome/test/data/extensions/permissions-high-v2.crxbin0 -> 914 bytes
-rwxr-xr-xchrome/test/data/extensions/permissions-low-v1.crxbin0 -> 852 bytes
-rwxr-xr-xchrome/test/data/extensions/permissions-low-v1.pem16
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json1
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json1
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json1
25 files changed, 460 insertions, 74 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3974b03..e2026c3 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2063,8 +2063,16 @@ each locale. -->
<message name="IDS_THEME_INSTALL_INFOBAR_UNDO_BUTTON" desc="Text displayed on the button to undo a theme installation and go back to the previous theme.">
Undo
</message>
-
- <!-- Extesion install prompt -->
+
+ <!-- Extension disabled info bar -->
+ <message name="IDS_EXTENSION_DISABLED_INFOBAR_LABEL" desc="Text displayed on an infobar when an extension was disabled due to a new upgrade requiring an explicit permission check from the user.">
+ The newest version of the extension "<ph name="EXTENSION_NAME">$1<ex>Flashblock</ex></ph>" requires more permissions, so it has been disabled.
+ </message>
+ <message name="IDS_EXTENSION_DISABLED_INFOBAR_ENABLE_BUTTON" desc="Text displayed on the button to re-enable the disabled extension.">
+ Reenable
+ </message>
+
+ <!-- Extension install prompt -->
<message name="IDS_EXTENSION_PROMPT_TITLE" desc="Titlebar of the extension installation prompt window">
Confirm Installation
</message>
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 8183090..5478464 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -23,6 +23,7 @@
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_shelf.h"
#include "chrome/browser/download/download_started_animation.h"
+#include "chrome/browser/extensions/extension_disabled_infobar_delegate.h"
#include "chrome/browser/find_bar.h"
#include "chrome/browser/find_bar_controller.h"
#include "chrome/browser/location_bar.h"
@@ -188,6 +189,8 @@ Browser::Browser(Type type, Profile* profile)
registrar_.Add(this, NotificationType::SSL_VISIBLE_STATE_CHANGED,
NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
+ NotificationService::AllSources());
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
NotificationService::AllSources());
registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
@@ -2079,6 +2082,14 @@ void Browser::Observe(NotificationType type,
UpdateToolbar(false);
break;
+ case NotificationType::EXTENSION_UPDATE_DISABLED: {
+ // Show the UI.
+ ExtensionsService* service = Source<ExtensionsService>(source).ptr();
+ Extension* extension = Details<Extension>(details).ptr();
+ ShowExtensionDisabledUI(service, profile_, extension);
+ break;
+ }
+
case NotificationType::EXTENSION_UNLOADED: {
// Close any tabs from the unloaded extension.
Extension* extension = Details<Extension>(details).ptr();
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 1165f13..385d5d7 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This comment is only here because changes to resources are not picked up
-without changes to the corresponding grd file. aa1 -->
+without changes to the corresponding grd file. mp6 -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/browser_resources.h" type="rc_header">
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index 7ef3239..04d496a 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -42,14 +42,15 @@ void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
bool ExtensionBrowserTest::LoadExtension(const FilePath& path) {
ExtensionsService* service = browser()->profile()->GetExtensionsService();
size_t num_before = service->extensions()->size();
- registrar_.Add(this, NotificationType::EXTENSIONS_LOADED,
- NotificationService::AllSources());
- service->LoadExtension(path);
- MessageLoop::current()->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
- kTimeoutMs);
- ui_test_utils::RunMessageLoop();
- registrar_.Remove(this, NotificationType::EXTENSIONS_LOADED,
- NotificationService::AllSources());
+ {
+ NotificationRegistrar registrar;
+ registrar.Add(this, NotificationType::EXTENSIONS_LOADED,
+ NotificationService::AllSources());
+ service->LoadExtension(path);
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, new MessageLoop::QuitTask, kTimeoutMs);
+ ui_test_utils::RunMessageLoop();
+ }
size_t num_after = service->extensions()->size();
if (num_after != (num_before + 1))
return false;
@@ -57,21 +58,26 @@ bool ExtensionBrowserTest::LoadExtension(const FilePath& path) {
return WaitForExtensionHostsToLoad();
}
-bool ExtensionBrowserTest::InstallExtension(const FilePath& path) {
+bool ExtensionBrowserTest::InstallExtension(const FilePath& path,
+ int expected_change) {
ExtensionsService* service = browser()->profile()->GetExtensionsService();
service->set_show_extensions_prompts(false);
size_t num_before = service->extensions()->size();
- registrar_.Add(this, NotificationType::EXTENSIONS_LOADED,
- NotificationService::AllSources());
- service->InstallExtension(path);
- MessageLoop::current()->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
- kTimeoutMs);
- ui_test_utils::RunMessageLoop();
- registrar_.Remove(this, NotificationType::EXTENSIONS_LOADED,
- NotificationService::AllSources());
+ {
+ NotificationRegistrar registrar;
+ registrar.Add(this, NotificationType::EXTENSIONS_LOADED,
+ NotificationService::AllSources());
+ registrar.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
+ NotificationService::AllSources());
+ service->InstallExtension(path);
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, new MessageLoop::QuitTask, kTimeoutMs);
+ ui_test_utils::RunMessageLoop();
+ }
+
size_t num_after = service->extensions()->size();
- if (num_after != (num_before + 1)) {
+ if (num_after != (num_before + expected_change)) {
std::cout << "Num extensions before: " << IntToString(num_before) << " "
<< "num after: " << IntToString(num_after) << " "
<< "Installed extensions follow:\n";
@@ -147,14 +153,19 @@ bool ExtensionBrowserTest::WaitForExtensionHostsToLoad() {
}
void ExtensionBrowserTest::Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details) {
+ const NotificationSource& source,
+ const NotificationDetails& details) {
switch (type.value) {
case NotificationType::EXTENSIONS_LOADED:
std::cout << "Got EXTENSION_LOADED notification.\n";
MessageLoopForUI::current()->Quit();
break;
+ case NotificationType::EXTENSION_UPDATE_DISABLED:
+ std::cout << "Got EXTENSIONS_DISABLED_LOAD notification.\n";
+ MessageLoopForUI::current()->Quit();
+ break;
+
default:
NOTREACHED();
break;
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 5990f5c..72b0de4 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -22,7 +22,11 @@ class ExtensionBrowserTest
protected:
virtual void SetUpCommandLine(CommandLine* command_line);
bool LoadExtension(const FilePath& path);
- bool InstallExtension(const FilePath& path);
+ // |expected_change| indicates how many extensions should be installed (or
+ // disabled, if negative).
+ // 1 means you expect a new install, 0 means you expect an upgrade, -1 means
+ // you expect a failed upgrade.
+ bool InstallExtension(const FilePath& path, int expected_change);
void UninstallExtension(const std::string& extension_id);
// Wait for the number of visible page actions to change to |count|.
@@ -37,8 +41,6 @@ class ExtensionBrowserTest
const NotificationSource& source,
const NotificationDetails& details);
bool WaitForExtensionHostsToLoad();
-
- NotificationRegistrar registrar_;
};
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_BROWSER_TEST_H_
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc
index ae3ece7..a630f52 100644
--- a/chrome/browser/extensions/extension_browsertests_misc.cc
+++ b/chrome/browser/extensions/extension_browsertests_misc.cc
@@ -151,7 +151,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, Incognito) {
Browser::OpenURLOffTheRecord(browser()->profile(),
GURL(chrome::kChromeUIExtensionsURL));
- ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx")));
+ ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx"), 1));
UninstallExtension("ldnnhddmnhbkjipkidpdiheffobcpfmf");
}
@@ -415,3 +415,49 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MessagingContentScript) {
host->render_view_host(), L"", L"testDisconnectOnClose()", &result);
EXPECT_TRUE(result);
}
+
+// Tests the process of updating an extension to one that requires higher
+// permissions.
+IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, UpdatePermissions) {
+ TabContents* contents = browser()->GetSelectedTabContents();
+ ASSERT_TRUE(contents);
+
+ // Install the initial version, which should happen just fine.
+ ASSERT_TRUE(InstallExtension(
+ test_data_dir_.AppendASCII("permissions-low-v1.crx"), 1));
+ DCHECK_EQ(0, contents->infobar_delegate_count());
+
+ // Upgrade to a version that wants more permissions. We should disable the
+ // extension and prompt the user to reenable.
+ ASSERT_TRUE(InstallExtension(
+ test_data_dir_.AppendASCII("permissions-high-v2.crx"), -1));
+ EXPECT_EQ(1, contents->infobar_delegate_count());
+
+ ExtensionsService* service = browser()->profile()->GetExtensionsService();
+ EXPECT_EQ(0u, service->extensions()->size());
+ ASSERT_EQ(1u, service->disabled_extensions()->size());
+
+ // Now try reenabling it, which should also dismiss the infobar.
+ service->EnableExtension(service->disabled_extensions()->at(0)->id());
+ EXPECT_EQ(0, contents->infobar_delegate_count());
+ EXPECT_EQ(1u, service->extensions()->size());
+ EXPECT_EQ(0u, service->disabled_extensions()->size());
+}
+
+// Tests that we can uninstall a disabled extension.
+IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, UninstallDisabled) {
+ // Install and upgrade, so that we have a disabled extension.
+ ASSERT_TRUE(InstallExtension(
+ test_data_dir_.AppendASCII("permissions-low-v1.crx"), 1));
+ ASSERT_TRUE(InstallExtension(
+ test_data_dir_.AppendASCII("permissions-high-v2.crx"), -1));
+
+ ExtensionsService* service = browser()->profile()->GetExtensionsService();
+ EXPECT_EQ(0u, service->extensions()->size());
+ ASSERT_EQ(1u, service->disabled_extensions()->size());
+
+ // Now try uninstalling it.
+ UninstallExtension(service->disabled_extensions()->at(0)->id());
+ EXPECT_EQ(0u, service->extensions()->size());
+ EXPECT_EQ(0u, service->disabled_extensions()->size());
+}
diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
new file mode 100644
index 0000000..f812392
--- /dev/null
+++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2009 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_disabled_infobar_delegate.h"
+
+#include "app/l10n_util.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/tab_contents/infobar_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "grit/generated_resources.h"
+
+class ExtensionDisabledInfobarDelegate
+ : public ConfirmInfoBarDelegate,
+ public NotificationObserver {
+ public:
+ ExtensionDisabledInfobarDelegate(TabContents* tab_contents,
+ ExtensionsService* service,
+ const std::string& extension_id,
+ const std::string& extension_name)
+ : ConfirmInfoBarDelegate(tab_contents),
+ tab_contents_(tab_contents),
+ service_(service),
+ extension_id_(extension_id),
+ extension_name_(extension_name) {
+ // The user might re-enable the extension in other ways, so watch for that.
+ registrar_.Add(this, NotificationType::EXTENSIONS_LOADED,
+ Source<ExtensionsService>(service));
+ }
+ virtual void InfoBarClosed() {
+ delete this;
+ }
+ virtual std::wstring GetMessageText() const {
+ return l10n_util::GetStringF(IDS_EXTENSION_DISABLED_INFOBAR_LABEL,
+ UTF8ToWide(extension_name_));
+ }
+ virtual SkBitmap* GetIcon() const {
+ return NULL;
+ }
+ virtual int GetButtons() const {
+ return BUTTON_OK;
+ }
+ virtual std::wstring GetButtonLabel(
+ ConfirmInfoBarDelegate::InfoBarButton button) const {
+ return l10n_util::GetString(IDS_EXTENSION_DISABLED_INFOBAR_ENABLE_BUTTON);
+ }
+ virtual bool Accept() {
+ service_->EnableExtension(extension_id_);
+ return true;
+ }
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::EXTENSIONS_LOADED);
+ ExtensionList* extensions = Details<ExtensionList>(details).ptr();
+
+ for (ExtensionList::iterator iter = extensions->begin();
+ iter != extensions->end(); ++iter) {
+ if ((*iter)->id() == extension_id_) {
+ // TODO(mpcomplete): This doesn't seem to always result in us getting
+ // deleted.
+ tab_contents_->RemoveInfoBar(this);
+ return;
+ }
+ }
+ }
+
+ private:
+ NotificationRegistrar registrar_;
+ TabContents* tab_contents_;
+ ExtensionsService* service_;
+ std::string extension_id_;
+ std::string extension_name_;
+};
+
+void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile,
+ Extension* extension) {
+ Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
+ if (!browser)
+ return;
+
+ TabContents* tab_contents = browser->GetSelectedTabContents();
+ if (!tab_contents)
+ return;
+
+ tab_contents->AddInfoBar(new ExtensionDisabledInfobarDelegate(
+ tab_contents, service, extension->id(), extension->name()));
+}
diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.h b/chrome/browser/extensions/extension_disabled_infobar_delegate.h
new file mode 100644
index 0000000..0c66ce9
--- /dev/null
+++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 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_DISABLED_INFOBAR_DELEGATE_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DISABLED_INFOBAR_DELEGATE_H_
+
+#include <string>
+
+class Extension;
+class ExtensionsService;
+class Profile;
+
+// Shows UI to inform the user that an extension was disabled after upgrading
+// to higher permissions.
+void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile,
+ Extension* extension);
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DISABLED_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index bc88831..84ee99d 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -299,7 +299,7 @@ void ExtensionPrefs::SetShelfToolstripOrder(const URLList& urls) {
}
void ExtensionPrefs::OnExtensionInstalled(Extension* extension) {
- std::string id = extension->id();
+ const std::string& id = extension->id();
UpdateExtensionPref(id, kPrefState,
Value::CreateIntegerValue(Extension::ENABLED));
UpdateExtensionPref(id, kPrefLocation,
@@ -326,12 +326,37 @@ void ExtensionPrefs::OnExtensionUninstalled(const Extension* extension,
}
}
+Extension::State ExtensionPrefs::GetExtensionState(
+ const std::string& extension_id) {
+ DictionaryValue* extension = GetExtensionPref(extension_id);
+
+ // If the extension doesn't have a pref, it's a --load-extension.
+ if (!extension)
+ return Extension::ENABLED;
+
+ int state = -1;
+ if (!extension->GetInteger(kPrefState, &state) ||
+ state < 0 || state >= Extension::NUM_STATES) {
+ LOG(ERROR) << "Bad or missing pref 'state' for extension '"
+ << extension_id << "'";
+ return Extension::ENABLED;
+ }
+ return static_cast<Extension::State>(state);
+}
+
+void ExtensionPrefs::SetExtensionState(Extension* extension,
+ Extension::State state) {
+ UpdateExtensionPref(extension->id(), kPrefState,
+ Value::CreateIntegerValue(state));
+ prefs_->SavePersistentPrefs();
+}
+
bool ExtensionPrefs::UpdateExtensionPref(const std::string& extension_id,
const std::wstring& key,
Value* data_value) {
DictionaryValue* extension = GetOrCreateExtensionPref(extension_id);
if (!extension->Set(key, data_value)) {
- NOTREACHED() << L"Cannot modify key: '" << key.c_str()
+ NOTREACHED() << "Cannot modify key: '" << key.c_str()
<< "' for extension: '" << extension_id.c_str() << "'";
return false;
}
@@ -359,3 +384,12 @@ DictionaryValue* ExtensionPrefs::GetOrCreateExtensionPref(
}
return extension;
}
+
+DictionaryValue* ExtensionPrefs::GetExtensionPref(
+ const std::string& extension_id) {
+ const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref);
+ DictionaryValue* extension = NULL;
+ std::wstring id = ASCIIToWide(extension_id);
+ dict->GetDictionary(id, &extension);
+ return extension;
+}
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index c819bcd..291556c 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -43,6 +43,12 @@ class ExtensionPrefs {
void OnExtensionUninstalled(const Extension* extension,
bool external_uninstall);
+ // Returns the state (enabled/disabled) of the given extension.
+ Extension::State GetExtensionState(const std::string& extension_id);
+
+ // Called to change the extension's state when it is enabled/disabled.
+ void SetExtensionState(Extension* extension, Extension::State);
+
// Returns base extensions install directory.
const FilePath& install_directory() const { return install_directory_; }
@@ -73,6 +79,9 @@ class ExtensionPrefs {
// Ensures and returns a mutable dictionary for extension |id|'s prefs.
DictionaryValue* GetOrCreateExtensionPref(const std::string& id);
+ // Same as above, but returns NULL if it doesn't exist.
+ DictionaryValue* GetExtensionPref(const std::string& id);
+
// Checks if kPrefBlacklist is set to true in the DictionaryValue.
// Return false if the value is false or kPrefBlacklist does not exist.
// This is used to decide if an extension is blacklisted.
@@ -106,6 +115,10 @@ class InstalledExtensions {
// the callback.
void VisitInstalledExtensions(Callback *callback);
+ // Same as above, but only for the given extension.
+ void VisitInstalledExtension(const std::string& extension_id,
+ Callback *callback);
+
private:
// A copy of the extensions pref dictionary so that this can be passed
// around without a dependency on prefs.
diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc
index 022382b..f193b6a 100644
--- a/chrome/browser/extensions/extension_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_ui_unittest.cc
@@ -44,13 +44,14 @@ namespace {
EXPECT_TRUE(extension.InitFromValue(*extension_data, true, &error));
EXPECT_EQ("", error);
- scoped_ptr<DictionaryValue>expected_output_data(DeserializeJSONTestData(
+ scoped_ptr<DictionaryValue> expected_output_data(DeserializeJSONTestData(
expected_output_path, &error));
EXPECT_EQ("", error);
// Produce test output.
scoped_ptr<DictionaryValue> actual_output_data(
- ExtensionsDOMHandler::CreateExtensionDetailValue(&extension, pages));
+ ExtensionsDOMHandler::CreateExtensionDetailValue(&extension, pages,
+ true));
// Compare the outputs.
return expected_output_data->Equals(actual_output_data.get());
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 70fa3db..f70d7cf 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -118,7 +118,7 @@ void ExtensionsService::InstallExtension(const FilePath& extension_path) {
void ExtensionsService::UpdateExtension(const std::string& id,
const FilePath& extension_path) {
- if (!GetExtensionById(id)) {
+ if (!GetExtensionByIdInternal(id, true, true)) {
LOG(WARNING) << "Will not update extension " << id << " because it is not "
<< "installed";
return;
@@ -142,7 +142,7 @@ void ExtensionsService::ReloadExtension(const std::string& extension_id) {
void ExtensionsService::UninstallExtension(const std::string& extension_id,
bool external_uninstall) {
- Extension* extension = GetExtensionById(extension_id);
+ Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
// Callers should not send us nonexistant extensions.
DCHECK(extension);
@@ -159,6 +159,29 @@ void ExtensionsService::UninstallExtension(const std::string& extension_id,
UnloadExtension(extension_id);
}
+void ExtensionsService::EnableExtension(const std::string& extension_id) {
+ Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
+ if (!extension) {
+ NOTREACHED() << "Trying to enable an extension that isn't disabled.";
+ return;
+ }
+
+ // Move it over to the enabled list.
+ extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
+ extensions_.push_back(extension);
+ ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
+ disabled_extensions_.end(),
+ extension);
+ disabled_extensions_.erase(iter);
+
+ ExtensionList extensions;
+ extensions.push_back(extension);
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSIONS_LOADED,
+ Source<ExtensionsService>(this),
+ Details<ExtensionList>(&extensions));
+}
+
void ExtensionsService::LoadExtension(const FilePath& extension_path) {
backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
&ExtensionsServiceBackend::LoadSingleExtension,
@@ -213,17 +236,22 @@ void ExtensionsService::CheckForExternalUpdates() {
}
void ExtensionsService::UnloadExtension(const std::string& extension_id) {
- Extension* extension = NULL;
- ExtensionList::iterator iter;
- for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
- if ((*iter)->id() == extension_id) {
- extension = *iter;
- break;
- }
- }
+ scoped_ptr<Extension> extension(
+ GetExtensionByIdInternal(extension_id, true, true));
// Callers should not send us nonexistant extensions.
- CHECK(extension);
+ CHECK(extension.get());
+
+ ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
+ disabled_extensions_.end(),
+ extension.get());
+ if (iter != disabled_extensions_.end()) {
+ // It's disabled, so don't send the unload notification.
+ disabled_extensions_.erase(iter);
+ return;
+ }
+
+ iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
// Remove the extension from our list.
extensions_.erase(iter);
@@ -231,9 +259,7 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) {
// Tell other services the extension is gone.
NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
Source<ExtensionsService>(this),
- Details<Extension>(extension));
-
- delete extension;
+ Details<Extension>(extension.get()));
}
void ExtensionsService::UnloadAllExtensions() {
@@ -277,32 +303,56 @@ void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
// - --load-extension
// - externally installed extensions
ExtensionList enabled_extensions;
+ ExtensionList disabled_extensions;
for (ExtensionList::iterator iter = new_extensions->begin();
iter != new_extensions->end(); ++iter) {
+ // Extensions that get enabled get added to extensions_ and deleted later.
+ // Anything skipped must be deleted now so we don't leak.
+ scoped_ptr<Extension> extension(*iter);
if (extensions_enabled() ||
- (*iter)->IsTheme() ||
- (*iter)->location() == Extension::LOAD ||
- Extension::IsExternalLocation((*iter)->location())) {
- Extension* old = GetExtensionById((*iter)->id());
+ extension->IsTheme() ||
+ extension->location() == Extension::LOAD ||
+ Extension::IsExternalLocation(extension->location())) {
+ Extension* old = GetExtensionById(extension->id());
if (old) {
- if ((*iter)->version()->CompareTo(*(old->version())) > 0) {
+ if (extension->version()->CompareTo(*(old->version())) > 0) {
+ bool higher_permissions =
+ (extension->GetPermissionClass() > old->GetPermissionClass());
+
// To upgrade an extension in place, unload the old one and
// then load the new one.
- // TODO(erikkay) issue 12399
UnloadExtension(old->id());
+ old = NULL;
+
+ if (higher_permissions) {
+ // Extension was upgraded to a high permission class. Disable it and
+ // notify the user.
+ extension_prefs_->SetExtensionState(extension.get(),
+ Extension::DISABLED);
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_UPDATE_DISABLED,
+ Source<ExtensionsService>(this),
+ Details<Extension>(extension.get()));
+ }
} else {
// We already have the extension of the same or older version.
LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
- delete *iter;
continue;
}
}
- enabled_extensions.push_back(*iter);
- extensions_.push_back(*iter);
- } else {
- // Extensions that get enabled get added to extensions_ and deleted later.
- // Anything skipped must be deleted now so we don't leak.
- delete *iter;
+
+ switch (extension_prefs_->GetExtensionState(extension->id())) {
+ case Extension::ENABLED:
+ enabled_extensions.push_back(extension.get());
+ extensions_.push_back(extension.release());
+ break;
+ case Extension::DISABLED:
+ disabled_extensions.push_back(extension.get());
+ disabled_extensions_.push_back(extension.release());
+ break;
+ default:
+ break;
+ }
}
}
@@ -346,7 +396,6 @@ void ExtensionsService::OnExtensionInstalled(Extension* extension) {
OnExtensionsLoaded(list);
}
-
void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
Extension* extension = GetExtensionById(id);
if (extension && extension->IsTheme()) {
@@ -357,12 +406,23 @@ void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
}
}
-Extension* ExtensionsService::GetExtensionById(const std::string& id) {
+Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
+ bool include_enabled,
+ bool include_disabled) {
std::string lowercase_id = StringToLowerASCII(id);
- for (ExtensionList::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
- if ((*iter)->id() == lowercase_id)
- return *iter;
+ if (include_enabled) {
+ for (ExtensionList::const_iterator iter = extensions_.begin();
+ iter != extensions_.end(); ++iter) {
+ if ((*iter)->id() == lowercase_id)
+ return *iter;
+ }
+ }
+ if (include_disabled) {
+ for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
+ iter != disabled_extensions_.end(); ++iter) {
+ if ((*iter)->id() == lowercase_id)
+ return *iter;
+ }
}
return NULL;
}
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index fe0a545..fafe97e 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -87,8 +87,9 @@ class ExtensionsService
virtual ~ExtensionsService();
// Gets the list of currently installed extensions.
- virtual const ExtensionList* extensions() const {
- return &extensions_;
+ virtual const ExtensionList* extensions() const { return &extensions_; }
+ virtual const ExtensionList* disabled_extensions() const {
+ return &disabled_extensions_;
}
const FilePath& install_directory() const { return install_directory_; }
@@ -96,6 +97,11 @@ class ExtensionsService
// Initialize and start all installed extensions.
void Init();
+ // Look up an extension by ID.
+ Extension* GetExtensionById(const std::string& id) {
+ return GetExtensionByIdInternal(id, true, false);
+ }
+
// Install the extension file at |extension_path|. Will install as an
// update if an older version is already installed.
// For fresh installs, this method also causes the extension to be
@@ -123,6 +129,11 @@ class ExtensionsService
void UninstallExtension(const std::string& extension_id,
bool external_uninstall);
+ // Enable a previously disabled extension and reload it. The extension should
+ // already exist in the extension prefs.
+ // TODO(mpcomplete): add DisableExtension.
+ void EnableExtension(const std::string& extension_id);
+
// Load the extension from the directory |extension_path|.
void LoadExtension(const FilePath& extension_path);
@@ -144,9 +155,6 @@ class ExtensionsService
// Scan the extension directory and clean up the cruft.
void GarbageCollectExtensions();
- // Lookup an extension by |id|.
- virtual Extension* GetExtensionById(const std::string& id);
-
// Lookup an extension by |url|. This uses the host of the URL as the id.
Extension* GetExtensionByURL(const GURL& url);
@@ -202,6 +210,12 @@ class ExtensionsService
bool is_ready() { return ready_; }
private:
+ // Look up an extension by ID, optionally including either or both of enabled
+ // and disabled extensions.
+ Extension* GetExtensionByIdInternal(const std::string& id,
+ bool include_enabled,
+ bool include_disabled);
+
// The profile this ExtensionsService is part of.
Profile* profile_;
@@ -214,6 +228,9 @@ class ExtensionsService
// The current list of installed extensions.
ExtensionList extensions_;
+ // The list of installed extensions that have been disabled.
+ ExtensionList disabled_extensions_;
+
// The full path to the directory where extensions are installed.
FilePath install_directory_;
diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc
index 849fc8f..126fa05 100644
--- a/chrome/browser/extensions/extensions_ui.cc
+++ b/chrome/browser/extensions/extensions_ui.cc
@@ -77,6 +77,8 @@ void ExtensionsDOMHandler::RegisterMessages() {
NewCallback(this, &ExtensionsDOMHandler::HandleInspectMessage));
dom_ui_->RegisterMessageCallback("reload",
NewCallback(this, &ExtensionsDOMHandler::HandleReloadMessage));
+ dom_ui_->RegisterMessageCallback("enable",
+ NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage));
dom_ui_->RegisterMessageCallback("uninstall",
NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage));
}
@@ -93,7 +95,15 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const Value* value) {
// themes.
if (!(*extension)->IsTheme()) {
extensions_list->Append(CreateExtensionDetailValue(
- *extension, GetActivePagesForExtension((*extension)->id())));
+ *extension, GetActivePagesForExtension((*extension)->id()), true));
+ }
+ }
+ extensions = extensions_service_->disabled_extensions();
+ for (ExtensionList::const_iterator extension = extensions->begin();
+ extension != extensions->end(); ++extension) {
+ if (!(*extension)->IsTheme()) {
+ extensions_list->Append(CreateExtensionDetailValue(
+ *extension, GetActivePagesForExtension((*extension)->id()), false));
}
}
results.Set(L"extensions", extensions_list);
@@ -132,6 +142,15 @@ void ExtensionsDOMHandler::HandleReloadMessage(const Value* value) {
extensions_service_->ReloadExtension(extension_id);
}
+void ExtensionsDOMHandler::HandleEnableMessage(const Value* value) {
+ CHECK(value->IsType(Value::TYPE_LIST));
+ const ListValue* list = static_cast<const ListValue*>(value);
+ CHECK(list->GetSize() == 1);
+ std::string extension_id;
+ CHECK(list->GetString(0, &extension_id));
+ extensions_service_->EnableExtension(extension_id);
+}
+
void ExtensionsDOMHandler::HandleUninstallMessage(const Value* value) {
CHECK(value->IsType(Value::TYPE_LIST));
const ListValue* list = static_cast<const ListValue*>(value);
@@ -187,13 +206,15 @@ DictionaryValue* ExtensionsDOMHandler::CreateContentScriptDetailValue(
// Static
DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
- const Extension *extension, const std::vector<ExtensionPage>& pages) {
+ const Extension *extension, const std::vector<ExtensionPage>& pages,
+ bool enabled) {
DictionaryValue* extension_data = new DictionaryValue();
extension_data->SetString(L"id", extension->id());
extension_data->SetString(L"name", extension->name());
extension_data->SetString(L"description", extension->description());
extension_data->SetString(L"version", extension->version()->GetString());
+ extension_data->SetBoolean(L"enabled", enabled);
// Add list of content_script detail DictionaryValues
ListValue *content_script_list = new ListValue();
diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h
index c92c2ca..b63b12b 100644
--- a/chrome/browser/extensions/extensions_ui.h
+++ b/chrome/browser/extensions/extensions_ui.h
@@ -57,7 +57,8 @@ class ExtensionsDOMHandler : public DOMMessageHandler {
// Extension Detail JSON Struct for page. (static for ease of testing).
static DictionaryValue* CreateExtensionDetailValue(
const Extension *extension,
- const std::vector<ExtensionPage>&);
+ const std::vector<ExtensionPage>&,
+ bool enabled);
// ContentScript JSON Struct for page. (static for ease of testing).
static DictionaryValue* CreateContentScriptDetailValue(
@@ -74,6 +75,9 @@ class ExtensionsDOMHandler : public DOMMessageHandler {
// Callback for "reload" message.
void HandleReloadMessage(const Value* value);
+ // Callback for "enable" message.
+ void HandleEnableMessage(const Value* value);
+
// Callback for "uninstall" message.
void HandleUninstallMessage(const Value* value);
diff --git a/chrome/browser/resources/extensions_ui.html b/chrome/browser/resources/extensions_ui.html
index 8cd383c..566a989 100644
--- a/chrome/browser/resources/extensions_ui.html
+++ b/chrome/browser/resources/extensions_ui.html
@@ -15,6 +15,7 @@ var extensionDataFormat = {
"name": "Extension Name",
"description": "Extension long format description",
"version": "1.0.231",
+ "enabled": "true",
"content_scripts": [
{
"js": ["script1_file1.js", "script1_file2.js"],
@@ -49,6 +50,7 @@ var extensionDataFormat = {
"name": "Extension Name",
"description": "Extension long format description",
"version": "1.0.231",
+ "enabled": "true",
"content_scripts": [
{
"js": ["script1_file1.js", "script1_file2.js"],
@@ -129,6 +131,15 @@ function handleReloadExtension(node) {
}
/**
+ * Handles a 'reenable' button getting clicked.
+ */
+function handleEnableExtension(node) {
+ // Tell the C++ ExtensionDOMHandler to reload the extension.
+ chrome.send('enable', [node.extensionId]);
+ requestExtensionsData();
+}
+
+/**
* Handles an 'uninstall' button getting clicked.
*/
function handleUninstallExtension(node) {
@@ -247,10 +258,19 @@ th.desc {
<div jsdisplay="extensions.length > 0">
<div class="extension" jsselect="extensions">
- <div class="extension-name" jscontent="name">Extension Name</div>
+ <div class="extension-name">
+ <span jscontent="name">Extension Name</span>
+ <span jsdisplay="!enabled">(Disabled)</span>
+ </div>
<div class="extension-actions">
<button
jsvalues=".extensionId:id"
+ jsdisplay="!enabled"
+ onclick="handleEnableExtension(this)"
+ >Enable</button>
+ <button
+ jsvalues=".extensionId:id"
+ jsdisplay="enabled"
onclick="handleReloadExtension(this)"
>Reload</button>
<button
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 74839ad..2f17cf7 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1001,6 +1001,8 @@
'browser/extensions/extension_bookmarks_module_constants.h',
'browser/extensions/extension_creator.cc',
'browser/extensions/extension_creator.h',
+ 'browser/extensions/extension_disabled_infobar_delegate.cc',
+ 'browser/extensions/extension_disabled_infobar_delegate.h',
'browser/extensions/extension_dom_ui.cc',
'browser/extensions/extension_dom_ui.h',
'browser/extensions/extension_event_names.cc',
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 5bcc456..b9761d0 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -33,9 +33,11 @@ class Extension {
};
enum State {
- DISABLED,
+ DISABLED = 0,
ENABLED,
KILLBIT, // Don't install/upgrade (applies to external extensions only).
+
+ NUM_STATES
};
enum InstallType {
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 2a04ffe..8ba2d84 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -603,6 +603,10 @@ class NotificationType {
// Sent when new extensions are loaded. The details are an ExtensionList*.
EXTENSIONS_LOADED,
+ // Sent when attempting to load a new extension, but they are disabled. The
+ // details are an Extension*.
+ EXTENSION_UPDATE_DISABLED,
+
// Sent when a new theme is installed. The details are an Extension.
THEME_INSTALLED,
diff --git a/chrome/test/data/extensions/permissions-high-v2.crx b/chrome/test/data/extensions/permissions-high-v2.crx
new file mode 100755
index 0000000..3dd4959
--- /dev/null
+++ b/chrome/test/data/extensions/permissions-high-v2.crx
Binary files differ
diff --git a/chrome/test/data/extensions/permissions-low-v1.crx b/chrome/test/data/extensions/permissions-low-v1.crx
new file mode 100755
index 0000000..fbea4d0
--- /dev/null
+++ b/chrome/test/data/extensions/permissions-low-v1.crx
Binary files differ
diff --git a/chrome/test/data/extensions/permissions-low-v1.pem b/chrome/test/data/extensions/permissions-low-v1.pem
new file mode 100755
index 0000000..c875aca
--- /dev/null
+++ b/chrome/test/data/extensions/permissions-low-v1.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIoqnmSYqIbYzFW66
+7SBmmaeWjYL4C6ik1palszZSI+ZmAmOhsWuOH7i8y1DHwUUSjzEGFlgH2RIe0xFie
+Kjj/4KX0qQXbfNOpvhlk3OZGLVDgAFZoo7d/IMjR40w9myLaBTYAEO1PlKSkSyTBD
+ubjo6NrO+/PKSveqRk0MyioU/AgMBAAECgYAZI/ogSdrYdphvvQxokvCaXZQCo5SO
+R8zSMwufiKX4YzVT/9gsHjBvfjJLeRwq229KsU/Q63mq0LmGvlyBnct0ZNLiQcYx7
+W+aXbGcAbaUOEfWOh50iIFiP/YkDTrHrbdaE6HtgZyeiKC/o1QTcC4rxjWvXDeSAA
+LHYtIPoHlhEQJBAL1qExbv/KHEsFzoy3+EozIwjcpl2yGJmmlULofroXHbReT4OPd
+E9A7WNjhV6f+wxBxO3L6Bhno2dfOqTUzPWrcCQQC6vJrrTdY6DBFm7KFB36SGl1KL
+nra5HUaKwzVJTaug4cK4gtliHdTnUO4UjiAkskGe316SP3r116ZNHkE3IsG5AkBNc
+JLBa/iTgsDAG4Una2j1Whh+SUpf2cxBh+NGOrXUwNtAk6NmpNBLSJT+T1HN8c0b7b
+oeQQJj8OQkbNoRryzdAkEAujoJjYysjmsml6x5DUbJv2f97Du2Ilpt1UjkRVxuQx2
+ioXvs6wqxHpb5OAqdR7t18cj1eYhRSgdsVBBOHXxOEQJATrAOD8w1CjUzXRJjRacs
+ub+1+ED6jYYQz4fo0pkHAyTG7MgL0WP9WrvsCJJFdOcdMgGLasNvu9wr5XYuecZ3M
+A==
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
index 6c50242..91d1ca8 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
@@ -2,6 +2,7 @@
"id": "behllobkkfkfnphdnhnkndlbkcpglgmj",
"version": "1.0.0.0",
"name": "My extension 1",
+ "enabled": true,
"description": "The first extension that I made.",
"permissions": ["http://*.google.com/*", "https://*.google.com/*"],
"content_scripts": [
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
index 63ea47a..807b5d0 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
@@ -2,6 +2,7 @@
"id": "hpiknbiabeeppbpihjehijgoemciehgk",
"version": "2",
"name": "My extension 2",
+ "enabled": true,
"description": "",
"permissions": [],
"content_scripts": [],
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
index a444d4b..fef5a50 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
@@ -2,6 +2,7 @@
"id": "bjafgdebaacbbbecmhlhpofkepfkgcpa",
"version": "1.0",
"name": "My extension 3",
+ "enabled": true,
"description": "",
"permissions": [],
"content_scripts": [