Skip to content

Commit

Permalink
[bookmarks] Add BookmarkStatsTabHelper for bookmark metrics - Part 1
Browse files Browse the repository at this point in the history
This adds a new struct BookmarkLaunchAction that captures the
location and time that a user opens their bookmarks.

Currently this struct is created only for bookmark launches
originating from the toolbar, follow up CLs will add this to other
launch locations.

The BookmarkStatsTabHelper has been introduced to help report CUJ
metrics following an initial launch action by the user. The launch
data is cleared after each navigation to ensure only the initial
navigation is considered when reporting metrics.

The first few UMA histograms for the helper will be added in
Part 2.

Bug: 1449016
Change-Id: I55f3c06f13bda9b232447a5c02c311b066b8dac0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4566539
Reviewed-by: Dana Fried <dfried@chromium.org>
Commit-Queue: Thomas Lukaszewicz <tluk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1152094}
  • Loading branch information
Thomas Lukaszewicz authored and Chromium LUCI CQ committed Jun 1, 2023
1 parent 83ce983 commit 84bb564
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 22 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,8 @@ static_library("ui") {
"bookmarks/bookmark_editor.h",
"bookmarks/bookmark_stats.cc",
"bookmarks/bookmark_stats.h",
"bookmarks/bookmark_stats_tab_helper.cc",
"bookmarks/bookmark_stats_tab_helper.h",
"bookmarks/bookmark_tab_helper.cc",
"bookmarks/bookmark_tab_helper.h",
"bookmarks/bookmark_tab_helper_observer.h",
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/ui/bookmarks/bookmark_stats.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ auto GetMetricProfile(const Profile* profile) {

} // namespace

std::ostream& operator<<(std::ostream& out,
const BookmarkLaunchAction& launch_action) {
return out << "BookmarkLaunchAction(location = "
<< static_cast<int>(launch_action.location)
<< ", action_time = " << launch_action.action_time << ")";
}

void RecordBookmarkLaunch(BookmarkLaunchLocation location,
profile_metrics::BrowserProfileType profile_type) {
if (IsBookmarkBarLocation(location)) {
Expand Down
12 changes: 12 additions & 0 deletions chrome/browser/ui/bookmarks/bookmark_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_STATS_H_
#define CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_STATS_H_

#include "base/time/time.h"
#include "components/profile_metrics/browser_profile_type.h"

class Profile;
Expand Down Expand Up @@ -64,6 +65,17 @@ enum class BookmarkLaunchLocation {
kMaxValue = kSidePanelContextMenu
};

// Captures information related to a bookmark's launch event. Used for metrics
// collection.
struct BookmarkLaunchAction {
// The location of the open action.
BookmarkLaunchLocation location = BookmarkLaunchLocation::kNone;

// The time at which the launch action was initiated.
base::TimeTicks action_time = base::TimeTicks::Now();
};
std::ostream& operator<<(std::ostream& out, const BookmarkLaunchAction& action);

// Records the launch of a bookmark for UMA purposes.
void RecordBookmarkLaunch(BookmarkLaunchLocation location,
profile_metrics::BrowserProfileType profile_type);
Expand Down
47 changes: 47 additions & 0 deletions chrome/browser/ui/bookmarks/bookmark_stats_tab_helper.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/bookmarks/bookmark_stats_tab_helper.h"

#include "content/public/browser/navigation_handle.h"

BookmarkStatsTabHelper::~BookmarkStatsTabHelper() = default;

bool BookmarkStatsTabHelper::SetLaunchAction(
const BookmarkLaunchAction& launch_action,
WindowOpenDisposition tab_disposition) {
if (web_contents()->GetController().GetPendingEntry()) {
launch_action_ = launch_action;
tab_disposition_ = tab_disposition;
// The launch data should remain valid through to the end of the current
// navigation and the begging of the next navigation.
should_reset_launch_data_ = false;
return true;
}
return false;
}

void BookmarkStatsTabHelper::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
// Reset the launch action data at the beginning of the next navigation.
if (navigation_handle->IsInPrimaryMainFrame() && should_reset_launch_data_) {
tab_disposition_.reset();
launch_action_.reset();
should_reset_launch_data_ = false;
}
}

void BookmarkStatsTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsInPrimaryMainFrame()) {
should_reset_launch_data_ = true;
}
}

