Make ungranted extension permissions not be an error, but show a warning
in the extension developer mode.

For example, the following permissions wouldn't be granted:
 - foobar
   It's not a real permission.
 - http://google.com:80
   It's a malformed URL pattern.
 - storage
   (If manifest_version == 1) because manifest_version must be >= 2.
 - storage
   (If on stable/beta channel) because it's only supported on dev channel.


BUG=121826
TEST=unit_tests --gtest_fitler='*ExtensionManifestTest*'
     Fake beta channel and try installing stylizr


Review URL: http://codereview.chromium.org/10067028

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133144 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 7f30fea..8dab806 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/common/extensions/extension.h"
 
-#include <algorithm>
-
 #include "base/base64.h"
 #include "base/basictypes.h"
 #include "base/command_line.h"
@@ -20,6 +18,7 @@
 #include "base/string_piece.h"
 #include "base/string_split.h"
 #include "base/string_util.h"
+#include "base/stringprintf.h"
 #include "base/utf_string_conversions.h"
 #include "base/values.h"
 #include "base/version.h"
@@ -3321,14 +3320,11 @@
                 extensions::Feature::ConvertLocation(location()),
                 manifest_version());
         if (availability != extensions::Feature::IS_AVAILABLE) {
-          // We special case hosted apps because some old versions of Chrome did
-          // not return errors here and we ended up with extensions in the store
-          // containing bad data: crbug.com/101993.
-          if (availability != extensions::Feature::INVALID_TYPE ||
-              !is_hosted_app()) {
-            *error = ASCIIToUTF16(feature->GetErrorMessage(availability));
-            return false;
-          }
+          // Don't fail, but warn the developer that the manifest contains
+          // unrecognized permissions. This may happen legitimately if the
+          // extensions requests platform- or channel-specific permissions.
+          install_warnings_.push_back(feature->GetErrorMessage(availability));
+          continue;
         }
 
         if (permission->id() == ExtensionAPIPermission::kExperimental) {
@@ -3339,6 +3335,7 @@
         }
 
         api_permissions->insert(permission->id());
+        continue;
       }
 
       // Check if it's a host pattern permission.
@@ -3367,17 +3364,14 @@
         }
 
         host_permissions->AddPattern(pattern);
+        continue;
       }
 
-      // If it's not a host permission, then it's probably an unknown API
-      // permission. Do not throw an error so extensions can retain
-      // backwards compatability (http://crbug.com/42742).
-      // TODO(jstritar): We can improve error messages by adding better
-      // validation of API permissions here.
-      // TODO(skerner): Consider showing the reason |permission_str| is not
-      // a valid URL pattern if it is almost valid.  For example, if it has
-      // a valid scheme, and failed to parse because it has a port, show an
-      // error.
+      // It's probably an unknown API permission. Do not throw an error so
+      // extensions can retain backwards compatability (http://crbug.com/42742).
+      install_warnings_.push_back(base::StringPrintf(
+          "Permission '%s' is unknown or URL pattern is malformed.",
+          permission_str.c_str()));
     }
   }
   return true;
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 23c02931..29ddca6d 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -6,12 +6,12 @@
 #define CHROME_COMMON_EXTENSIONS_EXTENSION_H_
 #pragma once
 
+#include <algorithm>
 #include <map>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
-#include <algorithm>
 
 #include "base/file_path.h"
 #include "base/gtest_prod_util.h"
@@ -615,6 +615,9 @@
   const ExtensionPermissionSet* required_permission_set() const {
     return required_permission_set_.get();
   }
