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 5 #include "components/search_provider_logos/google_logo_api.h" 6 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" 12 13 namespace search_provider_logos { 14 15 namespace { 16 const char kResponsePreamble[] = ")]}'"; 17 } 18 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 24 25 // TODO(newt): Switch to using net::AppendQueryParameter once it no longer 26 // escapes ":" 27 28 std::string query(logo_url.query()); 29 if (!query.empty()) 30 query += "&"; 31 32 query += "async=es_dfp:"; 33 query += fingerprint; 34 GURL::Replacements replacements; 35 replacements.SetQueryStr(query); 36 return logo_url.ReplaceComponents(replacements); 37 } 38 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 50 // }}} 51 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)); 56 57 scoped_ptr<base::Value> value(base::JSONReader::Read(response_sp)); 58 if (!value.get()) 59 return scoped_ptr<EncodedLogo>(); 60 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>(); 72 73 scoped_ptr<EncodedLogo> logo(new EncodedLogo()); 74 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>(); 85 } 86 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); 91 92 base::TimeDelta time_to_live; 93 int time_to_live_ms; 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; 98 } else { 99 time_to_live = base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS); 100 logo->metadata.can_show_after_expiration = true; 101 } 102 logo->metadata.expiration_time = response_time + time_to_live; 103 104 return logo.Pass(); 105 } 106 107 } // namespace search_provider_logos 108