OLD | NEW |
---|---|
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 18 matching lines...) Expand all Loading... | |
29 using base::DictionaryValue; | 29 using base::DictionaryValue; |
30 using base::ListValue; | 30 using base::ListValue; |
31 using base::Value; | 31 using base::Value; |
32 | 32 |
33 namespace extensions { | 33 namespace extensions { |
34 | 34 |
35 using api::GeneratedSchemas; | 35 using api::GeneratedSchemas; |
36 | 36 |
37 namespace { | 37 namespace { |
38 | 38 |
39 const char kUnavailableMessage[] = "You do not have permission to access this " | |
40 "API. Ensure that the required permission " | |
41 "or manifest property is included in your " | |
42 "manifest.json."; | |
39 const char* kChildKinds[] = { | 43 const char* kChildKinds[] = { |
40 "functions", | 44 "functions", |
41 "events" | 45 "events" |
42 }; | 46 }; |
43 | 47 |
44 // Returns true if |dict| has an unprivileged "true" property. | 48 // Returns true if |dict| has an unprivileged "true" property. |
45 bool IsUnprivileged(const DictionaryValue* dict) { | 49 bool IsUnprivileged(const DictionaryValue* dict) { |
46 bool unprivileged = false; | 50 bool unprivileged = false; |
47 return dict->GetBoolean("unprivileged", &unprivileged) && unprivileged; | 51 return dict->GetBoolean("unprivileged", &unprivileged) && unprivileged; |
48 } | 52 } |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
223 // TODO(aa): Remove this code when all API descriptions have been updated. | 227 // TODO(aa): Remove this code when all API descriptions have been updated. |
224 *feature_type = "api"; | 228 *feature_type = "api"; |
225 *feature_name = full_name; | 229 *feature_name = full_name; |
226 return; | 230 return; |
227 } | 231 } |
228 | 232 |
229 *feature_type = full_name.substr(0, colon_index); | 233 *feature_type = full_name.substr(0, colon_index); |
230 *feature_name = full_name.substr(colon_index + 1); | 234 *feature_name = full_name.substr(colon_index + 1); |
231 } | 235 } |
232 | 236 |
237 bool ExtensionAPI::UsesFeatureSystem(const std::string& full_name) { | |
238 std::string api_name = GetAPINameFromFullName(full_name, NULL); | |
239 return features_.find(api_name) != features_.end(); | |
240 } | |
241 | |
233 void ExtensionAPI::LoadSchema(const std::string& name, | 242 void ExtensionAPI::LoadSchema(const std::string& name, |
234 const base::StringPiece& schema) { | 243 const base::StringPiece& schema) { |
235 scoped_ptr<ListValue> schema_list(LoadSchemaList(name, schema)); | 244 scoped_ptr<ListValue> schema_list(LoadSchemaList(name, schema)); |
236 std::string schema_namespace; | 245 std::string schema_namespace; |
237 | 246 |
238 while (!schema_list->empty()) { | 247 while (!schema_list->empty()) { |
239 DictionaryValue* schema = NULL; | 248 DictionaryValue* schema = NULL; |
240 { | 249 { |
241 Value* value = NULL; | 250 Value* value = NULL; |
242 schema_list->Remove(schema_list->GetSize() - 1, &value); | 251 schema_list->Remove(schema_list->GetSize() - 1, &value); |
243 CHECK(value->IsType(Value::TYPE_DICTIONARY)); | 252 CHECK(value->IsType(Value::TYPE_DICTIONARY)); |
244 schema = static_cast<DictionaryValue*>(value); | 253 schema = static_cast<DictionaryValue*>(value); |
245 } | 254 } |
246 | 255 |
247 CHECK(schema->GetString("namespace", &schema_namespace)); | 256 CHECK(schema->GetString("namespace", &schema_namespace)); |
248 PrefixWithNamespace(schema_namespace, schema); | 257 PrefixWithNamespace(schema_namespace, schema); |
249 schemas_[schema_namespace] = make_linked_ptr(schema); | 258 schemas_[schema_namespace] = make_linked_ptr(schema); |
250 CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace)); | 259 CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace)); |
251 | 260 |
252 // Populate |{completely,partially}_unprivileged_apis_|. | 261 // Populate |{completely,partially}_unprivileged_apis_|. |
253 // | 262 |
254 // For "partially", only need to look at functions/events; even though | 263 // For "partially", only need to look at functions/events; even though |
255 // there are unprivileged properties (e.g. in extensions), access to those | 264 // there are unprivileged properties (e.g. in extensions), access to those |
256 // never reaches C++ land. | 265 // never reaches C++ land. |
257 bool unprivileged = false; | 266 bool unprivileged = false; |
258 if (schema->GetBoolean("unprivileged", &unprivileged) && unprivileged) { | 267 if (schema->GetBoolean("unprivileged", &unprivileged) && unprivileged) { |
259 completely_unprivileged_apis_.insert(schema_namespace); | 268 completely_unprivileged_apis_.insert(schema_namespace); |
260 } else if (HasUnprivilegedChild(schema, "functions") || | 269 } else if (HasUnprivilegedChild(schema, "functions") || |
261 HasUnprivilegedChild(schema, "events") || | 270 HasUnprivilegedChild(schema, "events") || |
262 HasUnprivilegedChild(schema, "properties")) { | 271 HasUnprivilegedChild(schema, "properties")) { |
263 partially_unprivileged_apis_.insert(schema_namespace); | 272 partially_unprivileged_apis_.insert(schema_namespace); |
264 } | 273 } |
265 | 274 |
266 // Populate |url_matching_apis_|. | 275 bool uses_feature_system = false; |
267 ListValue* matches = NULL; | 276 schema->GetBoolean("uses_feature_system", &uses_feature_system); |
268 if (schema->GetList("matches", &matches)) { | 277 |
269 URLPatternSet pattern_set; | 278 if (!uses_feature_system) { |
270 for (size_t i = 0; i < matches->GetSize(); ++i) { | 279 // Populate |url_matching_apis_|. |
271 std::string pattern; | 280 ListValue* matches = NULL; |
272 CHECK(matches->GetString(i, &pattern)); | 281 if (schema->GetList("matches", &matches)) { |
273 pattern_set.AddPattern( | 282 URLPatternSet pattern_set; |
274 URLPattern(UserScript::ValidUserScriptSchemes(), pattern)); | 283 for (size_t i = 0; i < matches->GetSize(); ++i) { |
284 std::string pattern; | |
285 CHECK(matches->GetString(i, &pattern)); | |
286 pattern_set.AddPattern( | |
287 URLPattern(UserScript::ValidUserScriptSchemes(), pattern)); | |
288 } | |
289 url_matching_apis_[schema_namespace] = pattern_set; | |
275 } | 290 } |
276 url_matching_apis_[schema_namespace] = pattern_set; | 291 continue; |
277 } | 292 } |
278 | 293 |
279 // Populate feature maps. | 294 // Populate feature maps. |
280 // TODO(aa): Consider not storing features that can never run on the current | 295 // TODO(aa): Consider not storing features that can never run on the current |
281 // machine (e.g., because of platform restrictions). | 296 // machine (e.g., because of platform restrictions). |
282 bool uses_feature_system = false; | |
283 schema->GetBoolean("uses_feature_system", &uses_feature_system); | |
284 if (!uses_feature_system) | |
285 continue; | |
286 | |
287 SimpleFeature* feature = new SimpleFeature(); | 297 SimpleFeature* feature = new SimpleFeature(); |
288 feature->set_name(schema_namespace); | 298 feature->set_name(schema_namespace); |
289 feature->Parse(schema); | 299 feature->Parse(schema); |
290 | 300 |
291 FeatureMap* schema_features = new FeatureMap(); | 301 FeatureMap* schema_features = new FeatureMap(); |
292 CHECK(features_.insert( | 302 CHECK(features_.insert( |
293 std::make_pair(schema_namespace, | 303 std::make_pair(schema_namespace, |
294 make_linked_ptr(schema_features))).second); | 304 make_linked_ptr(schema_features))).second); |
295 CHECK(schema_features->insert( | 305 CHECK(schema_features->insert( |
296 std::make_pair("", make_linked_ptr(feature))).second); | 306 std::make_pair("", make_linked_ptr(feature))).second); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
401 void ExtensionAPI::RegisterSchema(const std::string& name, | 411 void ExtensionAPI::RegisterSchema(const std::string& name, |
402 const base::StringPiece& source) { | 412 const base::StringPiece& source) { |
403 unloaded_schemas_[name] = source; | 413 unloaded_schemas_[name] = source; |
404 } | 414 } |
405 | 415 |
406 void ExtensionAPI::RegisterDependencyProvider(const std::string& name, | 416 void ExtensionAPI::RegisterDependencyProvider(const std::string& name, |
407 FeatureProvider* provider) { | 417 FeatureProvider* provider) { |
408 dependency_providers_[name] = provider; | 418 dependency_providers_[name] = provider; |
409 } | 419 } |
410 | 420 |
411 bool ExtensionAPI::IsAvailable(const std::string& full_name, | 421 Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name, |
412 const Extension* extension, | 422 const Extension* extension, |
413 Feature::Context context) { | 423 Feature::Context context, |
424 const GURL& url) { | |
414 std::set<std::string> dependency_names; | 425 std::set<std::string> dependency_names; |
415 dependency_names.insert(full_name); | 426 dependency_names.insert(full_name); |
416 ResolveDependencies(&dependency_names); | 427 ResolveDependencies(&dependency_names); |
417 | |
418 for (std::set<std::string>::iterator iter = dependency_names.begin(); | 428 for (std::set<std::string>::iterator iter = dependency_names.begin(); |
419 iter != dependency_names.end(); ++iter) { | 429 iter != dependency_names.end(); ++iter) { |
430 //////////////////////////////////////////////////////////////////////////// | |
431 // TODO(cduvall): Take this out once all APIs have been converted to | |
432 // features. | |
433 std::string feature_type; | |
434 std::string feature_name; | |
435 SplitDependencyName(*iter, &feature_type, &feature_name); | |
436 if (feature_type == "api" && !UsesFeatureSystem(feature_name)) { | |
cduvall
2013/03/22 20:26:45
This now *should* never happen since non-features
not at google - send to devlin
2013/03/22 22:10:12
Nah just take it out, code will be safe by virtue
cduvall
2013/03/22 22:52:29
Done.
| |
437 // Check the parent feature's availability if there are dependencies. | |
438 if (!IsNonFeatureAPIAvailable(feature_name, context, extension, url)) | |
439 return Feature::CreateAvailability(Feature::INVALID_CONTEXT, | |
440 kUnavailableMessage); | |
441 continue; | |
442 } | |
443 //////////////////////////////////////////////////////////////////////////// | |
444 | |
445 // This seems wrong. The dependencies aren't checked, it just checks the | |
446 // parent feature again. Changing to check dependencies makes these fail: | |
447 // ExtensionApiTest.OptionalPermissionsGranted | |
448 // ExtensionApiTest.OptionalPermissionsAutoConfirm | |
449 // ExtensionApiTest.OptionalPermissionsDeny | |
450 // | |
451 // Checking dependencies would mean that some APIs, like chrome.bookmarks | |
452 // would now be required to be in the manifest permissions when they | |
453 // previously did not need to be (the bookmarks API has the | |
454 // permission:bookmarks dependency). This seems like it would break a lot | |
455 // of things. | |
420 Feature* feature = GetFeatureDependency(full_name); | 456 Feature* feature = GetFeatureDependency(full_name); |
cduvall
2013/03/22 20:26:45
Still not quite sure what to do here. Check the ac
not at google - send to devlin
2013/03/22 22:10:12
see long IRC essay
not at google - send to devlin
2013/03/22 22:10:12
IRC resolved this right/
cduvall
2013/03/22 22:52:29
Yep, resolved.
| |
421 CHECK(feature) << *iter; | 457 CHECK(feature) << *iter; |
422 | 458 |
423 Feature::Availability availability = | 459 Feature::Availability availability = |
424 feature->IsAvailableToContext(extension, context); | 460 feature->IsAvailableToContext(extension, context, url); |
425 if (!availability.is_available()) | 461 if (!availability.is_available()) |
426 return false; | 462 return availability; |
427 } | 463 } |
428 | 464 |
429 return true; | 465 return Feature::CreateAvailability(Feature::IS_AVAILABLE, ""); |
430 } | 466 } |
431 | 467 |
432 bool ExtensionAPI::IsPrivileged(const std::string& full_name) { | 468 bool ExtensionAPI::IsPrivileged(const std::string& full_name) { |
433 std::string child_name; | 469 std::string child_name; |
434 std::string api_name = GetAPINameFromFullName(full_name, &child_name); | 470 std::string api_name = GetAPINameFromFullName(full_name, &child_name); |
435 | 471 |
436 // First try to use the feature system. | 472 // First try to use the feature system. |
437 Feature* feature(GetFeature(full_name)); | 473 Feature* feature(GetFeature(full_name)); |
438 if (feature) { | 474 if (feature) { |
439 // An API is 'privileged' if it or any of its dependencies can only be run | 475 // An API is 'privileged' if it or any of its dependencies can only be run |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
517 if (extension.is_platform_app()) { | 553 if (extension.is_platform_app()) { |
518 for (size_t i = 0; i < arraysize(kDisallowedPlatformAppFeatures); ++i) { | 554 for (size_t i = 0; i < arraysize(kDisallowedPlatformAppFeatures); ++i) { |
519 if (feature == kDisallowedPlatformAppFeatures[i]) | 555 if (feature == kDisallowedPlatformAppFeatures[i]) |
520 return false; | 556 return false; |
521 } | 557 } |
522 } | 558 } |
523 | 559 |
524 return true; | 560 return true; |
525 } | 561 } |
526 | 562 |
527 // Removes APIs from |apis| that should not be allowed for |extension|. | |
528 // TODO(kalman/asargent) - Make it possible to specify these rules | |
529 // declaratively. | |
530 void RemoveDisallowedAPIs(const Extension& extension, | |
531 std::set<std::string>* apis) { | |
532 CHECK(apis); | |
533 std::set<std::string>::iterator i = apis->begin(); | |
534 while (i != apis->end()) { | |
535 if (!IsFeatureAllowedForExtension(*i, extension)) { | |
536 apis->erase(i++); | |
537 } else { | |
538 ++i; | |
539 } | |
540 } | |
541 } | |
542 | |
543 } // namespace | 563 } // namespace |
544 | 564 |
545 std::set<std::string> ExtensionAPI::GetAPIsForContext( | 565 bool ExtensionAPI::IsNonFeatureAPIAvailable(const std::string& name, |
546 Feature::Context context, const Extension* extension, const GURL& url) { | 566 Feature::Context context, |
547 // We're forced to load all schemas now because we need to know the metadata | 567 const Extension* extension, |
548 // about every API -- and the metadata is stored in the schemas themselves. | 568 const GURL& url) { |
549 // This is a shame. | |
550 // TODO(aa/kalman): store metadata in a separate file and don't load all | |
551 // schemas. | |
552 LoadAllSchemas(); | |
553 | |
554 std::set<std::string> temp_result; | |
555 | |
556 // First handle all the APIs that have been converted to the feature system. | |
557 if (extension) { | |
558 for (APIFeatureMap::iterator iter = features_.begin(); | |
559 iter != features_.end(); ++iter) { | |
560 if (IsAvailable(iter->first, extension, context)) | |
561 temp_result.insert(iter->first); | |
562 } | |
563 } | |
564 | |
565 // Second, fall back to the old way. | |
566 // TODO(aa): Remove this when all APIs have been converted. | |
567 switch (context) { | 569 switch (context) { |
568 case Feature::UNSPECIFIED_CONTEXT: | 570 case Feature::UNSPECIFIED_CONTEXT: |
569 break; | 571 break; |
570 | 572 |
571 case Feature::BLESSED_EXTENSION_CONTEXT: | 573 case Feature::BLESSED_EXTENSION_CONTEXT: |
572 if (extension) { | 574 if (extension) { |
573 // Availability is determined by the permissions of the extension. | 575 // Availability is determined by the permissions of the extension. |
574 GetAllowedAPIs(extension, &temp_result); | 576 if (!IsAPIAllowed(name, extension)) |
575 ResolveDependencies(&temp_result); | 577 return false; |
576 RemoveDisallowedAPIs(*extension, &temp_result); | 578 if (!IsFeatureAllowedForExtension(name, *extension)) |
579 return false; | |
577 } | 580 } |
578 break; | 581 break; |
579 | 582 |
580 case Feature::UNBLESSED_EXTENSION_CONTEXT: | 583 case Feature::UNBLESSED_EXTENSION_CONTEXT: |
581 case Feature::CONTENT_SCRIPT_CONTEXT: | 584 case Feature::CONTENT_SCRIPT_CONTEXT: |
582 if (extension) { | 585 if (extension) { |
583 // Same as BLESSED_EXTENSION_CONTEXT, but only those APIs that are | 586 // Same as BLESSED_EXTENSION_CONTEXT, but only those APIs that are |
584 // unprivileged. | 587 // unprivileged. |
585 GetAllowedAPIs(extension, &temp_result); | 588 if (!IsAPIAllowed(name, extension)) |
586 // Resolving dependencies before removing unprivileged APIs means that | 589 return false; |
587 // some unprivileged APIs may have unrealised dependencies. Too bad! | 590 if (!IsPrivilegedAPI(name)) |
588 ResolveDependencies(&temp_result); | 591 return false; |
589 RemovePrivilegedAPIs(&temp_result); | |
590 } | 592 } |
591 break; | 593 break; |
592 | 594 |
593 case Feature::WEB_PAGE_CONTEXT: | 595 case Feature::WEB_PAGE_CONTEXT: |
594 if (url.is_valid()) { | 596 if (!url_matching_apis_.count(name)) |
595 // Availablility is determined by the url. | 597 return false; |
596 GetAPIsMatchingURL(url, &temp_result); | 598 CHECK(url.is_valid()); |
597 } | 599 // Availablility is determined by the url. |
598 break; | 600 return url_matching_apis_[name].MatchesURL(url); |
599 } | 601 } |
600 | 602 |
601 // Filter out all non-API features and remove the feature type part of the | 603 return true; |
602 // name. | |
603 std::set<std::string> result; | |
604 for (std::set<std::string>::iterator iter = temp_result.begin(); | |
605 iter != temp_result.end(); ++iter) { | |
606 std::string feature_type; | |
607 std::string feature_name; | |
608 SplitDependencyName(*iter, &feature_type, &feature_name); | |
609 if (feature_type == "api") | |
610 result.insert(feature_name); | |
611 } | |
612 | |
613 return result; | |
614 } | 604 } |
615 | 605 |
616 std::set<std::string> ExtensionAPI::GetAllAPINames() { | 606 std::set<std::string> ExtensionAPI::GetAllAPINames() { |
617 std::set<std::string> result; | 607 std::set<std::string> result; |
618 for (SchemaMap::iterator i = schemas_.begin(); i != schemas_.end(); ++i) | 608 for (SchemaMap::iterator i = schemas_.begin(); i != schemas_.end(); ++i) |
619 result.insert(i->first); | 609 result.insert(i->first); |
620 for (UnloadedSchemaMap::iterator i = unloaded_schemas_.begin(); | 610 for (UnloadedSchemaMap::iterator i = unloaded_schemas_.begin(); |
621 i != unloaded_schemas_.end(); ++i) { | 611 i != unloaded_schemas_.end(); ++i) { |
622 result.insert(i->first); | 612 result.insert(i->first); |
623 } | 613 } |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
692 if (last_dot_index == std::string::npos) | 682 if (last_dot_index == std::string::npos) |
693 break; | 683 break; |
694 | 684 |
695 api_name_candidate = api_name_candidate.substr(0, last_dot_index); | 685 api_name_candidate = api_name_candidate.substr(0, last_dot_index); |
696 } | 686 } |
697 | 687 |
698 *child_name = ""; | 688 *child_name = ""; |
699 return ""; | 689 return ""; |
700 } | 690 } |
701 | 691 |
702 void ExtensionAPI::GetAllowedAPIs( | 692 bool ExtensionAPI::IsAPIAllowed(const std::string& name, |
703 const Extension* extension, std::set<std::string>* out) { | 693 const Extension* extension) { |
704 for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end(); | 694 return extension->required_permission_set()->HasAnyAccessToAPI(name) || |
705 ++i) { | 695 extension->optional_permission_set()->HasAnyAccessToAPI(name); |
706 if (features_.find(i->first) != features_.end()) { | |
707 // This API is controlled by the feature system. Nothing to do here. | |
708 continue; | |
709 } | |
710 | |
711 if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) || | |
712 extension->optional_permission_set()->HasAnyAccessToAPI(i->first)) { | |
713 out->insert(i->first); | |
714 } | |
715 } | |
716 } | 696 } |
717 | 697 |
718 void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) { | 698 void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) { |
719 std::set<std::string> missing_dependencies; | 699 std::set<std::string> missing_dependencies; |
720 for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) | 700 for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) |
721 GetMissingDependencies(*i, *out, &missing_dependencies); | 701 GetMissingDependencies(*i, *out, &missing_dependencies); |
722 | 702 |
723 while (missing_dependencies.size()) { | 703 while (missing_dependencies.size()) { |
724 std::string next = *missing_dependencies.begin(); | 704 std::string next = *missing_dependencies.begin(); |
725 missing_dependencies.erase(next); | 705 missing_dependencies.erase(next); |
(...skipping 23 matching lines...) Expand all Loading... | |
749 | 729 |
750 for (size_t i = 0; i < dependencies->GetSize(); ++i) { | 730 for (size_t i = 0; i < dependencies->GetSize(); ++i) { |
751 std::string dependency_name; | 731 std::string dependency_name; |
752 if (dependencies->GetString(i, &dependency_name) && | 732 if (dependencies->GetString(i, &dependency_name) && |
753 !excluding.count(dependency_name)) { | 733 !excluding.count(dependency_name)) { |
754 out->insert(dependency_name); | 734 out->insert(dependency_name); |
755 } | 735 } |
756 } | 736 } |
757 } | 737 } |
758 | 738 |
759 void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) { | 739 bool ExtensionAPI::IsPrivilegedAPI(const std::string& name) { |
760 std::set<std::string> privileged_apis; | 740 return completely_unprivileged_apis_.count(name) || |
761 for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); | 741 partially_unprivileged_apis_.count(name); |
762 ++i) { | |
763 if (!completely_unprivileged_apis_.count(*i) && | |
764 !partially_unprivileged_apis_.count(*i)) { | |
765 privileged_apis.insert(*i); | |
766 } | |
767 } | |
768 for (std::set<std::string>::iterator i = privileged_apis.begin(); | |
769 i != privileged_apis.end(); ++i) { | |
770 apis->erase(*i); | |
771 } | |
772 } | |
773 | |
774 void ExtensionAPI::GetAPIsMatchingURL(const GURL& url, | |
775 std::set<std::string>* out) { | |
776 for (std::map<std::string, URLPatternSet>::const_iterator i = | |
777 url_matching_apis_.begin(); i != url_matching_apis_.end(); ++i) { | |
778 if (features_.find(i->first) != features_.end()) { | |
779 // This API is controlled by the feature system. Nothing to do. | |
780 continue; | |
781 } | |
782 | |
783 if (i->second.MatchesURL(url)) | |
784 out->insert(i->first); | |
785 } | |
786 } | |
787 | |
788 void ExtensionAPI::LoadAllSchemas() { | |
789 while (unloaded_schemas_.size()) { | |
790 std::map<std::string, base::StringPiece>::iterator it = | |
791 unloaded_schemas_.begin(); | |
792 LoadSchema(it->first, it->second); | |
793 } | |
794 } | 742 } |
795 | 743 |
796 } // namespace extensions | 744 } // namespace extensions |
OLD | NEW |