Chromium Code Reviews| Index: chrome/browser/extensions/api/page_capture/page_capture_api.cc |
| diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chrome/browser/extensions/api/page_capture/page_capture_api.cc |
| index 1e7c549d533f94e0f6f1c53a883d9c5680c85049..7de2d1086ba134effe59134f0b39f29d5cb41a07 100644 |
| --- a/chrome/browser/extensions/api/page_capture/page_capture_api.cc |
| +++ b/chrome/browser/extensions/api/page_capture/page_capture_api.cc |
| @@ -5,13 +5,17 @@ |
| #include "chrome/browser/extensions/api/page_capture/page_capture_api.h" |
| #include <limits> |
| -#include <memory> |
| +#include <utility> |
| #include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| #include "base/files/file_util.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| +#include "chrome/common/pref_names.h" |
| +#include "chromeos/login/login_state.h" |
| +#include "components/prefs/scoped_user_pref_update.h" |
| #include "content/public/browser/child_process_security_policy.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_source.h" |
| @@ -21,6 +25,9 @@ |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/mhtml_generation_params.h" |
| #include "extensions/common/extension_messages.h" |
| +#include "extensions/common/permissions/manifest_permission_set.h" |
| +#include "extensions/common/permissions/permission_set.h" |
| +#include "extensions/common/url_pattern_set.h" |
| using content::BrowserThread; |
| using content::ChildProcessSecurityPolicy; |
| @@ -37,6 +44,18 @@ const char kMHTMLGenerationFailedError[] = "Failed to generate MHTML."; |
| const char kTemporaryFileError[] = "Failed to create a temporary file."; |
| const char kTabClosedError[] = "Cannot find the tab for this request."; |
| +#if defined(OS_CHROMEOS) |
| +const char kUserDenied[] = "User denied request."; |
| +#endif |
| + |
| +bool IsPublicSession() { |
| +#if defined(OS_CHROMEOS) |
| + if (chromeos::LoginState::IsInitialized()) |
| + return chromeos::LoginState::Get()->IsPublicSessionUser(); |
| +#endif |
| + return false; |
| +} |
| + |
| } // namespace |
| static PageCaptureSaveAsMHTMLFunction::TestDelegate* test_delegate_ = NULL; |
| @@ -63,6 +82,17 @@ bool PageCaptureSaveAsMHTMLFunction::RunAsync() { |
| AddRef(); // Balanced in ReturnFailure/ReturnSuccess() |
| + // In Public Sessions, extensions (and apps) are force-installed by admin |
| + // policy so the user does not get a chance to review the permissions for |
| + // these extensions. This is not acceptable from a security/privacy |
| + // standpoint, so when an extension uses the PageCapture API for the first |
| + // time, we show the user a dialog where they can choose whether to allow the |
| + // extension access to the API. |
| + if (IsPublicSession()) { |
| + HandlePermission(); |
| + return true; |
| + } |
| + |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile, this)); |
| @@ -200,3 +230,98 @@ WebContents* PageCaptureSaveAsMHTMLFunction::GetWebContents() { |
| } |
| return web_contents; |
| } |
| + |
| +void PageCaptureSaveAsMHTMLFunction::HandlePermission() { |
| +#if defined(OS_CHROMEOS) |
| + auto user_choice = UserChoiceGet(); |
| + if (user_choice == PermissionState::ALLOWED || |
| + user_choice == PermissionState::DENIED) { |
| + ChainRunAsync(); |
| + return; |
| + } |
| + if (user_choice == PermissionState::NOT_PROMPTED) { |
| + ShowPermissionPrompt(); |
| + } |
| + // It's possible that several PageCapture requests come before user resolves |
| + // the permission dialog, so setup a callback that gets called when pref |
| + // changes (dialog is resolved). |
| + pref_change_registrar_.reset(new PrefChangeRegistrar); |
| + pref_change_registrar_->Init(GetPrefs()); |
| + // base::Unretained is safe here because PrefChangeRegistrar will unregister |
| + // callback on destruction of this. |
|
Devlin
2016/12/12 20:09:37
What if this object outlives the Prefs?
Ivan Šandrk
2016/12/13 16:32:47
Prefs are destoyed during logout. This object eith
|
| + pref_change_registrar_->Add(prefs::kPublicSessionPermissionsUserChoiceCache, |
| + base::Bind(&PageCaptureSaveAsMHTMLFunction::ChainRunAsync, |
| + base::Unretained(this))); |
| +#endif // defined(OS_CHROMEOS) |
| +} |
| + |
| +#if defined(OS_CHROMEOS) |
| +void PageCaptureSaveAsMHTMLFunction::ShowPermissionPrompt() { |
| + extensions::APIPermissionSet new_apis; |
| + new_apis.insert(extensions::APIPermission::kPageCapture); |
| + auto permission_set = base::MakeUnique<extensions::PermissionSet>( |
| + new_apis, extensions::ManifestPermissionSet(), |
| + extensions::URLPatternSet(), extensions::URLPatternSet()); |
| + |
| + prompt_ = base::MakeUnique<ExtensionInstallPrompt>(GetWebContents()); |
|
Devlin
2016/12/12 20:09:37
GetWebContents() can return null here, which would
Ivan Šandrk
2016/12/13 16:32:47
Good catch. Replaced it with GetAssociatedWebConte
Devlin
2016/12/13 21:51:29
GetAssociatedWebContents() isn't particularly appr
Ivan Šandrk
2017/01/04 17:09:59
I'd personally show the permission dialog anyway,
|
| + prompt_->ShowDialog( |
| + base::Bind(&PageCaptureSaveAsMHTMLFunction::ResolvePermissionPrompt, |
| + this), |
| + extension(), |
| + nullptr, // Uses the extension icon. |
| + base::MakeUnique<ExtensionInstallPrompt::Prompt>( |
| + ExtensionInstallPrompt::PERMISSIONS_PROMPT), |
| + std::move(permission_set), |
| + ExtensionInstallPrompt::GetDefaultShowDialogCallback()); |
| + |
| + UserChoiceSet(PermissionState::SHOWN_PROMPT); |
| +} |
| + |
| +void PageCaptureSaveAsMHTMLFunction::ResolvePermissionPrompt( |
| + ExtensionInstallPrompt::Result prompt_result) { |
| + // Dispose of the prompt as it's not needed anymore. |
| + prompt_.reset(); |
| + |
| + bool allowed = prompt_result == ExtensionInstallPrompt::Result::ACCEPTED; |
| + UserChoiceSet(allowed ? PermissionState::ALLOWED : PermissionState::DENIED); |
| +} |
| + |
| +void PageCaptureSaveAsMHTMLFunction::ChainRunAsync() { |
| + auto user_choice = UserChoiceGet(); |
| + // Permission was allowed/denied for some other extension, continue sleeping. |
| + if (user_choice == PermissionState::SHOWN_PROMPT) { |
| + return; |
| + } |
| + |
| + if (user_choice == PermissionState::DENIED) { |
| + ReturnFailure(kUserDenied); |
| + } else { |
| + DCHECK(user_choice == PermissionState::ALLOWED); |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile, this)); |
| + } |
| +} |
| + |
| +PrefService* PageCaptureSaveAsMHTMLFunction::GetPrefs() { |
| + return GetProfile()->GetOffTheRecordPrefs(); |
| +} |
| + |
| +void PageCaptureSaveAsMHTMLFunction::UserChoiceSet(PermissionState value) { |
| + DictionaryPrefUpdate update(GetPrefs(), |
| + prefs::kPublicSessionPermissionsUserChoiceCache); |
| + update->SetInteger(extension()->id(), value); |
| +} |
| + |
| +PageCaptureSaveAsMHTMLFunction::PermissionState |
| +PageCaptureSaveAsMHTMLFunction::UserChoiceGet() { |
| + auto dictionary = GetPrefs()->GetDictionary( |
| + prefs::kPublicSessionPermissionsUserChoiceCache); |
| + int value; |
| + if (!dictionary->GetInteger(extension()->id(), &value)) { |
| + value = PermissionState::NOT_PROMPTED; |
| + UserChoiceSet(static_cast<PermissionState>(value)); |
| + } |
| + return static_cast<PermissionState>(value); |
| +} |
| +#endif // defined(OS_CHROMEOS) |