1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h"
6 
7 #include <unordered_set>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/strings/string_util.h"
12 #include "base/syslog_logging.h"
13 #include "base/values.h"
14 #include "build/build_config.h"
15 #include "chrome/common/pref_names.h"
16 #include "components/policy/core/browser/policy_error_map.h"
17 #include "components/policy/policy_constants.h"
18 #include "components/prefs/pref_value_map.h"
19 #include "components/spellcheck/browser/pref_names.h"
20 #include "components/spellcheck/common/spellcheck_common.h"
21 #include "components/strings/grit/components_strings.h"
22 
23 SpellcheckLanguageBlacklistPolicyHandler::
SpellcheckLanguageBlacklistPolicyHandler()24     SpellcheckLanguageBlacklistPolicyHandler()
25     : TypeCheckingPolicyHandler(policy::key::kSpellcheckLanguageBlacklist,
26                                 base::Value::Type::LIST) {}
27 
28 SpellcheckLanguageBlacklistPolicyHandler::
29     ~SpellcheckLanguageBlacklistPolicyHandler() = default;
30 
CheckPolicySettings(const policy::PolicyMap & policies,policy::PolicyErrorMap * errors)31 bool SpellcheckLanguageBlacklistPolicyHandler::CheckPolicySettings(
32     const policy::PolicyMap& policies,
33     policy::PolicyErrorMap* errors) {
34   const base::Value* value = nullptr;
35   bool ok = CheckAndGetValue(policies, errors, &value);
36 
37   std::vector<base::Value> blacklisted;
38   std::vector<std::string> unknown;
39   std::vector<std::string> duplicates;
40   SortBlacklistedLanguages(policies, &blacklisted, &unknown, &duplicates);
41 
42 #if !defined(OS_MACOSX)
43   for (const std::string& language : duplicates) {
44     errors->AddError(policy_name(), IDS_POLICY_SPELLCHECK_BLACKLIST_IGNORE,
45                      language);
46   }
47 
48   for (const std::string& language : unknown) {
49     errors->AddError(policy_name(), IDS_POLICY_SPELLCHECK_UNKNOWN_LANGUAGE,
50                      language);
51   }
52 #endif
53 
54   return ok;
55 }
56 
ApplyPolicySettings(const policy::PolicyMap & policies,PrefValueMap * prefs)57 void SpellcheckLanguageBlacklistPolicyHandler::ApplyPolicySettings(
58     const policy::PolicyMap& policies,
59     PrefValueMap* prefs) {
60   // Ignore this policy if the SpellcheckEnabled policy disables spellcheck.
61   const base::Value* spellcheck_enabled_value =
62       policies.GetValue(policy::key::kSpellcheckEnabled);
63   if (spellcheck_enabled_value && spellcheck_enabled_value->GetBool() == false)
64     return;
65 
66   // If this policy isn't set, don't modify spellcheck languages.
67   const base::Value* value = policies.GetValue(policy_name());
68   if (!value)
69     return;
70 
71   // Set the blacklisted dictionaries preference based on this policy's values,
72   // and emit warnings for unknown or duplicate languages.
73   std::vector<base::Value> blacklisted;
74   std::vector<std::string> unknown;
75   std::vector<std::string> duplicates;
76   SortBlacklistedLanguages(policies, &blacklisted, &unknown, &duplicates);
77 
78   for (const std::string& language : duplicates) {
79     SYSLOG(WARNING)
80         << "SpellcheckLanguageBlacklist policy: an entry was also found in"
81            " the SpellcheckLanguage policy: \""
82         << language << "\". Blacklist entry will be ignored.";
83   }
84 
85   for (const std::string& language : unknown) {
86     SYSLOG(WARNING) << "SpellcheckLanguageBlacklist policy: Unknown or "
87                        "unsupported language \""
88                     << language << "\"";
89   }
90 
91   prefs->SetValue(spellcheck::prefs::kSpellCheckBlacklistedDictionaries,
92                   base::Value(std::move(blacklisted)));
93 }
94 
SortBlacklistedLanguages(const policy::PolicyMap & policies,std::vector<base::Value> * const blacklisted,std::vector<std::string> * const unknown,std::vector<std::string> * const duplicates)95 void SpellcheckLanguageBlacklistPolicyHandler::SortBlacklistedLanguages(
96     const policy::PolicyMap& policies,
97     std::vector<base::Value>* const blacklisted,
98     std::vector<std::string>* const unknown,
99     std::vector<std::string>* const duplicates) {
100   const base::Value* value = policies.GetValue(policy_name());
101   if (!value)
102     return;
103 
104   // Build a lookup of force-enabled spellcheck languages to find duplicates.
105   const base::Value* forced_enabled_value =
106       policies.GetValue(policy::key::kSpellcheckLanguage);
107   std::unordered_set<std::string> forced_languages_lookup;
108   if (forced_enabled_value) {
109     for (const auto& forced_language : forced_enabled_value->GetList())
110       forced_languages_lookup.insert(forced_language.GetString());
111   }
112 
113   // Separate the valid languages from the unknown / unsupported languages and
114   // the languages that also appear in the SpellcheckLanguage policy.
115   for (const base::Value& language : value->GetList()) {
116     std::string current_language =
117         spellcheck::GetCorrespondingSpellCheckLanguage(
118             base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL));
119 
120     if (current_language.empty()) {
121       unknown->emplace_back(language.GetString());
122     } else {
123       if (forced_languages_lookup.find(language.GetString()) !=
124           forced_languages_lookup.end()) {
125         // If a language is both force-enabled and force-disabled, force-enable
126         // wins. Put the language in the list of duplicates.
127         duplicates->emplace_back(std::move(current_language));
128       } else {
129         blacklisted->emplace_back(std::move(current_language));
130       }
131     }
132   }
133 }
134