blob: 4a38af1ae235b5f4bfab27adc600118d3e19d111 [file] [log] [blame]
// Copyright 2018 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/web_applications/components/web_app_data_retriever.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/optional.h"
#include "base/strings/nullable_string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/installable/fake_installable_manager.h"
#include "chrome/browser/installable/installable_data.h"
#include "chrome/browser/installable/installable_manager.h"
#include "chrome/common/chrome_render_frame.mojom-test-utils.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/site_instance.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/manifest/manifest.h"
namespace web_app {
namespace {
const char kFooUrl[] = "https://foo.example";
const char kFooUrl2[] = "https://foo.example/bar";
const char kFooTitle[] = "Foo Title";
const char kBarUrl[] = "https://bar.example";
} // namespace
class FakeChromeRenderFrame
: public chrome::mojom::ChromeRenderFrameInterceptorForTesting {
public:
explicit FakeChromeRenderFrame(const WebApplicationInfo& web_app_info)
: web_app_info_(web_app_info) {}
~FakeChromeRenderFrame() override = default;
ChromeRenderFrame* GetForwardingInterface() override {
NOTREACHED();
return nullptr;
}
void Bind(mojo::ScopedInterfaceEndpointHandle handle) {
binding_.Bind(
mojo::AssociatedInterfaceRequest<ChromeRenderFrame>(std::move(handle)));
}
void GetWebApplicationInfo(GetWebApplicationInfoCallback callback) override {
std::move(callback).Run(web_app_info_);
}
private:
WebApplicationInfo web_app_info_;
mojo::AssociatedBinding<chrome::mojom::ChromeRenderFrame> binding_{this};
};
class WebAppDataRetrieverTest : public ChromeRenderViewHostTestHarness {
public:
WebAppDataRetrieverTest() = default;
~WebAppDataRetrieverTest() override = default;
void SetFakeChromeRenderFrame(
FakeChromeRenderFrame* fake_chrome_render_frame) {
web_contents()
->GetMainFrame()
->GetRemoteAssociatedInterfaces()
->OverrideBinderForTesting(
chrome::mojom::ChromeRenderFrame::Name_,
base::BindRepeating(&FakeChromeRenderFrame::Bind,
base::Unretained(fake_chrome_render_frame)));
}
void GetWebApplicationInfoCallback(
base::OnceClosure quit_closure,
std::unique_ptr<WebApplicationInfo> web_app_info) {
web_app_info_ = std::move(web_app_info);
std::move(quit_closure).Run();
}
void GetIconsCallback(base::OnceClosure quit_closure,
std::vector<WebApplicationInfo::IconInfo> icons) {
icons_ = std::move(icons);
std::move(quit_closure).Run();
}
std::unique_ptr<WebApplicationInfo> CreateWebApplicationInfo(
const GURL& url,
const std::string name,
const std::string description,
const GURL& scope,
base::Optional<SkColor> theme_color) {
auto web_app_info = std::make_unique<WebApplicationInfo>();
web_app_info->app_url = url;
web_app_info->title = base::UTF8ToUTF16(name);
web_app_info->description = base::UTF8ToUTF16(description);
web_app_info->scope = scope;
web_app_info->theme_color = theme_color;
return web_app_info;
}
static base::NullableString16 ToNullableUTF16(const std::string& str) {
return base::NullableString16(base::UTF8ToUTF16(str), false);
}
protected:
content::WebContentsTester* web_contents_tester() {
return content::WebContentsTester::For(web_contents());
}
const std::unique_ptr<WebApplicationInfo>& web_app_info() {
return web_app_info_.value();
}
const std::vector<WebApplicationInfo::IconInfo>& icons() { return icons_; }
private:
base::Optional<std::unique_ptr<WebApplicationInfo>> web_app_info_;
std::vector<WebApplicationInfo::IconInfo> icons_;
DISALLOW_COPY_AND_ASSIGN(WebAppDataRetrieverTest);
};
TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_NoEntry) {
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(nullptr, web_app_info());
}
TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_AppUrlAbsent) {
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl));
WebApplicationInfo original_web_app_info;
original_web_app_info.app_url = GURL();
FakeChromeRenderFrame fake_chrome_render_frame(original_web_app_info);
SetFakeChromeRenderFrame(&fake_chrome_render_frame);
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
// If the WebApplicationInfo has no URL, we fallback to the last committed
// URL.
EXPECT_EQ(GURL(kFooUrl), web_app_info()->app_url);
}
TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_AppUrlPresent) {
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl));
WebApplicationInfo original_web_app_info;
original_web_app_info.app_url = GURL(kBarUrl);
FakeChromeRenderFrame fake_chrome_render_frame(original_web_app_info);
SetFakeChromeRenderFrame(&fake_chrome_render_frame);
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(original_web_app_info.app_url, web_app_info()->app_url);
}
TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_TitleAbsentFromRenderer) {
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl));
const auto web_contents_title = base::UTF8ToUTF16(kFooTitle);
web_contents_tester()->SetTitle(web_contents_title);
WebApplicationInfo original_web_app_info;
original_web_app_info.title = base::UTF8ToUTF16("");
FakeChromeRenderFrame fake_chrome_render_frame(original_web_app_info);
SetFakeChromeRenderFrame(&fake_chrome_render_frame);
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
// If the WebApplicationInfo has no title, we fallback to the WebContents
// title.
EXPECT_EQ(web_contents_title, web_app_info()->title);
}
TEST_F(WebAppDataRetrieverTest,
GetWebApplicationInfo_TitleAbsentFromWebContents) {
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl));
web_contents_tester()->SetTitle(base::UTF8ToUTF16(""));
WebApplicationInfo original_web_app_info;
original_web_app_info.title = base::UTF8ToUTF16("");
FakeChromeRenderFrame fake_chrome_render_frame(original_web_app_info);
SetFakeChromeRenderFrame(&fake_chrome_render_frame);
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
// If the WebApplicationInfo has no title and the WebContents has no title,
// we fallback to app_url.
EXPECT_EQ(base::UTF8ToUTF16(web_app_info()->app_url.spec()),
web_app_info()->title);
}
TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_WebContentsDestroyed) {
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl));
FakeChromeRenderFrame fake_chrome_render_frame{WebApplicationInfo()};
SetFakeChromeRenderFrame(&fake_chrome_render_frame);
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
DeleteContents();
run_loop.Run();
EXPECT_EQ(nullptr, web_app_info());
}
TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_FrameNavigated) {
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl));
FakeChromeRenderFrame fake_chrome_render_frame{WebApplicationInfo()};
SetFakeChromeRenderFrame(&fake_chrome_render_frame);
base::RunLoop run_loop;
WebAppDataRetriever retriever;
retriever.GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppDataRetrieverTest::GetWebApplicationInfoCallback,
base::Unretained(this), run_loop.QuitClosure()));
web_contents_tester()->NavigateAndCommit(GURL(kFooUrl2));
run_loop.Run();
EXPECT_EQ(nullptr, web_app_info());
}
TEST_F(WebAppDataRetrieverTest, CheckInstallabilityAndRetrieveManifest) {
const GURL manifest_start_url = GURL("https://example.com/start");
const std::string manifest_short_name = "Short Name from Manifest";
const std::string manifest_name = "Name from Manifest";
const GURL manifest_scope = GURL("https://example.com/scope");
const base::Optional<SkColor> manifest_theme_color = 0xAABBCCDD;
{
auto manifest = std::make_unique<blink::Manifest>();
manifest->short_name = ToNullableUTF16(manifest_short_name);
manifest->name = ToNullableUTF16(manifest_name);
manifest->start_url = manifest_start_url;
manifest->scope = manifest_scope;
manifest->theme_color = manifest_theme_color;
FakeInstallableManager::CreateForWebContentsWithManifest(
web_contents(), NO_ERROR_DETECTED, GURL("https://example.com/manifest"),
std::move(manifest));
}
base::RunLoop run_loop;
bool callback_called = false;
WebAppDataRetriever retriever;
retriever.CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindLambdaForTesting(
[&](const blink::Manifest& result, bool is_installable) {
EXPECT_TRUE(is_installable);
EXPECT_EQ(base::UTF8ToUTF16(manifest_short_name),
result.short_name.string());
EXPECT_EQ(base::UTF8ToUTF16(manifest_name), result.name.string());
EXPECT_EQ(manifest_start_url, result.start_url);
EXPECT_EQ(manifest_scope, result.scope);
EXPECT_EQ(manifest_theme_color, result.theme_color);
callback_called = true;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(callback_called);
}
TEST_F(WebAppDataRetrieverTest, CheckInstallabilityFails) {
{
auto manifest = std::make_unique<blink::Manifest>();
FakeInstallableManager::CreateForWebContentsWithManifest(
web_contents(), NO_MANIFEST, GURL(), std::move(manifest));
}
base::RunLoop run_loop;
bool callback_called = false;
WebAppDataRetriever retriever;
retriever.CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindLambdaForTesting(
[&](const blink::Manifest& result, bool is_installable) {
EXPECT_FALSE(is_installable);
callback_called = true;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(callback_called);
}
} // namespace web_app