blob: 71bed5e648d662f7b9d5256bf8b7e3916044e770 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/browser_switcher/ieem_sitelist_parser.h"
#include "base/bind.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "services/data_decoder/public/cpp/safe_xml_parser.h"
#include "services/data_decoder/public/mojom/constants.mojom.h"
#include "services/data_decoder/public/mojom/xml_parser.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace browser_switcher {
namespace {
using namespace data_decoder; // For |GetXmlElement...()| helper functions.
const char kInvalidRootElement[] = "Invalid XML root element";
const char kSchema1RulesElement[] = "rules";
const char kSchema1EmieElement[] = "emie";
const char kSchema1DocModeElement[] = "docMode";
const char kSchema1DomainElement[] = "domain";
const char kSchema1PathElement[] = "path";
const char kSchema1ExcludeAttribute[] = "exclude";
const char kSchema1DoNotTransitionAttribute[] = "doNotTransition";
const char kSchema1TrueValue[] = "true";
const char kSchema2SiteListElement[] = "site-list";
const char kSchema2SiteElement[] = "site";
const char kSchema2SiteUrlAttribute[] = "url";
const char kSchema2SiteOpenInElement[] = "open-in";
std::vector<const base::Value*> GetChildrenWithTag(const base::Value& node,
const std::string& tag) {
std::vector<const base::Value*> children;
GetAllXmlElementChildrenWithTag(node, tag, &children);
return children;
}
// Data in a v.1 schema <domain> or <path> element.
struct Entry {
// URL or path concerned.
std::string text;
// True if the exclude attribute is "true".
bool exclude;
// True if the doNotTransition attribute is "true".
bool do_not_transition;
};
Entry ParseDomainOrPath(const base::Value& node, ParsedXml* result) {
DCHECK(IsXmlElementNamed(node, kSchema1DomainElement) ||
IsXmlElementNamed(node, kSchema1PathElement));
Entry entry;
std::string exclude_attrib =
GetXmlElementAttribute(node, kSchema1ExcludeAttribute);
entry.exclude = (exclude_attrib == kSchema1TrueValue);
std::string do_not_transition_attrib =
GetXmlElementAttribute(node, kSchema1DoNotTransitionAttribute);
entry.do_not_transition = (do_not_transition_attrib == kSchema1TrueValue);
GetXmlElementText(node, &entry.text);
base::TrimWhitespaceASCII(entry.text, base::TRIM_ALL, &entry.text);
return entry;
}
// Parses Enterprise Mode schema 1 files according to:
// https://technet.microsoft.com/itpro/internet-explorer/ie11-deploy-guide/enterprise-mode-schema-version-1-guidance
void ParseIeFileVersionOne(const base::Value& xml, ParsedXml* result) {
DCHECK(IsXmlElementNamed(xml, kSchema1RulesElement));
for (const base::Value& node : GetXmlElementChildren(xml)->GetList()) {
// Skip over anything that is not a <emie> or <docMode> element.
if (!IsXmlElementNamed(node, kSchema1EmieElement) &&
!IsXmlElementNamed(node, kSchema1DocModeElement)) {
continue;
}
// Loop over <domain> elements.
for (const base::Value* domain_node :
GetChildrenWithTag(node, kSchema1DomainElement)) {
Entry domain = ParseDomainOrPath(*domain_node, result);
if (!domain.text.empty() && !domain.exclude) {
std::string prefix = (domain.do_not_transition ? "!" : "");
result->sitelist.push_back(prefix + domain.text);
}
// Loop over <path> elements.
for (const base::Value* path_node :
GetChildrenWithTag(*domain_node, kSchema1PathElement)) {
Entry path = ParseDomainOrPath(*path_node, result);
if (!path.text.empty() && !domain.text.empty() && !path.exclude) {
std::string prefix = (path.do_not_transition ? "!" : "");
result->sitelist.push_back(prefix + domain.text + path.text);
}
}
}
}
}
// Parses Enterprise Mode schema 2 files according to:
// https://technet.microsoft.com/itpro/internet-explorer/ie11-deploy-guide/enterprise-mode-schema-version-2-guidance
void ParseIeFileVersionTwo(const base::Value& xml, ParsedXml* result) {
DCHECK(IsXmlElementNamed(xml, kSchema2SiteListElement));
// Iterate over <site> elements. Notably, skip <created-by> elements.
for (const base::Value* site_node :
GetChildrenWithTag(xml, kSchema2SiteElement)) {
std::string url =
GetXmlElementAttribute(*site_node, kSchema2SiteUrlAttribute);
base::TrimWhitespaceASCII(url, base::TRIM_ALL, &url);
if (url.empty())
continue;
// Read all sub-elements and keep the content of the <open-in> element.
std::string mode;
for (const base::Value* open_in_node :
GetChildrenWithTag(*site_node, kSchema2SiteOpenInElement)) {
GetXmlElementText(*open_in_node, &mode);
}
base::TrimWhitespaceASCII(mode, base::TRIM_ALL, &mode);
std::string prefix = (mode.empty() || mode == "none") ? "!" : "";
result->sitelist.push_back(prefix + url);
}
}
void RawXmlParsed(base::OnceCallback<void(ParsedXml)> callback,
std::unique_ptr<base::Value> xml,
const base::Optional<std::string>& error) {
if (error) {
// Copies the string, but it should only be around 20 characters.
std::move(callback).Run(ParsedXml({}, {}, *error));
return;
}
DCHECK(xml);
DCHECK(data_decoder::IsXmlElementOfType(
*xml, data_decoder::mojom::XmlParser::kElementType));
ParsedXml result;
if (data_decoder::IsXmlElementNamed(*xml, kSchema1RulesElement)) {
// Enterprise Mode schema v.1 has <rules> element at its top level.
ParseIeFileVersionOne(*xml, &result);
} else if (data_decoder::IsXmlElementNamed(*xml, kSchema2SiteListElement)) {
// Enterprise Mode schema v.2 has <site-list> element at its top level.
ParseIeFileVersionTwo(*xml, &result);
} else {
result.error = kInvalidRootElement;
}
std::move(callback).Run(std::move(result));
}
} // namespace
ParsedXml::ParsedXml() = default;
ParsedXml::ParsedXml(ParsedXml&&) = default;
ParsedXml::ParsedXml(std::vector<std::string>&& sitelist_,
std::vector<std::string>&& greylist_,
base::Optional<std::string>&& error_)
: sitelist(std::move(sitelist_)),
greylist(std::move(greylist_)),
error(std::move(error_)) {}
ParsedXml::~ParsedXml() = default;
void ParseIeemXml(const std::string& xml,
base::OnceCallback<void(ParsedXml)> callback) {
data_decoder::ParseXml(
content::ServiceManagerConnection::GetForProcess()->GetConnector(), xml,
base::BindOnce(&RawXmlParsed, std::move(callback)));
}
} // namespace browser_switcher