File: tab_matcher_android.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 (219 lines) | stat: -rw-r--r-- 8,636 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
// Copyright 2021 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/android/autocomplete/tab_matcher_android.h"

#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/android/tab_android_user_data.h"
#include "chrome/browser/flags/android/chrome_session_state.h"
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/tab_matcher.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/browser/ui/android/omnibox/jni_headers/ChromeAutocompleteProviderClient_jni.h"

namespace {
class AutocompleteClientTabAndroidUserData
    : public TabAndroidUserData<AutocompleteClientTabAndroidUserData>,
      public TabAndroid::Observer {
 public:
  ~AutocompleteClientTabAndroidUserData() override {
    tab_->RemoveObserver(this);
  }

  const GURL& GetStrippedURL() const { return stripped_url_; }

  bool IsInitialized() const { return initialized_; }

  void UpdateStrippedURL(const GURL& url,
                         const TemplateURLService* template_url_service,
                         const bool keep_search_intent_params) {
    initialized_ = true;
    if (url.is_valid()) {
      // Use a blank input as the stripped URL will be reused with other inputs.
      // Also keep the search intent params. Otherwise, this can result in over
      // triggering of the Switch to Tab action on plain-text suggestions for
      // open entity SRPs, or vice versa, on entity suggestions for open
      // plain-text SRPs.
      stripped_url_ = AutocompleteMatch::GURLToStrippedGURL(
          url, AutocompleteInput(), template_url_service, std::u16string(),
          keep_search_intent_params);
    }
  }

  // TabAndroid::Observer implementation
  void OnInitWebContents(TabAndroid* tab) override {
    tab->RemoveUserData(UserDataKey());
  }

 private:
  explicit AutocompleteClientTabAndroidUserData(TabAndroid* tab) : tab_(tab) {
    DCHECK(tab);
    tab->AddObserver(this);
  }
  friend class TabAndroidUserData<AutocompleteClientTabAndroidUserData>;

  raw_ptr<TabAndroid> tab_;
  bool initialized_ = false;
  GURL stripped_url_;

  TAB_ANDROID_USER_DATA_KEY_DECL();
};
TAB_ANDROID_USER_DATA_KEY_IMPL(AutocompleteClientTabAndroidUserData)
}  // namespace

bool TabMatcherAndroid::IsTabOpenWithURL(const GURL& url,
                                         const AutocompleteInput* input) const {
  DCHECK(input);
  const AutocompleteInput empty_input;
  if (!input)
    input = &empty_input;

  // Use a blank input as the stripped URL will be reused with other inputs.
  // Also keep the search intent params. Otherwise, this can result in over
  // triggering of the Switch to Tab action on plain-text suggestions for
  // open entity SRPs, or vice versa, on entity suggestions for open plain-text
  // SRPs.
  const bool keep_search_intent_params = base::FeatureList::IsEnabled(
      omnibox::kDisambiguateTabMatchingForEntitySuggestions);
  const GURL stripped_url = AutocompleteMatch::GURLToStrippedGURL(
      url, *input, template_url_service_, std::u16string(),
      keep_search_intent_params);
  const auto all_tabs =
      GetAllHiddenAndNonCCTTabInfos(input, keep_search_intent_params);
  return all_tabs.find(stripped_url) != all_tabs.end();
}

void TabMatcherAndroid::FindMatchingTabs(GURLToTabInfoMap* map,
                                         const AutocompleteInput* input) const {
  DCHECK(map);
  DCHECK(input);
  const AutocompleteInput empty_input;
  if (!input)
    input = &empty_input;

  const bool keep_search_intent_params = base::FeatureList::IsEnabled(
      omnibox::kDisambiguateTabMatchingForEntitySuggestions);
  auto all_tabs =
      GetAllHiddenAndNonCCTTabInfos(input, keep_search_intent_params);

  for (auto& gurl_to_tab_info : *map) {
    const GURL stripped_url = AutocompleteMatch::GURLToStrippedGURL(
        gurl_to_tab_info.first, *input, template_url_service_, std::u16string(),
        keep_search_intent_params);
    auto found_tab = all_tabs.find(stripped_url);
    if (found_tab != all_tabs.end()) {
      gurl_to_tab_info.second = found_tab->second;
    }
  }
}

