002bbb319f1869d107fc001d45bf57b08bae464a
[platform/framework/web/crosswalk.git] / src / extensions / common / extension_api.cc
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 #include "extensions/common/extension_api.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/values.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extensions_client.h"
21 #include "extensions/common/features/feature.h"
22 #include "extensions/common/features/feature_provider.h"
23 #include "extensions/common/permissions/permission_set.h"
24 #include "extensions/common/permissions/permissions_data.h"
25 #include "grit/common_resources.h"
26 #include "grit/extensions_api_resources.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "url/gurl.h"
29
30 namespace extensions {
31
32 namespace {
33
34 const char* kChildKinds[] = {
35   "functions",
36   "events"
37 };
38
39 base::StringPiece ReadFromResource(int resource_id) {
40   return ResourceBundle::GetSharedInstance().GetRawDataResource(
41       resource_id);
42 }
43
44 scoped_ptr<base::ListValue> LoadSchemaList(const std::string& name,
45                                            const base::StringPiece& schema) {
46   std::string error_message;
47   scoped_ptr<base::Value> result(
48       base::JSONReader::ReadAndReturnError(
49           schema,
50           base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN,  // options
51           NULL,  // error code
52           &error_message));
53
54   // Tracking down http://crbug.com/121424
55   char buf[128];
56   base::snprintf(buf, arraysize(buf), "%s: (%d) '%s'",
57       name.c_str(),
58       result.get() ? result->GetType() : -1,
59       error_message.c_str());
60
61   CHECK(result.get()) << error_message << " for schema " << schema;
62   CHECK(result->IsType(base::Value::TYPE_LIST)) << " for schema " << schema;
63   return scoped_ptr<base::ListValue>(static_cast<base::ListValue*>(
64       result.release()));
65 }
66
67 const base::DictionaryValue* FindListItem(const base::ListValue* list,
68                                           const std::string& property_name,
69                                           const std::string& property_value) {
70   for (size_t i = 0; i < list->GetSize(); ++i) {
71     const base::DictionaryValue* item = NULL;
72     CHECK(list->GetDictionary(i, &item))
73         << property_value << "/" << property_name;
74     std::string value;
75     if (item->GetString(property_name, &value) && value == property_value)
76       return item;
77   }
78
79   return NULL;
80 }
81
82 const base::DictionaryValue* GetSchemaChild(
83     const base::DictionaryValue* schema_node,
84     const std::string& child_name) {
85   const base::DictionaryValue* child_node = NULL;
86   for (size_t i = 0; i < arraysize(kChildKinds); ++i) {
87     const base::ListValue* list_node = NULL;
88     if (!schema_node->GetList(kChildKinds[i], &list_node))
89       continue;
90     child_node = FindListItem(list_node, "name", child_name);
91     if (child_node)
92       return child_node;
93   }
94
95   return NULL;
96 }
97
98 struct Static {
99   Static()
100       : api(ExtensionAPI::CreateWithDefaultConfiguration()) {
101   }
102   scoped_ptr<ExtensionAPI> api;
103 };
104
105 base::LazyInstance<Static> g_lazy_instance = LAZY_INSTANCE_INITIALIZER;
106
107 // If it exists and does not already specify a namespace, then the value stored
108 // with key |key| in |schema| will be updated to |schema_namespace| + "." +
109 // |schema[key]|.
110 void MaybePrefixFieldWithNamespace(const std::string& schema_namespace,
111                                    base::DictionaryValue* schema,
112                                    const std::string& key) {
113   if (!schema->HasKey(key))
114     return;
115
116   std::string old_id;
117   CHECK(schema->GetString(key, &old_id));
118   if (old_id.find(".") == std::string::npos)
119     schema->SetString(key, schema_namespace + "." + old_id);
120 }
121
122 // Modify all "$ref" keys anywhere in |schema| to be prefxied by
123 // |schema_namespace| if they do not already specify a namespace.
124 void PrefixRefsWithNamespace(const std::string& schema_namespace,
125                              base::Value* value) {
126   base::ListValue* list = NULL;
127   base::DictionaryValue* dict = NULL;
128   if (value->GetAsList(&list)) {
129     for (base::ListValue::iterator i = list->begin(); i != list->end(); ++i) {
130       PrefixRefsWithNamespace(schema_namespace, *i);
131     }
132   } else if (value->GetAsDictionary(&dict)) {
133     MaybePrefixFieldWithNamespace(schema_namespace, dict, "$ref");
134     for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) {
135       base::Value* value = NULL;
136       CHECK(dict->GetWithoutPathExpansion(i.key(), &value));
137       PrefixRefsWithNamespace(schema_namespace, value);
138     }
139   }
140 }
141
142 // Modify all objects in the "types" section of the schema to be prefixed by
143 // |schema_namespace| if they do not already specify a namespace.
144 void PrefixTypesWithNamespace(const std::string& schema_namespace,
145                               base::DictionaryValue* schema) {
146   if (!schema->HasKey("types"))
147     return;
148
149   // Add the namespace to all of the types defined in this schema
150   base::ListValue *types = NULL;
151   CHECK(schema->GetList("types", &types));
152   for (size_t i = 0; i < types->GetSize(); ++i) {
153     base::DictionaryValue *type = NULL;
154     CHECK(types->GetDictionary(i, &type));
155     MaybePrefixFieldWithNamespace(schema_namespace, type, "id");
156     MaybePrefixFieldWithNamespace(schema_namespace, type, "customBindings");
157   }
158 }
159
160 // Modify the schema so that all types are fully qualified.
161 void PrefixWithNamespace(const std::string& schema_namespace,
162                          base::DictionaryValue* schema) {
163   PrefixTypesWithNamespace(schema_namespace, schema);
164   PrefixRefsWithNamespace(schema_namespace, schema);
165 }
166
167 }  // namespace
168
169 // static
170 ExtensionAPI* ExtensionAPI::GetSharedInstance() {
171   return g_lazy_instance.Get().api.get();
172 }
173
174 // static
175 ExtensionAPI* ExtensionAPI::CreateWithDefaultConfiguration() {
176   ExtensionAPI* api = new ExtensionAPI();
177   api->InitDefaultConfiguration();
178   return api;
179 }
180
181 // static
182 void ExtensionAPI::SplitDependencyName(const std::string& full_name,
183                                        std::string* feature_type,
184                                        std::string* feature_name) {
185   size_t colon_index = full_name.find(':');
186   if (colon_index == std::string::npos) {
187     // TODO(aa): Remove this code when all API descriptions have been updated.
188     *feature_type = "api";
189     *feature_name = full_name;
190     return;
191   }
192
193   *feature_type = full_name.substr(0, colon_index);
194   *feature_name = full_name.substr(colon_index + 1);
195 }
196
197 void ExtensionAPI::LoadSchema(const std::string& name,
198                               const base::StringPiece& schema) {
199   scoped_ptr<base::ListValue> schema_list(LoadSchemaList(name, schema));
200   std::string schema_namespace;
201   extensions::ExtensionsClient* extensions_client =
202       extensions::ExtensionsClient::Get();
203   DCHECK(extensions_client);
204   while (!schema_list->empty()) {
205     base::DictionaryValue* schema = NULL;
206     {
207       scoped_ptr<base::Value> value;
208       schema_list->Remove(schema_list->GetSize() - 1, &value);
209       CHECK(value.release()->GetAsDictionary(&schema));
210     }
211
212     CHECK(schema->GetString("namespace", &schema_namespace));
213     PrefixWithNamespace(schema_namespace, schema);
214     schemas_[schema_namespace] = make_linked_ptr(schema);
215     if (!extensions_client->IsAPISchemaGenerated(schema_namespace))
216       CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace));
217   }
218 }
219
220 ExtensionAPI::ExtensionAPI() : default_configuration_initialized_(false) {
221 }
222
223 ExtensionAPI::~ExtensionAPI() {
224 }
225
226 void ExtensionAPI::InitDefaultConfiguration() {
227   const char* names[] = {"api", "manifest", "permission"};
228   for (size_t i = 0; i < arraysize(names); ++i)
229     RegisterDependencyProvider(names[i], FeatureProvider::GetByName(names[i]));
230
231   // Schemas to be loaded from resources.
232   CHECK(unloaded_schemas_.empty());
233   RegisterSchemaResource("app", IDR_EXTENSION_API_JSON_APP);
234   RegisterSchemaResource("browserAction", IDR_EXTENSION_API_JSON_BROWSERACTION);
235   RegisterSchemaResource("commands", IDR_EXTENSION_API_JSON_COMMANDS);
236   RegisterSchemaResource("declarativeContent",
237       IDR_EXTENSION_API_JSON_DECLARATIVE_CONTENT);
238   RegisterSchemaResource("declarativeWebRequest",
239       IDR_EXTENSION_API_JSON_DECLARATIVE_WEBREQUEST);
240   RegisterSchemaResource("fileBrowserHandler",
241       IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER);
242   RegisterSchemaResource("inputMethodPrivate",
243       IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE);
244   RegisterSchemaResource("pageAction", IDR_EXTENSION_API_JSON_PAGEACTION);
245   RegisterSchemaResource("pageActions", IDR_EXTENSION_API_JSON_PAGEACTIONS);
246   RegisterSchemaResource("privacy", IDR_EXTENSION_API_JSON_PRIVACY);
247   RegisterSchemaResource("processes", IDR_EXTENSION_API_JSON_PROCESSES);
248   RegisterSchemaResource("proxy", IDR_EXTENSION_API_JSON_PROXY);
249   RegisterSchemaResource("scriptBadge", IDR_EXTENSION_API_JSON_SCRIPTBADGE);
250   RegisterSchemaResource("ttsEngine", IDR_EXTENSION_API_JSON_TTSENGINE);
251   RegisterSchemaResource("tts", IDR_EXTENSION_API_JSON_TTS);
252   RegisterSchemaResource("types", IDR_EXTENSION_API_JSON_TYPES);
253   RegisterSchemaResource("types.private", IDR_EXTENSION_API_JSON_TYPES_PRIVATE);
254   RegisterSchemaResource("webRequestInternal",
255       IDR_EXTENSION_API_JSON_WEBREQUESTINTERNAL);
256   RegisterSchemaResource("webstore", IDR_EXTENSION_API_JSON_WEBSTORE);
257   RegisterSchemaResource("webViewRequest",
258       IDR_EXTENSION_API_JSON_WEBVIEW_REQUEST);
259
260   default_configuration_initialized_ = true;
261 }
262
263 void ExtensionAPI::RegisterSchemaResource(const std::string& name,
264                                           int resource_id) {
265   unloaded_schemas_[name] = resource_id;
266 }
267
268 void ExtensionAPI::RegisterDependencyProvider(const std::string& name,
269                                               FeatureProvider* provider) {
270   dependency_providers_[name] = provider;
271 }
272
273 bool ExtensionAPI::IsAnyFeatureAvailableToContext(const Feature& api,
274                                                   const Extension* extension,
275                                                   Feature::Context context,
276                                                   const GURL& url) {
277   FeatureProviderMap::iterator provider = dependency_providers_.find("api");
278   CHECK(provider != dependency_providers_.end());
279   if (IsAvailable(api, extension, context, url).is_available())
280     return true;
281
282   // Check to see if there are any parts of this API that are allowed in this
283   // context.
284   const std::vector<Feature*> features = provider->second->GetChildren(api);
285   for (std::vector<Feature*>::const_iterator feature = features.begin();
286        feature != features.end();
287        ++feature) {
288     if (IsAvailable(**feature, extension, context, url).is_available())
289       return true;
290   }
291   return false;
292 }
293
294 Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name,
295                                                 const Extension* extension,
296                                                 Feature::Context context,
297                                                 const GURL& url) {
298   Feature* feature = GetFeatureDependency(full_name);
299   CHECK(feature) << full_name;
300   return IsAvailable(*feature, extension, context, url);
301 }
302
303 Feature::Availability ExtensionAPI::IsAvailable(const Feature& feature,
304                                                 const Extension* extension,
305                                                 Feature::Context context,
306                                                 const GURL& url) {
307   Feature::Availability availability =
308       feature.IsAvailableToContext(extension, context, url);
309   if (!availability.is_available())
310     return availability;
311
312   for (std::set<std::string>::iterator iter = feature.dependencies().begin();
313        iter != feature.dependencies().end(); ++iter) {
314     Feature::Availability dependency_availability =
315         IsAvailable(*iter, extension, context, url);
316     if (!dependency_availability.is_available())
317       return dependency_availability;
318   }
319
320   return Feature::CreateAvailability(Feature::IS_AVAILABLE, std::string());
321 }
322
323 bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
324   Feature* feature = GetFeatureDependency(full_name);
325   CHECK(feature);
326   DCHECK(!feature->GetContexts()->empty());
327   // An API is 'privileged' if it can only be run in a blessed context.
328   return feature->GetContexts()->size() ==
329       feature->GetContexts()->count(Feature::BLESSED_EXTENSION_CONTEXT);
330 }
331
332 const base::DictionaryValue* ExtensionAPI::GetSchema(
333     const std::string& full_name) {
334   std::string child_name;
335   std::string api_name = GetAPINameFromFullName(full_name, &child_name);
336
337   const base::DictionaryValue* result = NULL;
338   SchemaMap::iterator maybe_schema = schemas_.find(api_name);
339   if (maybe_schema != schemas_.end()) {
340     result = maybe_schema->second.get();
341   } else {
342     // Might not have loaded yet; or might just not exist.
343     UnloadedSchemaMap::iterator maybe_schema_resource =
344         unloaded_schemas_.find(api_name);
345     extensions::ExtensionsClient* extensions_client =
346         extensions::ExtensionsClient::Get();
347     DCHECK(extensions_client);
348     if (maybe_schema_resource != unloaded_schemas_.end()) {
349       LoadSchema(maybe_schema_resource->first,
350                  ReadFromResource(maybe_schema_resource->second));
351     } else if (default_configuration_initialized_ &&
352                extensions_client->IsAPISchemaGenerated(api_name)) {
353       LoadSchema(api_name, extensions_client->GetAPISchema(api_name));
354     } else {
355       return NULL;
356     }
357
358     maybe_schema = schemas_.find(api_name);
359     CHECK(schemas_.end() != maybe_schema);
360     result = maybe_schema->second.get();
361   }
362
363   if (!child_name.empty())
364     result = GetSchemaChild(result, child_name);
365
366   return result;
367 }
368
369 Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
370   std::string feature_type;
371   std::string feature_name;
372   SplitDependencyName(full_name, &feature_type, &feature_name);
373
374   FeatureProviderMap::iterator provider =
375       dependency_providers_.find(feature_type);
376   if (provider == dependency_providers_.end())
377     return NULL;
378
379   Feature* feature = provider->second->GetFeature(feature_name);
380   // Try getting the feature for the parent API, if this was a child.
381   if (!feature) {
382     std::string child_name;
383     feature = provider->second->GetFeature(
384         GetAPINameFromFullName(feature_name, &child_name));
385   }
386   return feature;
387 }
388
389 std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name,
390                                                  std::string* child_name) {
391   std::string api_name_candidate = full_name;
392   extensions::ExtensionsClient* extensions_client =
393       extensions::ExtensionsClient::Get();
394   DCHECK(extensions_client);
395   while (true) {
396     if (schemas_.find(api_name_candidate) != schemas_.end() ||
397         extensions_client->IsAPISchemaGenerated(api_name_candidate) ||
398         unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) {
399       std::string result = api_name_candidate;
400
401       if (child_name) {
402         if (result.length() < full_name.length())
403           *child_name = full_name.substr(result.length() + 1);
404         else
405           *child_name = "";
406       }
407
408       return result;
409     }
410
411     size_t last_dot_index = api_name_candidate.rfind('.');
412     if (last_dot_index == std::string::npos)
413       break;
414
415     api_name_candidate = api_name_candidate.substr(0, last_dot_index);
416   }
417
418   *child_name = "";
419   return std::string();
420 }
421
422 }  // namespace extensions