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..3a942271fd62320441aabdfc91b4f85fc391d1c0 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,20 @@ 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 kNoAvailableBrowser[] = |
| + "No available browser window to show the prompt."; |
| +const char kUserDenied[] = "User denied request."; |
| +#endif |
| + |
| +bool IsPublicSession() { |
|
Andrew T Wilson (Slow)
2016/12/14 13:39:21
Seems like this should be in ifdef(OS_CHROMEOS), a
Ivan Šandrk
2017/01/04 17:09:59
Done.
|
| +#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 +84,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()) { |
| + HandlePermissionRequest(); |
| + return true; |
| + } |
| + |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile, this)); |
| @@ -200,3 +232,103 @@ WebContents* PageCaptureSaveAsMHTMLFunction::GetWebContents() { |
| } |
| return web_contents; |
| } |
| + |
| +void PageCaptureSaveAsMHTMLFunction::HandlePermissionRequest() { |
|
Devlin
2016/12/13 21:51:29
It's kind of weird that we define this function fo
Ivan Šandrk
2017/01/04 17:09:59
Wanted to see how this variant fared - the regular
|
| +#if defined(OS_CHROMEOS) |
| + auto user_choice = UserChoiceGet(); |
| + if (user_choice == PermissionState::ALLOWED || |
| + user_choice == PermissionState::DENIED) { |
| + ResolvePermissionRequest(); |
| + 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. |
|
Andrew T Wilson (Slow)
2016/12/14 13:39:21
nit: this object
Ivan Šandrk
2017/01/04 17:09:59
Done.
|
| + pref_change_registrar_->Add(prefs::kPublicSessionPermissionsUserChoiceCache, |
|
Devlin
2016/12/13 21:51:29
Is this git cl format'd?
Ivan Šandrk
2017/01/04 17:09:59
Done
|
| + base::Bind(&PageCaptureSaveAsMHTMLFunction::ResolvePermissionRequest, |
| + 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()); |
| + |
| + auto web_contents = GetAssociatedWebContents(); |
| + if (!web_contents) { |
| + ReturnFailure(kNoAvailableBrowser); |
| + return; |
| + } |
| + prompt_ = base::MakeUnique<ExtensionInstallPrompt>(web_contents); |
| + 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::ResolvePermissionRequest() { |
| + 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(); |
|
Devlin
2016/12/13 21:51:29
Why OffTheRecordPrefs()? (add a comment)
Ivan Šandrk
2017/01/04 17:09:59
Done. It's used because the pref must not be persi
|
| +} |
| + |
| +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; |
|
Devlin
2016/12/13 21:51:29
nit: initialize value
Ivan Šandrk
2017/01/04 17:09:59
Done.
|
| + if (!dictionary->GetInteger(extension()->id(), &value)) { |
| + value = PermissionState::NOT_PROMPTED; |
| + UserChoiceSet(static_cast<PermissionState>(value)); |
|
Andrew T Wilson (Slow)
2016/12/14 13:39:20
Why do we need to call UserChoiceSet here? Why not
Ivan Šandrk
2017/01/04 17:09:59
Done.
|
| + } |
| + return static_cast<PermissionState>(value); |
| +} |
| +#endif // defined(OS_CHROMEOS) |