blob: 16e6ecacde2fe603afe736a41b80f237ece98557 [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/history/history_tab_helper.h"
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/url_row.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/page_transition_types.h"
#if defined(OS_ANDROID)
#include "chrome/browser/feed/android/feed_service_factory.h"
#include "components/feed/core/v2/public/feed_service.h"
#include "components/feed/core/v2/public/test/stub_feed_api.h"
#endif
using testing::NiceMock;
namespace {
#if defined(OS_ANDROID)
class TestFeedApi : public feed::StubFeedApi {
public:
MOCK_METHOD1(WasUrlRecentlyNavigatedFromFeed, bool(const GURL&));
};
#endif
} // namespace
class HistoryTabHelperTest : public ChromeRenderViewHostTestHarness {
protected:
HistoryTabHelperTest() = default;
HistoryTabHelperTest(const HistoryTabHelperTest&) = delete;
HistoryTabHelperTest& operator=(const HistoryTabHelperTest&) = delete;
// ChromeRenderViewHostTestHarness:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
#if defined(OS_ANDROID)
feed::FeedServiceFactory::GetInstance()->SetTestingFactory(
profile(),
base::BindLambdaForTesting([&](content::BrowserContext* context) {
std::unique_ptr<KeyedService> result =
feed::FeedService::CreateForTesting(&test_feed_api_);
return result;
}));
#endif
ASSERT_TRUE(profile()->CreateHistoryService());
history_service_ = HistoryServiceFactory::GetForProfile(
profile(), ServiceAccessType::IMPLICIT_ACCESS);
ASSERT_TRUE(history_service_);
history_service_->AddPage(
page_url_, base::Time::Now(), /*context_id=*/nullptr,
/*nav_entry_id=*/0,
/*referrer=*/GURL(), history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
history::SOURCE_BROWSED, /*did_replace_entry=*/false,
/*floc_allowed=*/true);
HistoryTabHelper::CreateForWebContents(web_contents());
}
HistoryTabHelper* history_tab_helper() {
return HistoryTabHelper::FromWebContents(web_contents());
}
content::WebContentsTester* web_contents_tester() {
return content::WebContentsTester::For(web_contents());
}
std::string QueryPageTitleFromHistory(const GURL& url) {
std::string title;
base::RunLoop loop;
history_service_->QueryURL(
url, /*want_visits=*/false,
base::BindLambdaForTesting([&](history::QueryURLResult result) {
EXPECT_TRUE(result.success);
title = base::UTF16ToUTF8(result.row.title());
loop.Quit();
}),
&tracker_);
loop.Run();
return title;
}
history::MostVisitedURLList QueryMostVisitedURLs() {
history::MostVisitedURLList result;
std::string title;
base::RunLoop loop;
history_service_->QueryMostVisitedURLs(
/*result_count=*/10, /*days_back=*/1,
base::BindLambdaForTesting([&](history::MostVisitedURLList v) {
result = v;
loop.Quit();
}),
&tracker_);
loop.Run();
return result;
}
std::set<GURL> GetMostVisitedURLSet() {
std::set<GURL> result;
for (const history::MostVisitedURL& mv_url : QueryMostVisitedURLs()) {
result.insert(mv_url.url);
}
return result;
}
const GURL page_url_ = GURL("http://foo.com");
protected:
base::CancelableTaskTracker tracker_;
history::HistoryService* history_service_;
#if defined(OS_ANDROID)
TestFeedApi test_feed_api_;
#endif
};
TEST_F(HistoryTabHelperTest, ShouldUpdateTitleInHistory) {
web_contents_tester()->NavigateAndCommit(page_url_);
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
ASSERT_NE(nullptr, entry);
web_contents()->UpdateTitleForEntry(entry, u"title1");
EXPECT_EQ("title1", QueryPageTitleFromHistory(page_url_));
}
TEST_F(HistoryTabHelperTest, ShouldLimitTitleUpdatesPerPage) {
web_contents_tester()->NavigateAndCommit(page_url_);
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
ASSERT_NE(nullptr, entry);
// The first 10 title updates are accepted and update history, as per
// history::kMaxTitleChanges.
for (int i = 1; i <= history::kMaxTitleChanges; ++i) {
const std::string title = base::StringPrintf("title%d", i);
web_contents()->UpdateTitleForEntry(entry, base::UTF8ToUTF16(title));
}
ASSERT_EQ("title10", QueryPageTitleFromHistory(page_url_));
// Further updates should be ignored.
web_contents()->UpdateTitleForEntry(entry, u"title11");
EXPECT_EQ("title10", QueryPageTitleFromHistory(page_url_));
}
TEST_F(HistoryTabHelperTest, CreateAddPageArgsReferringURLMainFrameNoReferrer) {
NiceMock<content::MockNavigationHandle> navigation_handle(web_contents());
navigation_handle.set_redirect_chain({GURL("https://someurl.com")});
navigation_handle.set_previous_main_frame_url(GURL("http://previousurl.com"));
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
EXPECT_TRUE(args.referrer.is_empty());
}
TEST_F(HistoryTabHelperTest,
CreateAddPageArgsReferringURLMainFrameSameOriginReferrer) {
NiceMock<content::MockNavigationHandle> navigation_handle(web_contents());
navigation_handle.set_redirect_chain({GURL("https://someurl.com")});
navigation_handle.set_previous_main_frame_url(
GURL("http://previousurl.com/abc"));
auto referrer = blink::mojom::Referrer::New();
referrer->url = navigation_handle.GetPreviousMainFrameURL().GetOrigin();
referrer->policy = network::mojom::ReferrerPolicy::kDefault;
navigation_handle.SetReferrer(std::move(referrer));
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
EXPECT_EQ(args.referrer, GURL("http://previousurl.com/abc"));
}
TEST_F(HistoryTabHelperTest,
CreateAddPageArgsReferringURLMainFrameSameOriginReferrerDifferentPath) {
NiceMock<content::MockNavigationHandle> navigation_handle(web_contents());
navigation_handle.set_redirect_chain({GURL("https://someurl.com")});
navigation_handle.set_previous_main_frame_url(
GURL("http://previousurl.com/def"));
auto referrer = blink::mojom::Referrer::New();
referrer->url = GURL("http://previousurl.com/abc");
referrer->policy = network::mojom::ReferrerPolicy::kDefault;
navigation_handle.SetReferrer(std::move(referrer));
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
EXPECT_EQ(args.referrer, GURL("http://previousurl.com/abc"));
}
TEST_F(HistoryTabHelperTest,
CreateAddPageArgsReferringURLMainFrameCrossOriginReferrer) {
NiceMock<content::MockNavigationHandle> navigation_handle(web_contents());
auto referrer = blink::mojom::Referrer::New();
referrer->url = GURL("http://crossorigin.com");
referrer->policy = network::mojom::ReferrerPolicy::kDefault;
navigation_handle.SetReferrer(std::move(referrer));
navigation_handle.set_redirect_chain({GURL("https://someurl.com")});
navigation_handle.set_previous_main_frame_url(GURL("http://previousurl.com"));
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
EXPECT_EQ(args.referrer, GURL("http://crossorigin.com"));
}
TEST_F(HistoryTabHelperTest, CreateAddPageArgsReferringURLNotMainFrame) {
content::RenderFrameHostTester* main_rfh_tester =
content::RenderFrameHostTester::For(main_rfh());
main_rfh_tester->InitializeRenderFrameIfNeeded();
content::RenderFrameHost* subframe = main_rfh_tester->AppendChild("subframe");
NiceMock<content::MockNavigationHandle> navigation_handle(
GURL("http://someurl.com"), subframe);
navigation_handle.set_redirect_chain({GURL("https://someurl.com")});
navigation_handle.set_previous_main_frame_url(GURL("http://previousurl.com"));
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
// Should default to referrer if not in main frame and the referrer should not
// be sent to the arbitrary previous URL that is set.
EXPECT_NE(args.referrer, GURL("http://previousurl.com"));
}
TEST_F(HistoryTabHelperTest, CreateAddPageArgsHasOpenerWebContentsFirstPage) {
std::unique_ptr<content::WebContents> opener_web_contents =
CreateTestWebContents();
content::WebContentsTester* opener_web_contents_tester =
content::WebContentsTester::For(opener_web_contents.get());
opener_web_contents_tester->NavigateAndCommit(
GURL("https://opensnewtab.com/"));
HistoryTabHelper::CreateForWebContents(opener_web_contents.get());
HistoryTabHelper::FromWebContents(opener_web_contents.get())
->DidOpenRequestedURL(web_contents(), nullptr,
GURL("http://someurl.com/"), content::Referrer(),
WindowOpenDisposition::NEW_WINDOW,
ui::PAGE_TRANSITION_LINK, false, true);
content::RenderFrameHostTester* main_rfh_tester =
content::RenderFrameHostTester::For(main_rfh());
main_rfh_tester->InitializeRenderFrameIfNeeded();
content::RenderFrameHost* subframe = main_rfh_tester->AppendChild("subframe");
NiceMock<content::MockNavigationHandle> navigation_handle(
GURL("http://someurl.com"), subframe);
navigation_handle.set_redirect_chain({GURL("http://someurl.com")});
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
ASSERT_TRUE(args.opener.has_value());
EXPECT_EQ(args.opener->url, GURL("https://opensnewtab.com/"));
}
TEST_F(HistoryTabHelperTest, CreateAddPageArgsSameDocNavigationUsesOpener) {
content::RenderFrameHostTester* main_rfh_tester =
content::RenderFrameHostTester::For(main_rfh());
main_rfh_tester->InitializeRenderFrameIfNeeded();
content::RenderFrameHost* subframe = main_rfh_tester->AppendChild("subframe");
NiceMock<content::MockNavigationHandle> navigation_handle(
GURL("http://someurl.com"), subframe);
navigation_handle.set_redirect_chain({GURL("https://someurl.com")});
navigation_handle.set_previous_main_frame_url(GURL("http://previousurl.com"));
navigation_handle.set_is_same_document(true);
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
ASSERT_TRUE(args.opener.has_value());
EXPECT_EQ(args.opener->url, GURL("http://previousurl.com/"));
}
TEST_F(HistoryTabHelperTest,
CreateAddPageArgsHasOpenerWebContentseNotFirstPage) {
std::unique_ptr<content::WebContents> opener_web_contents =
CreateTestWebContents();
content::WebContentsTester* opener_web_contents_tester =
content::WebContentsTester::For(opener_web_contents.get());
opener_web_contents_tester->NavigateAndCommit(
GURL("https://opensnewtab.com/"));
HistoryTabHelper::CreateForWebContents(opener_web_contents.get());
HistoryTabHelper::FromWebContents(opener_web_contents.get())
->DidOpenRequestedURL(web_contents(), nullptr,
GURL("http://someurl.com/"), content::Referrer(),
WindowOpenDisposition::NEW_WINDOW,
ui::PAGE_TRANSITION_LINK, false, true);
content::RenderFrameHostTester* main_rfh_tester =
content::RenderFrameHostTester::For(main_rfh());
main_rfh_tester->InitializeRenderFrameIfNeeded();
content::RenderFrameHost* subframe = main_rfh_tester->AppendChild("subframe");
NiceMock<content::MockNavigationHandle> navigation_handle(
GURL("http://someurl.com/2"), subframe);
navigation_handle.set_redirect_chain({GURL("http://someurl.com/2")});
navigation_handle.set_previous_main_frame_url(GURL("http://someurl.com"));
history::HistoryAddPageArgs args =
history_tab_helper()->CreateHistoryAddPageArgs(
GURL("http://someurl.com"), base::Time(), 1, &navigation_handle);
EXPECT_FALSE(args.opener.has_value());
}
#if defined(OS_ANDROID)
TEST_F(HistoryTabHelperTest, NonFeedNavigationsDoContributeToMostVisited) {
GURL new_url("http://newurl.com");
EXPECT_CALL(test_feed_api_, WasUrlRecentlyNavigatedFromFeed(new_url))
.WillOnce(testing::Return(false));
web_contents_tester()->NavigateAndCommit(new_url,
ui::PAGE_TRANSITION_AUTO_BOOKMARK);
EXPECT_THAT(GetMostVisitedURLSet(), testing::Contains(new_url));
}
TEST_F(HistoryTabHelperTest, FeedNavigationsDoNotContributeToMostVisited) {
GURL new_url("http://newurl.com");
EXPECT_CALL(test_feed_api_, WasUrlRecentlyNavigatedFromFeed(new_url))
.WillOnce(testing::Return(true));
web_contents_tester()->NavigateAndCommit(new_url,
ui::PAGE_TRANSITION_AUTO_BOOKMARK);
EXPECT_THAT(GetMostVisitedURLSet(), testing::Not(testing::Contains(new_url)));
}
#endif