logo_cache.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. // Copyright 2014 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 "components/search_provider_logos/logo_cache.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <memory>
  8. #include <utility>
  9. #include "base/files/file_util.h"
  10. #include "base/json/json_reader.h"
  11. #include "base/json/json_writer.h"
  12. #include "base/logging.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "base/values.h"
  15. namespace search_provider_logos {
  16. namespace {
  17. // The cached logo metadata is persisted as JSON using these keys.
  18. const char kSourceUrlKey[] = "url";
  19. const char kExpirationTimeKey[] = "expiration_time";
  20. const char kCanShowAfterExpirationKey[] = "can_show_after_expiration";
  21. const char kFingerprintKey[] = "fingerprint";
  22. const char kTypeKey[] = "type";
  23. const char kOnClickURLKey[] = "on_click_url";
  24. const char kFullPageURLKey[] = "full_page_url";
  25. const char kAltTextKey[] = "alt_text";
  26. const char kMimeTypeKey[] = "mime_type";
  27. const char kDarkMimeTypeKey[] = "dark_mime_type";
  28. const char kNumBytesKey[] = "num_bytes";
  29. const char kDarkNumBytesKey[] = "dark_num_bytes";
  30. const char kAnimatedUrlKey[] = "animated_url";
  31. const char kDarkAnimatedUrlKey[] = "dark_animated_url";
  32. const char kLogUrlKey[] = "log_url";
  33. const char kDarkLogUrlKey[] = "dark_log_url";
  34. const char kCtaLogUrlKey[] = "cta_log_url";
  35. const char kDarkCtaLogUrlKey[] = "dark_cta_log_url";
  36. const char kShortLinkKey[] = "short_link";
  37. const char kWidthPx[] = "width_px";
  38. const char kHeightPx[] = "height_px";
  39. const char kDarkWidthPx[] = "dark_width_px";
  40. const char kDarkHeightPx[] = "dark_height_px";
  41. const char kIframeWidthPx[] = "iframe_width_px";
  42. const char kIframeHeightPx[] = "iframe_height_px";
  43. const char kDarkBackgroundColorKey[] = "dark_background_color";
  44. const char kShareButtonX[] = "share_button_x";
  45. const char kShareButtonY[] = "share_button_y";
  46. const char kShareButtonOpacity[] = "share_button_opacity";
  47. const char kShareButtonIcon[] = "share_button_icon";
  48. const char kShareButtonBg[] = "share_button_bg";
  49. const char kDarkShareButtonX[] = "dark_share_button_x";
  50. const char kDarkShareButtonY[] = "dark_share_button_y";
  51. const char kDarkShareButtonOpacity[] = "dark_share_button_opacity";
  52. const char kDarkShareButtonIcon[] = "dark_share_button_icon";
  53. const char kDarkShareButtonBg[] = "dark_share_button_bg";
  54. const char kSimpleType[] = "SIMPLE";
  55. const char kAnimatedType[] = "ANIMATED";
  56. const char kInteractiveType[] = "INTERACTIVE";
  57. bool GetTimeValue(const base::DictionaryValue& dict,
  58. const std::string& key,
  59. base::Time* time) {
  60. std::string str;
  61. int64_t internal_time_value;
  62. if (dict.GetString(key, &str) &&
  63. base::StringToInt64(str, &internal_time_value)) {
  64. *time = base::Time::FromInternalValue(internal_time_value);
  65. return true;
  66. }
  67. return false;
  68. }
  69. void SetTimeValue(base::DictionaryValue& dict,
  70. const std::string& key,
  71. const base::Time& time) {
  72. int64_t internal_time_value = time.ToInternalValue();
  73. dict.SetStringKey(key, base::NumberToString(internal_time_value));
  74. }
  75. LogoType LogoTypeFromString(base::StringPiece type) {
  76. if (type == kSimpleType) {
  77. return LogoType::SIMPLE;
  78. }
  79. if (type == kAnimatedType) {
  80. return LogoType::ANIMATED;
  81. }
  82. if (type == kInteractiveType) {
  83. return LogoType::INTERACTIVE;
  84. }
  85. LOG(WARNING) << "invalid type " << type;
  86. return LogoType::SIMPLE;
  87. }
  88. std::string LogoTypeToString(LogoType type) {
  89. switch (type) {
  90. case LogoType::SIMPLE:
  91. return kSimpleType;
  92. case LogoType::ANIMATED:
  93. return kAnimatedType;
  94. case LogoType::INTERACTIVE:
  95. return kInteractiveType;
  96. }
  97. NOTREACHED();
  98. return "";
  99. }
  100. } // namespace
  101. LogoCache::LogoCache(const base::FilePath& cache_directory)
  102. : cache_directory_(cache_directory),
  103. metadata_is_valid_(false) {
  104. // The LogoCache can be constructed on any thread, as long as it's used
  105. // on a single sequence after construction.
  106. DETACH_FROM_SEQUENCE(sequence_checker_);
  107. }
  108. LogoCache::~LogoCache() {
  109. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  110. }
  111. void LogoCache::UpdateCachedLogoMetadata(const LogoMetadata& metadata) {
  112. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  113. DCHECK(metadata_);
  114. DCHECK_EQ(metadata_->fingerprint, metadata.fingerprint);
  115. UpdateMetadata(std::make_unique<LogoMetadata>(metadata));
  116. WriteMetadata();
  117. }
  118. const LogoMetadata* LogoCache::GetCachedLogoMetadata() {
  119. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  120. ReadMetadataIfNeeded();
  121. return metadata_.get();
  122. }
  123. void LogoCache::SetCachedLogo(const EncodedLogo* logo) {
  124. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  125. if (!logo) {
  126. UpdateMetadata(nullptr);
  127. DeleteLogoAndMetadata();
  128. return;
  129. }
  130. logo_num_bytes_ =
  131. logo->encoded_image ? static_cast<int>(logo->encoded_image->size()) : 0;
  132. dark_logo_num_bytes_ =
  133. logo->dark_encoded_image
  134. ? static_cast<int>(logo->dark_encoded_image->size())
  135. : 0;
  136. UpdateMetadata(std::make_unique<LogoMetadata>(logo->metadata));
  137. WriteLogo(logo->encoded_image, logo->dark_encoded_image);
  138. }
  139. std::unique_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
  140. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  141. ReadMetadataIfNeeded();
  142. if (!metadata_)
  143. return nullptr;
  144. base::FilePath logo_path = GetLogoPath();
  145. base::FilePath dark_logo_path = GetDarkLogoPath();
  146. scoped_refptr<base::RefCountedString> encoded_image;
  147. if (logo_num_bytes_ != 0) {
  148. encoded_image = new base::RefCountedString();
  149. if (!base::ReadFileToString(logo_path, &encoded_image->data())) {
  150. UpdateMetadata(nullptr);
  151. return nullptr;
  152. }
  153. if (encoded_image->size() != static_cast<size_t>(logo_num_bytes_)) {
  154. // Delete corrupt metadata and logo.
  155. DeleteLogoAndMetadata();
  156. UpdateMetadata(nullptr);
  157. return nullptr;
  158. }
  159. }
  160. scoped_refptr<base::RefCountedString> dark_encoded_image;
  161. if (dark_logo_num_bytes_ != 0) {
  162. dark_encoded_image = new base::RefCountedString();
  163. if (!base::ReadFileToString(dark_logo_path, &dark_encoded_image->data())) {
  164. UpdateMetadata(nullptr);
  165. return nullptr;
  166. }
  167. if (dark_encoded_image->size() !=
  168. static_cast<size_t>(dark_logo_num_bytes_)) {
  169. // Delete corrupt metadata and logo.
  170. DeleteLogoAndMetadata();
  171. UpdateMetadata(nullptr);
  172. return nullptr;
  173. }
  174. }
  175. std::unique_ptr<EncodedLogo> logo(new EncodedLogo());
  176. logo->encoded_image = encoded_image;
  177. logo->dark_encoded_image = dark_encoded_image;
  178. logo->metadata = *metadata_;
  179. return logo;
  180. }
  181. // static
  182. std::unique_ptr<LogoMetadata> LogoCache::LogoMetadataFromString(
  183. const std::string& str,
  184. int* logo_num_bytes,
  185. int* dark_logo_num_bytes) {
  186. std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(str);
  187. base::DictionaryValue* dict;
  188. if (!value || !value->GetAsDictionary(&dict))
  189. return nullptr;
  190. // These helpers replace the deprecated analogous methods on
  191. // base::DictionaryValue, so as to maintain the early exit behavior in the if
  192. // predicate below.
  193. auto get_string = [dict](const char* key, std::string* ret) -> bool {
  194. const std::string* v = dict->FindStringKey(key);
  195. if (v)
  196. *ret = *v;
  197. return v != nullptr;
  198. };
  199. auto get_boolean = [dict](const char* key, bool* ret) -> bool {
  200. absl::optional<bool> v = dict->FindBoolKey(key);
  201. if (v.has_value())
  202. *ret = v.value();
  203. return v.has_value();
  204. };
  205. auto get_integer = [dict](const char* key, int* ret) -> bool {
  206. absl::optional<int> v = dict->FindIntKey(key);
  207. if (v.has_value())
  208. *ret = v.value();
  209. return v.has_value();
  210. };
  211. auto get_double = [dict](const char* key, double* ret) -> bool {
  212. absl::optional<double> v = dict->FindDoubleKey(key);
  213. if (v.has_value())
  214. *ret = v.value();
  215. return v.has_value();
  216. };
  217. std::unique_ptr<LogoMetadata> metadata(new LogoMetadata());
  218. std::string source_url;
  219. std::string type;
  220. std::string on_click_url;
  221. std::string full_page_url;
  222. std::string animated_url;
  223. std::string dark_animated_url;
  224. std::string log_url;
  225. std::string dark_log_url;
  226. std::string cta_log_url;
  227. std::string dark_cta_log_url;
  228. std::string short_link;
  229. if (!get_string(kSourceUrlKey, &source_url) ||
  230. !get_string(kFingerprintKey, &metadata->fingerprint) ||
  231. !get_string(kTypeKey, &type) ||
  232. !get_string(kOnClickURLKey, &on_click_url) ||
  233. !get_string(kFullPageURLKey, &full_page_url) ||
  234. !get_string(kAltTextKey, &metadata->alt_text) ||
  235. !get_string(kAnimatedUrlKey, &animated_url) ||
  236. !get_string(kDarkAnimatedUrlKey, &dark_animated_url) ||
  237. !get_string(kLogUrlKey, &log_url) ||
  238. !get_string(kDarkLogUrlKey, &dark_log_url) ||
  239. !get_string(kCtaLogUrlKey, &cta_log_url) ||
  240. !get_string(kDarkCtaLogUrlKey, &dark_cta_log_url) ||
  241. !get_string(kShortLinkKey, &short_link) ||
  242. !get_string(kMimeTypeKey, &metadata->mime_type) ||
  243. !get_string(kDarkMimeTypeKey, &metadata->dark_mime_type) ||
  244. !get_boolean(kCanShowAfterExpirationKey,
  245. &metadata->can_show_after_expiration) ||
  246. !get_integer(kNumBytesKey, logo_num_bytes) ||
  247. !get_integer(kDarkNumBytesKey, dark_logo_num_bytes) ||
  248. !get_integer(kShareButtonX, &metadata->share_button_x) ||
  249. !get_integer(kShareButtonY, &metadata->share_button_y) ||
  250. !get_double(kShareButtonOpacity, &metadata->share_button_opacity) ||
  251. !get_string(kShareButtonIcon, &metadata->share_button_icon) ||
  252. !get_string(kShareButtonBg, &metadata->share_button_bg) ||
  253. !get_integer(kDarkShareButtonX, &metadata->dark_share_button_x) ||
  254. !get_integer(kDarkShareButtonY, &metadata->dark_share_button_y) ||
  255. !get_double(kDarkShareButtonOpacity,
  256. &metadata->dark_share_button_opacity) ||
  257. !get_string(kDarkShareButtonIcon, &metadata->dark_share_button_icon) ||
  258. !get_string(kDarkShareButtonBg, &metadata->dark_share_button_bg) ||
  259. !get_integer(kWidthPx, &metadata->width_px) ||
  260. !get_integer(kHeightPx, &metadata->height_px) ||
  261. !get_integer(kDarkWidthPx, &metadata->dark_width_px) ||
  262. !get_integer(kDarkHeightPx, &metadata->dark_height_px) ||
  263. !get_integer(kIframeWidthPx, &metadata->iframe_width_px) ||
  264. !get_integer(kIframeHeightPx, &metadata->iframe_height_px) ||
  265. !get_string(kDarkBackgroundColorKey, &metadata->dark_background_color) ||
  266. !GetTimeValue(*dict, kExpirationTimeKey, &metadata->expiration_time)) {
  267. return nullptr;
  268. }
  269. metadata->type = LogoTypeFromString(type);
  270. metadata->source_url = GURL(source_url);
  271. metadata->on_click_url = GURL(on_click_url);
  272. metadata->full_page_url = GURL(full_page_url);
  273. metadata->animated_url = GURL(animated_url);
  274. metadata->dark_animated_url = GURL(dark_animated_url);
  275. metadata->log_url = GURL(log_url);
  276. metadata->dark_log_url = GURL(dark_log_url);
  277. metadata->cta_log_url = GURL(cta_log_url);
  278. metadata->dark_cta_log_url = GURL(dark_cta_log_url);
  279. metadata->short_link = GURL(short_link);
  280. return metadata;
  281. }
  282. // static
  283. void LogoCache::LogoMetadataToString(const LogoMetadata& metadata,
  284. int num_bytes,
  285. int dark_num_bytes,
  286. std::string* str) {
  287. base::DictionaryValue dict;
  288. dict.SetStringKey(kSourceUrlKey, metadata.source_url.spec());
  289. dict.SetStringKey(kFingerprintKey, metadata.fingerprint);
  290. dict.SetStringKey(kTypeKey, LogoTypeToString(metadata.type));
  291. dict.SetStringKey(kOnClickURLKey, metadata.on_click_url.spec());
  292. dict.SetStringKey(kFullPageURLKey, metadata.full_page_url.spec());
  293. dict.SetStringKey(kAltTextKey, metadata.alt_text);
  294. dict.SetStringKey(kAnimatedUrlKey, metadata.animated_url.spec());
  295. dict.SetStringKey(kDarkAnimatedUrlKey, metadata.dark_animated_url.spec());
  296. dict.SetStringKey(kLogUrlKey, metadata.log_url.spec());
  297. dict.SetStringKey(kDarkLogUrlKey, metadata.dark_log_url.spec());
  298. dict.SetStringKey(kCtaLogUrlKey, metadata.cta_log_url.spec());
  299. dict.SetStringKey(kDarkCtaLogUrlKey, metadata.dark_cta_log_url.spec());
  300. dict.SetStringKey(kShortLinkKey, metadata.short_link.spec());
  301. dict.SetStringKey(kMimeTypeKey, metadata.mime_type);
  302. dict.SetStringKey(kDarkMimeTypeKey, metadata.dark_mime_type);
  303. dict.SetBoolKey(kCanShowAfterExpirationKey,
  304. metadata.can_show_after_expiration);
  305. dict.SetIntKey(kNumBytesKey, num_bytes);
  306. dict.SetIntKey(kDarkNumBytesKey, dark_num_bytes);
  307. dict.SetIntKey(kShareButtonX, metadata.share_button_x);
  308. dict.SetIntKey(kShareButtonY, metadata.share_button_y);
  309. dict.SetDoubleKey(kShareButtonOpacity, metadata.share_button_opacity);
  310. dict.SetStringKey(kShareButtonIcon, metadata.share_button_icon);
  311. dict.SetStringKey(kShareButtonBg, metadata.share_button_bg);
  312. dict.SetIntKey(kDarkShareButtonX, metadata.dark_share_button_x);
  313. dict.SetIntKey(kDarkShareButtonY, metadata.dark_share_button_y);
  314. dict.SetDoubleKey(kDarkShareButtonOpacity,
  315. metadata.dark_share_button_opacity);
  316. dict.SetStringKey(kDarkShareButtonIcon, metadata.dark_share_button_icon);
  317. dict.SetStringKey(kDarkShareButtonBg, metadata.dark_share_button_bg);
  318. dict.SetIntKey(kWidthPx, metadata.width_px);
  319. dict.SetIntKey(kHeightPx, metadata.height_px);
  320. dict.SetIntKey(kDarkWidthPx, metadata.dark_width_px);
  321. dict.SetIntKey(kDarkHeightPx, metadata.dark_height_px);
  322. dict.SetIntKey(kIframeWidthPx, metadata.iframe_width_px);
  323. dict.SetIntKey(kIframeHeightPx, metadata.iframe_height_px);
  324. dict.SetStringKey(kDarkBackgroundColorKey, metadata.dark_background_color);
  325. SetTimeValue(dict, kExpirationTimeKey, metadata.expiration_time);
  326. base::JSONWriter::Write(dict, str);
  327. }
  328. base::FilePath LogoCache::GetLogoPath() {
  329. return cache_directory_.Append(FILE_PATH_LITERAL("logo"));
  330. }
  331. base::FilePath LogoCache::GetDarkLogoPath() {
  332. return cache_directory_.Append(FILE_PATH_LITERAL("dark_logo"));
  333. }
  334. base::FilePath LogoCache::GetMetadataPath() {
  335. return cache_directory_.Append(FILE_PATH_LITERAL("metadata"));
  336. }
  337. void LogoCache::UpdateMetadata(std::unique_ptr<LogoMetadata> metadata) {
  338. metadata_ = std::move(metadata);
  339. metadata_is_valid_ = true;
  340. }
  341. void LogoCache::ReadMetadataIfNeeded() {
  342. if (metadata_is_valid_)
  343. return;
  344. std::unique_ptr<LogoMetadata> metadata;
  345. base::FilePath metadata_path = GetMetadataPath();
  346. std::string str;
  347. if (base::ReadFileToString(metadata_path, &str)) {
  348. metadata =
  349. LogoMetadataFromString(str, &logo_num_bytes_, &dark_logo_num_bytes_);
  350. if (!metadata) {
  351. // Delete corrupt metadata and logo.
  352. DeleteLogoAndMetadata();
  353. }
  354. }
  355. UpdateMetadata(std::move(metadata));
  356. }
  357. void LogoCache::WriteMetadata() {
  358. if (!EnsureCacheDirectoryExists())
  359. return;
  360. std::string str;
  361. LogoMetadataToString(*metadata_, logo_num_bytes_, dark_logo_num_bytes_, &str);
  362. base::WriteFile(GetMetadataPath(), str.data(), static_cast<int>(str.size()));
  363. }
  364. void LogoCache::WriteLogo(
  365. scoped_refptr<base::RefCountedMemory> encoded_image,
  366. scoped_refptr<base::RefCountedMemory> dark_encoded_image) {
  367. if (!EnsureCacheDirectoryExists())
  368. return;
  369. if (!metadata_) {
  370. DeleteLogoAndMetadata();
  371. return;
  372. }
  373. // To minimize the chances of ending up in an undetectably broken state:
  374. // First, delete the metadata file, then update the logo file, then update the
  375. // metadata file.
  376. base::FilePath logo_path = GetLogoPath();
  377. base::FilePath dark_logo_path = GetDarkLogoPath();
  378. base::FilePath metadata_path = GetMetadataPath();
  379. if (!base::DeleteFile(metadata_path))
  380. return;
  381. if (encoded_image &&
  382. base::WriteFile(logo_path, encoded_image->front_as<char>(),
  383. static_cast<int>(encoded_image->size())) == -1) {
  384. base::DeleteFile(logo_path);
  385. return;
  386. }
  387. if (dark_encoded_image &&
  388. base::WriteFile(dark_logo_path, dark_encoded_image->front_as<char>(),
  389. static_cast<int>(dark_encoded_image->size())) == -1) {
  390. base::DeleteFile(logo_path);
  391. base::DeleteFile(dark_logo_path);
  392. return;
  393. }
  394. WriteMetadata();
  395. }
  396. void LogoCache::DeleteLogoAndMetadata() {
  397. base::DeleteFile(GetLogoPath());
  398. base::DeleteFile(GetDarkLogoPath());
  399. base::DeleteFile(GetMetadataPath());
  400. }
  401. bool LogoCache::EnsureCacheDirectoryExists() {
  402. if (base::DirectoryExists(cache_directory_))
  403. return true;
  404. return base::CreateDirectory(cache_directory_);
  405. }
  406. } // namespace search_provider_logos