1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef EXTENSIONS_COMMON_MANIFEST_H_ |
6 | #define EXTENSIONS_COMMON_MANIFEST_H_ |
7 | |
8 | #include <map> |
9 | #include <memory> |
10 | #include <set> |
11 | #include <string> |
12 | #include <vector> |
13 | |
14 | #include "base/containers/span.h" |
15 | #include "base/macros.h" |
16 | #include "base/strings/string16.h" |
17 | #include "base/values.h" |
18 | #include "extensions/common/extension_id.h" |
19 | #include "extensions/common/hashed_extension_id.h" |
20 | |
21 | namespace extensions { |
22 | struct InstallWarning; |
23 | |
24 | // Wraps the DictionaryValue form of extension's manifest. Enforces access to |
25 | // properties of the manifest using ManifestFeatureProvider. |
26 | class Manifest { |
27 | public: |
28 | // Historically, where an extension was loaded from, and whether an |
29 | // extension's files were inside or outside of the profile's directory. In |
30 | // modern usage, a Location can be thought of as the installation source: |
31 | // whether an extension was explicitly installed by the user (through the |
32 | // UI), or implicitly installed by other means. For example, enterprise |
33 | // policy, being part of Chrome per se (but implemented as an extension), or |
34 | // installed as a side effect of installing third party software. |
35 | // |
36 | // NOTE: These values are stored as integers in the preferences and used |
37 | // in histograms so don't remove or reorder existing items. Just append |
38 | // to the end. |
39 | enum Location { |
40 | INVALID_LOCATION, |
41 | INTERNAL, // A crx file from the internal Extensions directory. This |
42 | // includes extensions explicitly installed by the user. It also |
43 | // includes installed-by-default extensions that are not part of |
44 | // Chrome itself (and thus not a COMPONENT), but are part of a |
45 | // larger system (such as Chrome OS). |
46 | EXTERNAL_PREF, // A crx file from an external directory (via prefs). |
47 | EXTERNAL_REGISTRY, // A crx file from an external directory (via eg the |
48 | // registry on Windows). |
49 | UNPACKED, // From loading an unpacked extension from the |
50 | // extensions settings page. |
51 | COMPONENT, // An integral component of Chrome itself, which |
52 | // happens to be implemented as an extension. We don't |
53 | // show these in the management UI. |
54 | EXTERNAL_PREF_DOWNLOAD, // A crx file from an external directory (via |
55 | // prefs), installed from an update URL. |
56 | EXTERNAL_POLICY_DOWNLOAD, // A crx file from an external directory (via |
57 | // admin policies), installed from an update URL. |
58 | COMMAND_LINE, // --load-extension. |
59 | EXTERNAL_POLICY, // A crx file from an external directory (via admin |
60 | // policies), cached locally and installed from the |
61 | // cache. |
62 | EXTERNAL_COMPONENT, // Similar to COMPONENT in that it's considered an |
63 | // internal implementation detail of chrome, but |
64 | // installed from an update URL like the *DOWNLOAD ones. |
65 | |
66 | // New enum values must go above here. |
67 | NUM_LOCATIONS |
68 | }; |
69 | |
70 | // Do not change the order of entries or remove entries in this list as this |
71 | // is used in ExtensionType enum in tools/metrics/histograms/enums.xml. |
72 | enum Type { |
73 | TYPE_UNKNOWN = 0, |
74 | TYPE_EXTENSION = 1, |
75 | TYPE_THEME = 2, |
76 | TYPE_USER_SCRIPT = 3, |
77 | TYPE_HOSTED_APP = 4, |
78 | // This is marked legacy because platform apps are preferred. For |
79 | // backwards compatibility, we can't remove support for packaged apps |
80 | TYPE_LEGACY_PACKAGED_APP = 5, |
81 | TYPE_PLATFORM_APP = 6, |
82 | TYPE_SHARED_MODULE = 7, |
83 | |
84 | // New enum values must go above here. |
85 | NUM_LOAD_TYPES |
86 | }; |
87 | |
88 | // Given two install sources, return the one which should take priority |
89 | // over the other. If an extension is installed from two sources A and B, |
90 | // its install source should be set to GetHigherPriorityLocation(A, B). |
91 | static Location GetHigherPriorityLocation(Location loc1, Location loc2); |
92 | |
93 | // Whether the |location| is external or not. |
94 | static inline bool IsExternalLocation(Location location) { |
95 | return location == EXTERNAL_PREF || |
96 | location == EXTERNAL_REGISTRY || |
97 | location == EXTERNAL_PREF_DOWNLOAD || |
98 | location == EXTERNAL_POLICY || |
99 | location == EXTERNAL_POLICY_DOWNLOAD || |
100 | location == EXTERNAL_COMPONENT; |
101 | } |
102 | |
103 | // Whether the |location| is unpacked (no CRX) or not. |
104 | static inline bool IsUnpackedLocation(Location location) { |
105 | return location == UNPACKED || location == COMMAND_LINE; |
106 | } |
107 | |
108 | // Whether extensions with |location| are auto-updatable or not. |
109 | static inline bool IsAutoUpdateableLocation(Location location) { |
110 | // Only internal and external extensions can be autoupdated. |
111 | return location == INTERNAL || |
112 | IsExternalLocation(location); |
113 | } |
114 | |
115 | // Whether the |location| is a source of extensions force-installed through |
116 | // policy. |
117 | static inline bool IsPolicyLocation(Location location) { |
118 | return location == EXTERNAL_POLICY || |
119 | location == EXTERNAL_POLICY_DOWNLOAD; |
120 | } |
121 | |
122 | // Whether the |location| is an extension intended to be an internal part of |
123 | // Chrome. |
124 | static inline bool IsComponentLocation(Location location) { |
125 | return location == COMPONENT || location == EXTERNAL_COMPONENT; |
126 | } |
127 | |
128 | static inline bool IsValidLocation(Location location) { |
129 | return location > INVALID_LOCATION && location < NUM_LOCATIONS; |
130 | } |
131 | |
132 | // Unpacked extensions start off with file access since they are a developer |
133 | // feature. |
134 | static inline bool ShouldAlwaysAllowFileAccess(Location location) { |
135 | return IsUnpackedLocation(location); |
136 | } |
137 | |
138 | // Returns the Manifest::Type for the given |value|. |
139 | static Type GetTypeFromManifestValue(const base::DictionaryValue& value); |
140 | |
141 | // Returns true if an item with the given |location| should always be loaded, |
142 | // even if extensions are otherwise disabled. |
143 | static bool ShouldAlwaysLoadExtension(Manifest::Location location, |
144 | bool is_theme); |
145 | |
146 | Manifest(Location location, std::unique_ptr<base::DictionaryValue> value); |
147 | virtual ~Manifest(); |
148 | |
149 | void SetExtensionId(const ExtensionId& id); |
150 | |
151 | const ExtensionId& extension_id() const { return extension_id_; } |
152 | const HashedExtensionId& hashed_id() const { return hashed_id_; } |
153 | |
154 | Location location() const { return location_; } |
155 | |
156 | // Returns false and |error| will be non-empty if the manifest is malformed. |
157 | // |warnings| will be populated if there are keys in the manifest that cannot |
158 | // be specified by the extension type. |
159 | bool ValidateManifest(std::string* error, |
160 | std::vector<InstallWarning>* warnings) const; |
161 | |
162 | // The version of this extension's manifest. We increase the manifest |
163 | // version when making breaking changes to the extension system. If the |
164 | // manifest contains no explicit manifest version, this returns the current |
165 | // system default. |
166 | int GetManifestVersion() const; |
167 | |
168 | // Returns the manifest type. |
169 | Type type() const { return type_; } |
170 | |
171 | bool is_theme() const { return type_ == TYPE_THEME; } |
172 | bool is_app() const { |
173 | return is_legacy_packaged_app() || is_hosted_app() || is_platform_app(); |
174 | } |
175 | bool is_platform_app() const { return type_ == TYPE_PLATFORM_APP; } |
176 | bool is_hosted_app() const { return type_ == TYPE_HOSTED_APP; } |
177 | bool is_legacy_packaged_app() const { |
178 | return type_ == TYPE_LEGACY_PACKAGED_APP; |
179 | } |
180 | bool is_extension() const { return type_ == TYPE_EXTENSION; } |
181 | bool is_shared_module() const { return type_ == TYPE_SHARED_MODULE; } |
182 | |
183 | // These access the wrapped manifest value, returning false when the property |
184 | // does not exist or if the manifest type can't access it. |
185 | // TODO(karandeepb): These methods should be changed to use base::StringPiece. |
186 | // Better, we should pass a list of path components instead of a unified |
187 | // |path| to do away with our usage of deprecated base::Value methods. |
188 | bool HasKey(const std::string& key) const; |
189 | bool HasPath(const std::string& path) const; |
190 | bool Get(const std::string& path, const base::Value** out_value) const; |
191 | bool GetBoolean(const std::string& path, bool* out_value) const; |
192 | bool GetInteger(const std::string& path, int* out_value) const; |
193 | bool GetString(const std::string& path, std::string* out_value) const; |
194 | bool GetString(const std::string& path, base::string16* out_value) const; |
195 | // Deprecated: Use the GetDictionary() overload that accepts a base::Value |
196 | // output parameter instead. |
197 | bool GetDictionary(const std::string& path, |
198 | const base::DictionaryValue** out_value) const; |
199 | bool GetDictionary(const std::string& path, |
200 | const base::Value** out_value) const; |
201 | // Deprecated: Use the GetList() overload that accepts a base::Value output |
202 | // parameter instead. |
203 | bool GetList(const std::string& path, |
204 | const base::ListValue** out_value) const; |
205 | bool GetList(const std::string& path, const base::Value** out_value) const; |
206 | |
207 | bool GetPathOfType(const std::string& path, |
208 | base::Value::Type type, |
209 | const base::Value** out_value) const; |
210 | |
211 | // Returns a new Manifest equal to this one. |
212 | std::unique_ptr<Manifest> CreateDeepCopy() const; |
213 | |
214 | // Returns true if this equals the |other| manifest. |
215 | bool Equals(const Manifest* other) const; |
216 | |
217 | // Gets the underlying DictionaryValue representing the manifest. |
218 | // Note: only use this when you KNOW you don't need the validation. |
219 | const base::DictionaryValue* value() const { return value_.get(); } |
220 | |
221 | private: |
222 | // Returns true if the extension can specify the given |path|. |
223 | bool CanAccessPath(const std::string& path) const; |
224 | bool CanAccessPath(const base::span<const base::StringPiece> path) const; |
225 | bool CanAccessKey(const std::string& key) const; |
226 | |
227 | // A persistent, globally unique ID. An extension's ID is used in things |
228 | // like directory structures and URLs, and is expected to not change across |
229 | // versions. It is generated as a SHA-256 hash of the extension's public |
230 | // key, or as a hash of the path in the case of unpacked extensions. |
231 | std::string extension_id_; |
232 | |
233 | // The hex-encoding of the SHA1 of the extension id; used to determine feature |
234 | // availability. |
235 | HashedExtensionId hashed_id_; |
236 | |
237 | // The location the extension was loaded from. |
238 | Location location_; |
239 | |
240 | // The underlying dictionary representation of the manifest. |
241 | std::unique_ptr<base::DictionaryValue> value_; |
242 | |
243 | Type type_; |
244 | |
245 | DISALLOW_COPY_AND_ASSIGN(Manifest); |
246 | }; |
247 | |
248 | } // namespace extensions |
249 | |
250 | #endif // EXTENSIONS_COMMON_MANIFEST_H_ |
251 | |