Chromium Code Reviews| Index: chrome/browser/chromeos/extensions/public_session_permission_helper.cc |
| diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper.cc b/chrome/browser/chromeos/extensions/public_session_permission_helper.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..dc523d282a6b8fde753ed089149e37069788f8bb |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/extensions/public_session_permission_helper.cc |
| @@ -0,0 +1,210 @@ |
| +// Copyright 2017 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/chromeos/extensions/public_session_permission_helper.h" |
| + |
| +#include <algorithm> |
| +#include <map> |
| +#include <memory> |
| +#include <utility> |
| +#include <vector> |
| + |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/macros.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "chrome/browser/extensions/extension_install_prompt.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "extensions/common/extension.h" |
| +#include "extensions/common/extension_id.h" |
| +#include "extensions/common/permissions/manifest_permission_set.h" |
| +#include "extensions/common/permissions/permission_set.h" |
| +#include "extensions/common/url_pattern_set.h" |
| + |
| +namespace extensions { |
| +namespace permission_helper { |
| + |
| +namespace { |
| + |
| +// This class is the internal implementation of HandlePermissionRequest(). It |
| +// contains the actual prompt showing and resolving logic, and it caches the |
| +// user choices. |
| +class PublicSessionPermissionHelper { |
| + public: |
| + PublicSessionPermissionHelper(); |
| + PublicSessionPermissionHelper(PublicSessionPermissionHelper&& other); |
| + ~PublicSessionPermissionHelper(); |
| + |
| + void HandlePermissionRequestImpl(const Extension& extension, |
| + PermissionIDSet requested_permissions, |
| + content::WebContents* web_contents, |
| + const RequestResolvedCallback& callback); |
| + |
| + private: |
| + void ResolvePermissionPrompt( |
| + const ExtensionInstallPrompt* prompt, |
| + const PermissionIDSet& unprompted_permissions, |
| + ExtensionInstallPrompt::Result prompt_result); |
| + |
| + PermissionIDSet FilterAllowedPermissions( |
| + const PermissionIDSet& permissions); |
| + |
| + struct RequestCallback { |
| + RequestCallback(const RequestResolvedCallback& callback, |
| + const PermissionIDSet& permission_list); |
| + RequestCallback(const RequestCallback& other); |
| + ~RequestCallback(); |
| + RequestResolvedCallback callback; |
| + PermissionIDSet permission_list; |
| + }; |
| + using RequestCallbackList = std::vector<RequestCallback>; |
| + |
| + std::set<std::unique_ptr<ExtensionInstallPrompt>> prompts_; |
| + PermissionIDSet prompted_permission_set_; |
| + PermissionIDSet allowed_permission_set_; |
| + PermissionIDSet denied_permission_set_; |
| + RequestCallbackList callbacks_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PublicSessionPermissionHelper); |
| +}; |
| + |
| +PublicSessionPermissionHelper::PublicSessionPermissionHelper() {} |
| + |
| +PublicSessionPermissionHelper::PublicSessionPermissionHelper( |
| + PublicSessionPermissionHelper&& other) = default; |
| + |
| +PublicSessionPermissionHelper::~PublicSessionPermissionHelper() {} |
| + |
| +void PublicSessionPermissionHelper::HandlePermissionRequestImpl( |
| + const Extension& extension, |
| + PermissionIDSet requested_permissions, |
| + content::WebContents* web_contents, |
| + const RequestResolvedCallback& callback) { |
| + CHECK(web_contents); |
| + |
| + PermissionIDSet unresolved_permissions = PermissionIDSet::Difference( |
| + requested_permissions, allowed_permission_set_); |
| + unresolved_permissions = PermissionIDSet::Difference( |
| + unresolved_permissions, denied_permission_set_); |
| + if (unresolved_permissions.empty()) { |
| + // All requested permissions are already resolved. |
| + callback.Run(FilterAllowedPermissions(requested_permissions)); |
| + return; |
| + } |
| + |
| + // Since not all permissions are resolved yet, queue the callback to be called |
| + // when all of them are resolved. |
| + callbacks_.push_back(RequestCallback(callback, requested_permissions)); |
| + |
| + PermissionIDSet unprompted_permissions = PermissionIDSet::Difference( |
| + unresolved_permissions, prompted_permission_set_); |
| + if (unprompted_permissions.empty()) |
| + // Some permissions aren't resolved yet, but they are currently being |
| + // prompted for, so no need to show a prompt. |
| + return; |
|
Andrew T Wilson (Slow)
2017/02/07 14:26:51
Since the body of this if-statement is multi-line
Ivan Šandrk
2017/02/07 20:41:32
Done.
I've written some tests now, this case is c
|
| + |
| + // Some permissions need prompting, setup the prompt and show it. |
| + APIPermissionSet new_apis; |
| + for (const auto& permission : unprompted_permissions) { |
| + prompted_permission_set_.insert(permission.id()); |
| + new_apis.insert(permission.id()); |
| + } |
| + auto permission_set = base::MakeUnique<PermissionSet>( |
| + new_apis, ManifestPermissionSet(), URLPatternSet(), URLPatternSet()); |
| + auto prompt = base::MakeUnique<ExtensionInstallPrompt>(web_contents); |
| + // This Unretained is safe because the lifetime of this object is until |
| + // process exit. |
| + prompt->ShowDialog( |
| + base::Bind(&PublicSessionPermissionHelper::ResolvePermissionPrompt, |
| + base::Unretained(this), prompt.get(), |
| + std::move(unprompted_permissions)), |
| + &extension, |
| + nullptr, // Use the extension icon. |
| + base::MakeUnique<ExtensionInstallPrompt::Prompt>( |
| + ExtensionInstallPrompt::PERMISSIONS_PROMPT), |
| + std::move(permission_set), |
| + ExtensionInstallPrompt::GetDefaultShowDialogCallback()); |
| + prompts_.insert(std::move(prompt)); |
| +} |
| + |
| +void PublicSessionPermissionHelper::ResolvePermissionPrompt( |
| + const ExtensionInstallPrompt* prompt, |
| + const PermissionIDSet& unprompted_permissions, |
| + ExtensionInstallPrompt::Result prompt_result) { |
| + const bool allowed = |
| + prompt_result == ExtensionInstallPrompt::Result::ACCEPTED; |
| + for (const auto& permission : unprompted_permissions) { |
| + prompted_permission_set_.erase(permission.id()); |
| + if (allowed) |
| + allowed_permission_set_.insert(permission.id()); |
| + else |
| + denied_permission_set_.insert(permission.id()); |
| + } |
| + |
| + RequestCallbackList callbacks_to_invoke; |
| + for (auto callback = callbacks_.begin(); callback != callbacks_.end(); ) { |
| + if (prompted_permission_set_.ContainsAnyID(callback->permission_list)) { |
| + // The request is still waiting on other permissions to be resolved - wait |
| + // until all of them are resolved before calling the callback. |
| + callback++; |
| + continue; |
| + } |
| + callbacks_to_invoke.push_back(std::move(*callback)); |
|
Andrew T Wilson (Slow)
2017/02/07 14:26:51
Explain why you're doing this (i.e. because callba
Ivan Šandrk
2017/02/07 20:41:32
Done.
|
| + callback = callbacks_.erase(callback); |
| + } |
| + for (auto callback = callbacks_to_invoke.begin(); |
| + callback != callbacks_to_invoke.end(); callback++) { |
| + callback->callback.Run(FilterAllowedPermissions(callback->permission_list)); |
| + } |
| + |
| + // Dispose of the prompt as it's not needed anymore. |
| + auto iter = std::find_if( |
| + prompts_.begin(), prompts_.end(), |
| + [prompt](const std::unique_ptr<ExtensionInstallPrompt>& check) { |
| + return check.get() == prompt; |
| + }); |
| + DCHECK(iter != prompts_.end()); |
| + prompts_.erase(iter); |
| +} |
| + |
| +PermissionIDSet PublicSessionPermissionHelper::FilterAllowedPermissions( |
| + const PermissionIDSet& permissions) { |
| + PermissionIDSet allowed_permissions; |
| + for (auto iter = permissions.begin(); iter != permissions.end(); iter++) { |
| + if (allowed_permission_set_.ContainsID(*iter)) { |
| + allowed_permissions.insert(iter->id()); |
| + } |
| + } |
| + return allowed_permissions; |
| +} |
| + |
| +PublicSessionPermissionHelper::RequestCallback::RequestCallback( |
| + const RequestResolvedCallback& callback, |
| + const PermissionIDSet& permission_list) |
| + : callback(callback), |
| + permission_list(permission_list) {} |
| + |
| +PublicSessionPermissionHelper::RequestCallback::RequestCallback( |
| + const RequestCallback& other) = default; |
| + |
| +PublicSessionPermissionHelper::RequestCallback::~RequestCallback() {} |
| + |
| +base::LazyInstance<std::map<ExtensionId, PublicSessionPermissionHelper>>::Leaky |
| + g_helpers = LAZY_INSTANCE_INITIALIZER; |
| + |
| +} // namespace |
| + |
| +void HandlePermissionRequest(const Extension& extension, |
| + PermissionIDSet requested_permissions, |
| + content::WebContents* web_contents, |
| + const RequestResolvedCallback& callback) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + return g_helpers.Get()[extension.id()].HandlePermissionRequestImpl( |
| + extension, requested_permissions, web_contents, callback); |
| +} |
| + |
| +} // namespace permission_helper |
| +} // namespace extensions |