diff options
author | dpolukhin@chromium.org <dpolukhin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-04 03:41:04 +0000 |
---|---|---|
committer | dpolukhin@chromium.org <dpolukhin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-04 03:41:04 +0000 |
commit | aa5a547a89c09639881232912a95a7428ed81272 (patch) | |
tree | bf49bf0026aa88aba634913005289a75039a2d32 | |
parent | 065cd860161f3e90f7467260b15e150393c22d81 (diff) | |
download | chromium_src-aa5a547a89c09639881232912a95a7428ed81272.zip chromium_src-aa5a547a89c09639881232912a95a7428ed81272.tar.gz chromium_src-aa5a547a89c09639881232912a95a7428ed81272.tar.bz2 |
Add first run prompt fot OEM default apps to acknowledge app permissions
BUG=296393
TEST=manual
Review URL: https://codereview.chromium.org/25769004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@226946 0039d316-1c4b-4281-b951-d872f2087c98
16 files changed, 200 insertions, 19 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 1e49b36..7e0084b 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4934,6 +4934,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON" desc="Text for the Revoke File Access button on the extension permissions prompt"> Revoke File Access </message> + <message name="IDS_EXTENSION_PROMPT_FIRST_RUN_ACCEPT_BUTTON" desc="Text for the allow button on the first run promopt to accept permissions and launch app"> + Accept and open + </message> <message name="IDS_EXTENSION_WEB_STORE_TITLE" desc="Text for the Chrome Web Store"> Chrome Web Store </message> @@ -5024,6 +5027,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON" desc="Button on the external install prompt to remove an extension installed by a third party."> Remove from Chrome </message> + <message name="IDS_EXTENSION_PROMPT_FIRST_RUN_DECLINE_BUTTON" desc="Button on the first run dialog to remove an app installed by OEM."> + Remove app + </message> <message name="IDS_EXTENSIONS_DISABLED" desc="(Disabled) label next to the application name when the application is disabled."> (Disabled) </message> diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 8c6313e..47d6a24 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -363,7 +363,7 @@ const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle( expected_change, Manifest::INTERNAL, browser(), - false, + Extension::NO_FLAGS, true); } @@ -376,7 +376,7 @@ const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore( expected_change, Manifest::INTERNAL, browser(), - true, + Extension::FROM_WEBSTORE, false); } @@ -386,7 +386,7 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( InstallUIType ui_type, int expected_change) { return InstallOrUpdateExtension(id, path, ui_type, expected_change, - Manifest::INTERNAL, browser(), false, false); + Manifest::INTERNAL, browser(), Extension::NO_FLAGS, false); } const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( @@ -395,9 +395,9 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( InstallUIType ui_type, int expected_change, Browser* browser, - bool from_webstore) { + Extension::InitFromValueFlags creation_flags) { return InstallOrUpdateExtension(id, path, ui_type, expected_change, - Manifest::INTERNAL, browser, from_webstore, + Manifest::INTERNAL, browser, creation_flags, false); } @@ -408,7 +408,7 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( int expected_change, Manifest::Location install_source) { return InstallOrUpdateExtension(id, path, ui_type, expected_change, - install_source, browser(), false, false); + install_source, browser(), Extension::NO_FLAGS, false); } const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( @@ -418,7 +418,7 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( int expected_change, Manifest::Location install_source, Browser* browser, - bool from_webstore, + Extension::InitFromValueFlags creation_flags, bool wait_for_idle) { ExtensionService* service = profile()->GetExtensionService(); service->set_show_extensions_prompts(false); @@ -448,10 +448,10 @@ const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( scoped_refptr<extensions::CrxInstaller> installer( extensions::CrxInstaller::Create(service, install_ui.Pass())); installer->set_expected_id(id); - installer->set_is_gallery_install(from_webstore); + installer->set_creation_flags(creation_flags); installer->set_install_source(install_source); installer->set_install_wait_for_idle(wait_for_idle); - if (!from_webstore) { + if (!installer->is_gallery_install()) { installer->set_off_store_install_allow_reason( extensions::CrxInstaller::OffStoreInstallAllowedInTest); } diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index 5962d33..ad127c3 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h @@ -164,7 +164,16 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, INSTALL_UI_TYPE_AUTO_CONFIRM, expected_change, browser, - false); + extensions::Extension::NO_FLAGS); + } + + const extensions::Extension* InstallExtensionWithSourceAndFlags( + const base::FilePath& path, + int expected_change, + extensions::Manifest::Location install_source, + extensions::Extension::InitFromValueFlags creation_flags) { + return InstallOrUpdateExtension(std::string(), path, INSTALL_UI_TYPE_NONE, + expected_change, install_source, browser(), creation_flags, false); } // Begins install process but simulates a user cancel. @@ -275,7 +284,7 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, InstallUIType ui_type, int expected_change, Browser* browser, - bool from_webstore); + extensions::Extension::InitFromValueFlags creation_flags); const extensions::Extension* InstallOrUpdateExtension( const std::string& id, const base::FilePath& path, @@ -289,7 +298,7 @@ class ExtensionBrowserTest : virtual public InProcessBrowserTest, int expected_change, extensions::Manifest::Location install_source, Browser* browser, - bool from_webstore, + extensions::Extension::InitFromValueFlags creation_flags, bool wait_for_idle); // Wait for a notification of the specified type to be sent. diff --git a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc index 8f20cfb..d2ca1c8 100644 --- a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc +++ b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc @@ -24,6 +24,13 @@ #include "net/url_request/url_fetcher.h" using extensions::Extension; +using extensions::Manifest; + +namespace { + +const char kTestExtensionId[] = "pgdpcfcocojkjfbgpiianjngphoopgmo"; + +} class ExtensionDisabledGlobalErrorTest : public ExtensionBrowserTest { protected: @@ -222,3 +229,58 @@ IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, service_->extension_prefs()->GetDisableReasons(extension_id)); EXPECT_TRUE(GetExtensionDisabledGlobalError()); } + +// Tests that default app with user consent are not enabled after install. +IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, DefaultWithConsent) { + InstallExtensionWithSourceAndFlags(path_v1_, 0, Manifest::EXTERNAL_PREF, + Extension::InitFromValueFlags(Extension::FROM_WEBSTORE | + Extension::WAS_INSTALLED_BY_DEFAULT | + Extension::REQUIRE_PERMISSIONS_CONSENT)); + ASSERT_FALSE(GetExtensionDisabledGlobalError()); + + // Should be disabled with no error after install. + const Extension* extension = + service_->GetExtensionById(kTestExtensionId, true); + ASSERT_TRUE(extension); + EXPECT_EQ("1", extension->VersionString()); + EXPECT_FALSE(service_->IsExtensionEnabled(kTestExtensionId)); + EXPECT_EQ(Extension::DISABLE_PERMISSIONS_CONSENT, + service_->extension_prefs()->GetDisableReasons(kTestExtensionId)); + + // Update to v2 with more permissions, extension should stay disabled with + // the same reason, no error message. + UpdateIncreasingPermissionExtension(extension, path_v2_, 0); + extension = service_->GetExtensionById(kTestExtensionId, true); + ASSERT_TRUE(extension); + EXPECT_EQ("2", extension->VersionString()); + EXPECT_FALSE(service_->IsExtensionEnabled(kTestExtensionId)); + EXPECT_EQ(Extension::DISABLE_PERMISSIONS_CONSENT | + Extension::DISABLE_PERMISSIONS_INCREASE, + service_->extension_prefs()->GetDisableReasons(kTestExtensionId)); + + service_->GrantPermissionsAndEnableExtension(extension); + EXPECT_TRUE(service_->IsExtensionEnabled(kTestExtensionId)); + + // Update to v3 with more permissions, should be disabled with error. + UpdateIncreasingPermissionExtension(extension, path_v3_, -1); + extension = service_->GetExtensionById(kTestExtensionId, true); + ASSERT_TRUE(extension); + EXPECT_EQ("3", extension->VersionString()); + ASSERT_TRUE(GetExtensionDisabledGlobalError()); + EXPECT_FALSE(service_->IsExtensionEnabled(kTestExtensionId)); + EXPECT_EQ(Extension::DISABLE_PERMISSIONS_INCREASE, + service_->extension_prefs()->GetDisableReasons(kTestExtensionId)); + + // Enabled extension with granting permissions. + extension = UpdateExtension(kTestExtensionId, path_v3_, 0); + extension = service_->GetExtensionById(kTestExtensionId, true); + ASSERT_TRUE(extension); + service_->GrantPermissionsAndEnableExtension(extension); + EXPECT_TRUE(service_->IsExtensionEnabled(kTestExtensionId)); + + // Update again to v3 with the same permissions. + extension = UpdateExtension(kTestExtensionId, path_v3_, 0); + ASSERT_TRUE(extension); + ASSERT_FALSE(GetExtensionDisabledGlobalError()); + EXPECT_TRUE(service_->IsExtensionEnabled(kTestExtensionId)); +} diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc index e4a08aa..1eb7171 100644 --- a/chrome/browser/extensions/extension_install_prompt.cc +++ b/chrome/browser/extensions/extension_install_prompt.cc @@ -59,6 +59,7 @@ static const int kTitleIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE, IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE, IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE, + 0, // The prompt should be extension description. }; static const int kHeadingIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_INSTALL_PROMPT_HEADING, @@ -68,6 +69,7 @@ static const int kHeadingIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING, 0, // External installs use different strings for extensions/apps. IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING, + 0, // First run dialog use the extension name. }; static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, @@ -77,6 +79,7 @@ static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, ui::DIALOG_BUTTON_CANCEL, + ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, }; static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_INSTALL_BUTTON, @@ -86,6 +89,7 @@ static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON, 0, // External installs use different strings for extensions/apps. IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON, + IDS_EXTENSION_PROMPT_FIRST_RUN_ACCEPT_BUTTON }; static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 0, // These all use the platform's default cancel label. @@ -95,6 +99,7 @@ static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON, IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON, IDS_CLOSE, + IDS_EXTENSION_PROMPT_FIRST_RUN_DECLINE_BUTTON }; static const int kPermissionsHeaderIds[ ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { @@ -105,6 +110,7 @@ static const int kPermissionsHeaderIds[ IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO, IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, IDS_EXTENSION_PROMPT_CAN_ACCESS, + IDS_EXTENSION_PROMPT_CAN_ACCESS, }; static const int kOAuthHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_OAUTH_HEADER, @@ -114,6 +120,7 @@ static const int kOAuthHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_OAUTH_PERMISSIONS_HEADER, 0, 0, + IDS_EXTENSION_PROMPT_OAUTH_HEADER, }; // Size of extension icon in top left of dialog. @@ -270,6 +277,8 @@ string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const { } else if (type_ == EXTERNAL_INSTALL_PROMPT) { return l10n_util::GetStringFUTF16( resource_id, UTF8ToUTF16(extension_->name())); + } else if (type_ == DEFAULT_INSTALL_FIRST_RUN_PROMPT) { + return UTF8ToUTF16(extension_->name()); } return l10n_util::GetStringUTF16(resource_id); @@ -289,6 +298,8 @@ string16 ExtensionInstallPrompt::Prompt::GetHeading() const { else resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION; return l10n_util::GetStringUTF16(resource_id); + } else if (type_ == DEFAULT_INSTALL_FIRST_RUN_PROMPT) { + return UTF8ToUTF16(extension_->description()); } else { return l10n_util::GetStringFUTF16( kHeadingIds[type_], UTF8ToUTF16(extension_->name())); @@ -623,6 +634,17 @@ void ExtensionInstallPrompt::ConfirmReEnable(Delegate* delegate, LoadImageIfNeeded(); } +void ExtensionInstallPrompt::ConfirmDefaultInstallFirstRun( + Delegate* delegate, + const Extension* extension) { + DCHECK(ui_loop_ == base::MessageLoop::current()); + extension_ = extension; + permissions_ = extension->GetActivePermissions(); + delegate_ = delegate; + prompt_.set_type(DEFAULT_INSTALL_FIRST_RUN_PROMPT); + LoadImageIfNeeded(); +} + void ExtensionInstallPrompt::ConfirmExternalInstall( Delegate* delegate, const Extension* extension, @@ -791,6 +813,7 @@ void ExtensionInstallPrompt::ShowConfirmation() { switch (prompt_.type()) { case PERMISSIONS_PROMPT: case RE_ENABLE_PROMPT: + case DEFAULT_INSTALL_FIRST_RUN_PROMPT: case INLINE_INSTALL_PROMPT: case EXTERNAL_INSTALL_PROMPT: case INSTALL_PROMPT: diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h index a8ac211..c59f97d 100644 --- a/chrome/browser/extensions/extension_install_prompt.h +++ b/chrome/browser/extensions/extension_install_prompt.h @@ -60,6 +60,7 @@ class ExtensionInstallPrompt PERMISSIONS_PROMPT, EXTERNAL_INSTALL_PROMPT, POST_INSTALL_PERMISSIONS_PROMPT, + DEFAULT_INSTALL_FIRST_RUN_PROMPT, NUM_PROMPT_TYPES }; @@ -305,6 +306,13 @@ class ExtensionInstallPrompt virtual void ConfirmReEnable(Delegate* delegate, const extensions::Extension* extension); + // This is called by the app handler launcher to verify whether the app + // should be launched first time. This is declared virtual for testing. + // + // We *MUST* eventually call either Proceed() or Abort() on |delegate|. + virtual void ConfirmDefaultInstallFirstRun(Delegate* delegate, + const extensions::Extension* extension); + // This is called by the external install alert UI to verify whether the // extension should be enabled (external extensions are installed disabled). // diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 23f684e..eddbf5c 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -937,6 +937,13 @@ bool ExtensionService::IsExtensionEnabledForLauncher( !GetTerminatedExtension(extension_id); } +bool ExtensionService::DoesExtensionRequirePermissionConsent( + const std::string& extension_id) const { + return extension_prefs_->IsExtensionDisabled(extension_id) && + (extension_prefs_->GetDisableReasons(extension_id) & + Extension::DISABLE_PERMISSIONS_CONSENT); +} + void ExtensionService::EnableExtension(const std::string& extension_id) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -2242,7 +2249,8 @@ void ExtensionService::CheckPermissionsIncrease(const Extension* extension, int disable_reasons = extension_prefs_->GetDisableReasons(extension->id()); bool auto_grant_permission = - (!is_extension_installed && extension->was_installed_by_default()) || + (!is_extension_installed && extension->was_installed_by_default() && + !extension->requires_permissions_consent()) || chrome::IsRunningInForcedAppMode(); // Silently grant all active permissions to default apps only on install. // After install they should behave like other apps. @@ -2291,6 +2299,9 @@ void ExtensionService::CheckPermissionsIncrease(const Extension* extension, disable_reasons |= Extension::DISABLE_USER_ACTION; } disable_reasons &= ~Extension::DISABLE_UNKNOWN_FROM_SYNC; + } else if (extension->requires_permissions_consent()) { + disable_reasons |= Extension::DISABLE_PERMISSIONS_CONSENT; + extension_prefs_->SetExtensionState(extension->id(), Extension::DISABLED); } // Extension has changed permissions significantly. Disable it. A diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index 92e0119..603849d 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -309,9 +309,14 @@ class ExtensionService virtual bool IsExternalExtensionUninstalled( const std::string& extension_id) const OVERRIDE; - // Whether the extension should show as enabled state in launcher. + // Whether the extension should be enabled for running. bool IsExtensionEnabledForLauncher(const std::string& extension_id) const; + // Whether the extension is waiting for permission consent. Such extensions + // technically disabled (can't be launched) but UI shows them as normal. + bool DoesExtensionRequirePermissionConsent( + const std::string& extension_id) const; + // Enables the extension. If the extension is already enabled, does // nothing. virtual void EnableExtension(const std::string& extension_id); diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index aca590f..4864187 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -4916,6 +4916,21 @@ TEST_F(ExtensionServiceTest, ExternalPrefProvider) { " }" "}"; EXPECT_EQ(1, from_webstore_visitor.Visit(json_data)); + + // Test require_permissions_consent. + MockProviderVisitor permissions_consent_visitor( + base_path, Extension::REQUIRE_PERMISSIONS_CONSENT); + json_data = + "{" + " \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\": {" + " \"external_crx\": \"RandomExtension.crx\"," + " \"external_version\": \"1.0\"," + " \"require_permissions_consent\": true" + " }" + "}"; + { + EXPECT_EQ(1, permissions_consent_visitor.Visit(json_data)); + } } // Test loading good extensions from the profile directory. diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc index 78b1417..c2d2851 100644 --- a/chrome/browser/extensions/external_provider_impl.cc +++ b/chrome/browser/extensions/external_provider_impl.cc @@ -58,6 +58,8 @@ const char ExternalProviderImpl::kSupportedLocales[] = "supported_locales"; const char ExternalProviderImpl::kIsBookmarkApp[] = "is_bookmark_app"; const char ExternalProviderImpl::kIsFromWebstore[] = "is_from_webstore"; const char ExternalProviderImpl::kKeepIfPresent[] = "keep_if_present"; +const char ExternalProviderImpl::kRequirePermissionsConsent[] = + "require_permissions_consent"; ExternalProviderImpl::ExternalProviderImpl(VisitorInterface* service, ExternalLoader* loader, @@ -214,6 +216,12 @@ void ExternalProviderImpl::SetPrefs(base::DictionaryValue* prefs) { continue; } } + bool require_permissions_consent; + if (extension->GetBoolean(kRequirePermissionsConsent, + &require_permissions_consent) && + require_permissions_consent) { + creation_flags |= Extension::REQUIRE_PERMISSIONS_CONSENT; + } if (has_external_crx) { if (crx_location_ == Manifest::INVALID_LOCATION) { diff --git a/chrome/browser/extensions/external_provider_impl.h b/chrome/browser/extensions/external_provider_impl.h index 8375826..991d0bb 100644 --- a/chrome/browser/extensions/external_provider_impl.h +++ b/chrome/browser/extensions/external_provider_impl.h @@ -73,6 +73,7 @@ class ExternalProviderImpl : public ExternalProviderInterface { static const char kIsBookmarkApp[]; static const char kIsFromWebstore[]; static const char kKeepIfPresent[]; + static const char kRequirePermissionsConsent[]; void set_auto_acknowledge(bool auto_acknowledge) { auto_acknowledge_ = auto_acknowledge; diff --git a/chrome/browser/ui/app_list/extension_app_item.cc b/chrome/browser/ui/app_list/extension_app_item.cc index 6050cb7..1680086 100644 --- a/chrome/browser/ui/app_list/extension_app_item.cc +++ b/chrome/browser/ui/app_list/extension_app_item.cc @@ -171,7 +171,9 @@ void ExtensionAppItem::UpdateIcon() { const ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->extension_service(); - const bool enabled = service->IsExtensionEnabledForLauncher(extension_id_); + // If app waits for permission confirmation, don't show it disabled in UI. + const bool enabled = service->IsExtensionEnabledForLauncher(extension_id_) || + service->DoesExtensionRequirePermissionConsent(extension_id_); if (!enabled) { const color_utils::HSL shift = {-1, 0, 0.6}; icon = gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon, shift); diff --git a/chrome/browser/ui/extensions/extension_enable_flow.cc b/chrome/browser/ui/extensions/extension_enable_flow.cc index 2ec1f98..3e27e0c 100644 --- a/chrome/browser/ui/extensions/extension_enable_flow.cc +++ b/chrome/browser/ui/extensions/extension_enable_flow.cc @@ -9,6 +9,7 @@ #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h" +#include "chrome/common/extensions/permissions/permission_set.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" @@ -94,7 +95,11 @@ void ExtensionEnableFlow::CheckPermissionAndMaybePromptUser() { } CreatePrompt(); - prompt_->ConfirmReEnable(this, extension); + int disable_reasons = extension_prefs->GetDisableReasons(extension->id()); + if (disable_reasons & Extension::DISABLE_PERMISSIONS_CONSENT) + prompt_->ConfirmDefaultInstallFirstRun(this, extension); + else + prompt_->ConfirmReEnable(this, extension); } void ExtensionEnableFlow::CreatePrompt() { diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc index d4adf07..59a9d04 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc @@ -135,6 +135,11 @@ class ExtensionInstallDialogView : public views::DialogDelegateView, return prompt_.type() == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT; } + bool is_first_run() const { + return prompt_.type() == + ExtensionInstallPrompt::DEFAULT_INSTALL_FIRST_RUN_PROMPT; + } + bool is_bundle_install() const { return prompt_.type() == ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT; } @@ -412,7 +417,10 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( icon_row_span = 4; } else if (prompt.ShouldShowPermissions()) { size_t permission_count = prompt.GetPermissionCount(); - if (permission_count > 0) { + if (is_first_run()) { + // In first run case we have separator. + icon_row_span = 1; + } else if (permission_count > 0) { // Also span the permission header and each of the permission rows (all // have a padding row above it). icon_row_span = 3 + permission_count * 2; @@ -482,7 +490,7 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); if (prompt.GetPermissionCount() > 0) { - if (is_inline_install()) { + if (is_inline_install() || is_first_run()) { layout->StartRow(0, column_set_id); layout->AddView(new views::Separator(views::Separator::HORIZONTAL), 3, 1, views::GridLayout::FILL, views::GridLayout::FILL); @@ -655,7 +663,10 @@ string16 ExtensionInstallDialogView::GetDialogButtonLabel( } int ExtensionInstallDialogView::GetDefaultDialogButton() const { - return ui::DIALOG_BUTTON_CANCEL; + if (is_first_run()) + return ui::DIALOG_BUTTON_OK; + else + return ui::DIALOG_BUTTON_CANCEL; } bool ExtensionInstallDialogView::Cancel() { diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 21cd609..3c1067d 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -89,6 +89,10 @@ class Extension : public base::RefCountedThreadSafe<Extension> { DISABLE_UNSUPPORTED_REQUIREMENT = 1 << 3, DISABLE_SIDELOAD_WIPEOUT = 1 << 4, DISABLE_UNKNOWN_FROM_SYNC = 1 << 5, + + // Disabled because the user has not yet consented to the permissions, + // for instance for a default installed item. + DISABLE_PERMISSIONS_CONSENT = 1 << 6, }; enum InstallType { @@ -145,6 +149,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // |WAS_INSTALLED_BY_DEFAULT| installed by default when the profile was // created. WAS_INSTALLED_BY_DEFAULT = 1 << 7, + + // |REQUIRE_PERMISSIONS_CONSENT| means that user needs to accept permissions + // before running the app even if it is marked as |WAS_INSTALLED_BY_DEFAULT| + // and |FROM_WEBSTORE|. + REQUIRE_PERMISSIONS_CONSENT = 1 << 8, }; static scoped_refptr<Extension> Create(const base::FilePath& path, @@ -312,6 +321,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool was_installed_by_default() const { return (creation_flags_ & WAS_INSTALLED_BY_DEFAULT) != 0; } + bool requires_permissions_consent() const { + return (creation_flags_ & REQUIRE_PERMISSIONS_CONSENT) != 0; + } // App-related. bool is_app() const; diff --git a/chrome/common/extensions/permissions/permissions_data.cc b/chrome/common/extensions/permissions/permissions_data.cc index d9e56ef..a05a2d8 100644 --- a/chrome/common/extensions/permissions/permissions_data.cc +++ b/chrome/common/extensions/permissions/permissions_data.cc @@ -391,6 +391,9 @@ const URLPatternSet& PermissionsData::GetEffectiveHostPermissions( // static bool PermissionsData::CanSilentlyIncreasePermissions( const Extension* extension) { + if (extension->requires_permissions_consent()) + return false; + return extension->location() != Manifest::INTERNAL; } |