std::vector<TabMatcher::TabWrapper> TabMatcherAndroid::GetOpenTabs(
    const AutocompleteInput* input,
    bool unused_exclude_active_tab) const {
  std::vector<TabMatcher::TabWrapper> open_tabs;
  for (auto& open_tab : GetOpenAndroidTabs(input)) {
    open_tabs.emplace_back(open_tab->GetTitle(), open_tab->GetURL(),
                           open_tab->GetLastShownTimestamp());
  }

  return open_tabs;
}

std::vector<raw_ptr<TabAndroid, VectorExperimental>>
TabMatcherAndroid::GetOpenAndroidTabs(const AutocompleteInput* input) const {
  using chrome::android::ActivityType;
  // Collect tab models that host tabs eligible for SwitchToTab.
  // Ignore:
  // - tab models for not matching profile (eg. incognito vs non-incognito)
  // - custom and trusted tabs.
  std::vector<TabModel*> tab_models;
  for (TabModel* model : TabModelList::models()) {
    if (profile_ != model->GetProfile())
      continue;

    auto type = model->activity_type();
    if (type == ActivityType::kCustomTab ||
        type == ActivityType::kTrustedWebActivity) {
      continue;
    }

    tab_models.push_back(model);
  }

  CHECK(input);
  if (input->current_page_classification() ==
          metrics::OmniboxEventProto_PageClassification_ANDROID_HUB &&
      profile_->IsRegularProfile()) {
    TabModel* archived_tab_model = TabModelList::GetArchivedTabModel();
    if (archived_tab_model) {
      tab_models.push_back(archived_tab_model);
    }
  }

  // Short circuit in the event we have no tab models hosting eligible tabs.
  if (tab_models.size() == 0)
    return std::vector<raw_ptr<TabAndroid, VectorExperimental>>();

  // Create and populate an array of Java TabModels.
  // The most expensive series of calls that reach to Java for every single tab
  // at least once start here and span until the end of this method.
  JNIEnv* env = base::android::AttachCurrentThread();
  jclass tab_model_clazz = TabModelJniBridge::GetClazz(env);
  base::android::ScopedJavaLocalRef<jobjectArray> j_tab_model_array(
      env, env->NewObjectArray(tab_models.size(), tab_model_clazz, nullptr));
  // Get all the hidden and non CCT tabs. Filter the tabs in CCT tabmodel first.
  for (size_t i = 0; i < tab_models.size(); ++i) {
    env->SetObjectArrayElement(j_tab_model_array.obj(), i,
                               tab_models[i]->GetJavaObject().obj());
  }

  // Retrieve all Tabs associated with previously built TabModels array.
  base::android::ScopedJavaLocalRef<jobjectArray> j_tabs =
      Java_ChromeAutocompleteProviderClient_getAllEligibleTabs(
          env, j_tab_model_array, input->current_page_classification());
  if (j_tabs.is_null())
    return std::vector<raw_ptr<TabAndroid, VectorExperimental>>();

  return TabAndroid::GetAllNativeTabs(env, j_tabs);
}

TabMatcher::GURLToTabInfoMap TabMatcherAndroid::GetAllHiddenAndNonCCTTabInfos(
    const AutocompleteInput* input,
    const bool keep_search_intent_params) const {
  using chrome::android::ActivityType;
  GURLToTabInfoMap tab_infos;
  JNIEnv* env = base::android::AttachCurrentThread();

  for (TabAndroid* tab : GetOpenAndroidTabs(input)) {
    // Browser did not load the tab yet after Chrome started. To avoid
    // reloading WebContents, we just compare URLs.
    AutocompleteClientTabAndroidUserData::CreateForTabAndroid(tab);
    AutocompleteClientTabAndroidUserData* user_data =
        AutocompleteClientTabAndroidUserData::FromTabAndroid(tab);
    DCHECK(user_data);
    if (!user_data->IsInitialized()) {
      user_data->UpdateStrippedURL(tab->GetURL(), template_url_service_,
                                   keep_search_intent_params);
    }

    const GURL& tab_stripped_url = user_data->GetStrippedURL();
    TabInfo info;
    info.has_matching_tab = true;
    info.android_tab = JavaObjectWeakGlobalRef(env, tab->GetJavaObject());
    tab_infos[tab_stripped_url] = info;
  }

  return tab_infos;
}