chromium/android_webview/browser/permission/permission_request_handler_unittest.cc

// Copyright 2014 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/permission/permission_request_handler.h"

#include <memory>
#include <utility>

#include "android_webview/browser/permission/aw_permission_request.h"
#include "android_webview/browser/permission/aw_permission_request_delegate.h"
#include "android_webview/browser/permission/permission_request_handler_client.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace android_webview {

class TestAwPermissionRequestDelegate : public AwPermissionRequestDelegate {
 public:
  TestAwPermissionRequestDelegate(const GURL& origin,
                                  int64_t resources,
                                  base::RepeatingCallback<void(bool)> callback)
      : origin_(origin),
        resources_(resources),
        callback_(std::move(callback)) {}

  // Get the origin which initiated the permission request.
  const GURL& GetOrigin() override { return origin_; }

  // Get the resources the origin wanted to access.
  int64_t GetResources() override { return resources_; }

  // Notify the permission request is allowed or not.
  void NotifyRequestResult(bool allowed) override { callback_.Run(allowed); }

 private:
  GURL origin_;
  int64_t resources_;
  base::RepeatingCallback<void(bool)> callback_;
};

class TestPermissionRequestHandlerClient
    : public PermissionRequestHandlerClient {
 public:
  struct Permission {
    Permission() : resources(0) {}
    Permission(const GURL& origin, int64_t resources)
        : origin(origin), resources(resources) {}
    GURL origin;
    int64_t resources;
  };

  TestPermissionRequestHandlerClient() : request_(nullptr) {}

  void OnPermissionRequest(base::android::ScopedJavaLocalRef<jobject> j_request,
                           AwPermissionRequest* request) override {
    DCHECK(request);
    request_ = request;
    java_request_ = j_request;
    requested_permission_ =
        Permission(request->GetOrigin(), request->GetResources());
  }

  void OnPermissionRequestCanceled(AwPermissionRequest* request) override {
    canceled_permission_ =
        Permission(request->GetOrigin(), request->GetResources());
  }

  AwPermissionRequest* request() { return request_; }

  const Permission& requested_permission() { return requested_permission_; }

  const Permission& canceled_permission() { return canceled_permission_; }

  void Grant() {
    request_->OnAccept(nullptr, nullptr, true);
    request_->DeleteThis();
    request_ = nullptr;
  }

  void Deny() {
    request_->OnAccept(nullptr, nullptr, false);
    request_->DeleteThis();
    request_ = nullptr;
  }

  void Reset() {
    request_ = nullptr;
    requested_permission_ = Permission();
    canceled_permission_ = Permission();
  }

 private:
  base::android::ScopedJavaLocalRef<jobject> java_request_;
  raw_ptr<AwPermissionRequest> request_;
  Permission requested_permission_;
  Permission canceled_permission_;
};

class TestPermissionRequestHandler : public PermissionRequestHandler {
 public:
  TestPermissionRequestHandler(PermissionRequestHandlerClient* client)
      : PermissionRequestHandler(client, nullptr) {}

  const std::vector<base::WeakPtr<AwPermissionRequest>> requests() {
    return requests_;
  }

  void PruneRequests() { return PermissionRequestHandler::PruneRequests(); }
};

class PermissionRequestHandlerTest : public testing::Test {
 public:
  PermissionRequestHandlerTest() : handler_(&client_), allowed_(false) {}

  void NotifyRequestResult(bool allowed) { allowed_ = allowed; }

 protected:
  void SetUp() override {
    testing::Test::SetUp();
    origin_ = GURL("http://www.google.com");
    resources_ =
        AwPermissionRequest::VideoCapture | AwPermissionRequest::AudioCapture;
    delegate_ = std::make_unique<TestAwPermissionRequestDelegate>(
        origin_, resources_,
        base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                            base::Unretained(this)));
  }

  const GURL& origin() { return origin_; }

  int64_t resources() { return resources_; }

  std::unique_ptr<AwPermissionRequestDelegate> delegate() {
    return std::move(delegate_);
  }

  TestPermissionRequestHandler* handler() { return &handler_; }

  TestPermissionRequestHandlerClient* client() { return &client_; }

  bool allowed() { return allowed_; }

 private:
  GURL origin_;
  int64_t resources_;
  std::unique_ptr<AwPermissionRequestDelegate> delegate_;
  TestPermissionRequestHandlerClient client_;
  TestPermissionRequestHandler handler_;
  bool allowed_;
};

