summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/chrome_bubble_manager.cc
blob: 56b686e10ca1619044bdd2c77c79b9b5bf4a104c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2015 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/ui/chrome_bubble_manager.h"

#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/bubble/bubble_controller.h"
#include "components/bubble/bubble_delegate.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"

namespace {

// Add any new enum before BUBBLE_TYPE_MAX and update BubbleType in
// tools/metrics/histograms/histograms.xml.
// Do not re-number these because they are used for collecting metrics.
// Related bubbles can be grouped together every 10 values.
enum BubbleType {
  // Special bubble values:
  BUBBLE_TYPE_UNKNOWN = 0,  // Used for unrecognized bubble names.
  BUBBLE_TYPE_MOCK = 1,     // Used for testing.

  // Extension-related bubbles:
  BUBBLE_TYPE_EXTENSION_INSTALLED = 10,  // Displayed after installing.

  // Translation-related bubbles:
  BUBBLE_TYPE_TRANSLATE = 20,  // Displays a request to translate a page.

  // Permissions-related bubbles:
  BUBBLE_TYPE_PERMISSION = 30,  // Displays a permission request to the user.
  BUBBLE_TYPE_CHOOSER_PERMISSION = 31,  // For chooser permissions.

  // Upper boundary for metrics.
  BUBBLE_TYPE_MAX,
};

// TODO(juncai): Since LogBubbleCloseReason function adds metrics for each
// close type, we can use only enum, and it may not be necessary to keep the
// bubble name.
// Convert from bubble name to ID. The bubble ID will allow collecting the
// close reason for each bubble type.
static int GetBubbleId(BubbleReference bubble) {
  BubbleType bubble_type = BUBBLE_TYPE_UNKNOWN;

  // Translate from bubble name to enum.
  if (bubble->GetName().compare("MockBubble") == 0)
    bubble_type = BUBBLE_TYPE_MOCK;
  else if (bubble->GetName().compare("ExtensionInstalled") == 0)
    bubble_type = BUBBLE_TYPE_EXTENSION_INSTALLED;
  else if (bubble->GetName().compare("TranslateBubble") == 0)
    bubble_type = BUBBLE_TYPE_TRANSLATE;
  else if (bubble->GetName().compare("PermissionBubble") == 0)
    bubble_type = BUBBLE_TYPE_PERMISSION;
  else if (bubble->GetName().compare("ChooserBubble") == 0)
    bubble_type = BUBBLE_TYPE_CHOOSER_PERMISSION;

  DCHECK_NE(bubble_type, BUBBLE_TYPE_UNKNOWN);
  DCHECK_NE(bubble_type, BUBBLE_TYPE_MAX);

  return bubble_type;
}

// Log the reason for closing this bubble.
// Each reason is its own metric. Each histogram call MUST have a runtime
// constant value passed in for the title.
static void LogBubbleCloseReason(BubbleReference bubble,
                                 BubbleCloseReason reason) {
  int bubble_id = GetBubbleId(bubble);
  switch (reason) {
    case BUBBLE_CLOSE_FORCED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.Forced", bubble_id);
      return;
    case BUBBLE_CLOSE_FOCUS_LOST:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.FocusLost", bubble_id);
      return;
    case BUBBLE_CLOSE_TABSWITCHED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.TabSwitched", bubble_id);
      return;
    case BUBBLE_CLOSE_TABDETACHED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.TabDetached", bubble_id);
      return;
    case BUBBLE_CLOSE_USER_DISMISSED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.UserDismissed", bubble_id);
      return;
    case BUBBLE_CLOSE_NAVIGATED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.Navigated", bubble_id);
      return;
    case BUBBLE_CLOSE_FULLSCREEN_TOGGLED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.FullscreenToggled", bubble_id);
      return;
    case BUBBLE_CLOSE_ACCEPTED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.Accepted", bubble_id);
      return;
    case BUBBLE_CLOSE_CANCELED:
      UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.Close.Canceled", bubble_id);
      return;
  }

  NOTREACHED();
}

}  // namespace

ChromeBubbleManager::ChromeBubbleManager(TabStripModel* tab_strip_model)
    : tab_strip_model_(tab_strip_model) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(tab_strip_model_);
  tab_strip_model_->AddObserver(this);
  AddBubbleManagerObserver(&chrome_bubble_metrics_);
}

ChromeBubbleManager::~ChromeBubbleManager() {
  tab_strip_model_->RemoveObserver(this);

  // Finalize requests before removing the BubbleManagerObserver so it can
  // collect metrics when closing any open bubbles.
  FinalizePendingRequests();
  RemoveBubbleManagerObserver(&chrome_bubble_metrics_);
}

void ChromeBubbleManager::TabDetachedAt(content::WebContents* contents,
                                        int index) {
  CloseAllBubbles(BUBBLE_CLOSE_TABDETACHED);
  // Any bubble that didn't close should update its anchor position.
  UpdateAllBubbleAnchors();
}

void ChromeBubbleManager::TabDeactivated(content::WebContents* contents) {
  CloseAllBubbles(BUBBLE_CLOSE_TABSWITCHED);
}

void ChromeBubbleManager::ActiveTabChanged(content::WebContents* old_contents,
                                           content::WebContents* new_contents,
                                           int index,
                                           int reason) {
  Observe(new_contents);
}

void ChromeBubbleManager::DidToggleFullscreenModeForTab(
    bool entered_fullscreen) {
  CloseAllBubbles(BUBBLE_CLOSE_FULLSCREEN_TOGGLED);
  // Any bubble that didn't close should update its anchor position.
  UpdateAllBubbleAnchors();
}

void ChromeBubbleManager::NavigationEntryCommitted(
    const content::LoadCommittedDetails& load_details) {
  CloseAllBubbles(BUBBLE_CLOSE_NAVIGATED);
}

void ChromeBubbleManager::ChromeBubbleMetrics::OnBubbleNeverShown(
    BubbleReference bubble) {
  UMA_HISTOGRAM_SPARSE_SLOWLY("Bubbles.NeverShown", GetBubbleId(bubble));
}

void ChromeBubbleManager::ChromeBubbleMetrics::OnBubbleClosed(
    BubbleReference bubble, BubbleCloseReason reason) {
  // Log the amount of time the bubble was visible.
  base::TimeDelta visible_time = bubble->GetVisibleTime();
  UMA_HISTOGRAM_LONG_TIMES("Bubbles.DisplayTime.All", visible_time);

  LogBubbleCloseReason(bubble, reason);
}