File: extensions_toolbar_container_view_controller.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (287 lines) | stat: -rw-r--r-- 11,045 bytes parent folder | download | duplicates (3)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// Copyright 2024 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/views/extensions/extensions_toolbar_container_view_controller.h"

#include "base/time/time.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/extension_ui_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
#include "chrome/browser/ui/views/extensions/extensions_request_access_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
#include "chrome/common/pref_names.h"
#include "components/user_education/common/feature_promo/feature_promo_controller.h"
#include "extensions/browser/extension_util.h"
#include "extensions/common/extension_features.h"

namespace {
// Only attempt to launch the Extensions Zero State Promo IPH after this
// timestamp, in order to reduce the cadence with which the this IPH calls
// the User Educations system.
std::optional<base::TimeTicks> g_zero_state_promo_next_show_time_opt =
    std::nullopt;

// The interval of time to wait for between attempting to launch the Zero
// State Promo.
constexpr base::TimeDelta kZeroStatePromoIntervalBetweenLaunchAttempt =
    base::Minutes(2);
}  // namespace

// static
void ExtensionsToolbarContainerViewController::WakeZeroStatePromoForTesting() {
  g_zero_state_promo_next_show_time_opt = base::TimeTicks::Now();
}

ExtensionsToolbarContainerViewController::
    ExtensionsToolbarContainerViewController(
        Browser* browser,
        ExtensionsToolbarContainer* extensions_container)
    : browser_(browser), extensions_container_(extensions_container) {
  model_observation_.Observe(ToolbarActionsModel::Get(browser_->profile()));
  permissions_manager_observation_.Observe(
      extensions::PermissionsManager::Get(browser_->profile()));
  browser_->tab_strip_model()->AddObserver(this);
}

ExtensionsToolbarContainerViewController::
    ~ExtensionsToolbarContainerViewController() {
  extensions_container_ = nullptr;
  model_observation_.Reset();
  permissions_manager_observation_.Reset();
}

void ExtensionsToolbarContainerViewController::
    WindowControlsOverlayEnabledChanged(bool enabled) {
  if (!extensions_container_->main_item()) {
    return;
  }

  extensions_container_->UpdateContainerVisibility();
  extensions_container_->main_item()->ClearProperty(views::kFlexBehaviorKey);

  views::MinimumFlexSizeRule min_flex_rule =
      enabled ? views::MinimumFlexSizeRule::kPreferred
              : views::MinimumFlexSizeRule::kPreferredSnapToZero;
  extensions_container_->main_item()->SetProperty(
      views::kFlexBehaviorKey,
      views::FlexSpecification(min_flex_rule,
                               views::MaximumFlexSizeRule::kPreferred)
          .WithOrder(ExtensionsToolbarContainerViewController::
                         kFlexOrderExtensionsButton));
}

void ExtensionsToolbarContainerViewController::MaybeShowIPH() {
  CHECK(browser_->window());

  // Extensions menu IPH, with priority order. These depend on the new access
  // control feature.
  if (base::FeatureList::IsEnabled(
          extensions_features::kExtensionsMenuAccessControl)) {
    ExtensionsRequestAccessButton* request_access_button =
        extensions_container_->GetRequestAccessButton();
    if (request_access_button->GetVisible()) {
      const int extensions_size = request_access_button->GetExtensionsCount();
      user_education::FeaturePromoParams params(
          feature_engagement::kIPHExtensionsRequestAccessButtonFeature);
      params.body_params = extensions_size;
      params.title_params = extensions_size;
      browser_->window()->MaybeShowFeaturePromo(std::move(params));
    }

    if (extensions_container_->GetExtensionsButton()->state() ==
        ExtensionsToolbarButton::State::kAnyExtensionHasAccess) {
      browser_->window()->MaybeShowFeaturePromo(
          feature_engagement::kIPHExtensionsMenuFeature);
    }
  }

  // The Extensions Zero State promo prompts users without extensions to
  // explore the Chrome Web Store.
  if (browser_->type() == Browser::TYPE_NORMAL) {
    if (!g_zero_state_promo_next_show_time_opt.has_value()) {
      g_zero_state_promo_next_show_time_opt =
          base::TimeTicks::Now() + kZeroStatePromoIntervalBetweenLaunchAttempt;
    } else if (base::TimeTicks::Now() >=
                   g_zero_state_promo_next_show_time_opt.value() &&
               !extensions::util::AnyCurrentlyInstalledExtensionIsFromWebstore(
                   browser_->profile())) {
      g_zero_state_promo_next_show_time_opt =
          base::TimeTicks::Now() + kZeroStatePromoIntervalBetweenLaunchAttempt;
      browser_->window()->MaybeShowFeaturePromo(
          feature_engagement::kIPHExtensionsZeroStatePromoFeature);
    }
  }
}

void ExtensionsToolbarContainerViewController::UpdateRequestAccessButton() {
  CHECK(extensions_container_);

  if (!base::FeatureList::IsEnabled(
          extensions_features::kExtensionsMenuAccessControl)) {
    return;
  }

  auto* web_contents = extensions_container_->GetCurrentWebContents();
  extensions::PermissionsManager::UserSiteSetting site_setting =
      extensions::PermissionsManager::Get(browser_->profile())
          ->GetUserSiteSetting(
              web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin());
  extensions_container_->UpdateRequestAccessButton(site_setting, web_contents);
}

