Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(173)

Side by Side Diff: chrome/common/extensions/api/extension_api.cc

Issue 12846011: Implement API features for the Extension API feature system (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/common/extensions/api/extension_api.h" 5 #include "chrome/common/extensions/api/extension_api.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 #include <vector> 9 #include <vector>
10 10
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 *feature_type = "api"; 228 *feature_type = "api";
229 *feature_name = full_name; 229 *feature_name = full_name;
230 return; 230 return;
231 } 231 }
232 232
233 *feature_type = full_name.substr(0, colon_index); 233 *feature_type = full_name.substr(0, colon_index);
234 *feature_name = full_name.substr(colon_index + 1); 234 *feature_name = full_name.substr(colon_index + 1);
235 } 235 }
236 236
237 bool ExtensionAPI::UsesFeatureSystem(const std::string& full_name) { 237 bool ExtensionAPI::UsesFeatureSystem(const std::string& full_name) {
238 std::string api_name = GetAPINameFromFullName(full_name, NULL); 238 return TryGetFeatureDependency(full_name) != NULL;
239 return features_.find(api_name) != features_.end();
240 } 239 }
241 240
242 void ExtensionAPI::LoadSchema(const std::string& name, 241 void ExtensionAPI::LoadSchema(const std::string& name,
243 const base::StringPiece& schema) { 242 const base::StringPiece& schema) {
244 scoped_ptr<ListValue> schema_list(LoadSchemaList(name, schema)); 243 scoped_ptr<ListValue> schema_list(LoadSchemaList(name, schema));
245 std::string schema_namespace; 244 std::string schema_namespace;
246 245
247 while (!schema_list->empty()) { 246 while (!schema_list->empty()) {
248 DictionaryValue* schema = NULL; 247 DictionaryValue* schema = NULL;
249 { 248 {
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 child_feature->set_name(schema_namespace + "." + child_name); 324 child_feature->set_name(schema_namespace + "." + child_name);
326 CHECK(schema_features->insert( 325 CHECK(schema_features->insert(
327 std::make_pair(child_name, 326 std::make_pair(child_name,
328 make_linked_ptr(child_feature.release()))).second); 327 make_linked_ptr(child_feature.release()))).second);
329 } 328 }
330 } 329 }
331 } 330 }
332 } 331 }
333 332
334 ExtensionAPI::ExtensionAPI() { 333 ExtensionAPI::ExtensionAPI() {
335 RegisterDependencyProvider("api", this);
336
337 // TODO(aa): Can remove this when all JSON files are converted.
338 RegisterDependencyProvider("", this);
339 } 334 }
340 335
341 ExtensionAPI::~ExtensionAPI() { 336 ExtensionAPI::~ExtensionAPI() {
342 } 337 }
343 338
344 void ExtensionAPI::InitDefaultConfiguration() { 339 void ExtensionAPI::InitDefaultConfiguration() {
345 RegisterDependencyProvider( 340 RegisterDependencyProvider(
341 "api", BaseFeatureProvider::GetApiFeatures());
342 RegisterDependencyProvider(
346 "manifest", BaseFeatureProvider::GetManifestFeatures()); 343 "manifest", BaseFeatureProvider::GetManifestFeatures());
347 RegisterDependencyProvider( 344 RegisterDependencyProvider(
348 "permission", BaseFeatureProvider::GetPermissionFeatures()); 345 "permission", BaseFeatureProvider::GetPermissionFeatures());
349 346
350 // Schemas to be loaded from resources. 347 // Schemas to be loaded from resources.
351 CHECK(unloaded_schemas_.empty()); 348 CHECK(unloaded_schemas_.empty());
352 RegisterSchema("app", ReadFromResource( 349 RegisterSchema("app", ReadFromResource(
353 IDR_EXTENSION_API_JSON_APP)); 350 IDR_EXTENSION_API_JSON_APP));
354 RegisterSchema("browserAction", ReadFromResource( 351 RegisterSchema("browserAction", ReadFromResource(
355 IDR_EXTENSION_API_JSON_BROWSERACTION)); 352 IDR_EXTENSION_API_JSON_BROWSERACTION));
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 412
416 void ExtensionAPI::RegisterDependencyProvider(const std::string& name, 413 void ExtensionAPI::RegisterDependencyProvider(const std::string& name,
417 FeatureProvider* provider) { 414 FeatureProvider* provider) {
418 dependency_providers_[name] = provider; 415 dependency_providers_[name] = provider;
419 } 416 }
420 417
421 Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name, 418 Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name,
422 const Extension* extension, 419 const Extension* extension,
423 Feature::Context context, 420 Feature::Context context,
424 const GURL& url) { 421 const GURL& url) {
425 std::set<std::string> dependency_names; 422 std::string feature_type;
426 dependency_names.insert(full_name); 423 std::string feature_name;
427 ResolveDependencies(&dependency_names); 424 SplitDependencyName(full_name, &feature_type, &feature_name);
428 425
426 std::string child_name;
427 std::string api_name = GetAPINameFromFullName(feature_name, &child_name);
not at google - send to devlin 2013/03/26 00:34:48 nit: eyes want a newline after api_name.
cduvall 2013/03/26 19:24:05 Done.
429 // Check APIs not using the feature system first. 428 // Check APIs not using the feature system first.
430 if (!UsesFeatureSystem(full_name)) { 429 if (!UsesFeatureSystem(api_name)) {
431 return IsNonFeatureAPIAvailable(full_name, context, extension, url) ? 430 // Make sure schema is loaded.
431 GetSchema(api_name);
432 return IsNonFeatureAPIAvailable(api_name, context, extension, url) ?
not at google - send to devlin 2013/03/26 00:34:48 I think the GetSchema thing should go in IsNonFeat
cduvall 2013/03/26 19:24:05 Done.
432 Feature::CreateAvailability(Feature::IS_AVAILABLE, "") : 433 Feature::CreateAvailability(Feature::IS_AVAILABLE, "") :
433 Feature::CreateAvailability(Feature::INVALID_CONTEXT, 434 Feature::CreateAvailability(Feature::INVALID_CONTEXT,
434 kUnavailableMessage); 435 kUnavailableMessage);
435 } 436 }
436 437
437 for (std::set<std::string>::iterator iter = dependency_names.begin(); 438 Feature* feature = GetFeatureDependency(full_name);
438 iter != dependency_names.end(); ++iter) { 439 CHECK(feature) << full_name;
not at google - send to devlin 2013/03/26 00:34:48 yeah, here we're CHECKing anyway.
cduvall 2013/03/26 19:24:05 Done.
439 Feature* feature = GetFeatureDependency(*iter); 440 Feature::Availability availability =
440 CHECK(feature) << *iter; 441 feature->IsAvailableToContext(extension, context, url);
442 if (!availability.is_available())
443 return availability;
441 444
442 Feature::Availability availability = 445 for (std::set<std::string>::iterator iter = feature->dependencies().begin();
443 feature->IsAvailableToContext(extension, context, url); 446 iter != feature->dependencies().end(); ++iter) {
444 if (!availability.is_available()) 447 Feature::Availability dependency_availability =
445 return availability; 448 IsAvailable(*iter, extension, context, url);
449 if (!dependency_availability.is_available())
450 return dependency_availability;
446 } 451 }
447 452
448 return Feature::CreateAvailability(Feature::IS_AVAILABLE, ""); 453 return Feature::CreateAvailability(Feature::IS_AVAILABLE, "");
449 } 454 }
450 455
451 bool ExtensionAPI::IsPrivileged(const std::string& full_name) { 456 bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
452 std::string child_name; 457 std::string child_name;
453 std::string api_name = GetAPINameFromFullName(full_name, &child_name); 458 std::string api_name = GetAPINameFromFullName(full_name, &child_name);
454 459
455 // First try to use the feature system. 460 // First try to use the feature system.
456 Feature* feature(GetFeature(full_name)); 461 if (UsesFeatureSystem(full_name)) {
457 if (feature) {
458 // An API is 'privileged' if it or any of its dependencies can only be run 462 // An API is 'privileged' if it or any of its dependencies can only be run
459 // in a blessed context. 463 // in a blessed context.
not at google - send to devlin 2013/03/26 00:34:48 I don't think this checking dependencies really ma
cduvall 2013/03/26 19:24:05 Taking out the dependency checks causes ExtensionA
not at google - send to devlin 2013/03/26 21:31:21 I can't find this. Are you referring to privileged
cduvall 2013/03/26 22:08:48 Ok, I removed dependency checking from IsPrivilege
460 std::set<std::string> resolved_dependencies; 464 Feature* feature = GetFeatureDependency(full_name);
not at google - send to devlin 2013/03/26 00:34:48 and here we could assign Feature up on line 460 an
cduvall 2013/03/26 19:24:05 Done.
461 resolved_dependencies.insert(full_name); 465 std::set<std::string> dependencies = feature->dependencies();
462 ResolveDependencies(&resolved_dependencies); 466 for (std::set<Feature::Context>::iterator context =
463 for (std::set<std::string>::iterator iter = resolved_dependencies.begin(); 467 feature->GetContexts()->begin();
464 iter != resolved_dependencies.end(); ++iter) { 468 context != feature->GetContexts()->end(); ++context) {
465 Feature* dependency = GetFeatureDependency(*iter); 469 if (*context != Feature::BLESSED_EXTENSION_CONTEXT)
466 for (std::set<Feature::Context>::iterator context = 470 return false;
467 dependency->GetContexts()->begin(); 471 }
468 context != dependency->GetContexts()->end(); ++context) { 472 for (std::set<std::string>::iterator iter = dependencies.begin();
469 if (*context != Feature::BLESSED_EXTENSION_CONTEXT) 473 iter != dependencies.end(); ++iter) {
470 return false; 474 if (!IsPrivileged(*iter))
471 } 475 return false;
472 } 476 }
473 return true; 477 return true;
474 } 478 }
475 479
480 const DictionaryValue* schema = GetSchema(api_name);
not at google - send to devlin 2013/03/26 00:34:48 nice catch. // get now to populate |completely_un
cduvall 2013/03/26 19:24:05 Done.
476 // If this API hasn't been converted yet, fall back to the old system. 481 // If this API hasn't been converted yet, fall back to the old system.
477 if (completely_unprivileged_apis_.count(api_name)) 482 if (completely_unprivileged_apis_.count(api_name))
478 return false; 483 return false;
479 484
480 const DictionaryValue* schema = GetSchema(api_name);
481 if (partially_unprivileged_apis_.count(api_name)) 485 if (partially_unprivileged_apis_.count(api_name))
482 return IsChildNamePrivileged(schema, child_name); 486 return IsChildNamePrivileged(schema, child_name);
483 487
484 return true; 488 return true;
485 } 489 }
486 490
487 bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node, 491 bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node,
488 const std::string& child_name) { 492 const std::string& child_name) {
489 bool unprivileged = false; 493 bool unprivileged = false;
490 const DictionaryValue* child = GetSchemaChild(name_space_node, child_name); 494 const DictionaryValue* child = GetSchemaChild(name_space_node, child_name);
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
590 std::set<std::string> result; 594 std::set<std::string> result;
591 for (SchemaMap::iterator i = schemas_.begin(); i != schemas_.end(); ++i) 595 for (SchemaMap::iterator i = schemas_.begin(); i != schemas_.end(); ++i)
592 result.insert(i->first); 596 result.insert(i->first);
593 for (UnloadedSchemaMap::iterator i = unloaded_schemas_.begin(); 597 for (UnloadedSchemaMap::iterator i = unloaded_schemas_.begin();
594 i != unloaded_schemas_.end(); ++i) { 598 i != unloaded_schemas_.end(); ++i) {
595 result.insert(i->first); 599 result.insert(i->first);
596 } 600 }
597 return result; 601 return result;
598 } 602 }
599 603
600 Feature* ExtensionAPI::GetFeature(const std::string& full_name) {
601 // Ensure it's loaded.
602 GetSchema(full_name);
603
604 std::string child_name;
605 std::string api_namespace = GetAPINameFromFullName(full_name, &child_name);
606
607 APIFeatureMap::iterator feature_map = features_.find(api_namespace);
608 if (feature_map == features_.end())
609 return NULL;
610
611 Feature* result = NULL;
612 FeatureMap::iterator child_feature = feature_map->second->find(child_name);
613 if (child_feature != feature_map->second->end()) {
614 result = child_feature->second.get();
615 } else {
616 FeatureMap::iterator parent_feature = feature_map->second->find("");
617 CHECK(parent_feature != feature_map->second->end());
618 result = parent_feature->second.get();
619 }
620
621 if (result->GetContexts()->empty()) {
622 LOG(ERROR) << "API feature '" << full_name
623 << "' must specify at least one context.";
624 return NULL;
625 }
626
627 return result;
628 }
629
630 Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) { 604 Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
605 Feature* feature = TryGetFeatureDependency(full_name);
606 CHECK(feature) << full_name;
607 return feature;
608 }
609
610 Feature* ExtensionAPI::TryGetFeatureDependency(const std::string& full_name) {
not at google - send to devlin 2013/03/26 00:34:48 I think this is my favourite method. Let's delete
cduvall 2013/03/26 19:24:05 Done.
631 std::string feature_type; 611 std::string feature_type;
632 std::string feature_name; 612 std::string feature_name;
633 SplitDependencyName(full_name, &feature_type, &feature_name); 613 SplitDependencyName(full_name, &feature_type, &feature_name);
634 614
635 FeatureProviderMap::iterator provider = 615 FeatureProviderMap::iterator provider =
636 dependency_providers_.find(feature_type); 616 dependency_providers_.find(feature_type);
637 CHECK(provider != dependency_providers_.end()) << full_name; 617 if (provider == dependency_providers_.end())
618 return NULL;
638 619
639 Feature* feature = provider->second->GetFeature(feature_name); 620 Feature* feature = provider->second->GetFeature(feature_name);
640 CHECK(feature) << full_name; 621 // Try getting the feature for the parent API, if this was a child.
not at google - send to devlin 2013/03/26 00:34:48 In the general case I think we need to step all th
cduvall 2013/03/26 19:24:05 GetAPINameFromFullName() guarantees to either retu
not at google - send to devlin 2013/03/26 21:31:21 Oh ok, never mind.
641 622 if (!feature) {
623 std::string child_name;
624 feature = provider->second->GetFeature(
625 GetAPINameFromFullName(feature_name, &child_name));
626 }
642 return feature; 627 return feature;
643 } 628 }
644 629
645 std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name, 630 std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name,
646 std::string* child_name) { 631 std::string* child_name) {
647 std::string api_name_candidate = full_name; 632 std::string api_name_candidate = full_name;
648 while (true) { 633 while (true) {
649 if (features_.find(api_name_candidate) != features_.end() || 634 if (features_.find(api_name_candidate) != features_.end() ||
650 schemas_.find(api_name_candidate) != schemas_.end() || 635 schemas_.find(api_name_candidate) != schemas_.end() ||
651 unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) { 636 unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) {
(...skipping 19 matching lines...) Expand all
671 *child_name = ""; 656 *child_name = "";
672 return ""; 657 return "";
673 } 658 }
674 659
675 bool ExtensionAPI::IsAPIAllowed(const std::string& name, 660 bool ExtensionAPI::IsAPIAllowed(const std::string& name,
676 const Extension* extension) { 661 const Extension* extension) {
677 return extension->required_permission_set()->HasAnyAccessToAPI(name) || 662 return extension->required_permission_set()->HasAnyAccessToAPI(name) ||
678 extension->optional_permission_set()->HasAnyAccessToAPI(name); 663 extension->optional_permission_set()->HasAnyAccessToAPI(name);
679 } 664 }
680 665
681 void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) {
682 std::set<std::string> missing_dependencies;
683 for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i)
684 GetMissingDependencies(*i, *out, &missing_dependencies);
685
686 while (missing_dependencies.size()) {
687 std::string next = *missing_dependencies.begin();
688 missing_dependencies.erase(next);
689 out->insert(next);
690 GetMissingDependencies(next, *out, &missing_dependencies);
691 }
692 }
693
694 void ExtensionAPI::GetMissingDependencies(
695 const std::string& api_name,
696 const std::set<std::string>& excluding,
697 std::set<std::string>* out) {
698 std::string feature_type;
699 std::string feature_name;
700 SplitDependencyName(api_name, &feature_type, &feature_name);
701
702 // Only API features can have dependencies for now.
703 if (feature_type != "api")
704 return;
705
706 const DictionaryValue* schema = GetSchema(feature_name);
707 CHECK(schema) << "Schema for " << feature_name << " not found";
708
709 const ListValue* dependencies = NULL;
710 if (!schema->GetList("dependencies", &dependencies))
711 return;
712
713 for (size_t i = 0; i < dependencies->GetSize(); ++i) {
714 std::string dependency_name;
715 if (dependencies->GetString(i, &dependency_name) &&
716 !excluding.count(dependency_name)) {
717 out->insert(dependency_name);
718 }
719 }
720 }
721
722 bool ExtensionAPI::IsPrivilegedAPI(const std::string& name) { 666 bool ExtensionAPI::IsPrivilegedAPI(const std::string& name) {
723 return completely_unprivileged_apis_.count(name) || 667 return completely_unprivileged_apis_.count(name) ||
724 partially_unprivileged_apis_.count(name); 668 partially_unprivileged_apis_.count(name);
725 } 669 }
726 670
727 } // namespace extensions 671 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698