File: pref_change_recorder.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 (186 lines) | stat: -rw-r--r-- 6,952 bytes parent folder | download | duplicates (6)
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
// Copyright 2022 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/ash/input_method/pref_change_recorder.h"

#include <string>
#include <utility>

#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/values.h"
#include "chrome/browser/ash/input_method/autocorrect_prefs.h"
#include "chrome/common/pref_names.h"

namespace ash::input_method {
namespace {

constexpr char kUsEnglish[] = "xkb:us::eng";

using AutocorrectPrefDetails = PrefChangeRecorder::AutocorrectPrefDetails;
using AutocorrectPrefs = PrefChangeRecorder::AutocorrectPrefs;
using KeyboardType = PrefChangeRecorder::KeyboardType;

struct AutocorrectPrefChange {
  AutocorrectPrefDetails details;
  AutocorrectPreference previous_value;
  AutocorrectPreference new_value;
};

// Extracts all autocorrect preference values from the input method options
// dictionary blob. Each preference will be keyed to a single engine id, with
// exactly two preferences per engine id for VK and PK.
AutocorrectPrefs ExtractAutocorrectPrefs(PrefService* pref_service) {
  AutocorrectPrefs autocorrect_prefs;

  for (const auto [engine_id, _] :
       pref_service->GetDict(prefs::kLanguageInputMethodSpecificSettings)) {
    autocorrect_prefs.insert(
        {base::StrCat({engine_id, ".VirtualKeyboard"}),
         AutocorrectPrefDetails{
             /*engine_id=*/engine_id,
             /*keyboard_type=*/KeyboardType::kVirtualKeyboard,
             /*preference=*/
             GetVirtualKeyboardAutocorrectPref(*(pref_service), engine_id)}});

    autocorrect_prefs.insert(
        {base::StrCat({engine_id, ".PhysicalKeyboard"}),
         AutocorrectPrefDetails{
             /*engine_id=*/engine_id,
             /*keyboard_type=*/KeyboardType::kPhysicalKeyboard,
             /*preference=*/
             GetPhysicalKeyboardAutocorrectPref(*(pref_service), engine_id)}});
  }

  return autocorrect_prefs;
}

// Iterate over two sets of autocorrect preferences and find the first
// preference where its respective value differs between the two sets. This
// function will be called once per setting change, so we can assume there
// would be at most one preference with different values in the two sets.
std::optional<AutocorrectPrefChange> FindPrefChange(
    const AutocorrectPrefs& previous,
    const AutocorrectPrefs& current) {
  for (const auto& [key, details] : current) {
    const auto find_it = previous.find(key);

    if (find_it == previous.end() &&
        details.preference != AutocorrectPreference::kDefault) {
      return AutocorrectPrefChange{
          /*details=*/details,
          /*previous_value=*/AutocorrectPreference::kDefault,
          /*new_value=*/details.preference};
    }

    if (find_it != previous.end()) {
      const AutocorrectPrefDetails& previous_details = find_it->second;
      if (previous_details.preference != details.preference) {
        return AutocorrectPrefChange{
            /*details=*/details,
            /*previous_value=*/previous_details.preference,
            /*new_value=*/details.preference};
      }
    }
  }

  return std::nullopt;
}

AutocorrectPrefStateTransition MapToAutocorrectPrefStateTransition(
    AutocorrectPreference previous_value,
    AutocorrectPreference new_value) {
  if (previous_value == AutocorrectPreference::kDefault &&
      new_value == AutocorrectPreference::kDisabled) {
    return AutocorrectPrefStateTransition::kDefaultToDisabled;
  }

  if (previous_value == AutocorrectPreference::kDefault &&
      new_value == AutocorrectPreference::kEnabled) {
    return AutocorrectPrefStateTransition::kDefaultToEnabled;
  }

  if (previous_value == AutocorrectPreference::kDisabled &&
      new_value == AutocorrectPreference::kEnabled) {
    return AutocorrectPrefStateTransition::kDisabledToEnabled;
  }

  if (previous_value == AutocorrectPreference::kEnabled &&
      new_value == AutocorrectPreference::kDisabled) {
    return AutocorrectPrefStateTransition::kEnabledToDisabled;
  }

  if (previous_value == AutocorrectPreference::kDefault &&
      new_value == AutocorrectPreference::kEnabledByDefault) {
    return AutocorrectPrefStateTransition::kDefaultToForceEnabled;
  }

  if (previous_value == AutocorrectPreference::kEnabledByDefault &&
      new_value == AutocorrectPreference::kDisabled) {
    return AutocorrectPrefStateTransition::kForceEnabledToDisabled;
  }

  if (previous_value == AutocorrectPreference::kEnabledByDefault &&
      new_value == AutocorrectPreference::kEnabled) {
    return AutocorrectPrefStateTransition::kForceEnabledToEnabled;
  }

  // Note that we do not record kEnabledByDefault to kDefault (this transition
  // would occur in a rampdown of the enabled by default experiment). Recording
  // this transition would require code to run outside of the enabled by default
  // flag (ie remove the enabledByDefault bool from prefs when the experiment
  // flag is disabled), so on the safe side we don't do that.

  return AutocorrectPrefStateTransition::kUnchanged;
}

void RecordAutocorrectPrefChangeMetric(
    const AutocorrectPrefChange& pref_change) {
  AutocorrectPrefStateTransition state_transition =
      MapToAutocorrectPrefStateTransition(pref_change.previous_value,
                                          pref_change.new_value);

  if (pref_change.details.engine_id == kUsEnglish) {
    base::UmaHistogramEnumeration(
        pref_change.details.keyboard_type == KeyboardType::kPhysicalKeyboard
            ? "InputMethod.Assistive.AutocorrectV2.UserPrefChange.English.PK"
            : "InputMethod.Assistive.AutocorrectV2.UserPrefChange.English.VK",
        state_transition);
  }

  base::UmaHistogramEnumeration(
      pref_change.details.keyboard_type == KeyboardType::kPhysicalKeyboard
          ? "InputMethod.Assistive.AutocorrectV2.UserPrefChange.All.PK"
          : "InputMethod.Assistive.AutocorrectV2.UserPrefChange.All.VK",
      state_transition);
}

}  // namespace

PrefChangeRecorder::PrefChangeRecorder(PrefService* pref_service)
    : input_method_options_observer_(pref_service),
      autocorrect_prefs_(ExtractAutocorrectPrefs(pref_service)),
      pref_service_(pref_service) {
  input_method_options_observer_.Observe(
      base::BindRepeating(&PrefChangeRecorder::OnInputMethodOptionsChanged,
                          weak_ptr_factory_.GetWeakPtr()));
}

PrefChangeRecorder::~PrefChangeRecorder() = default;

void PrefChangeRecorder::OnInputMethodOptionsChanged(
    const std::string& pref_path_changed) {
  AutocorrectPrefs new_autocorrect_prefs =
      ExtractAutocorrectPrefs(pref_service_);

  auto pref_change = FindPrefChange(autocorrect_prefs_, new_autocorrect_prefs);
  if (pref_change) {
    RecordAutocorrectPrefChangeMetric(*pref_change);
  }

  autocorrect_prefs_ = std::move(new_autocorrect_prefs);
}

}  // namespace ash::input_method