void ExtensionsToolbarContainerViewController::OnTabStripModelChanged(
    TabStripModel* tab_strip_model,
    const TabStripModelChange& change,
    const TabStripSelectionChange& selection) {
  if (tab_strip_model->empty() || !selection.active_tab_changed()) {
    return;
  }

  // Close Extensions menu IPH if it is open.
  browser_->window()->NotifyFeaturePromoFeatureUsed(
      feature_engagement::kIPHExtensionsMenuFeature,
      FeaturePromoFeatureUsedAction::kClosePromoIfPresent);

  extensions::MaybeShowExtensionControlledNewTabPage(browser_,
                                                     selection.new_contents);

  // Request access button confirmation is tab-specific. Therefore, we need to
  // reset if the active tab changes.
  if (extensions_container_->GetRequestAccessButton()) {
    extensions_container_->CollapseConfirmation();
  }

  MaybeShowIPH();
}

void ExtensionsToolbarContainerViewController::TabChangedAt(
    content::WebContents* contents,
    int index,
    TabChangeType change_type) {
  // Ignore changes that don't affect all the tab contents (e.g loading
  // changes).
  if (change_type != TabChangeType::kAll) {
    return;
  }

  // Close Extensions menu IPH if it is open.
  browser_->window()->AbortFeaturePromo(
      feature_engagement::kIPHExtensionsMenuFeature);

  // Request access button confirmation is tab-specific for a specific origin.
  // Therefore, we need to reset it if it's currently showing, we are on the
  // same tab and we have navigated to another origin.
  // Note: When we switch tabs, `OnTabStripModelChanged` is called before
  // `TabChangedAt` and takes care of resetting the confirmation if shown.
  ExtensionsRequestAccessButton* request_access_button =
      extensions_container_->GetRequestAccessButton();
  if (request_access_button && request_access_button->IsShowingConfirmation() &&
      !request_access_button->IsShowingConfirmationFor(
          contents->GetPrimaryMainFrame()->GetLastCommittedOrigin())) {
    extensions_container_->CollapseConfirmation();
  }

  MaybeShowIPH();
}

void ExtensionsToolbarContainerViewController::OnToolbarActionAdded(
    const ToolbarActionsModel::ActionId& action_id) {
  CHECK(extensions_container_);
  extensions_container_->AddAction(action_id);
}

void ExtensionsToolbarContainerViewController::OnToolbarActionRemoved(
    const ToolbarActionsModel::ActionId& action_id) {
  CHECK(extensions_container_);
  extensions_container_->RemoveAction(action_id);
}

void ExtensionsToolbarContainerViewController::OnToolbarActionUpdated(
    const ToolbarActionsModel::ActionId& action_id) {
  CHECK(extensions_container_);
  extensions_container_->UpdateAction(action_id);
}

void ExtensionsToolbarContainerViewController::OnToolbarModelInitialized() {
  CHECK(extensions_container_);
  extensions_container_->CreateActions();
}

void ExtensionsToolbarContainerViewController::OnToolbarPinnedActionsChanged() {
  CHECK(extensions_container_);
  extensions_container_->UpdatePinnedActions();
}

void ExtensionsToolbarContainerViewController::OnUserPermissionsSettingsChanged(
    const extensions::PermissionsManager::UserPermissionsSettings& settings) {
  CHECK(extensions_container_);
  extensions_container_->UpdateControlsVisibility();
  // TODO(crbug.com/40857356): Update request access button hover card. This
  // will be slightly different than 'OnToolbarActionUpdated' since site
  // settings update are not tied to a specific action.
}

void ExtensionsToolbarContainerViewController::
    OnShowAccessRequestsInToolbarChanged(
        const extensions::ExtensionId& extension_id,
        bool can_show_requests) {
  CHECK(extensions_container_);
  extensions_container_->UpdateControlsVisibility();
  // TODO(crbug.com/40857356): Update requests access button hover card. This is
  // tricky because it would need to change the items in the dialog. Another
  // option is to close the hover card if its shown whenever request access
  // button is updated.
}

void ExtensionsToolbarContainerViewController::
    OnHostAccessRequestDismissedByUser(
        const extensions::ExtensionId& extension_id,
        const url::Origin& origin) {
  UpdateRequestAccessButton();
}

void ExtensionsToolbarContainerViewController::OnHostAccessRequestAdded(
    const extensions::ExtensionId& extension_id,
    int tab_id) {
  int current_tab_id = extensions::ExtensionTabUtil::GetTabId(
      extensions_container_->GetCurrentWebContents());
  if (tab_id != current_tab_id) {
    return;
  }

  UpdateRequestAccessButton();
}

void ExtensionsToolbarContainerViewController::OnHostAccessRequestUpdated(
    const extensions::ExtensionId& extension_id,
    int tab_id) {
  UpdateRequestAccessButton();
}

void ExtensionsToolbarContainerViewController::OnHostAccessRequestRemoved(
    const extensions::ExtensionId& extension_id,
    int tab_id) {
  int current_tab_id = extensions::ExtensionTabUtil::GetTabId(
      extensions_container_->GetCurrentWebContents());
  if (tab_id != current_tab_id) {
    return;
  }

  UpdateRequestAccessButton();
}

void ExtensionsToolbarContainerViewController::OnHostAccessRequestsCleared(
    int tab_id) {
  int current_tab_id = extensions::ExtensionTabUtil::GetTabId(
      extensions_container_->GetCurrentWebContents());
  if (tab_id != current_tab_id) {
    return;
  }

  UpdateRequestAccessButton();
}