chromium/chrome/browser/ash/input_method/assistive_input_denylist.cc

// Copyright 2023 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/assistive_input_denylist.h"

#include "base/json/json_reader.h"
#include "chrome/browser/ash/input_method/url_utils.h"

namespace ash {
namespace input_method {
namespace {

// The default denylist of domains that will turn off autocorrect and multi word
// suggestions.
const char* kDefaultDomainDenylist[] = {
    "amazon",
    "b.corp.google",
    "buganizer.corp.google",
    "cider.corp.google",
    "classroom.google",
    "desmos",
    "docs.google",
    "facebook",
    "instagram",
    "mail.google",
    "outlook.live",
    "outlook.office",
    "quizlet",
    "reddit",
    "web.skype",
    "teams.microsoft",
    "twitter",
    "whatsapp",
    "youtube",
};

// Exceptions where the features are enabled.
const char* kAllowedDomainsWithPaths[][2] = {{"mail.google", "/chat"}};

std::vector<std::string> ToDenylist(const base::Value& value) {
  if (!value.is_list()) {
    return {};
  }
  std::vector<std::string> domains;
  for (const auto& item : value.GetList()) {
    if (item.is_string()) {
      domains.push_back(item.GetString());
    }
  }
  return domains;
}

bool MatchesSubDomainFromDefaultList(const GURL& url) {
  for (const char* domain : kDefaultDomainDenylist) {
    if (IsSubDomain(url, domain)) {
      return true;
    }
  }
  return false;
}

bool MatchesSubDomainFrom(const std::vector<std::string>& denylist,
                          const GURL& url) {
  for (const auto& domain : denylist) {
    if (IsSubDomain(url, domain)) {
      return true;
    }
  }
  return false;
}

bool AllowedSubDomainWithPathPrefix(const GURL& url) {
  for (const auto& [domain, path_prefix] : kAllowedDomainsWithPaths) {
    if (IsSubDomainWithPathPrefix(url, domain, path_prefix)) {
      return true;
    }
  }
  return false;
}

}  // namespace

AssistiveInputDenylist::AssistiveInputDenylist(
    const DenylistAdditions& additions) {
  if (auto parsed = base::JSONReader::Read(additions.autocorrect_denylist_json);
      parsed.has_value() && parsed->is_list()) {
    autocorrect_denylist_ = ToDenylist(*parsed);
  }

  if (auto parsed = base::JSONReader::Read(additions.multi_word_denylist_json);
      parsed.has_value() && parsed->is_list()) {
    multi_word_denylist_ = ToDenylist(*parsed);
  }
}

AssistiveInputDenylist::~AssistiveInputDenylist() = default;

bool AssistiveInputDenylist::Contains(const GURL& url) {
  return ((MatchesSubDomainFromDefaultList(url) ||
           MatchesSubDomainFrom(autocorrect_denylist_, url) ||
           MatchesSubDomainFrom(multi_word_denylist_, url)) &&
          // Used to allow specific paths on a top level domain that has been
          // denied (for example, "mail.google.com/chat").
          !AllowedSubDomainWithPathPrefix(url));
}

}  // namespace input_method
}  // namespace ash