diff options
author | rdevlin.cronin <rdevlin.cronin@chromium.org> | 2015-02-24 12:21:10 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-24 20:21:38 +0000 |
commit | 69bf75316e7ae533c0a0dccc1a56ca019aa95a1e (patch) | |
tree | 19c73070991a2965d12a784760136c3247be45b6 | |
parent | 1d7ceee2bfb36e0a21e2d224a3bd13c447d293d7 (diff) | |
download | chromium_src-69bf75316e7ae533c0a0dccc1a56ca019aa95a1e.zip chromium_src-69bf75316e7ae533c0a0dccc1a56ca019aa95a1e.tar.gz chromium_src-69bf75316e7ae533c0a0dccc1a56ca019aa95a1e.tar.bz2 |
[Extensions] Start making chrome://extensions use extensions APIs
Allow chrome://extensions to use the developerPrivate and
management apis, and make it do so for enabling/disabling
extensions.
Also, shave yaks:
- Make management api check requirements for extensions.
- Make feature provider aware of complex parents.
And bonus:
- Remove WeakPtr interface from RequirementsChecker
BUG=461039
Review URL: https://codereview.chromium.org/951633002
Cr-Commit-Position: refs/heads/master@{#317871}
26 files changed, 353 insertions, 185 deletions
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index b87d566..001c068 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc @@ -20,6 +20,7 @@ #include "chrome/browser/extensions/api/developer_private/entry_picker.h" #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" +#include "chrome/browser/extensions/chrome_requirements_checker.h" #include "chrome/browser/extensions/devtools_util.h" #include "chrome/browser/extensions/extension_disabled_ui.h" #include "chrome/browser/extensions/extension_error_reporter.h" @@ -802,7 +803,7 @@ bool DeveloperPrivateEnableFunction::RunSync() { scoped_ptr<Enable::Params> params(Enable::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); - std::string extension_id = params->item_id; + const std::string& extension_id = params->item_id; const Extension* extension = ExtensionRegistry::Get(GetProfile())->GetExtensionById( @@ -814,11 +815,11 @@ bool DeveloperPrivateEnableFunction::RunSync() { ExtensionSystem* system = ExtensionSystem::Get(GetProfile()); ManagementPolicy* policy = system->management_policy(); bool enable = params->enable; - if (!policy->UserMayModifySettings(extension, NULL) || - (!enable && policy->MustRemainEnabled(extension, NULL)) || - (enable && policy->MustRemainDisabled(extension, NULL, NULL))) { + if (!policy->UserMayModifySettings(extension, nullptr) || + (!enable && policy->MustRemainEnabled(extension, nullptr)) || + (enable && policy->MustRemainDisabled(extension, nullptr, nullptr))) { LOG(ERROR) << "Attempt to change enable state denied by management policy. " - << "Extension id: " << extension_id.c_str(); + << "Extension id: " << extension_id; return false; } @@ -826,37 +827,29 @@ bool DeveloperPrivateEnableFunction::RunSync() { if (enable) { ExtensionPrefs* prefs = ExtensionPrefs::Get(GetProfile()); if (prefs->DidExtensionEscalatePermissions(extension_id)) { - AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile()); - CHECK(registry); - AppWindow* app_window = - registry->GetAppWindowForRenderViewHost(render_view_host()); - if (!app_window) { + // If the extension escalated permissions, we have to show a dialog. + content::WebContents* web_contents = render_view_host() ? + content::WebContents::FromRenderViewHost(render_view_host()) : + nullptr; + if (!web_contents) return false; - } - ShowExtensionDisabledDialog( - service, app_window->web_contents(), extension); + ShowExtensionDisabledDialog(service, web_contents, extension); } else if ((prefs->GetDisableReasons(extension_id) & - Extension::DISABLE_UNSUPPORTED_REQUIREMENT) && - !requirements_checker_.get()) { + Extension::DISABLE_UNSUPPORTED_REQUIREMENT)) { // Recheck the requirements. - scoped_refptr<const Extension> extension = - service->GetExtensionById(extension_id, true); - requirements_checker_.reset(new RequirementsChecker); - // Released by OnRequirementsChecked. - AddRef(); + requirements_checker_.reset(new ChromeRequirementsChecker()); + AddRef(); // Released in OnRequirementsChecked. + // TODO(devlin): Uh... asynchronous code in a sync extension function? requirements_checker_->Check( - extension, + make_scoped_refptr(extension), base::Bind(&DeveloperPrivateEnableFunction::OnRequirementsChecked, this, extension_id)); } else { + // Otherwise, we're good to re-enable the extension. service->EnableExtension(extension_id); - - // Make sure any browser action contained within it is not hidden. - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, extension->id(), true); } - } else { + } else { // !enable (i.e., disable) service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION); } return true; @@ -864,7 +857,7 @@ bool DeveloperPrivateEnableFunction::RunSync() { void DeveloperPrivateEnableFunction::OnRequirementsChecked( const std::string& extension_id, - std::vector<std::string> requirements_errors) { + const std::vector<std::string>& requirements_errors) { if (requirements_errors.empty()) { GetExtensionService(GetProfile())->EnableExtension(extension_id); } else { diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h index 58e661c..c6f33b4 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h @@ -16,7 +16,6 @@ #include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/extensions/extension_uninstall_dialog.h" #include "chrome/browser/extensions/pack_extension_job.h" -#include "chrome/browser/extensions/requirements_checker.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/render_view_host.h" @@ -35,6 +34,7 @@ class ExtensionError; class ExtensionRegistry; class ExtensionSystem; class ManagementPolicy; +class RequirementsChecker; namespace api { @@ -300,8 +300,9 @@ class DeveloperPrivateEnableFunction ~DeveloperPrivateEnableFunction() override; // Callback for requirements checker. - void OnRequirementsChecked(const std::string& extension_id, - std::vector<std::string> requirements_errors); + void OnRequirementsChecked( + const std::string& extension_id, + const std::vector<std::string>& requirements_errors); // ExtensionFunction: bool RunSync() override; diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc index ea174a7..46289d3 100644 --- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc +++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc @@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/extensions/bookmark_app_helper.h" #include "chrome/browser/extensions/chrome_extension_function_details.h" +#include "chrome/browser/extensions/chrome_requirements_checker.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/launch_util.h" @@ -207,6 +208,11 @@ ChromeManagementAPIDelegate::SetEnabledFunctionDelegate( extension)); } +scoped_ptr<extensions::RequirementsChecker> +ChromeManagementAPIDelegate::CreateRequirementsChecker() const { + return make_scoped_ptr(new extensions::ChromeRequirementsChecker()); +} + scoped_ptr<extensions::UninstallDialogDelegate> ChromeManagementAPIDelegate::UninstallFunctionDelegate( extensions::ManagementUninstallFunctionBase* function, diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.h b/chrome/browser/extensions/api/management/chrome_management_api_delegate.h index 4d0f19e..09b5578 100644 --- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.h +++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.h @@ -33,6 +33,8 @@ class ChromeManagementAPIDelegate : public extensions::ManagementAPIDelegate { scoped_ptr<extensions::InstallPromptDelegate> SetEnabledFunctionDelegate( extensions::ManagementSetEnabledFunction* function, const extensions::Extension* extension) const override; + scoped_ptr<extensions::RequirementsChecker> CreateRequirementsChecker() + const override; scoped_ptr<extensions::UninstallDialogDelegate> UninstallFunctionDelegate( extensions::ManagementUninstallFunctionBase* function, const std::string& target_extension_id) const override; diff --git a/chrome/browser/extensions/api/management/management_api_unittest.cc b/chrome/browser/extensions/api/management/management_api_unittest.cc new file mode 100644 index 0000000..48fb40e --- /dev/null +++ b/chrome/browser/extensions/api/management/management_api_unittest.cc @@ -0,0 +1,140 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/host_desktop.h" +#include "chrome/test/base/test_browser_window.h" +#include "extensions/browser/api/management/management_api.h" +#include "extensions/browser/api/management/management_api_constants.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/management_policy.h" +#include "extensions/browser/test_management_policy.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_set.h" +#include "extensions/common/test_util.h" + +namespace extensions { + +namespace { + +KeyedService* BuildManagementApi(content::BrowserContext* context) { + return new ManagementAPI(context); +} + +} // namespace + +namespace constants = extension_management_api_constants; + +// TODO(devlin): Unittests are awesome. Test more with unittests and less with +// heavy api/browser tests. +class ManagementApiUnitTest : public ExtensionServiceTestBase { + protected: + ManagementApiUnitTest() {} + ~ManagementApiUnitTest() override {} + + // A wrapper around extension_function_test_utils::RunFunction that runs with + // the associated browser, no flags, and can take stack-allocated arguments. + bool RunFunction(const scoped_refptr<UIThreadExtensionFunction>& function, + const base::ListValue& args); + + Browser* browser() { return browser_.get(); } + + private: + // ExtensionServiceTestBase: + void SetUp() override; + void TearDown() override; + + // The browser (and accompanying window). + scoped_ptr<TestBrowserWindow> browser_window_; + scoped_ptr<Browser> browser_; + + DISALLOW_COPY_AND_ASSIGN(ManagementApiUnitTest); +}; + +bool ManagementApiUnitTest::RunFunction( + const scoped_refptr<UIThreadExtensionFunction>& function, + const base::ListValue& args) { + return extension_function_test_utils::RunFunction( + function.get(), + make_scoped_ptr(args.DeepCopy()), + browser(), + extension_function_test_utils::NONE); +} + +void ManagementApiUnitTest::SetUp() { + ExtensionServiceTestBase::SetUp(); + InitializeEmptyExtensionService(); + ManagementAPI::GetFactoryInstance()->SetTestingFactory(profile(), + &BuildManagementApi); + static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))-> + SetEventRouter(make_scoped_ptr( + new EventRouter(profile(), ExtensionPrefs::Get(profile())))); + + browser_window_.reset(new TestBrowserWindow()); + Browser::CreateParams params(profile(), chrome::HOST_DESKTOP_TYPE_NATIVE); + params.type = Browser::TYPE_TABBED; + params.window = browser_window_.get(); + browser_.reset(new Browser(params)); +} + +void ManagementApiUnitTest::TearDown() { + browser_.reset(); + browser_window_.reset(); + ExtensionServiceTestBase::TearDown(); +} + +// Test the basic parts of management.setEnabled. +TEST_F(ManagementApiUnitTest, ManagementSetEnabled) { + scoped_refptr<const Extension> extension = test_util::CreateEmptyExtension(); + service()->AddExtension(extension.get()); + std::string extension_id = extension->id(); + scoped_refptr<ManagementSetEnabledFunction> function( + new ManagementSetEnabledFunction()); + + base::ListValue disable_args; + disable_args.AppendString(extension_id); + disable_args.AppendBoolean(false); + + // Test disabling an (enabled) extension. + EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); + EXPECT_TRUE(RunFunction(function, disable_args)); + EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); + + base::ListValue enable_args; + enable_args.AppendString(extension_id); + enable_args.AppendBoolean(true); + + // Test re-enabling it. + function = new ManagementSetEnabledFunction(); + EXPECT_TRUE(RunFunction(function, enable_args)); + EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); + + // Test that the enable function checks management policy, so that we can't + // disable an extension that is required. + TestManagementPolicyProvider provider( + TestManagementPolicyProvider::PROHIBIT_MODIFY_STATUS); + ManagementPolicy* policy = + ExtensionSystem::Get(profile())->management_policy(); + policy->RegisterProvider(&provider); + + function = new ManagementSetEnabledFunction(); + EXPECT_FALSE(RunFunction(function, disable_args)); + EXPECT_EQ(ErrorUtils::FormatErrorMessage(constants::kUserCantModifyError, + extension_id), + function->GetError()); + policy->UnregisterProvider(&provider); + + // TODO(devlin): We should also test enabling an extenion that has escalated + // permissions, but that needs a web contents (which is a bit of a pain in a + // unit test). +} + +} // namespace extensions diff --git a/chrome/browser/extensions/requirements_checker.cc b/chrome/browser/extensions/chrome_requirements_checker.cc index 23a3e77..811bfdd 100644 --- a/chrome/browser/extensions/requirements_checker.cc +++ b/chrome/browser/extensions/chrome_requirements_checker.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/requirements_checker.h" +#include "chrome/browser/extensions/chrome_requirements_checker.h" #include "base/bind.h" #include "chrome/browser/gpu/gpu_feature_checker.h" @@ -20,15 +20,16 @@ namespace extensions { -RequirementsChecker::RequirementsChecker() - : pending_requirement_checks_(0) { +ChromeRequirementsChecker::ChromeRequirementsChecker() + : pending_requirement_checks_(0), weak_ptr_factory_(this) { } -RequirementsChecker::~RequirementsChecker() { +ChromeRequirementsChecker::~ChromeRequirementsChecker() { } -void RequirementsChecker::Check(scoped_refptr<const Extension> extension, - base::Callback<void(std::vector<std::string> errors)> callback) { +void ChromeRequirementsChecker::Check( + const scoped_refptr<const Extension>& extension, + const RequirementsCheckedCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); callback_ = callback; @@ -58,9 +59,9 @@ void RequirementsChecker::Check(scoped_refptr<const Extension> extension, if (requirements.webgl) { ++pending_requirement_checks_; webgl_checker_ = new GPUFeatureChecker( - gpu::GPU_FEATURE_TYPE_WEBGL, - base::Bind(&RequirementsChecker::SetWebGLAvailability, - AsWeakPtr())); + gpu::GPU_FEATURE_TYPE_WEBGL, + base::Bind(&ChromeRequirementsChecker::SetWebGLAvailability, + weak_ptr_factory_.GetWeakPtr())); } if (pending_requirement_checks_ == 0) { @@ -76,7 +77,7 @@ void RequirementsChecker::Check(scoped_refptr<const Extension> extension, webgl_checker_->CheckGPUFeatureAvailability(); } -void RequirementsChecker::SetWebGLAvailability(bool available) { +void ChromeRequirementsChecker::SetWebGLAvailability(bool available) { if (!available) { errors_.push_back( l10n_util::GetStringUTF8(IDS_EXTENSION_WEBGL_NOT_SUPPORTED)); @@ -84,7 +85,7 @@ void RequirementsChecker::SetWebGLAvailability(bool available) { MaybeRunCallback(); } -void RequirementsChecker::MaybeRunCallback() { +void ChromeRequirementsChecker::MaybeRunCallback() { if (--pending_requirement_checks_ == 0) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(callback_, errors_)); diff --git a/chrome/browser/extensions/requirements_checker.h b/chrome/browser/extensions/chrome_requirements_checker.h index 1626789..b892377 100644 --- a/chrome/browser/extensions/requirements_checker.h +++ b/chrome/browser/extensions/chrome_requirements_checker.h @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_EXTENSIONS_REQUIREMENTS_CHECKER_H_ -#define CHROME_BROWSER_EXTENSIONS_REQUIREMENTS_CHECKER_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_CHROME_REQUIREMENTS_CHECKER_H_ +#define CHROME_BROWSER_EXTENSIONS_CHROME_REQUIREMENTS_CHECKER_H_ #include <vector> #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/extensions/extension_service.h" +#include "extensions/browser/requirements_checker.h" class GPUFeatureChecker; @@ -21,20 +21,16 @@ class Extension; // asynchronous process that involves several threads, but the public interface // of this class (including constructor and destructor) must only be used on // the UI thread. -class RequirementsChecker : public base::SupportsWeakPtr<RequirementsChecker> { +class ChromeRequirementsChecker : public RequirementsChecker { public: - RequirementsChecker(); - ~RequirementsChecker(); - - // The vector passed to the callback are any localized errors describing - // requirement violations. If this vector is non-empty, requirements checking - // failed. This should only be called once. |callback| will always be invoked - // asynchronously on the UI thread. |callback| will only be called once, and - // will be reset after called. - void Check(scoped_refptr<const Extension> extension, - base::Callback<void(std::vector<std::string> requirement)> callback); + ChromeRequirementsChecker(); + ~ChromeRequirementsChecker() override; private: + // RequirementsChecker: + void Check(const scoped_refptr<const Extension>& extension, + const RequirementsCheckedCallback& callback) override; + // Callbacks for the GPUFeatureChecker. void SetWebGLAvailability(bool available); @@ -48,9 +44,13 @@ class RequirementsChecker : public base::SupportsWeakPtr<RequirementsChecker> { scoped_refptr<GPUFeatureChecker> webgl_checker_; - base::Callback<void(std::vector<std::string> requirement_errorss)> callback_; + RequirementsCheckedCallback callback_; + + base::WeakPtrFactory<ChromeRequirementsChecker> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ChromeRequirementsChecker); }; } // namespace extensions -#endif // CHROME_BROWSER_EXTENSIONS_REQUIREMENTS_CHECKER_H_ +#endif // CHROME_BROWSER_EXTENSIONS_CHROME_REQUIREMENTS_CHECKER_H_ diff --git a/chrome/browser/extensions/extension_install_checker.cc b/chrome/browser/extensions/extension_install_checker.cc index 0901a00..313225df 100644 --- a/chrome/browser/extensions/extension_install_checker.cc +++ b/chrome/browser/extensions/extension_install_checker.cc @@ -6,7 +6,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/extensions/blacklist.h" -#include "chrome/browser/extensions/requirements_checker.h" +#include "chrome/browser/extensions/chrome_requirements_checker.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/extension_system.h" @@ -90,7 +90,7 @@ void ExtensionInstallChecker::CheckRequirements() { DCHECK(extension_.get()); if (!requirements_checker_.get()) - requirements_checker_.reset(new RequirementsChecker()); + requirements_checker_.reset(new ChromeRequirementsChecker()); requirements_checker_->Check( extension_, base::Bind(&ExtensionInstallChecker::OnRequirementsCheckDone, @@ -100,7 +100,7 @@ void ExtensionInstallChecker::CheckRequirements() { void ExtensionInstallChecker::OnRequirementsCheckDone( int sequence_number, - std::vector<std::string> errors) { + const std::vector<std::string>& errors) { // Some pending results may arrive after fail fast. if (sequence_number != current_sequence_number_) return; diff --git a/chrome/browser/extensions/extension_install_checker.h b/chrome/browser/extensions/extension_install_checker.h index 17a517bd..a13bdff 100644 --- a/chrome/browser/extensions/extension_install_checker.h +++ b/chrome/browser/extensions/extension_install_checker.h @@ -84,7 +84,7 @@ class ExtensionInstallChecker { virtual void CheckRequirements(); void OnRequirementsCheckDone(int sequence_number, - std::vector<std::string> errors); + const std::vector<std::string>& errors); virtual void CheckBlacklistState(); void OnBlacklistStateCheckDone(int sequence_number, BlacklistState state); diff --git a/chrome/browser/extensions/requirements_checker_browsertest.cc b/chrome/browser/extensions/requirements_checker_browsertest.cc index 7168151..efda739 100644 --- a/chrome/browser/extensions/requirements_checker_browsertest.cc +++ b/chrome/browser/extensions/requirements_checker_browsertest.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/requirements_checker.h" - #include <vector> #include "base/bind.h" @@ -12,6 +10,7 @@ #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/strings/string_util.h" +#include "chrome/browser/extensions/chrome_requirements_checker.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/common/chrome_paths.h" #include "chrome/grit/generated_resources.h" @@ -27,6 +26,9 @@ namespace extensions { class RequirementsCheckerBrowserTest : public ExtensionBrowserTest { public: + RequirementsCheckerBrowserTest() + : checker_(new ChromeRequirementsChecker()) {} + scoped_refptr<const Extension> LoadExtensionFromDirName( const std::string& extension_dir_name) { base::FilePath extension_path; @@ -40,10 +42,10 @@ class RequirementsCheckerBrowserTest : public ExtensionBrowserTest { return extension; } - void ValidateRequirementErrors(std::vector<std::string> expected_errors, - std::vector<std::string> actual_errors) { + void ValidateRequirementErrors( + const std::vector<std::string>& expected_errors, + const std::vector<std::string>& actual_errors) { ASSERT_EQ(expected_errors, actual_errors); - requirement_errors_.swap(actual_errors); } // This should only be called once per test instance. Calling more than once @@ -73,15 +75,14 @@ class RequirementsCheckerBrowserTest : public ExtensionBrowserTest { } protected: - std::vector<std::string> requirement_errors_; - RequirementsChecker checker_; + scoped_ptr<RequirementsChecker> checker_; }; IN_PROC_BROWSER_TEST_F(RequirementsCheckerBrowserTest, CheckEmptyExtension) { scoped_refptr<const Extension> extension( LoadExtensionFromDirName("no_requirements")); ASSERT_TRUE(extension.get()); - checker_.Check(extension, base::Bind( + checker_->Check(extension, base::Bind( &RequirementsCheckerBrowserTest::ValidateRequirementErrors, base::Unretained(this), std::vector<std::string>())); content::RunAllBlockingPoolTasksUntilIdle(); @@ -98,7 +99,7 @@ IN_PROC_BROWSER_TEST_F(RequirementsCheckerBrowserTest, CheckNpapiExtension) { IDS_EXTENSION_NPAPI_NOT_SUPPORTED)); #endif - checker_.Check(extension, base::Bind( + checker_->Check(extension, base::Bind( &RequirementsCheckerBrowserTest::ValidateRequirementErrors, base::Unretained(this), expected_errors)); content::RunAllBlockingPoolTasksUntilIdle(); @@ -116,7 +117,7 @@ IN_PROC_BROWSER_TEST_F(RequirementsCheckerBrowserTest, IDS_EXTENSION_WINDOW_SHAPE_NOT_SUPPORTED)); #endif // !defined(USE_AURA) - checker_.Check(extension, base::Bind( + checker_->Check(extension, base::Bind( &RequirementsCheckerBrowserTest::ValidateRequirementErrors, base::Unretained(this), expected_errors)); content::RunAllBlockingPoolTasksUntilIdle(); @@ -137,7 +138,7 @@ IN_PROC_BROWSER_TEST_F(RequirementsCheckerBrowserTest, DisallowWebGL) { expected_errors.push_back(l10n_util::GetStringUTF8( IDS_EXTENSION_WEBGL_NOT_SUPPORTED)); - checker_.Check(extension, base::Bind( + checker_->Check(extension, base::Bind( &RequirementsCheckerBrowserTest::ValidateRequirementErrors, base::Unretained(this), expected_errors)); content::RunAllBlockingPoolTasksUntilIdle(); @@ -155,7 +156,7 @@ IN_PROC_BROWSER_TEST_F(RequirementsCheckerBrowserTest, Check3DExtension) { IDS_EXTENSION_WEBGL_NOT_SUPPORTED)); } - checker_.Check(extension, base::Bind( + checker_->Check(extension, base::Bind( &RequirementsCheckerBrowserTest::ValidateRequirementErrors, base::Unretained(this), expected_errors)); content::RunAllBlockingPoolTasksUntilIdle(); diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js index 6083af0..6bd4f0a 100644 --- a/chrome/browser/resources/extensions/extension_list.js +++ b/chrome/browser/resources/extensions/extension_list.js @@ -271,7 +271,8 @@ cr.define('options', function() { // The 'Enabled' checkbox. this.addListener_('change', node, '.enable-checkbox input', function(e) { var checked = e.target.checked; - chrome.send('extensionSettingsEnable', [extension.id, String(checked)]); + // TODO(devlin): What should we do if this fails? + chrome.management.setEnabled(extension.id, checked); // This may seem counter-intuitive (to not set/clear the checkmark) // but this page will be updated asynchronously if the extension diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc index 9537f61..64afdb0 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc @@ -29,7 +29,6 @@ #include "chrome/browser/extensions/devtools_util.h" #include "chrome/browser/extensions/error_console/error_console.h" #include "chrome/browser/extensions/extension_action_manager.h" -#include "chrome/browser/extensions/extension_disabled_ui.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/extensions/extension_service.h" @@ -693,9 +692,6 @@ void ExtensionSettingsHandler::RegisterMessages() { web_ui()->RegisterMessageCallback("extensionSettingsRepair", base::Bind(&ExtensionSettingsHandler::HandleRepairMessage, AsWeakPtr())); - web_ui()->RegisterMessageCallback("extensionSettingsEnable", - base::Bind(&ExtensionSettingsHandler::HandleEnableMessage, - AsWeakPtr())); web_ui()->RegisterMessageCallback("extensionSettingsEnableIncognito", base::Bind(&ExtensionSettingsHandler::HandleEnableIncognitoMessage, AsWeakPtr())); @@ -1065,50 +1061,6 @@ void ExtensionSettingsHandler::HandleRepairMessage( reinstaller->BeginReinstall(); } -void ExtensionSettingsHandler::HandleEnableMessage( - const base::ListValue* args) { - CHECK_EQ(2U, args->GetSize()); - std::string extension_id, enable_str; - CHECK(args->GetString(0, &extension_id)); - CHECK(args->GetString(1, &enable_str)); - - const Extension* extension = - extension_service_->GetInstalledExtension(extension_id); - if (!extension) - return; - - if (!management_policy_->UserMayModifySettings(extension, NULL)) { - LOG(ERROR) << "An attempt was made to enable an extension that is " - << "non-usermanagable. Extension id: " << extension->id(); - return; - } - - if (enable_str == "true") { - ExtensionPrefs* prefs = ExtensionPrefs::Get(extension_service_->profile()); - if (prefs->DidExtensionEscalatePermissions(extension_id)) { - ShowExtensionDisabledDialog( - extension_service_, web_ui()->GetWebContents(), extension); - } else if ((prefs->GetDisableReasons(extension_id) & - Extension::DISABLE_UNSUPPORTED_REQUIREMENT) && - !requirements_checker_.get()) { - // Recheck the requirements. - scoped_refptr<const Extension> extension = - extension_service_->GetExtensionById(extension_id, - true /* include disabled */); - requirements_checker_.reset(new RequirementsChecker); - requirements_checker_->Check( - extension, - base::Bind(&ExtensionSettingsHandler::OnRequirementsChecked, - AsWeakPtr(), extension_id)); - } else { - extension_service_->EnableExtension(extension_id); - } - } else { - extension_service_->DisableExtension( - extension_id, Extension::DISABLE_USER_ACTION); - } -} - void ExtensionSettingsHandler::HandleEnableIncognitoMessage( const base::ListValue* args) { CHECK_EQ(2U, args->GetSize()); @@ -1521,17 +1473,4 @@ void ExtensionSettingsHandler::OnReinstallComplete( MaybeUpdateAfterNotification(); } -void ExtensionSettingsHandler::OnRequirementsChecked( - std::string extension_id, - std::vector<std::string> requirement_errors) { - if (requirement_errors.empty()) { - extension_service_->EnableExtension(extension_id); - } else { - ExtensionErrorReporter::GetInstance()->ReportError( - base::UTF8ToUTF16(JoinString(requirement_errors, ' ')), - true); // Be noisy. - } - requirements_checker_.reset(); -} - } // namespace extensions diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chrome/browser/ui/webui/extensions/extension_settings_handler.h index 779a484..1a99f98 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.h +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.h @@ -15,7 +15,6 @@ #include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/extensions/extension_uninstall_dialog.h" -#include "chrome/browser/extensions/requirements_checker.h" #include "chrome/common/extensions/webstore_install_result.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_observer.h" @@ -171,9 +170,6 @@ class ExtensionSettingsHandler // Callback for "repair" message. void HandleRepairMessage(const base::ListValue* args); - // Callback for "enable" message. - void HandleEnableMessage(const base::ListValue* args); - // Callback for "enableIncognito" message. void HandleEnableIncognitoMessage(const base::ListValue* args); @@ -240,10 +236,6 @@ class ExtensionSettingsHandler const std::string& error, webstore_install::Result result); - // Callback for RequirementsChecker. - void OnRequirementsChecked(std::string extension_id, - std::vector<std::string> requirement_errors); - // Handles the load retry notification sent from // ExtensionService::ReportExtensionLoadError. Attempts to retry loading // extension from |path| if retry is true, otherwise removes |path| from the @@ -288,11 +280,6 @@ class ExtensionSettingsHandler content::NotificationRegistrar registrar_; - // This will not be empty when a requirements check is in progress. Doing - // another Check() before the previous one is complete will cause the first - // one to abort. - scoped_ptr<RequirementsChecker> requirements_checker_; - // The UI for showing what permissions the extension has. scoped_ptr<ExtensionInstallPrompt> prompt_; diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 66e4e45b..0d8eec1 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -508,6 +508,8 @@ 'browser/extensions/chrome_notification_observer.h', 'browser/extensions/chrome_process_manager_delegate.cc', 'browser/extensions/chrome_process_manager_delegate.h', + 'browser/extensions/chrome_requirements_checker.cc', + 'browser/extensions/chrome_requirements_checker.h', 'browser/extensions/chrome_url_request_util.cc', 'browser/extensions/chrome_url_request_util.h', 'browser/extensions/component_loader.cc', @@ -722,8 +724,6 @@ 'browser/extensions/plugin_manager.h', 'browser/extensions/proxy_overridden_bubble_controller.cc', 'browser/extensions/proxy_overridden_bubble_controller.h', - 'browser/extensions/requirements_checker.cc', - 'browser/extensions/requirements_checker.h', 'browser/extensions/settings_api_bubble_controller.cc', 'browser/extensions/settings_api_bubble_controller.h', 'browser/extensions/settings_api_helpers.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 5c98e09..371ce1e 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -27,6 +27,7 @@ 'browser/autocomplete/builtin_provider_unittest.cc', 'browser/autocomplete/history_quick_provider_unittest.cc', 'browser/autocomplete/history_url_provider_unittest.cc', + 'browser/autocomplete/scored_history_match_builder_impl_unittest.cc', 'browser/autocomplete/search_provider_unittest.cc', 'browser/autocomplete/shortcuts_backend_unittest.cc', 'browser/autocomplete/shortcuts_database_unittest.cc', @@ -111,7 +112,6 @@ 'browser/history/history_querying_unittest.cc', 'browser/history/history_unittest.cc', 'browser/history/in_memory_url_index_unittest.cc', - 'browser/autocomplete/scored_history_match_builder_impl_unittest.cc', 'browser/history/thumbnail_database_unittest.cc', 'browser/history/top_sites_impl_unittest.cc', 'browser/history/typed_url_syncable_service_unittest.cc', @@ -692,6 +692,7 @@ 'browser/extensions/api/image_writer_private/test_utils.cc', 'browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc', 'browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc', + 'browser/extensions/api/management/management_api_unittest.cc', 'browser/extensions/api/mdns/dns_sd_registry_unittest.cc', 'browser/extensions/api/omnibox/omnibox_unittest.cc', 'browser/extensions/api/permissions/permissions_api_helpers_unittest.cc', diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json index ca31e20..251a48a 100644 --- a/chrome/common/extensions/api/_api_features.json +++ b/chrome/common/extensions/api/_api_features.json @@ -248,10 +248,18 @@ ], "contexts": ["blessed_extension"] }], - "developerPrivate": { + "developerPrivate": [{ "dependencies": ["permission:developerPrivate"], "contexts": ["blessed_extension"] - }, + }, { + "channel": "stable", + "contexts": ["webui"], + "matches": [ + "chrome://extensions/*", + "chrome://extensions-frame/*", + "chrome://chrome/extensions/*" + ] + }], // All devtools APIs are implemented by hand, so don't compile them. "devtools.inspectedWindow": { "nocompile": true, diff --git a/chrome/test/data/extensions/webui/sanity_check_available_apis.js b/chrome/test/data/extensions/webui/sanity_check_available_apis.js index b345b3b..195b02a 100644 --- a/chrome/test/data/extensions/webui/sanity_check_available_apis.js +++ b/chrome/test/data/extensions/webui/sanity_check_available_apis.js @@ -14,8 +14,10 @@ var expected = [ 'csi', + 'developerPrivate', 'getVariableValue', 'loadTimes', + 'management', 'runtime', 'send', 'test', diff --git a/extensions/browser/api/management/management_api.cc b/extensions/browser/api/management/management_api.cc index 08700dc..4d2292c 100644 --- a/extensions/browser/api/management/management_api.cc +++ b/extensions/browser/api/management/management_api.cc @@ -26,6 +26,7 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/management_policy.h" +#include "extensions/browser/requirements_checker.h" #include "extensions/browser/uninstall_reason.h" #include "extensions/common/api/management.h" #include "extensions/common/constants.h" @@ -419,7 +420,7 @@ ManagementSetEnabledFunction::ManagementSetEnabledFunction() { ManagementSetEnabledFunction::~ManagementSetEnabledFunction() { } -bool ManagementSetEnabledFunction::RunAsync() { +ExtensionFunction::ResponseAction ManagementSetEnabledFunction::Run() { scoped_ptr<management::SetEnabled::Params> params( management::SetEnabled::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); @@ -432,36 +433,41 @@ bool ManagementSetEnabledFunction::RunAsync() { const Extension* extension = registry->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING); - if (!extension || ShouldNotBeVisible(extension, browser_context())) { - error_ = - ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, extension_id_); - return false; - } + if (!extension || ShouldNotBeVisible(extension, browser_context())) + return RespondNow(Error(keys::kNoExtensionError, extension_id_)); + bool enabled = params->enabled; const ManagementPolicy* policy = ExtensionSystem::Get(browser_context())->management_policy(); - if (!policy->UserMayModifySettings(extension, NULL) || - (!params->enabled && policy->MustRemainEnabled(extension, NULL)) || - (params->enabled && policy->MustRemainDisabled(extension, NULL, NULL))) { - error_ = ErrorUtils::FormatErrorMessage(keys::kUserCantModifyError, - extension_id_); - return false; + if (!policy->UserMayModifySettings(extension, nullptr) || + (!enabled && policy->MustRemainEnabled(extension, nullptr)) || + (enabled && policy->MustRemainDisabled(extension, nullptr, nullptr))) { + return RespondNow(Error(keys::kUserCantModifyError, extension_id_)); } bool currently_enabled = registry->enabled_extensions().Contains(extension_id_) || registry->terminated_extensions().Contains(extension_id_); - if (!currently_enabled && params->enabled) { + if (!currently_enabled && enabled) { ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context()); if (prefs->DidExtensionEscalatePermissions(extension_id_)) { - if (!user_gesture()) { - SetError(keys::kGestureNeededForEscalationError); - return false; - } + if (!user_gesture()) + return RespondNow(Error(keys::kGestureNeededForEscalationError)); + AddRef(); // Matched in InstallUIProceed/InstallUIAbort install_prompt_ = delegate->SetEnabledFunctionDelegate(this, extension); - return true; + return RespondLater(); + } + if (prefs->GetDisableReasons(extension_id_) & + Extension::DISABLE_UNSUPPORTED_REQUIREMENT) { + // Recheck the requirements. + requirements_checker_ = delegate->CreateRequirementsChecker(); + requirements_checker_->Check( + extension, + base::Bind(&ManagementSetEnabledFunction::OnRequirementsChecked, + this)); // This bind creates a reference. + return RespondLater(); } delegate->EnableExtension(browser_context(), extension_id_); } else if (currently_enabled && !params->enabled) { @@ -469,11 +475,7 @@ bool ManagementSetEnabledFunction::RunAsync() { Extension::DISABLE_USER_ACTION); } - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&ManagementSetEnabledFunction::SendResponse, this, true)); - - return true; + return RespondNow(NoArguments()); } void ManagementSetEnabledFunction::InstallUIProceed() { @@ -481,16 +483,28 @@ void ManagementSetEnabledFunction::InstallUIProceed() { ->Get(browser_context()) ->GetDelegate() ->EnableExtension(browser_context(), extension_id_); - SendResponse(true); + Respond(OneArgument(new base::FundamentalValue(true))); Release(); } void ManagementSetEnabledFunction::InstallUIAbort(bool user_initiated) { - error_ = keys::kUserDidNotReEnableError; - SendResponse(false); + Respond(Error(keys::kUserDidNotReEnableError)); Release(); } +void ManagementSetEnabledFunction::OnRequirementsChecked( + const std::vector<std::string>& requirements_errors) { + if (requirements_errors.empty()) { + ManagementAPI::GetFactoryInstance()->Get(browser_context())->GetDelegate()-> + EnableExtension(browser_context(), extension_id_); + Respond(NoArguments()); + } else { + // TODO(devlin): Should we really be noisy here all the time? + Respond(Error(keys::kMissingRequirementsError, + JoinString(requirements_errors, ' '))); + } +} + ManagementUninstallFunctionBase::ManagementUninstallFunctionBase() { } diff --git a/extensions/browser/api/management/management_api.h b/extensions/browser/api/management/management_api.h index ab7fb84..e49789b 100644 --- a/extensions/browser/api/management/management_api.h +++ b/extensions/browser/api/management/management_api.h @@ -20,6 +20,7 @@ struct WebApplicationInfo; namespace extensions { class ExtensionRegistry; +class RequirementsChecker; class ManagementFunction : public SyncExtensionFunction { protected: @@ -104,7 +105,7 @@ class ManagementLaunchAppFunction : public ManagementFunction { bool RunSync() override; }; -class ManagementSetEnabledFunction : public AsyncManagementFunction { +class ManagementSetEnabledFunction : public UIThreadExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("management.setEnabled", MANAGEMENT_SETENABLED) @@ -117,12 +118,16 @@ class ManagementSetEnabledFunction : public AsyncManagementFunction { ~ManagementSetEnabledFunction() override; // ExtensionFunction: - bool RunAsync() override; + ResponseAction Run() override; private: + void OnRequirementsChecked(const std::vector<std::string>& requirements); + std::string extension_id_; scoped_ptr<InstallPromptDelegate> install_prompt_; + + scoped_ptr<RequirementsChecker> requirements_checker_; }; class ManagementUninstallFunctionBase : public AsyncManagementFunction { diff --git a/extensions/browser/api/management/management_api_constants.cc b/extensions/browser/api/management/management_api_constants.cc index f4fb8ff..924b357 100644 --- a/extensions/browser/api/management/management_api_constants.cc +++ b/extensions/browser/api/management/management_api_constants.cc @@ -25,6 +25,7 @@ const char kUninstallCanceledError[] = "Extension * uninstall canceled by user."; const char kUserDidNotReEnableError[] = "The user did not accept the re-enable dialog."; +const char kMissingRequirementsError[] = "There were missing requirements: *."; const char kGestureNeededForCreateAppShortcutError[] = "chrome.management.createAppShortcut requires a user gesture."; const char kNoBrowserToCreateShortcut[] = diff --git a/extensions/browser/api/management/management_api_constants.h b/extensions/browser/api/management/management_api_constants.h index faadae1..bdbeffb 100644 --- a/extensions/browser/api/management/management_api_constants.h +++ b/extensions/browser/api/management/management_api_constants.h @@ -23,6 +23,7 @@ extern const char kNotAnAppError[]; extern const char kUserCantModifyError[]; extern const char kUninstallCanceledError[]; extern const char kUserDidNotReEnableError[]; +extern const char kMissingRequirementsError[]; extern const char kGestureNeededForCreateAppShortcutError[]; extern const char kNoBrowserToCreateShortcut[]; extern const char kCreateOnlyPackagedAppShortcutMac[]; diff --git a/extensions/browser/api/management/management_api_delegate.h b/extensions/browser/api/management/management_api_delegate.h index 6311b3d..1af47bf 100644 --- a/extensions/browser/api/management/management_api_delegate.h +++ b/extensions/browser/api/management/management_api_delegate.h @@ -25,6 +25,7 @@ class ManagementGenerateAppForLinkFunction; class ManagementGetPermissionWarningsByManifestFunction; class ManagementSetEnabledFunction; class ManagementUninstallFunctionBase; +class RequirementsChecker; // Manages the lifetime of the install prompt. class InstallPromptDelegate { @@ -76,6 +77,9 @@ class ManagementAPIDelegate { ManagementSetEnabledFunction* function, const Extension* extension) const = 0; + // Returns a new RequirementsChecker. + virtual scoped_ptr<RequirementsChecker> CreateRequirementsChecker() const = 0; + // Enables the extension identified by |extension_id|. virtual void EnableExtension(content::BrowserContext* context, const std::string& extension_id) const = 0; diff --git a/extensions/browser/requirements_checker.h b/extensions/browser/requirements_checker.h new file mode 100644 index 0000000..a5c1614 --- /dev/null +++ b/extensions/browser/requirements_checker.h @@ -0,0 +1,38 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_REQUIREMENTS_CHECKER_H_ +#define CHROME_BROWSER_EXTENSIONS_REQUIREMENTS_CHECKER_H_ + +#include <vector> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" + +namespace extensions { +class Extension; + +// Validates the 'requirements' extension manifest field. This is an +// asynchronous process that involves several threads, but the public interface +// of this class (including constructor and destructor) must only be used on +// the UI thread. +class RequirementsChecker { + public: + virtual ~RequirementsChecker() {} + + using RequirementsCheckedCallback = + base::Callback<void(const std::vector<std::string>& /* requirements */)>; + + // The vector passed to the callback are any localized errors describing + // requirement violations. If this vector is non-empty, requirements checking + // failed. This should only be called once. |callback| will always be invoked + // asynchronously on the UI thread. |callback| will only be called once, and + // will be reset after called. + virtual void Check(const scoped_refptr<const Extension>& extension, + const RequirementsCheckedCallback& callback) = 0; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_REQUIREMENTS_CHECKER_H_ diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index c2c16ed..76a6cdf 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -173,10 +173,19 @@ "dependencies": ["permission:idle"], "contexts": ["blessed_extension"] }, - "management": { + "management": [{ "dependencies": ["permission:management"], - "contexts": ["blessed_extension"] - }, + "contexts": ["blessed_extension"], + "default_parent": true + }, { + "channel": "stable", + "contexts": ["webui"], + "matches": [ + "chrome://extensions/*", + "chrome://extensions-frame/*", + "chrome://chrome/extensions/*" + ] + }], "management.getPermissionWarningsByManifest": { "dependencies": [], "channel": "stable", diff --git a/extensions/common/features/base_feature_provider.cc b/extensions/common/features/base_feature_provider.cc index 9b927cc..40807f41 100644 --- a/extensions/common/features/base_feature_provider.cc +++ b/extensions/common/features/base_feature_provider.cc @@ -66,7 +66,20 @@ BaseFeatureProvider::BaseFeatureProvider(const base::DictionaryValue& root, split.pop_back(); if (root.HasKey(parent_name)) { const base::DictionaryValue* parent = nullptr; - CHECK(root.GetDictionaryWithoutPathExpansion(parent_name, &parent)); + if (!root.GetDictionaryWithoutPathExpansion(parent_name, &parent)) { + // If the parent is a complex feature, find the parent with the + // 'default_parent' flag. + const base::ListValue* parent_list = nullptr; + CHECK(root.GetListWithoutPathExpansion(parent_name, &parent_list)); + for (size_t i = 0; i < parent_list->GetSize(); ++i) { + CHECK(parent_list->GetDictionary(i, &parent)); + if (parent->HasKey("default_parent")) + break; + parent = nullptr; + } + CHECK(parent) << parent_name << " must declare one of its features" + << " the default parent, with {\"default_parent\": true}."; + } parse_stack.push(std::make_pair(parent_name, parent)); bool no_parent = false; parent->GetBoolean("noparent", &no_parent); diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi index 7caee4a..b8641e0 100644 --- a/extensions/extensions.gypi +++ b/extensions/extensions.gypi @@ -707,6 +707,7 @@ 'browser/quota_service.h', 'browser/renderer_startup_helper.cc', 'browser/renderer_startup_helper.h', + 'browser/requirements_checker.h', 'browser/runtime_data.cc', 'browser/runtime_data.h', 'browser/sandboxed_unpacker.cc', |