blob: 7abd0d102e3f5b32bb700091864acfc826f3d3f2 [file] [log] [blame]
// Copyright (c) 2012 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 "base/bind.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
#include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/browser/url_and_title.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/gfx/image/image_skia.h"
using bookmarks::BookmarkModel;
using bookmarks::BookmarkNode;
using bookmarks::UrlAndTitle;
namespace {
const char kPersistBookmarkURL[] = "http://www.cnn.com/";
const char kPersistBookmarkTitle[] = "CNN";
bool IsShowingInterstitial(content::WebContents* tab) {
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
tab);
return helper &&
helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting();
}
} // namespace
class TestBookmarkTabHelperObserver : public BookmarkTabHelperObserver {
public:
TestBookmarkTabHelperObserver() : starred_(false) {}
~TestBookmarkTabHelperObserver() override {}
void URLStarredChanged(content::WebContents*, bool starred) override {
starred_ = starred;
}
bool is_starred() const { return starred_; }
private:
bool starred_;
DISALLOW_COPY_AND_ASSIGN(TestBookmarkTabHelperObserver);
};
class BookmarkBrowsertest : public InProcessBrowserTest {
public:
BookmarkBrowsertest() {}
bool IsVisible() {
return browser()->bookmark_bar_state() == BookmarkBar::SHOW;
}
static void CheckAnimation(Browser* browser, base::RunLoop* loop) {
if (!browser->window()->IsBookmarkBarAnimating())
loop->Quit();
}
base::TimeDelta WaitForBookmarkBarAnimationToFinish() {
base::Time start(base::Time::Now());
{
base::RunLoop loop;
base::RepeatingTimer timer;
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(15),
base::BindRepeating(&CheckAnimation, browser(), &loop));
loop.Run();
}
return base::Time::Now() - start;
}
BookmarkModel* WaitForBookmarkModel(Profile* profile) {
BookmarkModel* bookmark_model =
BookmarkModelFactory::GetForBrowserContext(profile);
bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
return bookmark_model;
}
base::HistogramTester* histogram_tester() { return &histogram_tester_; }
private:
// We make the histogram tester a member field to make sure it starts
// recording as early as possible.
base::HistogramTester histogram_tester_;
DISALLOW_COPY_AND_ASSIGN(BookmarkBrowsertest);
};
// Test of bookmark bar toggling, visibility, and animation.
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, BookmarkBarVisibleWait) {
ASSERT_FALSE(IsVisible());
chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
base::TimeDelta delay = WaitForBookmarkBarAnimationToFinish();
LOG(INFO) << "Took " << delay.InMilliseconds() << " ms to show bookmark bar";
ASSERT_TRUE(IsVisible());
chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
delay = WaitForBookmarkBarAnimationToFinish();
LOG(INFO) << "Took " << delay.InMilliseconds() << " ms to hide bookmark bar";
ASSERT_FALSE(IsVisible());
}
// Verify that bookmarks persist browser restart.
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, PRE_Persist) {
BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
bookmarks::AddIfNotBookmarked(bookmark_model, GURL(kPersistBookmarkURL),
base::ASCIIToUTF16(kPersistBookmarkTitle));
}
#if defined(OS_WIN)
// TODO(crbug.com/935607): The test fails on Windows.
#define MAYBE_Persist DISABLED_Persist
#else
#define MAYBE_Persist Persist
#endif
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, MAYBE_Persist) {
BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
std::vector<UrlAndTitle> urls;
bookmark_model->GetBookmarks(&urls);
ASSERT_EQ(1u, urls.size());
ASSERT_EQ(GURL(kPersistBookmarkURL), urls[0].url);
ASSERT_EQ(base::ASCIIToUTF16(kPersistBookmarkTitle), urls[0].title);
}
#if !defined(OS_CHROMEOS) // No multi-profile on ChromeOS.
// Sanity check that bookmarks from different profiles are separate.
// DISABLED_ because it regularly times out: http://crbug.com/159002.
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, DISABLED_MultiProfile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
BookmarkModel* bookmark_model1 = WaitForBookmarkModel(browser()->profile());
g_browser_process->profile_manager()->CreateMultiProfileAsync(
base::string16(), std::string(), ProfileManager::CreateCallback());
Browser* browser2 = ui_test_utils::WaitForBrowserToOpen();
BookmarkModel* bookmark_model2 = WaitForBookmarkModel(browser2->profile());
bookmarks::AddIfNotBookmarked(bookmark_model1, GURL(kPersistBookmarkURL),
base::ASCIIToUTF16(kPersistBookmarkTitle));
std::vector<UrlAndTitle> urls1, urls2;
bookmark_model1->GetBookmarks(&urls1);
bookmark_model2->GetBookmarks(&urls2);
ASSERT_EQ(1u, urls1.size());
ASSERT_TRUE(urls2.empty());
}
#endif
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest,
HideStarOnNonbookmarkedInterstitial) {
// Start an HTTPS server with a certificate error.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
ASSERT_TRUE(embedded_test_server()->Start());
BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
GURL bookmark_url = embedded_test_server()->GetURL("example.test", "/");
bookmarks::AddIfNotBookmarked(bookmark_model, bookmark_url,
base::ASCIIToUTF16("Bookmark"));
TestBookmarkTabHelperObserver bookmark_observer;
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
BookmarkTabHelper* tab_helper =
BookmarkTabHelper::FromWebContents(web_contents);
tab_helper->AddObserver(&bookmark_observer);
// Go to a bookmarked url. Bookmark star should show.
ui_test_utils::NavigateToURL(browser(), bookmark_url);
EXPECT_FALSE(IsShowingInterstitial(web_contents));
EXPECT_TRUE(bookmark_observer.is_starred());
// Now go to a non-bookmarked url which triggers an SSL warning. Bookmark
// star should disappear.
GURL error_url = https_server.GetURL("/");
ui_test_utils::NavigateToURL(browser(), error_url);
web_contents = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(IsShowingInterstitial(web_contents));
EXPECT_FALSE(bookmark_observer.is_starred());
tab_helper->RemoveObserver(&bookmark_observer);
}
// Provides coverage for the Bookmark Manager bookmark drag and drag image
// generation for dragging a single bookmark.
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, DragSingleBookmark) {
BookmarkModel* model = WaitForBookmarkModel(browser()->profile());
const base::string16 page_title(base::ASCIIToUTF16("foo"));
const GURL page_url("http://www.google.com");
const BookmarkNode* root = model->bookmark_bar_node();
const BookmarkNode* node = model->AddURL(root, 0, page_title, page_url);
const gfx::Point expected_point(100, 100);
auto run_loop = std::make_unique<base::RunLoop>();
chrome::DoBookmarkDragCallback cb = base::BindLambdaForTesting(
[&run_loop, page_title, page_url, expected_point](
std::unique_ptr<ui::OSExchangeData> drag_data,
gfx::NativeView native_view, ui::mojom::DragEventSource source,
gfx::Point point, int operation) {
GURL url;
base::string16 title;
EXPECT_TRUE(drag_data->provider().GetURLAndTitle(
ui::FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES, &url, &title));
EXPECT_EQ(page_url, url);
EXPECT_EQ(page_title, title);
#if !defined(OS_WIN)
// On Windows, GetDragImage() is a NOTREACHED() as the Windows
// implementation of OSExchangeData just sets the drag image on the OS
// API.
// See https://crbug.com/893388.
EXPECT_FALSE(drag_data->provider().GetDragImage().isNull());
#endif
EXPECT_EQ(expected_point, point);
run_loop->Quit();
});
constexpr int kDragNodeIndex = 0;
chrome::DragBookmarksForTest(
browser()->profile(),
{{node},
kDragNodeIndex,
platform_util::GetViewForWindow(browser()->window()->GetNativeWindow()),
ui::mojom::DragEventSource::kMouse,
expected_point},
std::move(cb));
run_loop->Run();
}
// Provides coverage for the Bookmark Manager bookmark drag and drag image
// generation for dragging multiple bookmarks.
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, DragMultipleBookmarks) {
BookmarkModel* model = WaitForBookmarkModel(browser()->profile());
const base::string16 page_title(base::ASCIIToUTF16("foo"));
const GURL page_url("http://www.google.com");
const BookmarkNode* root = model->bookmark_bar_node();
const BookmarkNode* node1 = model->AddURL(root, 0, page_title, page_url);
const BookmarkNode* node2 = model->AddFolder(root, 0, page_title);
const gfx::Point expected_point(100, 100);
auto run_loop = std::make_unique<base::RunLoop>();
chrome::DoBookmarkDragCallback cb = base::BindLambdaForTesting(
[&run_loop, expected_point](std::unique_ptr<ui::OSExchangeData> drag_data,
gfx::NativeView native_view,
ui::mojom::DragEventSource source,
gfx::Point point, int operation) {
#if !defined(OS_MAC)
GURL url;
base::string16 title;
// On Mac 10.11 and 10.12, this returns true, even though we set no url.
// See https://crbug.com/893432.
EXPECT_FALSE(drag_data->provider().GetURLAndTitle(
ui::FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES, &url, &title));
#endif
#if !defined(OS_WIN)
// On Windows, GetDragImage() is a NOTREACHED() as the Windows
// implementation of OSExchangeData just sets the drag image on the OS
// API.
// See https://crbug.com/893388.
EXPECT_FALSE(drag_data->provider().GetDragImage().isNull());
#endif
EXPECT_EQ(expected_point, point);
run_loop->Quit();
});
constexpr int kDragNodeIndex = 1;
chrome::DragBookmarksForTest(browser()->profile(),
{
{node1, node2},
kDragNodeIndex,
platform_util::GetViewForWindow(
browser()->window()->GetNativeWindow()),
ui::mojom::DragEventSource::kMouse,
expected_point,
},
std::move(cb));
run_loop->Run();
}
// ChromeOS initializes two profiles (Default and test-user) and it's impossible
// to distinguish UMA samples separately.
#if !defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, PRE_EmitUmaForDuplicates) {
BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
const BookmarkNode* parent = bookmarks::GetParentForNewNodes(bookmark_model);
// Add one bookmark with a unique URL, two other bookmarks with a shared URL,
// and three more with another shared URL.
bookmark_model->AddURL(parent, parent->children().size(),
base::ASCIIToUTF16("title1"), GURL("http://a.com"));
bookmark_model->AddURL(parent, parent->children().size(),
base::ASCIIToUTF16("title2"), GURL("http://b.com"));
bookmark_model->AddURL(parent, parent->children().size(),
base::ASCIIToUTF16("title3"), GURL("http://b.com"));
bookmark_model->AddURL(parent, parent->children().size(),
base::ASCIIToUTF16("title4"), GURL("http://c.com"));
bookmark_model->AddURL(parent, parent->children().size(),
base::ASCIIToUTF16("title5"), GURL("http://c.com"));
bookmark_model->AddURL(parent, parent->children().size(),
base::ASCIIToUTF16("title6"), GURL("http://c.com"));
}
IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, EmitUmaForDuplicates) {
WaitForBookmarkModel(browser()->profile());
ASSERT_THAT(
histogram_tester()->GetAllSamples("Bookmarks.Count.OnProfileLoad"),
testing::ElementsAre(base::Bucket(/*min=*/6, /*count=*/1)));
EXPECT_THAT(histogram_tester()->GetAllSamples(
"Bookmarks.Count.OnProfileLoad.DuplicateUrl"),
testing::ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
}
#endif // !defined(OS_CHROMEOS)