summaryrefslogtreecommitdiffstats
path: root/extensions/browser/api/runtime/runtime_api.cc
diff options
context:
space:
mode:
authorlazyboy <lazyboy@chromium.org>2015-12-09 15:39:35 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-09 23:40:51 +0000
commit90c1469d03da00e99c0b6ff6a9871c65fa2b3e4c (patch)
treef3572f3c54f5e913d0e0a9855ec874df97de6888 /extensions/browser/api/runtime/runtime_api.cc
parent84b9c43f19b18bbd7ce85b664fa8a98f4a53dbf6 (diff)
downloadchromium_src-90c1469d03da00e99c0b6ff6a9871c65fa2b3e4c.zip
chromium_src-90c1469d03da00e99c0b6ff6a9871c65fa2b3e4c.tar.gz
chromium_src-90c1469d03da00e99c0b6ff6a9871c65fa2b3e4c.tar.bz2
Dispatch runtime.onInstalled event if an extension is re-enabled after
it was disabled due to permission escalation. BUG=561660 Test=Provide an update of an extension that has more permissions than the previous one (e.g. "bookmarks"). Once the new version is installed, notice that there's a warning in chrome hotdog menu to accept new permissions. Upon accepting the permissions, observe chrome.runtime.onInstalled handler fires on the new version of the extension. See https://github.com/lazyboy/chromium/tree/master/tests/extensions/update_flow for detailed repro steps. Review URL: https://codereview.chromium.org/1499493003 Cr-Commit-Position: refs/heads/master@{#364215}
Diffstat (limited to 'extensions/browser/api/runtime/runtime_api.cc')
-rw-r--r--extensions/browser/api/runtime/runtime_api.cc82
1 files changed, 72 insertions, 10 deletions
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index a7e6ef4..1473a43 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -62,6 +62,15 @@ const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
// A preference key storing the url loaded when an extension is uninstalled.
const char kUninstallUrl[] = "uninstall_url";
+// A preference key storing the information about an extension that was
+// installed but not loaded. We keep the pending info here so that we can send
+// chrome.runtime.onInstalled event during the extension load.
+const char kPrefPendingOnInstalledEventDispatchInfo[] =
+ "pending_on_installed_event_dispatch_info";
+
+// Previously installed version number.
+const char kPrefPreviousVersion[] = "previous_version";
+
// The name of the directory to be returned by getPackageDirectoryEntry. This
// particular value does not matter to user code, but is chosen for consistency
// with the equivalent Pepper API.
@@ -182,6 +191,15 @@ void RuntimeAPI::Observe(int type,
void RuntimeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
const Extension* extension) {
+ base::Version previous_version;
+ if (ReadPendingOnInstallInfoFromPref(extension->id(), &previous_version)) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
+ browser_context_, extension->id(), previous_version, false));
+ RemovePendingOnInstallInfoFromPref(extension->id());
+ }
+
if (!dispatch_chrome_updated_event_)
return;
@@ -200,22 +218,19 @@ void RuntimeAPI::OnExtensionWillBeInstalled(
const Extension* extension,
bool is_update,
const std::string& old_name) {
- Version old_version = delegate_->GetPreviousExtensionVersion(extension);
-
- // Dispatch the onInstalled event.
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
- browser_context_,
- extension->id(),
- old_version,
- false));
+ // This extension might be disabled before it has a chance to load, e.g. if
+ // the extension increased its permissions. So instead of trying to send the
+ // onInstalled event here, we remember the fact in prefs and fire the event
+ // when the extension is actually loaded.
+ StorePendingOnInstallInfoToPref(extension);
}
void RuntimeAPI::OnExtensionUninstalled(
content::BrowserContext* browser_context,
const Extension* extension,
UninstallReason reason) {
+ RemovePendingOnInstallInfoFromPref(extension->id());
+
RuntimeEventRouter::OnExtensionUninstalled(
browser_context_, extension->id(), reason);
}
@@ -237,6 +252,53 @@ void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
}
+bool RuntimeAPI::ReadPendingOnInstallInfoFromPref(
+ const ExtensionId& extension_id,
+ base::Version* previous_version) {
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_);
+ DCHECK(prefs);
+
+ const base::DictionaryValue* info = nullptr;
+ if (!prefs->ReadPrefAsDictionary(
+ extension_id, kPrefPendingOnInstalledEventDispatchInfo, &info)) {
+ return false;
+ }
+
+ std::string previous_version_string;
+ info->GetString(kPrefPreviousVersion, &previous_version_string);
+ // |previous_version_string| can be empty.
+ *previous_version = base::Version(previous_version_string);
+ return true;
+}
+
+void RuntimeAPI::RemovePendingOnInstallInfoFromPref(
+ const ExtensionId& extension_id) {
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_);
+ DCHECK(prefs);
+
+ prefs->UpdateExtensionPref(extension_id,
+ kPrefPendingOnInstalledEventDispatchInfo, nullptr);
+}
+
+void RuntimeAPI::StorePendingOnInstallInfoToPref(const Extension* extension) {
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_);
+ DCHECK(prefs);
+
+ // |pending_on_install_info| currently only contains a version string. Instead
+ // of making the pref hold a plain string, we store it as a dictionary value
+ // so that we can add more stuff to it in the future if necessary.
+ scoped_ptr<base::DictionaryValue> pending_on_install_info(
+ new base::DictionaryValue());
+ base::Version previous_version =
+ delegate_->GetPreviousExtensionVersion(extension);
+ pending_on_install_info->SetString(
+ kPrefPreviousVersion,
+ previous_version.IsValid() ? previous_version.GetString() : "");
+ prefs->UpdateExtensionPref(extension->id(),
+ kPrefPendingOnInstalledEventDispatchInfo,
+ pending_on_install_info.release());
+}
+
void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
delegate_->ReloadExtension(extension_id);
}