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.
5 #include "components/search_provider_logos/google_logo_api.h"
7 #include "base/base64.h"
8 #include "base/json/json_reader.h"
9 #include "base/memory/ref_counted_memory.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
13 namespace search_provider_logos
{
16 const char kResponsePreamble
[] = ")]}'";
19 GURL
GoogleAppendFingerprintToLogoURL(const GURL
& logo_url
,
20 const std::string
& fingerprint
) {
21 // Note: we can't just use net::AppendQueryParameter() because it escapes
22 // ":" to "%3A", but the server requires the colon not to be escaped.
23 // See: http://crbug.com/413845
25 // TODO(newt): Switch to using net::AppendQueryParameter once it no longer
28 std::string
query(logo_url
.query());
32 query
+= "async=es_dfp:";
34 GURL::Replacements replacements
;
35 replacements
.SetQueryStr(query
);
36 return logo_url
.ReplaceComponents(replacements
);
39 scoped_ptr
<EncodedLogo
> GoogleParseLogoResponse(
40 const scoped_ptr
<std::string
>& response
,
41 base::Time response_time
) {
42 // Google doodles are sent as JSON with a prefix. Example:
43 // )]}' {"update":{"logo":{
44 // "data": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/...",
45 // "mime_type": "image/png",
46 // "fingerprint": "db063e32",
47 // "target": "http://www.google.com.au/search?q=Wilbur+Christiansen",
48 // "alt": "Wilbur Christiansen's Birthday"
49 // "time_to_live": 1389304799
52 // The response may start with )]}'. Ignore this.
53 base::StringPiece
response_sp(*response
);
54 if (response_sp
.starts_with(kResponsePreamble
))
55 response_sp
.remove_prefix(strlen(kResponsePreamble
));
57 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response_sp
));
59 return scoped_ptr
<EncodedLogo
>();
61 // The important data lives inside several nested dictionaries:
62 // {"update": {"logo": { "mime_type": ..., etc } } }
63 const base::DictionaryValue
* outer_dict
;
64 if (!value
->GetAsDictionary(&outer_dict
))
65 return scoped_ptr
<EncodedLogo
>();
66 const base::DictionaryValue
* update_dict
;
67 if (!outer_dict
->GetDictionary("update", &update_dict
))
68 return scoped_ptr
<EncodedLogo
>();
69 const base::DictionaryValue
* logo_dict
;
70 if (!update_dict
->GetDictionary("logo", &logo_dict
))
71 return scoped_ptr
<EncodedLogo
>();
73 scoped_ptr
<EncodedLogo
> logo(new EncodedLogo());
75 std::string encoded_image_base64
;
76 if (logo_dict
->GetString("data", &encoded_image_base64
)) {
77 // Data is optional, since we may be revalidating a cached logo.
78 base::RefCountedString
* encoded_image_string
= new base::RefCountedString();
79 if (!base::Base64Decode(encoded_image_base64
,
80 &encoded_image_string
->data()))
81 return scoped_ptr
<EncodedLogo
>();
82 logo
->encoded_image
= encoded_image_string
;
83 if (!logo_dict
->GetString("mime_type", &logo
->metadata
.mime_type
))
84 return scoped_ptr
<EncodedLogo
>();
87 // Don't check return values since these fields are optional.
88 logo_dict
->GetString("target", &logo
->metadata
.on_click_url
);
89 logo_dict
->GetString("fingerprint", &logo
->metadata
.fingerprint
);
90 logo_dict
->GetString("alt", &logo
->metadata
.alt_text
);
92 base::TimeDelta time_to_live
;
94 if (logo_dict
->GetInteger("time_to_live", &time_to_live_ms
)) {
95 time_to_live
= base::TimeDelta::FromMilliseconds(
96 std::min(static_cast<int64
>(time_to_live_ms
), kMaxTimeToLiveMS
));
97 logo
->metadata
.can_show_after_expiration
= false;
99 time_to_live
= base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS
);
100 logo
->metadata
.can_show_after_expiration
= true;
102 logo
->metadata
.expiration_time
= response_time
+ time_to_live
;
107 } // namespace search_provider_logos