+  const std::vector<std::string>& install_warnings() const {
+    return install_warnings_;
+  }
   const GURL& update_url() const { return update_url_; }
   const ExtensionIconSet& icons() const { return icons_; }
   const extensions::Manifest* manifest() const {
@@ -908,6 +911,9 @@
   // The extension's required / default set of permissions.
   scoped_refptr<const ExtensionPermissionSet> required_permission_set_;
 
+  // Any warnings that occurred when trying to create/parse the extension.
+  std::vector<std::string> install_warnings_;
+
   // The icons for the extension.
   ExtensionIconSet icons_;
 
diff --git a/chrome/common/extensions/feature.cc b/chrome/common/extensions/feature.cc
index e2fd613..9f683d7 100644
--- a/chrome/common/extensions/feature.cc
+++ b/chrome/common/extensions/feature.cc
@@ -215,27 +215,44 @@
     case IS_AVAILABLE:
       return "";
     case NOT_FOUND_IN_WHITELIST:
-      return "Not allowed for specified extension ID.";
+      return base::StringPrintf(
+          "'%s' is not allowed for specified extension ID.",
+          name().c_str());
     case INVALID_TYPE:
-      return "Not allowed for specified package type (theme, app, etc.).";
+      return base::StringPrintf(
+          "'%s' is not allowed for specified package type (theme, app, etc.).",
+          name().c_str());
     case INVALID_CONTEXT:
-      return "Not allowed for specified context type content script, extension "
-          "page, web page, etc.).";
+      return base::StringPrintf(
+          "'%s' is not allowed for specified context type content script, "
+          " extension page, web page, etc.).",
+          name().c_str());
     case INVALID_LOCATION:
-      return "Not allowed for specified install location.";
+      return base::StringPrintf(
+          "'%s' is not allowed for specified install location.",
+          name().c_str());
     case INVALID_PLATFORM:
-      return "Not allowed for specified platform.";
+      return base::StringPrintf(
+          "'%s' is not allowed for specified platform.",
+          name().c_str());
     case INVALID_MIN_MANIFEST_VERSION:
-      return base::StringPrintf("Requires manifest version of at least %d.",
-                                min_manifest_version_);
+      return base::StringPrintf(
+          "'%s' requires manifest version of at least %d.",
+          name().c_str(),
+          min_manifest_version_);
     case INVALID_MAX_MANIFEST_VERSION:
-      return base::StringPrintf("Requires manifest version of %d or lower.",
-                                max_manifest_version_);
+      return base::StringPrintf(
+          "'%s' requires manifest version of %d or lower.",
+          name().c_str(),
+          max_manifest_version_);
     case NOT_PRESENT:
-      return "Requires a different Feature that is not present.";
+      return base::StringPrintf(
+          "'%s' requires a different Feature that is not present.",
+          name().c_str());
     case UNSUPPORTED_CHANNEL:
       return base::StringPrintf(
-          "Google Chrome %s channel or newer is required, but current is %s.",
+          "'%s' requires Google Chrome %s channel or newer but current is %s.",
+          name().c_str(),
           GetChannelName(channel_).c_str(),
           GetChannelName(g_channel.Get().channel).c_str());
   }
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
index dc8567b..dc994dc 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
@@ -25,7 +25,7 @@
   LoadAndExpectError(
       "init_invalid_platform_app_3.json",
       "Feature 'platform_app' is not accessible. "
-          "Requires manifest version of at least 2.");
+          "'platform_app' requires manifest version of at least 2.");
 }
 
 TEST_F(ExtensionManifestTest, CertainApisRequirePlatformApps) {
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_storage_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_storage_unittest.cc
index 9900b472..8acb36c6 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_storage_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_storage_unittest.cc
@@ -19,12 +19,18 @@
     base_manifest.Set(keys::kPermissions, permissions);
   }
 
-  std::string kManifestVersionError("Requires manifest version of at least 2.");
+  std::string kManifestVersionError =
+      "'storage' requires manifest version of at least 2.";
 
   // Extension with no manifest version cannot use storage API.
   {
     Manifest manifest(&base_manifest, "test");
-    LoadAndExpectError(manifest, kManifestVersionError);
+    scoped_refptr<Extension> extension = LoadAndExpectSuccess(manifest);
+    if (extension.get()) {
+      std::vector<std::string> warnings;
+      warnings.push_back(kManifestVersionError);
+      EXPECT_EQ(warnings, extension->install_warnings());
+    }
   }
 
   // Extension with manifest version 1 cannot use storage API.
@@ -34,7 +40,12 @@
     manifest_with_version.MergeDictionary(&base_manifest);
 
     Manifest manifest(&manifest_with_version, "test");
-    LoadAndExpectError(manifest, kManifestVersionError);
+    scoped_refptr<Extension> extension = LoadAndExpectSuccess(manifest);
+    if (extension.get()) {
+      std::vector<std::string> warnings;
+      warnings.push_back(kManifestVersionError);
+      EXPECT_EQ(warnings, extension->install_warnings());
+    }
   }
 
   // Extension with manifest version 2 *can* use storage API.
@@ -44,6 +55,10 @@
     manifest_with_version.MergeDictionary(&base_manifest);
 
     Manifest manifest(&manifest_with_version, "test");
-    LoadAndExpectSuccess(manifest);
+    scoped_refptr<Extension> extension = LoadAndExpectSuccess(manifest);
+    if (extension.get()) {
+      std::vector<std::string> empty;
+      EXPECT_EQ(empty, extension->install_warnings());
+    }
   }
 }
diff --git a/chrome/common/extensions/manifest_unittest.cc b/chrome/common/extensions/manifest_unittest.cc
index f7842052..5cabe03e 100644
--- a/chrome/common/extensions/manifest_unittest.cc
+++ b/chrome/common/extensions/manifest_unittest.cc
@@ -74,6 +74,7 @@
   EXPECT_FALSE(manifest->ValidateManifest(&error));
   {
     Feature feature;
+    feature.set_name("background_page");
     feature.set_max_manifest_version(1);
     EXPECT_EQ(ExtensionErrorUtils::FormatErrorMessageUTF16(
         errors::kFeatureNotAllowed,