PageRenderTime 59ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/chrome/browser/ash/printing/bulk_printers_calculator.cc

https://github.com/chromium/chromium
C++ | 380 lines | 291 code | 51 blank | 38 comment | 23 complexity | db04efee39617dd8ed5c67625e858d68 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. // Copyright 2017 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/ash/printing/bulk_printers_calculator.h"
  5. #include <set>
  6. #include "base/bind.h"
  7. #include "base/containers/contains.h"
  8. #include "base/json/json_reader.h"
  9. #include "base/logging.h"
  10. #include "base/memory/ref_counted.h"
  11. #include "base/memory/scoped_refptr.h"
  12. #include "base/memory/weak_ptr.h"
  13. #include "base/observer_list.h"
  14. #include "base/sequence_checker.h"
  15. #include "base/task/post_task.h"
  16. #include "base/task/sequenced_task_runner.h"
  17. #include "base/task/task_runner_util.h"
  18. #include "base/task/thread_pool.h"
  19. #include "base/threading/scoped_blocking_call.h"
  20. #include "base/values.h"
  21. #include "chromeos/printing/printer_translator.h"
  22. namespace ash {
  23. namespace {
  24. constexpr int kMaxRecords = 20000;
  25. // Represents a task scheduled to process in the Restrictions class.
  26. struct TaskDataInternal {
  27. const unsigned task_id; // unique ID in increasing order
  28. std::unordered_map<std::string, chromeos::Printer>
  29. printers; // resultant list (output)
  30. explicit TaskDataInternal(unsigned id) : task_id(id) {}
  31. };
  32. using PrinterCache = std::vector<std::unique_ptr<chromeos::Printer>>;
  33. using TaskData = std::unique_ptr<TaskDataInternal>;
  34. // Parses |data|, a JSON blob, into a vector of Printers. If |data| cannot be
  35. // parsed, returns nullptr. This is run off the UI thread as it could be very
  36. // slow.
  37. std::unique_ptr<PrinterCache> ParsePrinters(std::unique_ptr<std::string> data) {
  38. if (!data) {
  39. LOG(WARNING) << "Received null data";
  40. return nullptr;
  41. }
  42. // This could be really slow.
  43. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  44. base::BlockingType::MAY_BLOCK);
  45. base::JSONReader::ValueWithError value_with_error =
  46. base::JSONReader::ReadAndReturnValueWithError(
  47. *data, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
  48. if (!value_with_error.value) {
  49. LOG(WARNING) << "Failed to parse printers policy ("
  50. << value_with_error.error_message << ") on line "
  51. << value_with_error.error_line << " at position "
  52. << value_with_error.error_column;
  53. return nullptr;
  54. }
  55. base::Value& json_blob = value_with_error.value.value();
  56. if (!json_blob.is_list()) {
  57. LOG(WARNING) << "Failed to parse printers policy (an array was expected)";
  58. return nullptr;
  59. }
  60. base::Value::ConstListView printer_list = json_blob.GetList();
  61. if (printer_list.size() > kMaxRecords) {
  62. LOG(WARNING) << "Too many records in printers policy: "
  63. << printer_list.size();
  64. return nullptr;
  65. }
  66. auto parsed_printers = std::make_unique<PrinterCache>();
  67. parsed_printers->reserve(printer_list.size());
  68. for (const base::Value& val : printer_list) {
  69. // TODO(skau): Convert to the new Value APIs.
  70. const base::DictionaryValue* printer_dict;
  71. if (!val.GetAsDictionary(&printer_dict)) {
  72. LOG(WARNING) << "Entry in printers policy skipped. Not a dictionary.";
  73. continue;
  74. }
  75. auto printer = chromeos::RecommendedPrinterToPrinter(*printer_dict);
  76. if (!printer) {
  77. LOG(WARNING) << "Failed to parse printer configuration. Skipped.";
  78. continue;
  79. }
  80. parsed_printers->push_back(std::move(printer));
  81. }
  82. return parsed_printers;
  83. }
  84. // Computes the effective printer list using the access mode and
  85. // blocklist/allowlist. Methods are required to be sequenced. This object is
  86. // the owner of all the policy data. Methods updating the list of available
  87. // printers take TaskData (see above) as |task_data| parameter and returned it.
  88. class Restrictions : public base::RefCountedThreadSafe<Restrictions> {
  89. public:
  90. Restrictions() : printers_cache_(nullptr) {
  91. DETACH_FROM_SEQUENCE(sequence_checker_);
  92. }
  93. Restrictions(const Restrictions&) = delete;
  94. Restrictions& operator=(const Restrictions&) = delete;
  95. // Sets the printer cache using the policy blob |data|.
  96. TaskData SetData(TaskData task_data, std::unique_ptr<std::string> data) {
  97. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  98. base::ScopedBlockingCall scoped_blocking_call(
  99. FROM_HERE, base::BlockingType::MAY_BLOCK);
  100. printers_cache_ = ParsePrinters(std::move(data));
  101. return ComputePrinters(std::move(task_data));
  102. }
  103. // Clear the printer cache.
  104. void ClearData() {
  105. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  106. printers_cache_.reset();
  107. }
  108. // Sets the access mode to |mode|.
  109. TaskData UpdateAccessMode(TaskData task_data,
  110. BulkPrintersCalculator::AccessMode mode) {
  111. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  112. mode_ = mode;
  113. return ComputePrinters(std::move(task_data));
  114. }
  115. // Sets the blocklist to |blocklist|.
  116. TaskData UpdateBlocklist(TaskData task_data,
  117. const std::vector<std::string>& blocklist) {
  118. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  119. has_blocklist_ = true;
  120. blocklist_ = std::set<std::string>(blocklist.begin(), blocklist.end());
  121. return ComputePrinters(std::move(task_data));
  122. }
  123. // Sets the allowlist to |allowlist|.
  124. TaskData UpdateAllowlist(TaskData task_data,
  125. const std::vector<std::string>& allowlist) {
  126. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  127. has_allowlist_ = true;
  128. allowlist_ = std::set<std::string>(allowlist.begin(), allowlist.end());
  129. return ComputePrinters(std::move(task_data));
  130. }
  131. private:
  132. friend class base::RefCountedThreadSafe<Restrictions>;
  133. ~Restrictions() {}
  134. // Returns true if we have enough data to compute the effective printer list.
  135. bool IsReady() const {
  136. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  137. if (!printers_cache_) {
  138. return false;
  139. }
  140. switch (mode_) {
  141. case BulkPrintersCalculator::AccessMode::ALL_ACCESS:
  142. return true;
  143. case BulkPrintersCalculator::AccessMode::BLOCKLIST_ONLY:
  144. return has_blocklist_;
  145. case BulkPrintersCalculator::AccessMode::ALLOWLIST_ONLY:
  146. return has_allowlist_;
  147. case BulkPrintersCalculator::AccessMode::UNSET:
  148. return false;
  149. }
  150. NOTREACHED();
  151. return false;
  152. }
  153. // Calculates resultant list of available printers.
  154. TaskData ComputePrinters(TaskData task_data) {
  155. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  156. if (!IsReady()) {
  157. return task_data;
  158. }
  159. switch (mode_) {
  160. case BulkPrintersCalculator::UNSET:
  161. NOTREACHED();
  162. break;
  163. case BulkPrintersCalculator::ALLOWLIST_ONLY:
  164. for (const auto& printer : *printers_cache_) {
  165. if (base::Contains(allowlist_, printer->id())) {
  166. task_data->printers.insert({printer->id(), *printer});
  167. }
  168. }
  169. break;
  170. case BulkPrintersCalculator::BLOCKLIST_ONLY:
  171. for (const auto& printer : *printers_cache_) {
  172. if (!base::Contains(blocklist_, printer->id())) {
  173. task_data->printers.insert({printer->id(), *printer});
  174. }
  175. }
  176. break;
  177. case BulkPrintersCalculator::ALL_ACCESS:
  178. for (const auto& printer : *printers_cache_) {
  179. task_data->printers.insert({printer->id(), *printer});
  180. }
  181. break;
  182. }
  183. return task_data;
  184. }
  185. // Cache of the parsed printer configuration file.
  186. std::unique_ptr<PrinterCache> printers_cache_;
  187. // The type of restriction which is enforced.
  188. BulkPrintersCalculator::AccessMode mode_ = BulkPrintersCalculator::UNSET;
  189. // Blocklist: the list of ids which should not appear in the final list.
  190. bool has_blocklist_ = false;
  191. std::set<std::string> blocklist_;
  192. // Allowlist: the list of the only ids which should appear in the final list.
  193. bool has_allowlist_ = false;
  194. std::set<std::string> allowlist_;
  195. SEQUENCE_CHECKER(sequence_checker_);
  196. };
  197. class BulkPrintersCalculatorImpl : public BulkPrintersCalculator {
  198. public:
  199. BulkPrintersCalculatorImpl()
  200. : restrictions_(base::MakeRefCounted<Restrictions>()),
  201. restrictions_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  202. {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
  203. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
  204. BulkPrintersCalculatorImpl(const BulkPrintersCalculatorImpl&) = delete;
  205. BulkPrintersCalculatorImpl& operator=(const BulkPrintersCalculatorImpl&) =
  206. delete;
  207. void AddObserver(Observer* observer) override {
  208. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  209. observers_.AddObserver(observer);
  210. }
  211. void RemoveObserver(Observer* observer) override {
  212. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  213. observers_.RemoveObserver(observer);
  214. }
  215. void ClearData() override {
  216. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  217. data_is_set_ = false;
  218. last_processed_task_ = ++last_received_task_;
  219. printers_.clear();
  220. // Forward data to Restrictions to clear "Data".
  221. restrictions_runner_->PostTask(
  222. FROM_HERE, base::BindOnce(&Restrictions::ClearData, restrictions_));
  223. // Notify observers.
  224. for (auto& observer : observers_) {
  225. observer.OnPrintersChanged(this);
  226. }
  227. }
  228. void SetData(std::unique_ptr<std::string> data) override {
  229. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  230. data_is_set_ = true;
  231. TaskData task_data =
  232. std::make_unique<TaskDataInternal>(++last_received_task_);
  233. base::PostTaskAndReplyWithResult(
  234. restrictions_runner_.get(), FROM_HERE,
  235. base::BindOnce(&Restrictions::SetData, restrictions_,
  236. std::move(task_data), std::move(data)),
  237. base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
  238. weak_ptr_factory_.GetWeakPtr()));
  239. }
  240. void SetAccessMode(AccessMode mode) override {
  241. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  242. TaskData task_data =
  243. std::make_unique<TaskDataInternal>(++last_received_task_);
  244. base::PostTaskAndReplyWithResult(
  245. restrictions_runner_.get(), FROM_HERE,
  246. base::BindOnce(&Restrictions::UpdateAccessMode, restrictions_,
  247. std::move(task_data), mode),
  248. base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
  249. weak_ptr_factory_.GetWeakPtr()));
  250. }
  251. void SetBlocklist(const std::vector<std::string>& blocklist) override {
  252. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  253. TaskData task_data =
  254. std::make_unique<TaskDataInternal>(++last_received_task_);
  255. base::PostTaskAndReplyWithResult(
  256. restrictions_runner_.get(), FROM_HERE,
  257. base::BindOnce(&Restrictions::UpdateBlocklist, restrictions_,
  258. std::move(task_data), blocklist),
  259. base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
  260. weak_ptr_factory_.GetWeakPtr()));
  261. }
  262. void SetAllowlist(const std::vector<std::string>& allowlist) override {
  263. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  264. TaskData task_data =
  265. std::make_unique<TaskDataInternal>(++last_received_task_);
  266. base::PostTaskAndReplyWithResult(
  267. restrictions_runner_.get(), FROM_HERE,
  268. base::BindOnce(&Restrictions::UpdateAllowlist, restrictions_,
  269. std::move(task_data), allowlist),
  270. base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
  271. weak_ptr_factory_.GetWeakPtr()));
  272. }
  273. bool IsDataPolicySet() const override {
  274. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  275. return data_is_set_;
  276. }
  277. bool IsComplete() const override {
  278. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  279. return (last_processed_task_ == last_received_task_);
  280. }
  281. const std::unordered_map<std::string, chromeos::Printer>& GetPrinters()
  282. const override {
  283. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  284. return printers_;
  285. }
  286. private:
  287. // Called on computation completion. |task_data| corresponds to finalized
  288. // task.
  289. void OnComputationComplete(TaskData task_data) {
  290. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  291. if (!task_data || task_data->task_id <= last_processed_task_) {
  292. // The task is outdated (ClearData() was called in the meantime).
  293. return;
  294. }
  295. last_processed_task_ = task_data->task_id;
  296. if (last_processed_task_ < last_received_task_ && printers_.empty() &&
  297. task_data->printers.empty()) {
  298. // No changes in the object's state.
  299. return;
  300. }
  301. printers_.swap(task_data->printers);
  302. task_data.reset();
  303. // Notifies observers about changes.
  304. for (auto& observer : observers_) {
  305. observer.OnPrintersChanged(this);
  306. }
  307. }
  308. // Holds the blocklist and allowlist. Computes the effective printer list.
  309. scoped_refptr<Restrictions> restrictions_;
  310. // Off UI sequence for computing the printer view.
  311. scoped_refptr<base::SequencedTaskRunner> restrictions_runner_;
  312. // True if printers_ is based on a current policy.
  313. bool data_is_set_ = false;
  314. // Id of the last scheduled task.
  315. unsigned last_received_task_ = 0;
  316. // Id of the last completed task.
  317. unsigned last_processed_task_ = 0;
  318. // The computed set of printers.
  319. std::unordered_map<std::string, chromeos::Printer> printers_;
  320. base::ObserverList<BulkPrintersCalculator::Observer>::Unchecked observers_;
  321. SEQUENCE_CHECKER(sequence_checker_);
  322. base::WeakPtrFactory<BulkPrintersCalculatorImpl> weak_ptr_factory_{this};
  323. };
  324. } // namespace
  325. // static
  326. std::unique_ptr<BulkPrintersCalculator> BulkPrintersCalculator::Create() {
  327. return std::make_unique<BulkPrintersCalculatorImpl>();
  328. }
  329. } // namespace ash