blob: d690e47cce17c22be5e277b5c7556d4df5a6a878 [file] [log] [blame]
// Copyright 2019 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/native_file_system/native_file_system_permission_request_manager.h"
#include "base/command_line.h"
#include "chrome/browser/ui/native_file_system_dialogs.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/switches.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace {
bool RequestsAreIdentical(
const NativeFileSystemPermissionRequestManager::RequestData& a,
const NativeFileSystemPermissionRequestManager::RequestData& b) {
return a.origin == b.origin && a.path == b.path &&
a.handle_type == b.handle_type && a.access == b.access;
}
bool RequestsAreForSamePath(
const NativeFileSystemPermissionRequestManager::RequestData& a,
const NativeFileSystemPermissionRequestManager::RequestData& b) {
return a.origin == b.origin && a.path == b.path &&
a.handle_type == b.handle_type;
}
} // namespace
struct NativeFileSystemPermissionRequestManager::Request {
Request(
RequestData data,
base::OnceCallback<void(permissions::PermissionAction result)> callback,
base::ScopedClosureRunner fullscreen_block)
: data(std::move(data)) {
callbacks.push_back(std::move(callback));
fullscreen_blocks.push_back(std::move(fullscreen_block));
}
RequestData data;
std::vector<base::OnceCallback<void(permissions::PermissionAction result)>>
callbacks;
std::vector<base::ScopedClosureRunner> fullscreen_blocks;
};
NativeFileSystemPermissionRequestManager::
~NativeFileSystemPermissionRequestManager() = default;
void NativeFileSystemPermissionRequestManager::AddRequest(
RequestData data,
base::OnceCallback<void(permissions::PermissionAction result)> callback,
base::ScopedClosureRunner fullscreen_block) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
permissions::switches::kDenyPermissionPrompts)) {
std::move(callback).Run(permissions::PermissionAction::DENIED);
return;
}
// Check if any pending requests are identical to the new request.
if (current_request_ && RequestsAreIdentical(current_request_->data, data)) {
current_request_->callbacks.push_back(std::move(callback));
current_request_->fullscreen_blocks.push_back(std::move(fullscreen_block));
return;
}
for (const auto& request : queued_requests_) {
if (RequestsAreIdentical(request->data, data)) {
request->callbacks.push_back(std::move(callback));
request->fullscreen_blocks.push_back(std::move(fullscreen_block));
return;
}
if (RequestsAreForSamePath(request->data, data)) {
// This means access levels are different. Change the existing request
// to kReadWrite, and add the new callback.
request->data.access = Access::kReadWrite;
request->callbacks.push_back(std::move(callback));
request->fullscreen_blocks.push_back(std::move(fullscreen_block));
return;
}
}
queued_requests_.push_back(std::make_unique<Request>(
std::move(data), std::move(callback), std::move(fullscreen_block)));
if (!IsShowingRequest())
ScheduleShowRequest();
}
NativeFileSystemPermissionRequestManager::
NativeFileSystemPermissionRequestManager(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {}
bool NativeFileSystemPermissionRequestManager::CanShowRequest() const {
// Deley showing requests until the main frame is fully loaded.
// ScheduleShowRequest() will be called again when that happens.
return main_frame_has_fully_loaded_ && !queued_requests_.empty() &&
!current_request_;
}
void NativeFileSystemPermissionRequestManager::ScheduleShowRequest() {
if (!CanShowRequest())
return;
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&NativeFileSystemPermissionRequestManager::DequeueAndShowRequest,
weak_factory_.GetWeakPtr()));
}
void NativeFileSystemPermissionRequestManager::DequeueAndShowRequest() {
if (!CanShowRequest())
return;
current_request_ = std::move(queued_requests_.front());
queued_requests_.pop_front();
if (auto_response_for_test_) {
OnPermissionDialogResult(*auto_response_for_test_);
return;
}
ShowNativeFileSystemPermissionDialog(
current_request_->data,
base::BindOnce(
&NativeFileSystemPermissionRequestManager::OnPermissionDialogResult,
weak_factory_.GetWeakPtr()),
web_contents());
}
void NativeFileSystemPermissionRequestManager::
DocumentOnLoadCompletedInMainFrame() {
main_frame_has_fully_loaded_ = true;
// This is scheduled because while all calls to the browser have been
// issued at DOMContentLoaded, they may be bouncing around in scheduled
// callbacks finding the UI thread still. This makes sure we allow those
// scheduled calls to AddRequest to complete before we show the page-load
// permissions bubble.
if (!queued_requests_.empty())
ScheduleShowRequest();
}
void NativeFileSystemPermissionRequestManager::OnPermissionDialogResult(
permissions::PermissionAction result) {
DCHECK(current_request_);
for (auto& callback : current_request_->callbacks)
std::move(callback).Run(result);
current_request_ = nullptr;
if (!queued_requests_.empty())
ScheduleShowRequest();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(NativeFileSystemPermissionRequestManager)