blob: a5c102949ea043f9c466e515e69679b7a0e93322 [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/browser_switcher_service.h"
#include "build/build_config.h"
#include "chrome/browser/browser_switcher/alternative_browser_launcher.h"
#include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
#include "chrome/browser/browser_switcher/browser_switcher_sitelist.h"
#include "chrome/browser/browser_switcher/ieem_sitelist_parser.h"
#include "chrome/browser/profiles/profile.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/simple_url_loader.h"
#if defined(OS_WIN)
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#endif
namespace browser_switcher {
namespace {
#if defined(OS_WIN)
const wchar_t kIeSiteListKey[] =
L"SOFTWARE\\Policies\\Microsoft\\Internet Explorer\\Main\\EnterpriseMode";
const wchar_t kIeSiteListValue[] = L"SiteList";
// How long to wait after |BrowserSwitcherService| is created before initiating
// the sitelist fetch.
const base::TimeDelta kFetchSitelistDelay = base::TimeDelta::FromSeconds(60);
// How many times to re-try fetching the XML file for the sitelist.
const int kFetchNumRetries = 1;
// TODO(nicolaso): Add chrome_policy for this annotation once the policy is
// implemented.
constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("browser_switcher_ieem_sitelist", R"(
semantics {
sender: "Browser Switcher"
description:
"BrowserSwitcher may download Internet Explorer's Enterprise Mode "
"SiteList XML, to load the list of URLs to open in an alternative "
"browser. This is often on the organization's intranet. For more "
"information on Internet Explorer's Enterprise Mode, see: "
"https://docs.microsoft.com/internet-explorer/ie11-deploy-guide"
"/what-is-enterprise-mode"
trigger:
"This happens only once per profile, 60s after the first page "
"starts loading. The request may be retried once if it failed the "
"first time."
data:
"An HTTP or HTTPS GET request to the URL configured in Internet "
"Explorer's SiteList policy."
destination: OTHER
destination_other:
"URL configured in Internet Explorer's SiteList policy."
}
policy {
cookies_allowed: NO
setting: "This feature cannot be disabled by settings."
policy_exception_justification:
"This feature is still in development, and is disabled by default."
})");
#endif
} // namespace
BrowserSwitcherService::BrowserSwitcherService(Profile* profile)
: launcher_(nullptr), sitelist_(nullptr), prefs_(profile->GetPrefs()) {
DCHECK(profile);
DCHECK(prefs_);
#if defined(OS_WIN)
if (prefs_->GetBoolean(prefs::kUseIeSitelist) &&
prefs_->IsManagedPreference(prefs::kUseIeSitelist)) {
GURL sitelist_url = GetIeemSitelistUrl();
if (sitelist_url.is_valid()) {
auto factory =
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess();
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&BrowserSwitcherService::FetchIeemSitelist,
base::Unretained(this), std::move(sitelist_url),
std::move(factory)),
fetch_sitelist_delay_);
} else {
DoneLoadingIeemSitelist();
}
} else {
DoneLoadingIeemSitelist();
}
#endif
}
BrowserSwitcherService::~BrowserSwitcherService() {}
AlternativeBrowserLauncher* BrowserSwitcherService::launcher() {
if (!launcher_)
launcher_ = std::make_unique<AlternativeBrowserLauncherImpl>(prefs_);
return launcher_.get();
}
BrowserSwitcherSitelist* BrowserSwitcherService::sitelist() {
if (!sitelist_)
sitelist_ = std::make_unique<BrowserSwitcherSitelistImpl>(prefs_);
return sitelist_.get();
}
void BrowserSwitcherService::SetLauncherForTesting(
std::unique_ptr<AlternativeBrowserLauncher> launcher) {
launcher_ = std::move(launcher);
}
void BrowserSwitcherService::SetSitelistForTesting(
std::unique_ptr<BrowserSwitcherSitelist> sitelist) {
sitelist_ = std::move(sitelist);
}
#if defined(OS_WIN)
base::TimeDelta BrowserSwitcherService::fetch_sitelist_delay_ =
kFetchSitelistDelay;
// static
void BrowserSwitcherService::SetIeemFetchDelayForTesting(
base::TimeDelta delay) {
fetch_sitelist_delay_ = delay;
}
base::OnceCallback<void()>
BrowserSwitcherService::xml_parsed_callback_for_testing_;
// static
void BrowserSwitcherService::SetXmlParsedCallbackForTesting(
base::OnceCallback<void()> callback) {
xml_parsed_callback_for_testing_ = std::move(callback);
}
std::string BrowserSwitcherService::ieem_sitelist_url_for_testing_;
// static
void BrowserSwitcherService::SetIeemSitelistUrlForTesting(
const std::string& spec) {
ieem_sitelist_url_for_testing_ = spec;
}
GURL BrowserSwitcherService::GetIeemSitelistUrl() {
if (!ieem_sitelist_url_for_testing_.empty())
return GURL(ieem_sitelist_url_for_testing_);
base::win::RegKey key;
if (ERROR_SUCCESS != key.Open(HKEY_LOCAL_MACHINE, kIeSiteListKey, KEY_READ) &&
ERROR_SUCCESS != key.Open(HKEY_CURRENT_USER, kIeSiteListKey, KEY_READ)) {
return GURL();
}
std::wstring url_string;
if (ERROR_SUCCESS != key.ReadValue(kIeSiteListValue, &url_string))
return GURL();
return GURL(base::UTF16ToUTF8(url_string));
}
void BrowserSwitcherService::FetchIeemSitelist(
GURL url,
scoped_refptr<network::SharedURLLoaderFactory> factory) {
DCHECK(factory);
DCHECK(!url_loader_);
auto request = std::make_unique<network::ResourceRequest>();
request->url = url;
request->load_flags =
(net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DISABLE_CACHE);
url_loader_ =
network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
url_loader_->SetRetryOptions(
kFetchNumRetries,
network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE);
url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
factory.get(), base::BindOnce(&BrowserSwitcherService::ParseXml,
base::Unretained(this)));
}
void BrowserSwitcherService::ParseXml(std::unique_ptr<std::string> bytes) {
if (!bytes) {
DoneLoadingIeemSitelist();
return;
}
ParseIeemXml(*bytes,
base::BindOnce(&BrowserSwitcherService::OnIeemSitelistXmlParsed,
base::Unretained(this)));
}
void BrowserSwitcherService::OnIeemSitelistXmlParsed(ParsedXml xml) {
if (xml.error) {
LOG(ERROR) << "Unable to parse IEEM SiteList: " << *xml.error;
} else {
VLOG(2) << "Done parsing IEEM SiteList. "
<< "Applying rules to future navigations.";
sitelist()->SetIeemSitelist(std::move(xml));
}
DoneLoadingIeemSitelist();
}
void BrowserSwitcherService::DoneLoadingIeemSitelist() {
if (xml_parsed_callback_for_testing_)
std::move(xml_parsed_callback_for_testing_).Run();
}
#endif
} // namespace browser_switcher