chromium/content/browser/lock_screen/lock_screen_service_impl_browsertest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>

#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/path_service.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/lock_screen/lock_screen_service_impl.h"
#include "content/browser/lock_screen/lock_screen_storage_impl.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/dns/mock_host_resolver.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/lock_screen/lock_screen.mojom.h"

namespace content {

class LockScreenServiceImplBrowserTest : public ContentBrowserTest {
 public:
  LockScreenServiceImplBrowserTest() {
    feature_list_.InitAndEnableFeature(features::kWebLockScreenApi);
  }

  LockScreenServiceImplBrowserTest(const LockScreenServiceImplBrowserTest&) =
      delete;
  LockScreenServiceImplBrowserTest& operator=(
      const LockScreenServiceImplBrowserTest&) = delete;

  ~LockScreenServiceImplBrowserTest() override = default;

  base::FilePath GetStoragePath() {
    base::FilePath path;
    EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &path));
    path = path.AppendASCII("web_lock_screen_api_data");
    path = path.AppendASCII("test-user");
    return path;
  }

  void SetUpOnMainThread() override {
    ContentBrowserTest::SetUpOnMainThread();
    EXPECT_TRUE(base::DeletePathRecursively(GetStoragePath()));
    LockScreenStorageImpl::GetInstance()->InitForTesting(
        shell()->web_contents()->GetBrowserContext(), GetStoragePath());
    host_resolver()->AddRule("*", "127.0.0.1");
    content::SetupCrossSiteRedirector(embedded_test_server());
    ASSERT_TRUE(embedded_test_server()->Start());
    GURL url = embedded_test_server()->GetURL("/lock_screen/simple.html");
    lock_screen_service_ = NavigateAndCreateService(url);
  }

  void TearDownOnMainThread() override {
    ContentBrowserTest::TearDownOnMainThread();
  }

  mojo::Remote<blink::mojom::LockScreenService> NavigateAndCreateService(
      const GURL& url) {
    Shell* shell = CreateBrowser();
    EXPECT_TRUE(NavigateToURL(shell, url));
    RenderFrameHost* rfh = shell->web_contents()->GetPrimaryMainFrame();
    mojo::Remote<blink::mojom::LockScreenService> service;
    LockScreenServiceImpl::Create(rfh, service.BindNewPipeAndPassReceiver());
    return service;
  }

  blink::mojom::LockScreenService* service() {
    return lock_screen_service_.get();
  }

  blink::mojom::LockScreenServiceStatus AwaitSetData(
      blink::mojom::LockScreenService* service,
      const std::string& key,
      const std::string& data) {
    base::RunLoop run_loop;
    blink::mojom::LockScreenServiceStatus result;
    service->SetData(key, data,
                     base::BindLambdaForTesting(
                         [&](blink::mojom::LockScreenServiceStatus status) {
                           result = status;
                           run_loop.Quit();
                         }));
    run_loop.Run();
    return result;
  }

  std::vector<std::string> AwaitGetKeys(
      blink::mojom::LockScreenService* service) {
    base::RunLoop run_loop;
    std::vector<std::string> result;
    service->GetKeys(
        base::BindLambdaForTesting([&](const std::vector<std::string>& keys) {
          result = keys;
          run_loop.Quit();
        }));
    run_loop.Run();
    return result;
  }

 private:
  base::test::ScopedFeatureList feature_list_;
  mojo::Remote<blink::mojom::LockScreenService> lock_screen_service_;
};

IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest,
                       CorrectDirectoryCreated) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::FilePath expected_dir = GetStoragePath();
  EXPECT_FALSE(base::PathExists(expected_dir));
  // TODO(crbug.com/40204655): Consider testing write failure case.
  ASSERT_EQ(blink::mojom::LockScreenServiceStatus::kSuccess,
            AwaitSetData(service(), "key1", "data1"));
  ASSERT_TRUE(base::PathExists(expected_dir));

  base::FileEnumerator e(expected_dir, false,
                         base::FileEnumerator::DIRECTORIES);
  std::vector<base::FilePath> directories;
  for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) {
    directories.push_back(name);
  }
  ASSERT_EQ(1u, directories.size());
  EXPECT_EQ(64u, directories[0].BaseName().MaybeAsASCII().size());
}

IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest, SetDataOpaqueOrigin) {
  auto service = NavigateAndCreateService(GURL("about:blank"));
  ASSERT_EQ(blink::mojom::LockScreenServiceStatus::kNotAllowedFromContext,
            AwaitSetData(service.get(), "key1", "data1"));
  ASSERT_EQ(blink::mojom::LockScreenServiceStatus::kNotAllowedFromContext,
            AwaitSetData(service.get(), "key2", "data2"));

  std::vector<std::string> result = AwaitGetKeys(service.get());
  ASSERT_EQ(0u, result.size());
}

IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest, GetKeys) {
  std::vector<std::string> result = AwaitGetKeys(service());
  ASSERT_EQ(0u, result.size());

  AwaitSetData(service(), "key1", "data1");
  AwaitSetData(service(), "key2", "data2");
  result = AwaitGetKeys(service());
  ASSERT_EQ(2u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key2", result[1]);

  AwaitSetData(service(), "key2", "data3");
  result = AwaitGetKeys(service());
  ASSERT_EQ(2u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key2", result[1]);
}

IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest,
                       DataNotSharedBetweenDifferentOrigins) {
  GURL url_a =
      embedded_test_server()->GetURL("a.com", "/lock_screen/simple.html");
  GURL url_b =
      embedded_test_server()->GetURL("b.com", "/lock_screen/simple.html");
  auto service_a = NavigateAndCreateService(url_a);
  auto service_b = NavigateAndCreateService(url_b);

  AwaitSetData(service_a.get(), "key1", "a");
  AwaitSetData(service_a.get(), "key2", "a");
  std::vector<std::string> result = AwaitGetKeys(service_a.get());
  ASSERT_EQ(2u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key2", result[1]);

  AwaitSetData(service_b.get(), "key1", "b");
  AwaitSetData(service_b.get(), "key3", "b");
  result = AwaitGetKeys(service_b.get());
  ASSERT_EQ(2u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key3", result[1]);
}

IN_PROC_BROWSER_TEST_F(LockScreenServiceImplBrowserTest,
                       DataSharedBetweenSameOrigins) {
  GURL url_a = embedded_test_server()->GetURL("/lock_screen/simple.html");
  GURL url_b = embedded_test_server()->GetURL("/lock_screen/simple.html?abcd");
  auto service_a = NavigateAndCreateService(url_a);
  auto service_b = NavigateAndCreateService(url_b);

  AwaitSetData(service_a.get(), "key1", "a");
  AwaitSetData(service_a.get(), "key2", "a");
  std::vector<std::string> result = AwaitGetKeys(service_a.get());
  ASSERT_EQ(2u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key2", result[1]);

  result = AwaitGetKeys(service_b.get());
  ASSERT_EQ(2u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key2", result[1]);

  AwaitSetData(service_b.get(), "key1", "b");
  AwaitSetData(service_b.get(), "key3", "b");
  result = AwaitGetKeys(service_a.get());
  ASSERT_EQ(3u, result.size());
  EXPECT_EQ("key1", result[0]);
  EXPECT_EQ("key2", result[1]);
  EXPECT_EQ("key3", result[2]);
}

}  // namespace content