// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/browser/aw_permission_manager.h"
#include <list>
#include <memory>
#include "android_webview/browser/aw_browser_permission_request_delegate.h"
#include "android_webview/browser/aw_context_permissions_delegate.h"
#include "android_webview/browser/permission/permission_callback.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "content/public/browser/permission_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "url/gurl.h"
using blink::PermissionType;
using blink::mojom::PermissionStatus;
namespace android_webview {
namespace {
int kRenderProcessIDForTesting = 8;
int kRenderFrameIDForTesting = 19;
const char kEmbeddingOrigin[] = "https://www.google.com/";
const char kRequestingOrigin1[] = "https://www.google.com/";
const char kRequestingOrigin2[] = "https://www.chromium.org/";
class AwBrowserPermissionRequestDelegateForTesting final
: public AwBrowserPermissionRequestDelegate {
public:
void EnqueueResponse(const std::string& origin,
PermissionType type,
bool grant) {
for (auto it = request_.begin(); it != request_.end(); ++it) {
if ((*it)->type != type || (*it)->origin != origin)
continue;
PermissionCallback callback = std::move((*it)->callback);
request_.erase(it);
std::move(callback).Run(grant);
return;
}
response_.push_back(std::make_unique<Response>(origin, type, grant));
}
// AwBrowserPermissionRequestDelegate:
void RequestProtectedMediaIdentifierPermission(
const GURL& origin,
PermissionCallback callback) override {}
void CancelProtectedMediaIdentifierPermissionRequests(
const GURL& origin) override {}
void RequestGeolocationPermission(const GURL& origin,
PermissionCallback callback) override {
RequestPermission(origin, PermissionType::GEOLOCATION, std::move(callback));
}
void RequestStorageAccess(const url::Origin& origin,
PermissionCallback callback) override {
NOTREACHED();
}
void CancelGeolocationPermissionRequests(const GURL& origin) override {
CancelPermission(origin, PermissionType::GEOLOCATION);
}
void RequestMIDISysexPermission(const GURL& origin,
PermissionCallback callback) override {
RequestPermission(origin, PermissionType::MIDI_SYSEX, std::move(callback));
}
void CancelMIDISysexPermissionRequests(const GURL& origin) override {
CancelPermission(origin, PermissionType::MIDI_SYSEX);
}
private:
void RequestPermission(const GURL& origin,
PermissionType type,
PermissionCallback callback) {
for (auto it = response_.begin(); it != response_.end(); ++it) {
if ((*it)->type != type || (*it)->origin != origin)
continue;
bool grant = (*it)->grant;
response_.erase(it);
std::move(callback).Run(grant);
return;
}
request_.push_back(
std::make_unique<Request>(origin, type, std::move(callback)));
}
void CancelPermission(const GURL& origin, PermissionType type) {
for (auto it = request_.begin(); it != request_.end(); ++it) {
if ((*it)->type != type || (*it)->origin != origin)
continue;
request_.erase(it);
return;
}
NOTREACHED();
}
private:
struct Request {
GURL origin;
PermissionType type;
PermissionCallback callback;
Request(const GURL& origin,
PermissionType type,
PermissionCallback callback)
: origin(origin), type(type), callback(std::move(callback)) {}
};
struct Response {
GURL origin;
PermissionType type;
bool grant;
Response(const std::string& origin, PermissionType type, bool grant)
: origin(GURL(origin)), type(type), grant(grant) {}
};
std::list<std::unique_ptr<Response>> response_;
std::list<std::unique_ptr<Request>> request_;
};
class MockContextPermissionDelegate : public AwContextPermissionsDelegate {
public:
MockContextPermissionDelegate() = default;
PermissionStatus GetGeolocationPermission(
const GURL& requesting_origin) const override {
return PermissionStatus::ASK;
}
};
class AwPermissionManagerForTesting : public AwPermissionManager {
public:
AwPermissionManagerForTesting() : AwPermissionManager(context_delegate_) {}
~AwPermissionManagerForTesting() override {
// Call CancelPermissionRequests() from here so that it calls virtual
// methods correctly.
CancelPermissionRequests();
}
void EnqueuePermissionResponse(const std::string& origin,
PermissionType type,
bool grant) {
delegate()->EnqueueResponse(origin, type, grant);
}
private:
AwBrowserPermissionRequestDelegateForTesting* delegate() {
if (!delegate_) {
delegate_ =
std::make_unique<AwBrowserPermissionRequestDelegateForTesting>();
}
return delegate_.get();
}
// AwPermissionManager:
int GetRenderProcessID(content::RenderFrameHost* render_frame_host) override {
return kRenderProcessIDForTesting;
}
int GetRenderFrameID(content::RenderFrameHost* render_frame_host) override {
return kRenderFrameIDForTesting;
}
GURL LastCommittedMainOrigin(
content::RenderFrameHost* render_frame_host) override {
return GURL(kEmbeddingOrigin);
}
AwBrowserPermissionRequestDelegate* GetDelegate(
int render_process_id,
int render_frame_id) override {
CHECK_EQ(kRenderProcessIDForTesting, render_process_id);
CHECK_EQ(kRenderFrameIDForTesting, render_frame_id);
return delegate();
}
std::unique_ptr<AwBrowserPermissionRequestDelegateForTesting> delegate_;
MockContextPermissionDelegate context_delegate_;
};
class AwPermissionManagerTest : public testing::Test {
public:
AwPermissionManagerTest()
: render_frame_host(nullptr) {}
void PermissionRequestResponse(int id,
const std::vector<PermissionStatus>& status) {
ASSERT_EQ(status.size(), 1u);
resolved_permission_status.push_back(status[0]);
resolved_permission_request_id.push_back(id);
}
void PermissionsRequestResponse(int id,
const std::vector<PermissionStatus>& status) {
resolved_permission_status.insert(resolved_permission_status.end(),
status.begin(), status.end());
for (size_t i = 0; i < status.size(); ++i)
resolved_permission_request_id.push_back(id);
}
protected:
void SetUp() override {
manager = std::make_unique<AwPermissionManagerForTesting>();
}
void TearDown() override { manager.reset(); }
void EnqueuePermissionResponse(const std::string& origin,
PermissionType type,
bool grant) {
CHECK(manager);
manager->EnqueuePermissionResponse(origin, type, grant);
}
void RequestPermissions(
const std::vector<blink::PermissionType>& permissions,
content::RenderFrameHost* rfh,
const GURL& requesting_origin,
bool user_gesture,
base::OnceCallback<void(const std::vector<PermissionStatus>& status)>
callback) {
CHECK(manager);
manager->RequestPermissions(
rfh,
content::PermissionRequestDescription(permissions, user_gesture,
requesting_origin),
std::move(callback));
}
std::unique_ptr<AwPermissionManagerForTesting> manager;
// Use nullptr for testing. AwPermissionManagerForTesting override all methods
// that touch RenderFrameHost to work with nullptr.
raw_ptr<content::RenderFrameHost> render_frame_host;
std::vector<PermissionStatus> resolved_permission_status;
std::vector<int> resolved_permission_request_id;
};
// The most simple test, PermissionType::MIDI is hard-coded to be granted.
TEST_F(AwPermissionManagerTest, MIDIPermissionIsGrantedSynchronously) {
RequestPermissions(
{PermissionType::MIDI}, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 0));
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
}
TEST_F(AwPermissionManagerTest, ClipboardPermissionIsGrantedWithUserGesture) {
struct {
PermissionType type;
bool user_gesture;
PermissionStatus expected_result;
} test_cases[] = {
{PermissionType::CLIPBOARD_SANITIZED_WRITE, true,
PermissionStatus::GRANTED},
{PermissionType::CLIPBOARD_SANITIZED_WRITE, false,
PermissionStatus::DENIED},
{PermissionType::CLIPBOARD_READ_WRITE, true, PermissionStatus::DENIED},
{PermissionType::CLIPBOARD_READ_WRITE, false, PermissionStatus::DENIED}};
size_t permissions_requested = 0;
for (auto& test_case : test_cases) {
RequestPermissions(
{test_case.type}, render_frame_host, GURL(kRequestingOrigin1),
test_case.user_gesture,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), /*id=*/permissions_requested++));
ASSERT_EQ(resolved_permission_status.size(), permissions_requested);
EXPECT_EQ(test_case.expected_result,
resolved_permission_status[permissions_requested - 1]);
}
}
// Test the case a delegate is called, and it resolves the permission
// synchronously.
TEST_F(AwPermissionManagerTest, SinglePermissionRequestIsGrantedSynchronously) {
// Permission should be granted in this scenario.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, true);
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 0));
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
// Permission should not be granted in this scenario.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, false);
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 0));
ASSERT_EQ(2u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[1]);
}
// Test the case a delegate is called, and it resolves the permission
// asynchronously.
TEST_F(AwPermissionManagerTest,
SinglePermissionRequestIsGrantedAsynchronously) {
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 0));
EXPECT_EQ(0u, resolved_permission_status.size());
// This will resolve the permission.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, true);
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
}
// Test the case a delegate is called, and the manager is deleted before the
// delegate callback is invoked.
TEST_F(AwPermissionManagerTest, ManagerIsDeletedWhileDelegateProcesses) {
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 0));
EXPECT_EQ(0u, resolved_permission_status.size());
// Delete the manager.
manager.reset();
// All requests are cancelled internally.
EXPECT_EQ(0u, resolved_permission_status.size());
}
// Test the case multiple permissions are requested for the same origin, and the
// second permission is also resolved when the first permission is resolved.
TEST_F(AwPermissionManagerTest,
MultiplePermissionRequestsAreGrantedTogether) {
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 1));
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 2));
EXPECT_EQ(0u, resolved_permission_status.size());
// This will resolve the permission.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, true);
ASSERT_EQ(2u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[1]);
}
// Test the case multiple permissions are requested for different origins, and
// each permission is resolved respectively in the requested order.
TEST_F(AwPermissionManagerTest,
MultiplePermissionRequestsAreGrantedRespectively) {
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 1));
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin2), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 2));
EXPECT_EQ(0u, resolved_permission_status.size());
// This will resolve the first request.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, true);
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(1, resolved_permission_request_id[0]);
// This will resolve the second request.
manager->EnqueuePermissionResponse(kRequestingOrigin2,
PermissionType::GEOLOCATION, false);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[1]);
EXPECT_EQ(2, resolved_permission_request_id[1]);
}
// Test the case multiple permissions are requested through single
// RequestPermissions call, then resolved synchronously.
TEST_F(AwPermissionManagerTest,
SinglePermissionsRequestIsGrantedSynchronously) {
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, false);
std::vector<PermissionType> permissions = {PermissionType::MIDI,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 0));
ASSERT_EQ(2u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[1]);
}
// Test the case multiple permissions are requested through single
// RequestPermissions call, then one is resolved synchronously, the other is
// resolved asynchronously.
TEST_F(AwPermissionManagerTest,
SinglePermissionsRequestIsGrantedAsynchronously) {
std::vector<PermissionType> permissions = {PermissionType::MIDI,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 0));
// PermissionType::MIDI is resolved synchronously, but all permissions result
// are notified together when all permissions are resolved.
EXPECT_EQ(0u, resolved_permission_status.size());
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, false);
ASSERT_EQ(2u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[1]);
}
// Test the case multiple permissions are requested multiple times as follow.
// 1. Permission A and B are requested.
// 2. Permission A is resolved.
// 3. Permission A is requested for the same origin before the B is resolved.
TEST_F(AwPermissionManagerTest, ComplicatedRequestScenario1) {
// In the first case, the permission A is a type that does not call an
// internal delegate method.
std::vector<PermissionType> permissions_1 = {PermissionType::MIDI,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_1, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 1));
EXPECT_EQ(0u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::MIDI}, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 2));
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(2, resolved_permission_request_id[0]);
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, true);
ASSERT_EQ(3u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[1]);
EXPECT_EQ(1, resolved_permission_request_id[1]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[2]);
EXPECT_EQ(1, resolved_permission_request_id[2]);
// In the second case, the permission A is a type that calls an internal
// delegate method.
std::vector<PermissionType> permissions_2 = {PermissionType::GEOLOCATION,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_2, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 3));
ASSERT_EQ(3u, resolved_permission_status.size());
// The permission A is resolved, but the first request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, false);
ASSERT_EQ(3u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 4));
// The second request is finished first by using the resolved result for the
// first request.
ASSERT_EQ(4u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[3]);
EXPECT_EQ(4, resolved_permission_request_id[3]);
// Then the first request is finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, true);
ASSERT_EQ(6u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[4]);
EXPECT_EQ(3, resolved_permission_request_id[4]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[5]);
EXPECT_EQ(3, resolved_permission_request_id[5]);
}
// Test the case multiple permissions are requested multiple times as follow.
// 1. Permission A and B are requested.
// 2. Permission A is resolved.
// 3. Permission A is requested for a different origin before the B is
// resolved.
TEST_F(AwPermissionManagerTest, ComplicatedRequestScenario2) {
// In the first case, the permission A is a type that does not call an
// internal delegate method.
std::vector<PermissionType> permissions_1 = {PermissionType::MIDI,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_1, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 1));
EXPECT_EQ(0u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::MIDI}, render_frame_host, GURL(kRequestingOrigin2), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 2));
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(2, resolved_permission_request_id[0]);
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, true);
ASSERT_EQ(3u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[1]);
EXPECT_EQ(1, resolved_permission_request_id[1]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[2]);
EXPECT_EQ(1, resolved_permission_request_id[2]);
// In the second case, the permission A is a type that calls an internal
// delegate method.
std::vector<PermissionType> permissions_2 = {PermissionType::GEOLOCATION,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_2, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 3));
ASSERT_EQ(3u, resolved_permission_status.size());
// The permission A is resolved, but the first request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, false);
ASSERT_EQ(3u, resolved_permission_status.size());
// The second request could be resolved synchronously even if the first
// request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin2,
PermissionType::GEOLOCATION, true);
RequestPermissions(
{PermissionType::GEOLOCATION}, render_frame_host,
GURL(kRequestingOrigin2), true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 4));
ASSERT_EQ(4u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[3]);
EXPECT_EQ(4, resolved_permission_request_id[3]);
// The first request is finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, true);
ASSERT_EQ(6u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[4]);
EXPECT_EQ(3, resolved_permission_request_id[4]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[5]);
EXPECT_EQ(3, resolved_permission_request_id[5]);
}
// Test the case multiple permissions are requested multiple times as follow.
// 1. Permission A and B are requested.
// 2. Permission B is resolved.
// 3. Permission A is requested for the same origin before the A is resolved.
TEST_F(AwPermissionManagerTest, ComplicatedRequestScenario3) {
// In the first case, the permission A is a type that does not call an
// internal delegate method.
std::vector<PermissionType> permissions_1 = {PermissionType::MIDI,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_1, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 1));
EXPECT_EQ(0u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::MIDI_SYSEX}, render_frame_host, GURL(kRequestingOrigin1),
true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 2));
EXPECT_EQ(0u, resolved_permission_status.size());
// Resolving the first request results in both requests finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, false);
ASSERT_EQ(3u, resolved_permission_status.size());
// Note: The result order in the same requiest is ensured, but each results
// for a request can be swapped because the manager use IDMap to resolve
// matched requests.
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[1]);
EXPECT_EQ(1, resolved_permission_request_id[1]);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[2]);
EXPECT_EQ(1, resolved_permission_request_id[2]);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[0]);
EXPECT_EQ(2, resolved_permission_request_id[0]);
// In the second case, the permission A is a type that calls an internal
// delegate method.
std::vector<PermissionType> permissions_2 = {PermissionType::GEOLOCATION,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_2, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 3));
ASSERT_EQ(3u, resolved_permission_status.size());
// The permission B is resolved, but the first request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, false);
ASSERT_EQ(3u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::MIDI_SYSEX}, render_frame_host, GURL(kRequestingOrigin1),
true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 4));
ASSERT_EQ(3u, resolved_permission_status.size());
// Resolving the first request results in both requests finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, true);
ASSERT_EQ(6u, resolved_permission_status.size());
// Order can be swapped. See Note in ComplicatedRequestScenario1.
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[4]);
EXPECT_EQ(3, resolved_permission_request_id[4]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[5]);
EXPECT_EQ(3, resolved_permission_request_id[5]);
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[3]);
EXPECT_EQ(4, resolved_permission_request_id[3]);
}
// Test the case multiple permissions are requested multiple times as follow.
// 1. Permission A and B are requested.
// 2. Permission B is resolved.
// 3. Permission A is requested for a different origin before the A is
// resolved.
TEST_F(AwPermissionManagerTest, ComplicatedRequestScenario4) {
// In the first case, the permission A is a type that does not call an
// internal delegate method.
std::vector<PermissionType> permissions_1 = {PermissionType::MIDI,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_1, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 1));
EXPECT_EQ(0u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::MIDI_SYSEX}, render_frame_host, GURL(kRequestingOrigin2),
true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 2));
EXPECT_EQ(0u, resolved_permission_status.size());
// The second request could be resolved synchronously even if the first
// request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin2,
PermissionType::MIDI_SYSEX, true);
ASSERT_EQ(1u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[0]);
EXPECT_EQ(2, resolved_permission_request_id[0]);
// The first request is finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, false);
ASSERT_EQ(3u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::GRANTED, resolved_permission_status[1]);
EXPECT_EQ(1, resolved_permission_request_id[1]);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[2]);
EXPECT_EQ(1, resolved_permission_request_id[2]);
// In the second case, the permission A is a type that calls an internal
// delegate method.
std::vector<PermissionType> permissions_2 = {PermissionType::GEOLOCATION,
PermissionType::MIDI_SYSEX};
RequestPermissions(
permissions_2, render_frame_host, GURL(kRequestingOrigin1), true,
base::BindOnce(&AwPermissionManagerTest::PermissionsRequestResponse,
base::Unretained(this), 3));
ASSERT_EQ(3u, resolved_permission_status.size());
// The permission B is resolved, but the first request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::GEOLOCATION, false);
ASSERT_EQ(3u, resolved_permission_status.size());
RequestPermissions(
{PermissionType::MIDI_SYSEX}, render_frame_host, GURL(kRequestingOrigin2),
true,
base::BindOnce(&AwPermissionManagerTest::PermissionRequestResponse,
base::Unretained(this), 4));
ASSERT_EQ(3u, resolved_permission_status.size());
// The second request could be resolved synchronously even if the first
// request isn't finished.
manager->EnqueuePermissionResponse(kRequestingOrigin2,
PermissionType::MIDI_SYSEX, false);
ASSERT_EQ(4u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[3]);
EXPECT_EQ(4, resolved_permission_request_id[3]);
// Resolving the first request results in resuming the second request.
manager->EnqueuePermissionResponse(kRequestingOrigin1,
PermissionType::MIDI_SYSEX, false);
ASSERT_EQ(6u, resolved_permission_status.size());
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[4]);
EXPECT_EQ(3, resolved_permission_request_id[4]);
EXPECT_EQ(PermissionStatus::DENIED, resolved_permission_status[5]);
EXPECT_EQ(3, resolved_permission_request_id[5]);
}
} // namespace
} // namespace android_webview