blob: 4954acc9296680d83842f835bd632ad9193edb0d [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/badging/badge_manager.h"
#include "chrome/browser/badging/badge_manager_factory.h"
#include "chrome/browser/badging/test_badge_manager_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
#include "chrome/browser/web_applications/components/web_app_provider_base.h"
#include "content/public/browser/web_contents.h"
using content::RenderFrameHost;
using content::WebContents;
namespace web_app {
class WebAppBadgingBrowserTest : public WebAppControllerBrowserTest {
public:
WebAppBadgingBrowserTest()
: WebAppControllerBrowserTest(),
cross_origin_https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
void SetUpCommandLine(base::CommandLine* command_line) override {
WebAppControllerBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII("enable-blink-features", "Badging");
}
void SetUpOnMainThread() override {
WebAppControllerBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(cross_origin_https_server_.Start());
ASSERT_TRUE(https_server()->Start());
ASSERT_TRUE(embedded_test_server()->Start());
GURL cross_site_frame_url =
cross_origin_https_server_.GetURL("/ssl/google.html");
cross_site_app_id_ = InstallPWA(cross_site_frame_url);
// Note: The url for the cross site frame is embedded in the query string.
GURL app_url = https_server()->GetURL(
"/ssl/page_with_in_scope_and_cross_site_frame.html?url=" +
cross_site_frame_url.spec());
main_app_id_ = InstallPWA(app_url);
content::WebContents* web_contents = OpenApplication(main_app_id_);
// There should be exactly 3 frames:
// 1) The main frame.
// 2) A cross site frame, on |cross_site_frame_url|.
// 3) A sub frame in the app's scope.
auto frames = web_contents->GetAllFrames();
ASSERT_EQ(3u, frames.size());
main_frame_ = web_contents->GetMainFrame();
for (auto* frame : frames) {
if (url::IsSameOriginWith(frame->GetLastCommittedURL(),
main_frame_->GetLastCommittedURL())) {
in_scope_frame_ = frame;
} else if (frame != main_frame_) {
cross_site_frame_ = frame;
}
}
ASSERT_TRUE(main_frame_);
ASSERT_TRUE(in_scope_frame_);
ASSERT_TRUE(cross_site_frame_);
awaiter_ = std::make_unique<base::RunLoop>();
badging::BadgeManager* badge_manager =
badging::BadgeManagerFactory::GetInstance()->GetForProfile(profile());
// The delegate is owned by the badge manager. We hold a pointer to it for
// the test.
std::unique_ptr<badging::TestBadgeManagerDelegate> owned_delegate =
std::make_unique<badging::TestBadgeManagerDelegate>(profile(),
badge_manager);
owned_delegate->SetOnBadgeChanged(base::BindRepeating(
&WebAppBadgingBrowserTest::OnBadgeChanged, base::Unretained(this)));
delegate_ = owned_delegate.get();
badge_manager->SetDelegate(std::move(owned_delegate));
}
void OnBadgeChanged() {
// This is only set up to deal with one badge change at a time, in order to
// make asserting the result of a badge change easier.
int total_changes =
delegate_->cleared_badges().size() + delegate_->set_badges().size();
ASSERT_EQ(total_changes, 1);
if (delegate_->cleared_badges().size()) {
changed_app_id_ = delegate_->cleared_badges()[0];
was_cleared_ = true;
}
if (delegate_->set_badges().size() == 1) {
changed_app_id_ = delegate_->set_badges()[0].first;
last_badge_content_ = delegate_->set_badges()[0].second;
was_flagged_ = last_badge_content_ == base::nullopt;
}
awaiter_->Quit();
}
protected:
void ExecuteScriptAndWaitForBadgeChange(std::string script,
RenderFrameHost* on) {
was_cleared_ = false;
was_flagged_ = false;
changed_app_id_ = base::nullopt;
last_badge_content_ = base::nullopt;
awaiter_ = std::make_unique<base::RunLoop>();
delegate_->ResetBadges();
ASSERT_TRUE(content::ExecuteScript(on, script));
if (was_cleared_ || was_flagged_ || changed_app_id_ || last_badge_content_)
return;
awaiter_->Run();
}
const web_app::AppId& main_app_id() { return main_app_id_; }
const web_app::AppId& cross_site_app_id() { return cross_site_app_id_; }
RenderFrameHost* main_frame_;
RenderFrameHost* in_scope_frame_;
RenderFrameHost* cross_site_frame_;
bool was_cleared_ = false;
bool was_flagged_ = false;
base::Optional<web_app::AppId> changed_app_id_ = base::nullopt;
base::Optional<uint64_t> last_badge_content_ = base::nullopt;
private:
web_app::AppId main_app_id_;
web_app::AppId cross_site_app_id_;
std::unique_ptr<base::RunLoop> awaiter_;
badging::TestBadgeManagerDelegate* delegate_;
net::EmbeddedTestServer cross_origin_https_server_;
};
// Tests that the badge for the main frame is not affected by changing the badge
// of a cross site subframe.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest,
CrossSiteFrameCannotChangeMainFrameBadge) {
// Clearing from cross site frame should affect only the cross site app.
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.clear()",
cross_site_frame_);
ASSERT_TRUE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(cross_site_app_id(), changed_app_id_);
// Setting from cross site frame should affect only the cross site app.
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(77)",
cross_site_frame_);
ASSERT_FALSE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(77u, last_badge_content_);
ASSERT_EQ(cross_site_app_id(), changed_app_id_);
}
// Tests that setting the badge to an integer will be propagated across
// processes.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest, BadgeCanBeSetToAnInteger) {
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(99)", main_frame_);
ASSERT_FALSE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::Optional<uint64_t>(99u), last_badge_content_);
}
// Tests that calls to |Badge.clear| are propagated across processes.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest,
BadgeCanBeClearedWithClearMethod) {
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(55)", main_frame_);
ASSERT_FALSE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::Optional<uint64_t>(55u), last_badge_content_);
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.clear()", main_frame_);
ASSERT_TRUE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::nullopt, last_badge_content_);
}
// Tests that calling Badge.set(0) is equivalent to calling |Badge.clear| and
// that it propagates across processes.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest, BadgeCanBeClearedWithZero) {
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set(0)", main_frame_);
ASSERT_TRUE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::nullopt, last_badge_content_);
}
// Tests that setting the badge without content is propagated across processes.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest, BadgeCanBeSetWithoutAValue) {
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set()", main_frame_);
ASSERT_FALSE(was_cleared_);
ASSERT_TRUE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::nullopt, last_badge_content_);
}
// Tests that the badge can be set and cleared from an in scope frame.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest,
BadgeCanBeSetAndClearedFromInScopeFrame) {
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.set()",
in_scope_frame_);
ASSERT_FALSE(was_cleared_);
ASSERT_TRUE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::nullopt, last_badge_content_);
ExecuteScriptAndWaitForBadgeChange("ExperimentalBadge.clear()",
in_scope_frame_);
ASSERT_TRUE(was_cleared_);
ASSERT_FALSE(was_flagged_);
ASSERT_EQ(main_app_id(), changed_app_id_);
ASSERT_EQ(base::nullopt, last_badge_content_);
}
// Tests that badging incognito windows does not cause a crash.
IN_PROC_BROWSER_TEST_P(WebAppBadgingBrowserTest,
BadgingIncognitoWindowsDoesNotCrash) {
Browser* incognito_browser =
OpenURLOffTheRecord(profile(), main_frame_->GetLastCommittedURL());
RenderFrameHost* incognito_frame = incognito_browser->tab_strip_model()
->GetActiveWebContents()
->GetMainFrame();
ASSERT_TRUE(
content::ExecuteScript(incognito_frame, "ExperimentalBadge.set()"));
ASSERT_TRUE(
content::ExecuteScript(incognito_frame, "ExperimentalBadge.clear()"));
}
INSTANTIATE_TEST_SUITE_P(
/* no prefix */,
WebAppBadgingBrowserTest,
::testing::Values(ControllerType::kHostedAppController,
ControllerType::kUnifiedControllerWithBookmarkApp,
ControllerType::kUnifiedControllerWithWebApp));
} // namespace web_app