Roll src/third_party/WebKit b944946:201fd41 (svn 192400:192407)
[chromium-blink-merge.git] / device / nfc / nfc_ndef_record.cc
blob5b8bb994a81b53a446333417f04e886e9470f4f3
1 // Copyright 2013 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 "device/nfc/nfc_ndef_record.h"
7 #include <map>
9 #include "base/logging.h"
10 #include "url/gurl.h"
12 using base::DictionaryValue;
13 using base::ListValue;
15 namespace device {
17 namespace {
19 typedef std::map<std::string, base::Value::Type> FieldValueMap;
21 bool ValidateURI(const DictionaryValue* data) {
22 std::string uri;
23 if (!data->GetString(NfcNdefRecord::kFieldURI, &uri)) {
24 VLOG(1) << "No URI entry in data.";
25 return false;
27 DCHECK(!uri.empty());
29 // Use GURL to check validity.
30 GURL url(uri);
31 if (!url.is_valid()) {
32 LOG(ERROR) << "Invalid URI given: " << uri;
33 return false;
35 return true;
38 bool CheckFieldsAreValid(
39 const FieldValueMap& required_fields,
40 const FieldValueMap& optional_fields,
41 const DictionaryValue* data) {
42 size_t required_count = 0;
43 for (DictionaryValue::Iterator iter(*data);
44 !iter.IsAtEnd(); iter.Advance()) {
45 FieldValueMap::const_iterator field_iter =
46 required_fields.find(iter.key());
47 if (field_iter == required_fields.end()) {
48 // Field wasn't one of the required fields. Check if optional.
49 field_iter = optional_fields.find(iter.key());
51 if (field_iter == optional_fields.end()) {
52 // If the field isn't one of the optional fields either, then it's
53 // invalid.
54 VLOG(1) << "Tried to populate record with invalid field: "
55 << iter.key();
56 return false;
58 } else {
59 required_count++;
61 // The field is invalid, if the type of its value is incorrect.
62 if (field_iter->second != iter.value().GetType()) {
63 VLOG(1) << "Provided value for field \"" << iter.key() << "\" has type "
64 << iter.value().GetType() << ", expected: "
65 << field_iter->second;
66 return false;
68 // Make sure that the value is non-empty, if the value is a string.
69 std::string string_value;
70 if (iter.value().GetAsString(&string_value) && string_value.empty()) {
71 VLOG(1) << "Empty value given for field of type string: " << iter.key();
72 return false;
75 // Check for required fields.
76 if (required_count != required_fields.size()) {
77 VLOG(1) << "Provided data did not contain all required fields for "
78 << "requested NDEF type.";
79 return false;
81 return true;
84 // Verifies that the contents of |data| conform to the fields of NDEF type
85 // "Text".
86 bool HandleTypeText(const DictionaryValue* data) {
87 VLOG(1) << "Populating record with type \"Text\".";
88 FieldValueMap required_fields;
89 required_fields[NfcNdefRecord::kFieldText] = base::Value::TYPE_STRING;
90 required_fields[NfcNdefRecord::kFieldEncoding] = base::Value::TYPE_STRING;
91 required_fields[NfcNdefRecord::kFieldLanguageCode] = base::Value::TYPE_STRING;
92 FieldValueMap optional_fields;
93 if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
94 VLOG(1) << "Failed to populate record.";
95 return false;
98 // Verify that the "Encoding" property has valid values.
99 std::string encoding;
100 if (!data->GetString(NfcNdefRecord::kFieldEncoding, &encoding)) {
101 if (encoding != NfcNdefRecord::kEncodingUtf8 ||
102 encoding != NfcNdefRecord::kEncodingUtf16) {
103 VLOG(1) << "Invalid \"Encoding\" value:" << encoding;
104 return false;
107 return true;
110 // Verifies that the contents of |data| conform to the fields of NDEF type
111 // "SmartPoster".
112 bool HandleTypeSmartPoster(const DictionaryValue* data) {
113 VLOG(1) << "Populating record with type \"SmartPoster\".";
114 FieldValueMap required_fields;
115 required_fields[NfcNdefRecord::kFieldURI] = base::Value::TYPE_STRING;
116 FieldValueMap optional_fields;
117 optional_fields[NfcNdefRecord::kFieldAction] = base::Value::TYPE_STRING;
118 optional_fields[NfcNdefRecord::kFieldMimeType] = base::Value::TYPE_STRING;
119 // base::Value restricts the number types to BOOL, INTEGER, and DOUBLE only.
120 // uint32 will automatically get converted to a double. "target size" is
121 // really a uint32 but we define it as a double for this reason.
122 // (See dbus/values_util.h).
123 optional_fields[NfcNdefRecord::kFieldTargetSize] = base::Value::TYPE_DOUBLE;
124 optional_fields[NfcNdefRecord::kFieldTitles] = base::Value::TYPE_LIST;
125 if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
126 VLOG(1) << "Failed to populate record.";
127 return false;
129 // Verify that the "titles" field was formatted correctly, if it exists.
130 const ListValue* titles = NULL;
131 if (data->GetList(NfcNdefRecord::kFieldTitles, &titles)) {
132 if (titles->empty()) {
133 VLOG(1) << "\"titles\" field of SmartPoster is empty.";
134 return false;
136 for (ListValue::const_iterator iter = titles->begin();
137 iter != titles->end(); ++iter) {
138 const DictionaryValue* title_data = NULL;
139 if (!(*iter)->GetAsDictionary(&title_data)) {
140 VLOG(1) << "\"title\" entry for SmartPoster contains an invalid value "
141 << "type";
142 return false;
144 if (!HandleTypeText(title_data)) {
145 VLOG(1) << "Badly formatted \"title\" entry for SmartPoster.";
146 return false;
150 return ValidateURI(data);
153 // Verifies that the contents of |data| conform to the fields of NDEF type
154 // "URI".
155 bool HandleTypeUri(const DictionaryValue* data) {
156 VLOG(1) << "Populating record with type \"URI\".";
157 FieldValueMap required_fields;
158 required_fields[NfcNdefRecord::kFieldURI] = base::Value::TYPE_STRING;
159 FieldValueMap optional_fields;
160 optional_fields[NfcNdefRecord::kFieldMimeType] = base::Value::TYPE_STRING;
161 optional_fields[NfcNdefRecord::kFieldTargetSize] = base::Value::TYPE_DOUBLE;
163 // Allow passing TargetSize as an integer, but convert it to a double.
164 if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
165 VLOG(1) << "Failed to populate record.";
166 return false;
168 return ValidateURI(data);
171 } // namespace
173 // static
174 const char NfcNdefRecord::kFieldEncoding[] = "encoding";
175 // static
176 const char NfcNdefRecord::kFieldLanguageCode[] = "languageCode";
177 // static
178 const char NfcNdefRecord::kFieldText[] = "text";
179 // static
180 const char NfcNdefRecord::kFieldURI[] = "uri";
181 // static
182 const char NfcNdefRecord::kFieldMimeType[] = "mimeType";
183 // static
184 const char NfcNdefRecord::kFieldTargetSize[] = "targetSize";
185 // static
186 const char NfcNdefRecord::kFieldTitles[] = "titles";
187 // static
188 const char NfcNdefRecord::kFieldAction[] = "action";
189 // static
190 const char NfcNdefRecord::kEncodingUtf8[] = "UTF-8";
191 // static
192 const char NfcNdefRecord::kEncodingUtf16[] = "UTF-16";
193 // static
194 const char NfcNdefRecord::kSmartPosterActionDo[] = "do";
195 // static
196 const char NfcNdefRecord::kSmartPosterActionSave[] = "save";
197 // static
198 const char NfcNdefRecord::kSmartPosterActionOpen[] = "open";
200 NfcNdefRecord::NfcNdefRecord() : type_(kTypeUnknown) {
203 NfcNdefRecord::~NfcNdefRecord() {
206 bool NfcNdefRecord::IsPopulated() const {
207 return type_ != kTypeUnknown;
210 bool NfcNdefRecord::Populate(Type type, const DictionaryValue* data) {
211 if (IsPopulated())
212 return false;
214 DCHECK(data_.empty());
216 // At this time, only "Text", "URI", and "SmartPoster" are supported.
217 bool result = false;
218 switch (type) {
219 case kTypeText:
220 result = HandleTypeText(data);
221 break;
222 case kTypeSmartPoster:
223 result = HandleTypeSmartPoster(data);
224 break;
225 case kTypeURI:
226 result = HandleTypeUri(data);
227 break;
228 default:
229 VLOG(1) << "Unsupported NDEF type: " << type;
230 break;
232 if (!result)
233 return false;
234 type_ = type;
235 data_.MergeDictionary(data);
236 return true;
239 NfcNdefMessage::NfcNdefMessage() {
242 NfcNdefMessage::~NfcNdefMessage() {
245 void NfcNdefMessage::AddRecord(NfcNdefRecord* record) {
246 records_.push_back(record);
249 bool NfcNdefMessage::RemoveRecord(NfcNdefRecord* record) {
250 for (RecordList::iterator iter = records_.begin();
251 iter != records_.end(); ++iter) {
252 if (*iter == record) {
253 records_.erase(iter);
254 return true;
257 return false;
260 } // namespace device