PageRenderTime 69ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/chrome/browser/browser_switcher/browser_switcher_service.cc

https://github.com/chromium/chromium
C++ | 412 lines | 394 code | 9 blank | 9 comment | 2 complexity | 38977bc62f3dd0534d6aec6d8e9583ff MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. // Copyright 2018 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. #include "chrome/browser/browser_switcher/browser_switcher_service.h"
  5. #include <algorithm>
  6. #include <string>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/metrics/histogram_macros.h"
  10. #include "base/syslog_logging.h"
  11. #include "chrome/browser/browser_switcher/alternative_browser_driver.h"
  12. #include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
  13. #include "chrome/browser/browser_switcher/browser_switcher_sitelist.h"
  14. #include "chrome/browser/browser_switcher/ieem_sitelist_parser.h"
  15. #include "chrome/browser/profiles/profile.h"
  16. #include "components/prefs/pref_service.h"
  17. #include "content/public/browser/browser_context.h"
  18. #include "content/public/browser/browser_thread.h"
  19. #include "content/public/browser/file_url_loader.h"
  20. #include "content/public/browser/shared_cors_origin_access_list.h"
  21. #include "content/public/browser/storage_partition.h"
  22. #include "net/base/load_flags.h"
  23. #include "net/traffic_annotation/network_traffic_annotation.h"
  24. #include "services/network/public/cpp/resource_request.h"
  25. namespace browser_switcher {
  26. namespace {
  27. // How long to wait after |BrowserSwitcherService| is created before initiating
  28. // the sitelist fetch. Non-zero values are used for testing.
  29. //
  30. // TODO(nicolaso): get rid of this.
  31. const base::TimeDelta kFetchSitelistDelay = base::TimeDelta();
  32. // How long to wait after a fetch to re-fetch the sitelist to keep it fresh.
  33. const base::TimeDelta kRefreshSitelistDelay = base::Minutes(30);
  34. // How many times to re-try fetching the XML file for the sitelist.
  35. const int kFetchNumRetries = 1;
  36. constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
  37. net::DefineNetworkTrafficAnnotation("browser_switcher_ieem_sitelist", R"(
  38. semantics {
  39. sender: "Legacy Browser Support "
  40. description:
  41. "Legacy Browser Support may download Internet Explorer's "
  42. "Enterprise Mode SiteList XML, to load the list of URLs to open in "
  43. "an alternative browser. This is often on the organization's "
  44. "intranet. For more information on Internet Explorer's Enterprise "
  45. "Mode, see: "
  46. "https://docs.microsoft.com/internet-explorer/ie11-deploy-guide"
  47. "/what-is-enterprise-mode"
  48. trigger:
  49. "1 minute after browser startup, and then refreshes every 30 "
  50. "minutes afterwards. Only happens if Legacy Browser Support is "
  51. "enabled via enterprise policies."
  52. data:
  53. "Up to 3 (plus retries) HTTP or HTTPS GET requests to the URLs "
  54. "configured in Internet Explorer's SiteList policy, and Chrome's "
  55. "BrowserSwitcherExternalSitelistUrl and "
  56. "BrowserSwitcherExternalGreylistUrl policies."
  57. destination: OTHER
  58. destination_other:
  59. "URL configured in Internet Explorer's SiteList policy, and URLs "
  60. "configured in Chrome's BrowserSwitcherExternalSitelistUrl and "
  61. "BrowserSwitcherExternalGreylistUrl policies."
  62. }
  63. policy {
  64. cookies_allowed: NO
  65. setting: "This feature cannot be disabled by settings."
  66. chrome_policy: {
  67. BrowserSwitcherEnabled: {
  68. BrowserSwitcherEnabled: false
  69. }
  70. }
  71. })");
  72. } // namespace
  73. RulesetSource::RulesetSource(
  74. std::string pref_name_,
  75. GURL url_,
  76. bool contains_inverted_rules_,
  77. base::OnceCallback<void(ParsedXml xml)> parsed_callback_)
  78. : pref_name(std::move(pref_name_)),
  79. url(std::move(url_)),
  80. contains_inverted_rules(contains_inverted_rules_),
  81. parsed_callback(std::move(parsed_callback_)) {}
  82. RulesetSource::RulesetSource(RulesetSource&&) = default;
  83. RulesetSource::~RulesetSource() = default;
  84. XmlDownloader::XmlDownloader(Profile* profile,
  85. BrowserSwitcherService* service,
  86. base::TimeDelta first_fetch_delay,
  87. base::RepeatingCallback<void()> all_done_callback)
  88. : service_(service), all_done_callback_(std::move(all_done_callback)) {
  89. file_url_factory_.Bind(
  90. content::CreateFileURLLoaderFactory(base::FilePath(), nullptr));
  91. other_url_factory_ = profile->GetDefaultStoragePartition()
  92. ->GetURLLoaderFactoryForBrowserProcess();
  93. sources_ = service_->GetRulesetSources();
  94. for (auto& source : sources_) {
  95. if (!source.url.is_valid())
  96. DoneParsing(&source, ParsedXml({}));
  97. }
  98. // Fetch in 1 minute.
  99. ScheduleRefresh(first_fetch_delay);
  100. }
  101. XmlDownloader::~XmlDownloader() = default;
  102. bool XmlDownloader::HasValidSources() const {
  103. return std::any_of(
  104. sources_.begin(), sources_.end(),
  105. [](const RulesetSource& source) { return source.url.is_valid(); });
  106. }
  107. base::Time XmlDownloader::last_refresh_time() const {
  108. return last_refresh_time_;
  109. }
  110. base::Time XmlDownloader::next_refresh_time() const {
  111. return next_refresh_time_;
  112. }
  113. void XmlDownloader::FetchXml() {
  114. for (auto& source : sources_) {
  115. if (!source.url.is_valid()) {
  116. DoneParsing(&source, ParsedXml({}));
  117. continue;
  118. }
  119. auto request = std::make_unique<network::ResourceRequest>();
  120. request->url = source.url;
  121. request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
  122. request->credentials_mode = network::mojom::CredentialsMode::kInclude;
  123. request->priority = net::IDLE;
  124. source.url_loader = network::SimpleURLLoader::Create(std::move(request),
  125. traffic_annotation);
  126. source.url_loader->SetRetryOptions(
  127. kFetchNumRetries,
  128. network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE);
  129. source.url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
  130. GetURLLoaderFactoryForURL(source.url),
  131. base::BindOnce(&XmlDownloader::ParseXml, weak_ptr_factory_.GetWeakPtr(),
  132. base::Unretained(&source)));
  133. }
  134. }
  135. network::mojom::URLLoaderFactory* XmlDownloader::GetURLLoaderFactoryForURL(
  136. const GURL& url) {
  137. if (url.SchemeIsFile())
  138. return file_url_factory_.get();
  139. return other_url_factory_.get();
  140. }
  141. void XmlDownloader::ParseXml(RulesetSource* source,
  142. std::unique_ptr<std::string> bytes) {
  143. if (!bytes) {
  144. DoneParsing(source, ParsedXml({}, "could not fetch XML"));
  145. return;
  146. }
  147. ParseIeemXml(*bytes, base::BindOnce(&XmlDownloader::DoneParsing,
  148. weak_ptr_factory_.GetWeakPtr(),
  149. base::Unretained(source)));
  150. }
  151. void XmlDownloader::DoneParsing(RulesetSource* source, ParsedXml xml) {
  152. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  153. // Greylists can't contain any negative rules, so remove the leading "!".
  154. if (source->contains_inverted_rules) {
  155. for (auto& rule : xml.rules) {
  156. if (base::StartsWith(rule, "!", base::CompareCase::SENSITIVE))
  157. rule.erase(0, 1);
  158. }
  159. }
  160. if (xml.error)
  161. LOG(ERROR) << *xml.error;
  162. std::move(source->parsed_callback).Run(std::move(xml));
  163. // Run the "all done" callback if this was the last ruleset.
  164. counter_++;
  165. DCHECK(counter_ <= sources_.size());
  166. if (counter_ == sources_.size()) {
  167. all_done_callback_.Run();
  168. if (HasValidSources())
  169. last_refresh_time_ = base::Time::Now();
  170. ScheduleRefresh(service_->refresh_delay());
  171. }
  172. }
  173. void XmlDownloader::ScheduleRefresh(base::TimeDelta delay) {
  174. // Avoid doing unnecessary work.
  175. if (!HasValidSources())
  176. return;
  177. // Refresh in 30 minutes, so the sitelists are never too stale.
  178. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  179. FROM_HERE,
  180. base::BindOnce(&XmlDownloader::Refresh, weak_ptr_factory_.GetWeakPtr()),
  181. delay);
  182. next_refresh_time_ = base::Time::Now() + delay;
  183. }
  184. void XmlDownloader::Refresh() {
  185. sources_ = service_->GetRulesetSources();
  186. counter_ = 0;
  187. FetchXml();
  188. }
  189. BrowserSwitcherService::BrowserSwitcherService(Profile* profile)
  190. : profile_(profile),
  191. prefs_(profile),
  192. driver_(new AlternativeBrowserDriverImpl(&prefs_)),
  193. sitelist_(new BrowserSwitcherSitelistImpl(&prefs_)) {
  194. prefs_subscription_ =
  195. prefs().RegisterPrefsChangedCallback(base::BindRepeating(
  196. &BrowserSwitcherService::OnBrowserSwitcherPrefsChanged,
  197. base::Unretained(this)));
  198. if (prefs_.IsEnabled()) {
  199. UMA_HISTOGRAM_ENUMERATION("BrowserSwitcher.AlternativeBrowser",
  200. driver_->GetBrowserType());
  201. }
  202. }
  203. BrowserSwitcherService::~BrowserSwitcherService() = default;
  204. void BrowserSwitcherService::Init() {
  205. LoadRulesFromPrefs();
  206. StartDownload(fetch_delay());
  207. }
  208. void BrowserSwitcherService::OnAllRulesetsLoadedForTesting(
  209. base::OnceCallback<void()> cb) {
  210. all_rulesets_loaded_callback_for_testing_ = std::move(cb);
  211. }
  212. void BrowserSwitcherService::StartDownload(base::TimeDelta delay) {
  213. // This destroys the previous XmlDownloader, which cancels any scheduled
  214. // refresh operations.
  215. sitelist_downloader_ = std::make_unique<XmlDownloader>(
  216. profile_, this, delay,
  217. base::BindRepeating(&BrowserSwitcherService::OnAllRulesetsParsed,
  218. base::Unretained(this)));
  219. }
  220. void BrowserSwitcherService::Shutdown() {
  221. prefs_.Shutdown();
  222. }
  223. AlternativeBrowserDriver* BrowserSwitcherService::driver() {
  224. return driver_.get();
  225. }
  226. BrowserSwitcherSitelist* BrowserSwitcherService::sitelist() {
  227. return sitelist_.get();
  228. }
  229. BrowserSwitcherPrefs& BrowserSwitcherService::prefs() {
  230. return prefs_;
  231. }
  232. Profile* BrowserSwitcherService::profile() {
  233. return profile_;
  234. }
  235. XmlDownloader* BrowserSwitcherService::sitelist_downloader() {
  236. return sitelist_downloader_.get();
  237. }
  238. base::TimeDelta BrowserSwitcherService::fetch_delay() {
  239. return fetch_delay_;
  240. }
  241. base::TimeDelta BrowserSwitcherService::refresh_delay() {
  242. return refresh_delay_;
  243. }
  244. void BrowserSwitcherService::SetDriverForTesting(
  245. std::unique_ptr<AlternativeBrowserDriver> driver) {
  246. driver_ = std::move(driver);
  247. }
  248. void BrowserSwitcherService::SetSitelistForTesting(
  249. std::unique_ptr<BrowserSwitcherSitelist> sitelist) {
  250. sitelist_ = std::move(sitelist);
  251. }
  252. std::vector<RulesetSource> BrowserSwitcherService::GetRulesetSources() {
  253. std::vector<RulesetSource> sources;
  254. GURL sitelist_url = prefs_.GetExternalSitelistUrl();
  255. sources.emplace_back(
  256. prefs::kExternalSitelistUrl, sitelist_url, /* invert_rules */ false,
  257. base::BindOnce(&BrowserSwitcherService::OnExternalSitelistParsed,
  258. weak_ptr_factory_.GetWeakPtr()));
  259. GURL greylist_url = prefs_.GetExternalGreylistUrl();
  260. sources.emplace_back(
  261. prefs::kExternalGreylistUrl, greylist_url, /* invert_rules */ true,
  262. base::BindOnce(&BrowserSwitcherService::OnExternalGreylistParsed,
  263. weak_ptr_factory_.GetWeakPtr()));
  264. return sources;
  265. }
  266. void BrowserSwitcherService::LoadRulesFromPrefs() {
  267. if (prefs().GetExternalSitelistUrl().is_valid())
  268. sitelist()->SetExternalSitelist(
  269. ParsedXml(prefs().GetCachedExternalSitelist(), absl::nullopt));
  270. if (prefs().GetExternalGreylistUrl().is_valid())
  271. sitelist()->SetExternalGreylist(
  272. ParsedXml(prefs().GetCachedExternalGreylist(), absl::nullopt));
  273. }
  274. void BrowserSwitcherService::OnAllRulesetsParsed() {
  275. callback_list_.Notify(this);
  276. if (all_rulesets_loaded_callback_for_testing_)
  277. std::move(all_rulesets_loaded_callback_for_testing_).Run();
  278. }
  279. base::CallbackListSubscription
  280. BrowserSwitcherService::RegisterAllRulesetsParsedCallback(
  281. AllRulesetsParsedCallback callback) {
  282. return callback_list_.Add(callback);
  283. }
  284. void BrowserSwitcherService::OnBrowserSwitcherPrefsChanged(
  285. BrowserSwitcherPrefs* prefs,
  286. const std::vector<std::string>& changed_prefs) {
  287. // Record |BrowserSwitcher.AlternativeBrowser| when the
  288. // |BrowserSwitcherEnabled| or |AlternativeBrowserPath| policies change.
  289. bool should_record_metrics =
  290. changed_prefs.end() !=
  291. std::find_if(changed_prefs.begin(), changed_prefs.end(),
  292. [](const std::string& pref) {
  293. return pref == prefs::kEnabled ||
  294. pref == prefs::kAlternativeBrowserPath;
  295. });
  296. if (should_record_metrics && prefs_.IsEnabled()) {
  297. UMA_HISTOGRAM_ENUMERATION("BrowserSwitcher.AlternativeBrowser",
  298. driver_->GetBrowserType());
  299. }
  300. auto sources = GetRulesetSources();
  301. // Re-download if one of the URLs changed. O(n^2), with n <= 3.
  302. bool should_redownload = std::any_of(
  303. sources.begin(), sources.end(),
  304. [&changed_prefs](const RulesetSource& source) {
  305. return (std::find(changed_prefs.begin(), changed_prefs.end(),
  306. source.pref_name) != changed_prefs.end());
  307. });
  308. if (should_redownload)
  309. StartDownload(fetch_delay());
  310. }
  311. void BrowserSwitcherService::OnExternalSitelistParsed(ParsedXml xml) {
  312. if (xml.error) {
  313. SYSLOG(INFO) << "Unable to parse IEEM SiteList: " << *xml.error;
  314. } else {
  315. VLOG(2) << "Done parsing external SiteList for sitelist rules. "
  316. << "Applying rules to future navigations.";
  317. if (prefs().GetExternalSitelistUrl().is_valid())
  318. prefs().SetCachedExternalSitelist(xml.rules);
  319. sitelist()->SetExternalSitelist(std::move(xml));
  320. }
  321. }
  322. void BrowserSwitcherService::OnExternalGreylistParsed(ParsedXml xml) {
  323. if (xml.error) {
  324. SYSLOG(INFO) << "Unable to parse IEEM SiteList: " << *xml.error;
  325. } else {
  326. VLOG(2) << "Done parsing external SiteList for greylist rules. "
  327. << "Applying rules to future navigations.";
  328. if (prefs().GetExternalGreylistUrl().is_valid())
  329. prefs().SetCachedExternalGreylist(xml.rules);
  330. sitelist()->SetExternalGreylist(std::move(xml));
  331. }
  332. }
  333. base::TimeDelta BrowserSwitcherService::fetch_delay_ = kFetchSitelistDelay;
  334. base::TimeDelta BrowserSwitcherService::refresh_delay_ = kRefreshSitelistDelay;
  335. // static
  336. void BrowserSwitcherService::SetFetchDelayForTesting(base::TimeDelta delay) {
  337. fetch_delay_ = delay;
  338. }
  339. // static
  340. void BrowserSwitcherService::SetRefreshDelayForTesting(base::TimeDelta delay) {
  341. refresh_delay_ = delay;
  342. }
  343. } // namespace browser_switcher