BookmarkStatsTabHelper::BookmarkStatsTabHelper(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<BookmarkStatsTabHelper>(*web_contents) {}

WEB_CONTENTS_USER_DATA_KEY_IMPL(BookmarkStatsTabHelper);
64 changes: 64 additions & 0 deletions chrome/browser/ui/bookmarks/bookmark_stats_tab_helper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_STATS_TAB_HELPER_H_
#define CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_STATS_TAB_HELPER_H_

#include "chrome/browser/ui/bookmarks/bookmark_stats.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/base/window_open_disposition.h"

// A tab helper responsible for reporting metrics on various bookmarks user
// journeys.
class BookmarkStatsTabHelper
: public content::WebContentsObserver,
public content::WebContentsUserData<BookmarkStatsTabHelper> {
public:
BookmarkStatsTabHelper();
BookmarkStatsTabHelper(const BookmarkStatsTabHelper&) = delete;
BookmarkStatsTabHelper& operator=(const BookmarkStatsTabHelper&) = delete;
~BookmarkStatsTabHelper() override;

// The launch action should be set immediately after a given tab has been
// navigated following a bookmark launch action. The launch action will only
// be set if there is a pending navigation for the current tab.
// `tab_disposition` is the disposition requested for by this tab for the
// navigation.
bool SetLaunchAction(const BookmarkLaunchAction& launch_action,
WindowOpenDisposition tab_disposition);

// content::WebContentsObserver:
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;

const absl::optional<BookmarkLaunchAction>& launch_action_for_testing()
const {
return launch_action_;
}
const absl::optional<WindowOpenDisposition>& tab_disposition_for_testing()
const {
return tab_disposition_;
}

private:
friend class content::WebContentsUserData<BookmarkStatsTabHelper>;

explicit BookmarkStatsTabHelper(content::WebContents* web_contents);

// The launch action and requested tab disposition for the current navigation.
// These will be reset after the following navigation begins.
absl::optional<BookmarkLaunchAction> launch_action_;
absl::optional<WindowOpenDisposition> tab_disposition_;

// Tracks whether launch data should be reset as a new primary frame
// navigation begins.
bool should_reset_launch_data_ = false;

WEB_CONTENTS_USER_DATA_KEY_DECL();
};

#endif // CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_STATS_TAB_HELPER_H_
96 changes: 96 additions & 0 deletions chrome/browser/ui/bookmarks/bookmark_stats_tab_helper_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/bookmarks/bookmark_stats_tab_helper.h"

#include <memory>

#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace {

constexpr char kTestUrl1[] = "https://www.test-url-1.com/";
constexpr char kTestUrl2[] = "https://www.test-url-2.com/";

} // namespace