TEST_F(PermissionRequestHandlerTest, TestPermissionGranted) {
  handler()->SendRequest(delegate());
  // Verify Handler store the request correctly.
  ASSERT_EQ(1u, handler()->requests().size());
  EXPECT_EQ(origin(), handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(resources(), handler()->requests()[0]->GetResources());

  // Verify client's onPermissionRequest was called
  EXPECT_EQ(origin(), client()->request()->GetOrigin());
  EXPECT_EQ(resources(), client()->request()->GetResources());

  // Simulate the grant request.
  client()->Grant();
  // Verify the request is notified as granted
  EXPECT_TRUE(allowed());
  handler()->PruneRequests();
  // Verify the weak reference in handler was removed.
  EXPECT_TRUE(handler()->requests().empty());
}

TEST_F(PermissionRequestHandlerTest, TestPermissionDenied) {
  handler()->SendRequest(delegate());
  // Verify Handler store the request correctly.
  ASSERT_EQ(1u, handler()->requests().size());
  EXPECT_EQ(origin(), handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(resources(), handler()->requests()[0]->GetResources());

  // Verify client's onPermissionRequest was called
  EXPECT_EQ(origin(), client()->request()->GetOrigin());
  EXPECT_EQ(resources(), client()->request()->GetResources());

  // Simulate the deny request.
  client()->Deny();
  // Verify the request is notified as granted
  EXPECT_FALSE(allowed());
  handler()->PruneRequests();
  // Verify the weak reference in handler was removed.
  EXPECT_TRUE(handler()->requests().empty());
}

TEST_F(PermissionRequestHandlerTest, TestMultiplePermissionRequest) {
  GURL origin1 = GURL("http://a.google.com");
  int64_t resources1 = AwPermissionRequest::Geolocation;

  std::unique_ptr<AwPermissionRequestDelegate> delegate1;
  delegate1 = std::make_unique<TestAwPermissionRequestDelegate>(
      origin1, resources1,
      base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                          base::Unretained(this)));

  // Send 1st request
  handler()->SendRequest(delegate());
  // Verify Handler store the request correctly.
  ASSERT_EQ(1u, handler()->requests().size());
  EXPECT_EQ(origin(), handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(resources(), handler()->requests()[0]->GetResources());
  // Verify client's onPermissionRequest was called
  EXPECT_EQ(origin(), client()->request()->GetOrigin());
  EXPECT_EQ(resources(), client()->request()->GetResources());

  // Send 2nd request
  handler()->SendRequest(std::move(delegate1));
  // Verify Handler store the request correctly.
  ASSERT_EQ(2u, handler()->requests().size());
  EXPECT_EQ(origin(), handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(resources(), handler()->requests()[0]->GetResources());
  EXPECT_EQ(origin1, handler()->requests()[1]->GetOrigin());
  EXPECT_EQ(resources1, handler()->requests()[1]->GetResources());
  // Verify client's onPermissionRequest was called
  EXPECT_EQ(origin1, client()->request()->GetOrigin());
  EXPECT_EQ(resources1, client()->request()->GetResources());

  // Send 3rd request which has same origin and resources as first one.
  delegate1 = std::make_unique<TestAwPermissionRequestDelegate>(
      origin(), resources(),
      base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                          base::Unretained(this)));
  handler()->SendRequest(std::move(delegate1));
  // Verify Handler store the request correctly.
  ASSERT_EQ(3u, handler()->requests().size());
  EXPECT_EQ(origin(), handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(resources(), handler()->requests()[0]->GetResources());
  EXPECT_EQ(origin1, handler()->requests()[1]->GetOrigin());
  EXPECT_EQ(resources1, handler()->requests()[1]->GetResources());
  EXPECT_EQ(origin(), handler()->requests()[2]->GetOrigin());
  EXPECT_EQ(resources(), handler()->requests()[2]->GetResources());
  // Verify client's onPermissionRequest was called
  EXPECT_EQ(origin(), client()->request()->GetOrigin());
  EXPECT_EQ(resources(), client()->request()->GetResources());

  // Cancel the request.
  handler()->CancelRequest(origin(), resources());
  // Verify client's OnPermissionRequestCancled() was called.
  EXPECT_EQ(origin(), client()->canceled_permission().origin);
  EXPECT_EQ(resources(), client()->canceled_permission().resources);
  // Verify Handler store the request correctly, the 1st and 3rd were removed.
  handler()->PruneRequests();
  ASSERT_EQ(1u, handler()->requests().size());
  EXPECT_EQ(origin1, handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(resources1, handler()->requests()[0]->GetResources());
}

TEST_F(PermissionRequestHandlerTest, TestPreauthorizePermission) {
  handler()->PreauthorizePermission(origin(), resources());

  // Permission should granted without asking PermissionRequestHandlerClient.
  handler()->SendRequest(delegate());
  EXPECT_TRUE(allowed());
  EXPECT_EQ(nullptr, client()->request());

  // Only ask one preauthorized resource, permission should granted
  // without asking PermissionRequestHandlerClient.
  std::unique_ptr<AwPermissionRequestDelegate> delegate;
  delegate = std::make_unique<TestAwPermissionRequestDelegate>(
      origin(), AwPermissionRequest::AudioCapture,
      base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                          base::Unretained(this)));
  client()->Reset();
  handler()->SendRequest(std::move(delegate));
  EXPECT_TRUE(allowed());
  EXPECT_EQ(nullptr, client()->request());
}

TEST_F(PermissionRequestHandlerTest, TestOriginNotPreauthorized) {
  handler()->PreauthorizePermission(origin(), resources());

  // Ask the origin which wasn't preauthorized.
  GURL origin("http://a.google.com/a/b");
  std::unique_ptr<AwPermissionRequestDelegate> delegate;
  int64_t requested_resources = AwPermissionRequest::AudioCapture;
  delegate = std::make_unique<TestAwPermissionRequestDelegate>(
      origin, requested_resources,
      base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                          base::Unretained(this)));
  handler()->SendRequest(std::move(delegate));
  EXPECT_EQ(origin, handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(requested_resources, handler()->requests()[0]->GetResources());
  client()->Grant();
  EXPECT_TRUE(allowed());
}

TEST_F(PermissionRequestHandlerTest, TestResourcesNotPreauthorized) {
  handler()->PreauthorizePermission(origin(), resources());

  // Ask the resources which weren't preauthorized.
  std::unique_ptr<AwPermissionRequestDelegate> delegate;
  int64_t requested_resources =
      AwPermissionRequest::AudioCapture | AwPermissionRequest::Geolocation;
  delegate = std::make_unique<TestAwPermissionRequestDelegate>(
      origin(), requested_resources,
      base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                          base::Unretained(this)));

  handler()->SendRequest(std::move(delegate));
  EXPECT_EQ(origin(), handler()->requests()[0]->GetOrigin());
  EXPECT_EQ(requested_resources, handler()->requests()[0]->GetResources());
  client()->Deny();
  EXPECT_FALSE(allowed());
}

TEST_F(PermissionRequestHandlerTest, TestPreauthorizeMultiplePermission) {
  handler()->PreauthorizePermission(origin(), resources());
  // Preauthorize another permission.
  GURL origin("http://a.google.com/a/b");
  handler()->PreauthorizePermission(origin, AwPermissionRequest::Geolocation);
  GURL origin_hostname("http://a.google.com/");
  std::unique_ptr<AwPermissionRequestDelegate> delegate;
  delegate = std::make_unique<TestAwPermissionRequestDelegate>(
      origin_hostname, AwPermissionRequest::Geolocation,
      base::BindRepeating(&PermissionRequestHandlerTest::NotifyRequestResult,
                          base::Unretained(this)));
  handler()->SendRequest(std::move(delegate));
  EXPECT_TRUE(allowed());
  EXPECT_EQ(nullptr, client()->request());
}

}  // namespace android_webview