namespace test {

class BookmarkStatsTabHelperTest : public ::testing::Test {
public:
// ::testing::Test:
void SetUp() override {
web_contents_ =
content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
Test::SetUp();
}

protected:
void LoadUrl(const char* url) {
web_contents()->GetController().LoadURL(GURL(url), content::Referrer(),
ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
std::string());
}

void CommitPendingNavigation() {
content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
}

BookmarkStatsTabHelper* GetHelper() {
BookmarkStatsTabHelper::CreateForWebContents(web_contents_.get());
return BookmarkStatsTabHelper::FromWebContents(web_contents_.get());
}

content::WebContents* web_contents() { return web_contents_.get(); }

private:
content::BrowserTaskEnvironment task_environment_;
content::RenderViewHostTestEnabler rvh_enabler_;
TestingProfile profile_;
std::unique_ptr<content::WebContents> web_contents_;
};

TEST_F(BookmarkStatsTabHelperTest, LaunchActionAddedWithPendingEntry) {
// The launch action should start unset.
BookmarkStatsTabHelper* helper = GetHelper();
EXPECT_FALSE(helper->launch_action_for_testing().has_value());
EXPECT_FALSE(helper->tab_disposition_for_testing().has_value());

// Attempting to set the launch action without a pending navigation having
// been set should result in a no-op.
helper->SetLaunchAction(
{BookmarkLaunchLocation::kAttachedBar, base::TimeTicks::Now()},
WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(helper->launch_action_for_testing().has_value());
EXPECT_FALSE(helper->tab_disposition_for_testing().has_value());

// Begin a navigation and attempt to set the launch action. This should now
// be reflected in the helper.
LoadUrl(kTestUrl1);
helper->SetLaunchAction(
{BookmarkLaunchLocation::kAttachedBar, base::TimeTicks::Now()},
WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(helper->launch_action_for_testing().has_value());
EXPECT_TRUE(helper->tab_disposition_for_testing().has_value());

// After the navigation completes the launch action data should remain.
CommitPendingNavigation();
EXPECT_TRUE(helper->launch_action_for_testing().has_value());
EXPECT_TRUE(helper->tab_disposition_for_testing().has_value());

// A following navigation should clear the launch action data.
LoadUrl(kTestUrl2);
CommitPendingNavigation();
EXPECT_FALSE(helper->launch_action_for_testing().has_value());
EXPECT_FALSE(helper->tab_disposition_for_testing().has_value());
}

} // namespace test
29 changes: 21 additions & 8 deletions chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
#include "chrome/browser/ui/bookmarks/bookmark_editor.h"
#include "chrome/browser/ui/bookmarks/bookmark_stats.h"
#include "chrome/browser/ui/bookmarks/bookmark_stats_tab_helper.h"
#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
Expand Down Expand Up @@ -141,9 +142,11 @@ using OpenedWebContentsSet = base::flat_set<const content::WebContents*>;
// Opens all of the URLs in `bookmark_urls` using `navigator` and
// `initial_disposition` as a starting point. Returns a reference set of the
// WebContents created; see OpenedWebContentsSet.
OpenedWebContentsSet OpenAllHelper(Browser* browser,
std::vector<UrlAndId> bookmark_urls,
WindowOpenDisposition initial_disposition) {
OpenedWebContentsSet OpenAllHelper(
Browser* browser,
std::vector<UrlAndId> bookmark_urls,
WindowOpenDisposition initial_disposition,
absl::optional<BookmarkLaunchAction> launch_action) {
OpenedWebContentsSet::container_type opened_tabs;
WindowOpenDisposition disposition = initial_disposition;
// We keep track of (potentially) two browsers in addition to the original
Expand Down Expand Up @@ -196,6 +199,12 @@ OpenedWebContentsSet OpenAllHelper(Browser* browser,
if (!opened_tab)
continue;

if (launch_action.has_value()) {
BookmarkStatsTabHelper::CreateForWebContents(opened_tab);
BookmarkStatsTabHelper::FromWebContents(opened_tab)
->SetLaunchAction(launch_action.value(), disposition);
}

if (url_and_id_it->id.is_valid()) {
ChromeNavigationUIData* ui_data =
static_cast<ChromeNavigationUIData*>(handle->GetNavigationUIData());
Expand Down Expand Up @@ -246,18 +255,21 @@ OpenedWebContentsSet OpenAllHelper(Browser* browser,
void OpenAllIfAllowed(Browser* browser,
const std::vector<const bookmarks::BookmarkNode*>& nodes,
WindowOpenDisposition initial_disposition,
bool add_to_group) {
bool add_to_group,
absl::optional<BookmarkLaunchAction> launch_action) {
std::vector<UrlAndId> url_and_ids = GetURLsToOpen(
nodes, browser->profile(),
initial_disposition == WindowOpenDisposition::OFF_THE_RECORD);
auto do_open = [](Browser* browser, std::vector<UrlAndId> url_and_ids_to_open,
WindowOpenDisposition initial_disposition,
absl::optional<std::u16string> folder_title,
absl::optional<BookmarkLaunchAction> launch_action,
chrome::MessageBoxResult result) {
if (result != chrome::MESSAGE_BOX_RESULT_YES)
return;
const auto opened_web_contents = OpenAllHelper(
browser, std::move(url_and_ids_to_open), initial_disposition);
const auto opened_web_contents =
OpenAllHelper(browser, std::move(url_and_ids_to_open),
initial_disposition, std::move(launch_action));
if (folder_title.has_value()) {
TabStripModel* model = browser->tab_strip_model();

Expand Down Expand Up @@ -299,7 +311,7 @@ void OpenAllIfAllowed(Browser* browser,
add_to_group ? absl::optional<std::u16string>(
nodes[0]->GetTitledUrlNodeTitle())
: absl::nullopt,
chrome::MESSAGE_BOX_RESULT_YES);
std::move(launch_action), chrome::MESSAGE_BOX_RESULT_YES);
return;
}

Expand All @@ -316,7 +328,8 @@ void OpenAllIfAllowed(Browser* browser,
initial_disposition,
add_to_group ? absl::optional<std::u16string>(
nodes[0]->GetTitledUrlNodeTitle())
: absl::nullopt));
: absl::nullopt,
absl::nullopt));
}

int OpenCount(gfx::NativeWindow parent,
Expand Down
25 changes: 14 additions & 11 deletions chrome/browser/ui/bookmarks/bookmark_utils_desktop.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "base/functional/callback_forward.h"
#include "chrome/browser/ui/bookmarks/bookmark_editor.h"
#include "chrome/browser/ui/bookmarks/bookmark_stats.h"
#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/browser/ui/tabs/tab_group.h"
#include "ui/base/window_open_disposition.h"
Expand All @@ -23,7 +24,6 @@ class BookmarkNode;

namespace content {
class BrowserContext;
class PageNavigator;
class NavigationHandle;
}

Expand Down Expand Up @@ -52,18 +52,21 @@ using TabGroupData =
// value.
extern size_t kNumBookmarkUrlsBeforePrompting;

// Tries to open all bookmarks in |nodes|. If there are many, prompts
// Tries to open all bookmarks in `nodes`. If there are many, prompts
// the user first. Returns immediately, opening the bookmarks
// asynchronously if prompting the user. |browser| is the browser from
// asynchronously if prompting the user. `browser` is the browser from
// which the bookmarks were opened. Its window is used as the anchor for
// the dialog (if shown). |get_navigator| is used to fetch the
// PageNavigator used for opening the bookmarks. It may be called
// arbitrarily later as long as |browser| is alive. If it is not
// callable or returns null, this will fail gracefully.
void OpenAllIfAllowed(Browser* browser,
const std::vector<const bookmarks::BookmarkNode*>& nodes,
WindowOpenDisposition initial_disposition,
bool add_to_group);
// the dialog (if shown).
// `launch_action` represents the location and time of the bookmark launch
// action for callsites that support it.
// TODO(crbug.com/1449016): This should be made non-optional once all callsites
// have all the information needed to correctly construct the `launch_action`.
void OpenAllIfAllowed(
Browser* browser,
const std::vector<const bookmarks::BookmarkNode*>& nodes,
WindowOpenDisposition initial_disposition,
bool add_to_group,
absl::optional<BookmarkLaunchAction> launch_action = absl::nullopt);

// Returns the count of bookmarks that would be opened by OpenAll. If
// |incognito_context| is set, the function will use it to check if the URLs
Expand Down

0 comments on commit 84bb564

Please sign in to comment.