Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / nacl / renderer / json_manifest.cc
blob1043316f6097698d07a7350feabc239e76565eaa
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/nacl/renderer/json_manifest.h"
7 #include <set>
9 #include "base/json/json_reader.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "components/nacl/common/nacl_types.h"
13 #include "components/nacl/renderer/nexe_load_manager.h"
14 #include "url/gurl.h"
16 namespace nacl {
18 namespace {
19 // Top-level section name keys
20 const char* const kProgramKey = "program";
21 const char* const kInterpreterKey = "interpreter";
22 const char* const kFilesKey = "files";
24 // ISA Dictionary keys
25 const char* const kX8632Key = "x86-32";
26 const char* const kX8632NonSFIKey = "x86-32-nonsfi";
27 const char* const kX8664Key = "x86-64";
28 const char* const kX8664NonSFIKey = "x86-64-nonsfi";
29 const char* const kArmKey = "arm";
30 const char* const kArmNonSFIKey = "arm-nonsfi";
31 const char* const kPortableKey = "portable";
33 // Url Resolution keys
34 const char* const kPnaclDebugKey = "pnacl-debug";
35 const char* const kPnaclTranslateKey = "pnacl-translate";
36 const char* const kUrlKey = "url";
38 // PNaCl keys
39 const char* const kOptLevelKey = "optlevel";
41 // Sample NaCl manifest file:
42 // {
43 // "program": {
44 // "x86-32": {"url": "myprogram_x86-32.nexe"},
45 // "x86-64": {"url": "myprogram_x86-64.nexe"},
46 // "arm": {"url": "myprogram_arm.nexe"}
47 // },
48 // "interpreter": {
49 // "x86-32": {"url": "interpreter_x86-32.nexe"},
50 // "x86-64": {"url": "interpreter_x86-64.nexe"},
51 // "arm": {"url": "interpreter_arm.nexe"}
52 // },
53 // "files": {
54 // "foo.txt": {
55 // "portable": {"url": "foo.txt"}
56 // },
57 // "bar.txt": {
58 // "x86-32": {"url": "x86-32/bar.txt"},
59 // "portable": {"url": "bar.txt"}
60 // },
61 // "libfoo.so": {
62 // "x86-64" : { "url": "..." }
63 // }
64 // }
65 // }
67 // Sample PNaCl manifest file:
68 // {
69 // "program": {
70 // "portable": {
71 // "pnacl-translate": {
72 // "url": "myprogram.pexe"
73 // },
74 // "pnacl-debug": {
75 // "url": "myprogram.debug.pexe",
76 // "opt_level": 0
77 // }
78 // }
79 // },
80 // "files": {
81 // "foo.txt": {
82 // "portable": {"url": "foo.txt"}
83 // },
84 // "bar.txt": {
85 // "portable": {"url": "bar.txt"}
86 // }
87 // }
88 // }
90 // Returns the key for the architecture in non-SFI mode.
91 std::string GetNonSFIKey(const std::string& sandbox_isa) {
92 return sandbox_isa + "-nonsfi";
95 // Looks up |property_name| in the vector |valid_names| with length
96 // |valid_name_count|. Returns true if |property_name| is found.
97 bool FindMatchingProperty(const std::string& property_name,
98 const char** valid_names,
99 size_t valid_name_count) {
100 for (size_t i = 0; i < valid_name_count; ++i) {
101 if (property_name == valid_names[i]) {
102 return true;
105 return false;
108 // Return true if this is a valid dictionary. Having only keys present in
109 // |valid_keys| and having at least the keys in |required_keys|.
110 // Error messages will be placed in |error_string|, given that the dictionary
111 // was the property value of |container_key|.
112 // E.g., "container_key" : dictionary
113 bool IsValidDictionary(const base::DictionaryValue& dictionary,
114 const std::string& container_key,
115 const std::string& parent_key,
116 const char** valid_keys,
117 size_t valid_key_count,
118 const char** required_keys,
119 size_t required_key_count,
120 std::string* error_string) {
121 // Check for unknown dictionary members.
122 for (base::DictionaryValue::Iterator it(dictionary); !it.IsAtEnd();
123 it.Advance()) {
124 const std::string& property_name = it.key();
125 if (!FindMatchingProperty(property_name,
126 valid_keys,
127 valid_key_count)) {
128 // For forward compatibility, we do not prohibit other keys being in
129 // the dictionary.
130 VLOG(1) << "WARNING: '" << parent_key << "' property '"
131 << container_key << "' has unknown key '"
132 << property_name << "'.";
135 // Check for required members.
136 for (size_t i = 0; i < required_key_count; ++i) {
137 if (!dictionary.HasKey(required_keys[i])) {
138 std::stringstream error_stream;
139 error_stream << parent_key << " property '" << container_key
140 << "' does not have required key: '"
141 << required_keys[i] << "'.";
142 *error_string = error_stream.str();
143 return false;
146 return true;
149 // Validate a "url" dictionary assuming it was resolved from container_key.
150 // E.g., "container_key" : { "url": "foo.txt" }
151 bool IsValidUrlSpec(const base::Value& url_spec,
152 const std::string& container_key,
153 const std::string& parent_key,
154 const std::string& sandbox_isa,
155 std::string* error_string) {
156 const base::DictionaryValue* url_dict = nullptr;
157 if (!url_spec.GetAsDictionary(&url_dict)) {
158 std::stringstream error_stream;
159 error_stream << parent_key << " property '" << container_key
160 << "' is non-dictionary value '" << url_spec << "'.";
161 *error_string = error_stream.str();
162 return false;
164 static const char* kManifestUrlSpecRequired[] = {
165 kUrlKey
167 const char** urlSpecPlusOptional;
168 size_t urlSpecPlusOptionalLength;
169 if (sandbox_isa == kPortableKey) {
170 static const char* kPnaclUrlSpecPlusOptional[] = {
171 kUrlKey,
172 kOptLevelKey,
174 urlSpecPlusOptional = kPnaclUrlSpecPlusOptional;
175 urlSpecPlusOptionalLength = arraysize(kPnaclUrlSpecPlusOptional);
176 } else {
177 // URL specifications must not contain "pnacl-translate" keys.
178 // This prohibits NaCl clients from invoking PNaCl.
179 if (url_dict->HasKey(kPnaclTranslateKey)) {
180 std::stringstream error_stream;
181 error_stream << "PNaCl-like NMF with application/x-nacl mimetype instead "
182 << "of x-pnacl mimetype (has " << kPnaclTranslateKey << ").";
183 *error_string = error_stream.str();
184 return false;
186 urlSpecPlusOptional = kManifestUrlSpecRequired;
187 urlSpecPlusOptionalLength = arraysize(kManifestUrlSpecRequired);
189 if (!IsValidDictionary(*url_dict, container_key, parent_key,
190 urlSpecPlusOptional, urlSpecPlusOptionalLength,
191 kManifestUrlSpecRequired,
192 arraysize(kManifestUrlSpecRequired), error_string)) {
193 return false;
195 // Verify the correct types of the fields if they exist.
196 const base::Value* url = nullptr;
197 // URL was already verified above by IsValidDictionary to be required.
198 url_dict->GetWithoutPathExpansion(kUrlKey, &url);
199 DCHECK(url);
200 if (!url->IsType(base::Value::TYPE_STRING)) {
201 std::stringstream error_stream;
202 error_stream << parent_key << " property '" << container_key
203 << "' has non-string value '" << *url << "' for key '"
204 << kUrlKey << "'.";
205 *error_string = error_stream.str();
206 return false;
208 if (url_dict->HasKey(kOptLevelKey)) {
209 const base::Value* opt_level = nullptr;
210 url_dict->GetWithoutPathExpansion(kOptLevelKey, &opt_level);
211 DCHECK(opt_level);
212 if (!opt_level->IsType(base::Value::TYPE_INTEGER)) {
213 std::stringstream error_stream;
214 error_stream << parent_key << " property '" << container_key
215 << "' has non-numeric value '" << *opt_level << "' for key '"
216 << kOptLevelKey << "'.";
217 *error_string = error_stream.str();
218 return false;
221 return true;
224 // Validate a "pnacl-translate" or "pnacl-debug" dictionary, assuming
225 // it was resolved from container_key.
226 // E.g., "container_key" : { "pnacl-translate" : URLSpec }
227 bool IsValidPnaclTranslateSpec(const base::Value& pnacl_spec,
228 const std::string& container_key,
229 const std::string& parent_key,
230 const std::string& sandbox_isa,
231 std::string* error_string) {
232 static const char* kManifestPnaclSpecValid[] = {
233 kPnaclDebugKey,
234 kPnaclTranslateKey
236 static const char* kManifestPnaclSpecRequired[] = { kPnaclTranslateKey };
237 const base::DictionaryValue* pnacl_dict = nullptr;
238 if (!pnacl_spec.GetAsDictionary(&pnacl_dict)) {
239 std::stringstream error_stream;
240 error_stream << parent_key << " property '" << container_key
241 << "' is non-dictionary value '" << pnacl_spec << "'.";
242 *error_string = error_stream.str();
243 return false;
246 if (!IsValidDictionary(
247 *pnacl_dict, container_key, parent_key, kManifestPnaclSpecValid,
248 arraysize(kManifestPnaclSpecValid), kManifestPnaclSpecRequired,
249 arraysize(kManifestPnaclSpecRequired), error_string)) {
250 return false;
252 // kPnaclTranslateKey checked to be required above.
253 const base::Value* url_spec = nullptr;
254 pnacl_dict->GetWithoutPathExpansion(kPnaclTranslateKey, &url_spec);
255 DCHECK(url_spec);
256 return IsValidUrlSpec(*url_spec, kPnaclTranslateKey, container_key,
257 sandbox_isa, error_string);
260 // Validates that parent_dictionary[parent_key] is a valid ISA dictionary.
261 // An ISA dictionary is validated to have keys from within the set of
262 // recognized ISAs. Unknown ISAs are allowed, but ignored and warnings
263 // are produced. It is also validated that it must have an entry to match the
264 // ISA specified in |sandbox_isa| or have a fallback 'portable' entry if
265 // there is no match. Returns true if parent_dictionary[parent_key] is an
266 // ISA to URL map. Sets |error_info| to something descriptive if it fails.
267 bool IsValidISADictionary(const base::DictionaryValue& parent_dictionary,
268 const std::string& parent_key,
269 const std::string& sandbox_isa,
270 bool must_find_matching_entry,
271 bool nonsfi_enabled,
272 JsonManifest::ErrorInfo* error_info) {
273 const base::DictionaryValue* dictionary = nullptr;
274 if (!parent_dictionary.GetDictionaryWithoutPathExpansion(parent_key,
275 &dictionary)) {
276 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
277 error_info->string = std::string("manifest: ") + parent_key +
278 " property is not an ISA to URL dictionary";
279 return false;
281 // Build the set of reserved ISA dictionary keys.
282 const char** isaProperties;
283 size_t isaPropertiesLength;
284 if (sandbox_isa == kPortableKey) {
285 // The known values for PNaCl ISA dictionaries in the manifest.
286 static const char* kPnaclManifestISAProperties[] = {
287 kPortableKey
289 isaProperties = kPnaclManifestISAProperties;
290 isaPropertiesLength = arraysize(kPnaclManifestISAProperties);
291 } else {
292 // The known values for NaCl ISA dictionaries in the manifest.
293 static const char* kNaClManifestISAProperties[] = {
294 kX8632Key,
295 kX8632NonSFIKey,
296 kX8664Key,
297 kX8664NonSFIKey,
298 kArmKey,
299 kArmNonSFIKey,
300 // "portable" is here to allow checking that, if present, it can
301 // only refer to an URL, such as for a data file, and not to
302 // "pnacl-translate", which would cause the creation of a nexe.
303 kPortableKey
305 isaProperties = kNaClManifestISAProperties;
306 isaPropertiesLength = arraysize(kNaClManifestISAProperties);
308 // Check that entries in the dictionary are structurally correct.
309 for (base::DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd();
310 it.Advance()) {
311 const std::string& property_name = it.key();
312 const base::Value& property_value = it.value();
313 std::string error_string;
314 if (FindMatchingProperty(property_name,
315 isaProperties,
316 isaPropertiesLength)) {
317 // For NaCl, arch entries can only be
318 // "arch/portable" : URLSpec
319 // For PNaCl arch in "program" dictionary entries can be
320 // "portable" : { "pnacl-translate": URLSpec }
321 // or "portable" : { "pnacl-debug": URLSpec }
322 // For PNaCl arch elsewhere, dictionary entries can only be
323 // "portable" : URLSpec
324 if ((sandbox_isa != kPortableKey &&
325 !IsValidUrlSpec(property_value, property_name, parent_key,
326 sandbox_isa, &error_string)) ||
327 (sandbox_isa == kPortableKey &&
328 parent_key == kProgramKey &&
329 !IsValidPnaclTranslateSpec(property_value, property_name, parent_key,
330 sandbox_isa, &error_string)) ||
331 (sandbox_isa == kPortableKey &&
332 parent_key != kProgramKey &&
333 !IsValidUrlSpec(property_value, property_name, parent_key,
334 sandbox_isa, &error_string))) {
335 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
336 error_info->string = "manifest: " + error_string;
337 return false;
339 } else {
340 // For forward compatibility, we do not prohibit other keys being in
341 // the dictionary, as they may be architectures supported in later
342 // versions. However, the value of these entries must be an URLSpec.
343 VLOG(1) << "IsValidISADictionary: unrecognized key '"
344 << property_name << "'.";
345 if (!IsValidUrlSpec(property_value, property_name, parent_key,
346 sandbox_isa, &error_string)) {
347 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
348 error_info->string = "manifest: " + error_string;
349 return false;
354 if (sandbox_isa == kPortableKey) {
355 if (!dictionary->HasKey(kPortableKey)) {
356 error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH;
357 error_info->string = "manifest: no version of " + parent_key +
358 " given for portable.";
359 return false;
361 } else if (must_find_matching_entry) {
362 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include
363 // micro-architectures that can resolve to multiple valid sandboxes.
364 bool has_isa = dictionary->HasKey(sandbox_isa);
365 bool has_nonsfi_isa =
366 nonsfi_enabled && dictionary->HasKey(GetNonSFIKey(sandbox_isa));
367 bool has_portable = dictionary->HasKey(kPortableKey);
369 if (!has_isa && !has_nonsfi_isa && !has_portable) {
370 error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH;
371 error_info->string = "manifest: no version of " + parent_key +
372 " given for current arch and no portable version found.";
373 return false;
376 return true;
379 void GrabUrlAndPnaclOptions(const base::DictionaryValue& url_spec,
380 std::string* url,
381 PP_PNaClOptions* pnacl_options) {
382 // url_spec should have been validated as a first pass.
383 bool get_url_success = url_spec.GetStringWithoutPathExpansion(kUrlKey, url);
384 DCHECK(get_url_success);
385 pnacl_options->translate = PP_TRUE;
386 if (url_spec.HasKey(kOptLevelKey)) {
387 int32_t opt_raw = 0;
388 bool get_opt_success =
389 url_spec.GetIntegerWithoutPathExpansion(kOptLevelKey, &opt_raw);
390 DCHECK(get_opt_success);
391 // Currently only allow 0 or 2, since that is what we test.
392 if (opt_raw <= 0)
393 pnacl_options->opt_level = 0;
394 else
395 pnacl_options->opt_level = 2;
399 } // namespace
401 JsonManifest::JsonManifest(const std::string& manifest_base_url,
402 const std::string& sandbox_isa,
403 bool nonsfi_enabled,
404 bool pnacl_debug)
405 : manifest_base_url_(manifest_base_url),
406 sandbox_isa_(sandbox_isa),
407 nonsfi_enabled_(nonsfi_enabled),
408 pnacl_debug_(pnacl_debug) { }
410 JsonManifest::~JsonManifest() {}
412 bool JsonManifest::Init(const std::string& manifest_json_data,
413 ErrorInfo* error_info) {
414 CHECK(error_info);
416 base::JSONReader json_reader;
417 int json_read_error_code;
418 std::string json_read_error_msg;
419 scoped_ptr<base::Value> json_data(json_reader.ReadAndReturnError(
420 manifest_json_data, base::JSON_PARSE_RFC, &json_read_error_code,
421 &json_read_error_msg));
422 if (!json_data) {
423 error_info->error = PP_NACL_ERROR_MANIFEST_PARSING;
424 error_info->string =
425 std::string("manifest JSON parsing failed: ") + json_read_error_msg;
426 return false;
428 // Ensure it's actually a dictionary before capturing as dictionary_.
429 base::DictionaryValue* json_dict = nullptr;
430 if (!json_data->GetAsDictionary(&json_dict)) {
431 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
432 error_info->string = "manifest: is not a json dictionary.";
433 return false;
435 // Can't quite use json_data.swap(dictionary_), since the types are different
436 // so do this kludgy manual swap.
437 DCHECK(json_dict == json_data.get());
438 dictionary_.reset(static_cast<base::DictionaryValue*>(json_data.release()));
439 // Parse has ensured the string was valid JSON. Check that it matches the
440 // manifest schema.
441 return MatchesSchema(error_info);
444 bool JsonManifest::GetProgramURL(std::string* full_url,
445 PP_PNaClOptions* pnacl_options,
446 bool* uses_nonsfi_mode,
447 ErrorInfo* error_info) const {
448 if (!full_url)
449 return false;
450 CHECK(pnacl_options);
451 CHECK(uses_nonsfi_mode);
452 CHECK(error_info);
454 std::string nexe_url;
455 if (!GetURLFromISADictionary(*dictionary_, kProgramKey, &nexe_url,
456 pnacl_options, uses_nonsfi_mode, error_info)) {
457 return false;
460 // The contents of the manifest are resolved relative to the manifest URL.
461 GURL base_gurl(manifest_base_url_);
462 if (!base_gurl.is_valid())
463 return false;
465 GURL resolved_gurl = base_gurl.Resolve(nexe_url);
466 if (!resolved_gurl.is_valid()) {
467 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
468 error_info->string =
469 "could not resolve url '" + nexe_url +
470 "' relative to manifest base url '" + manifest_base_url_.c_str() +
471 "'.";
472 return false;
474 *full_url = resolved_gurl.possibly_invalid_spec();
475 return true;
478 void JsonManifest::GetPrefetchableFiles(
479 std::vector<NaClResourcePrefetchRequest>* out_files) const {
480 const base::DictionaryValue* files_dict;
481 if (!dictionary_->GetDictionaryWithoutPathExpansion(kFilesKey, &files_dict))
482 return;
484 for (base::DictionaryValue::Iterator it(*files_dict); !it.IsAtEnd();
485 it.Advance()) {
486 std::string full_url;
487 PP_PNaClOptions unused_pnacl_options; // pnacl does not support "files".
488 const std::string& file_key = it.key();
489 // We skip invalid entries in "files".
490 if (GetKeyUrl(*files_dict, file_key, &full_url, &unused_pnacl_options)) {
491 if (GURL(full_url).SchemeIs("chrome-extension"))
492 out_files->push_back(NaClResourcePrefetchRequest(file_key, full_url));
497 bool JsonManifest::ResolveKey(const std::string& key,
498 std::string* full_url,
499 PP_PNaClOptions* pnacl_options) const {
500 if (full_url == NULL || pnacl_options == NULL)
501 return false;
503 const base::DictionaryValue* files_dict;
504 if (!dictionary_->GetDictionaryWithoutPathExpansion(kFilesKey, &files_dict)) {
505 VLOG(1) << "ResolveKey failed: no \"files\" dictionary";
506 return false;
509 if (!files_dict->HasKey(key)) {
510 VLOG(1) << "ResolveKey failed: no such \"files\" entry: " << key;
511 return false;
513 return GetKeyUrl(*files_dict, key, full_url, pnacl_options);
516 bool JsonManifest::MatchesSchema(ErrorInfo* error_info) {
517 // The top level dictionary entries valid in the manifest file.
518 static const char* kManifestTopLevelProperties[] = {
519 kProgramKey, kInterpreterKey, kFilesKey};
520 for (base::DictionaryValue::Iterator it(*dictionary_); !it.IsAtEnd();
521 it.Advance()) {
522 const std::string& property_name = it.key();
523 if (!FindMatchingProperty(property_name,
524 kManifestTopLevelProperties,
525 arraysize(kManifestTopLevelProperties))) {
526 VLOG(1) << "JsonManifest::MatchesSchema: WARNING: unknown top-level "
527 << "section '" << property_name << "' in manifest.";
531 // A manifest file must have a program section.
532 if (!dictionary_->HasKey(kProgramKey)) {
533 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
534 error_info->string = std::string("manifest: missing '") + kProgramKey +
535 "' section.";
536 return false;
539 // Validate the program section.
540 // There must be a matching (portable or sandbox_isa_) entry for program for
541 // NaCl.
542 if (!IsValidISADictionary(*dictionary_, kProgramKey, sandbox_isa_, true,
543 nonsfi_enabled_, error_info)) {
544 return false;
547 // Validate the interpreter section (if given).
548 // There must be a matching (portable or sandbox_isa_) entry for interpreter
549 // for NaCl.
550 if (dictionary_->HasKey(kInterpreterKey)) {
551 if (!IsValidISADictionary(*dictionary_, kInterpreterKey, sandbox_isa_, true,
552 nonsfi_enabled_, error_info)) {
553 return false;
557 // Validate the file dictionary (if given).
558 // The "files" key does not require a matching (portable or sandbox_isa_)
559 // entry at schema validation time for NaCl. This allows manifests to
560 // specify resources that are only loaded for a particular sandbox_isa.
561 if (dictionary_->HasKey(kFilesKey)) {
562 const base::DictionaryValue* files_dictionary = nullptr;
563 if (!dictionary_->GetDictionaryWithoutPathExpansion(kFilesKey,
564 &files_dictionary)) {
565 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
566 error_info->string = std::string("manifest: '") + kFilesKey +
567 "' is not a dictionary.";
569 for (base::DictionaryValue::Iterator it(*files_dictionary); !it.IsAtEnd();
570 it.Advance()) {
571 const std::string& file_name = it.key();
572 if (!IsValidISADictionary(*files_dictionary, file_name, sandbox_isa_,
573 false, nonsfi_enabled_, error_info)) {
574 return false;
578 return true;
581 bool JsonManifest::GetKeyUrl(const base::DictionaryValue& dictionary,
582 const std::string& key,
583 std::string* full_url,
584 PP_PNaClOptions* pnacl_options) const {
585 DCHECK(full_url && pnacl_options);
586 if (!dictionary.HasKey(key)) {
587 VLOG(1) << "GetKeyUrl failed: file " << key << " not found in manifest.";
588 return false;
590 std::string relative_url;
591 bool uses_nonsfi_mode;
592 ErrorInfo ignored_error_info;
593 if (!GetURLFromISADictionary(dictionary, key, &relative_url, pnacl_options,
594 &uses_nonsfi_mode, &ignored_error_info))
595 return false;
597 // The contents of the manifest are resolved relative to the manifest URL.
598 GURL base_gurl(manifest_base_url_);
599 if (!base_gurl.is_valid())
600 return false;
601 GURL resolved_gurl = base_gurl.Resolve(relative_url);
602 if (!resolved_gurl.is_valid())
603 return false;
604 *full_url = resolved_gurl.possibly_invalid_spec();
605 return true;
608 bool JsonManifest::GetURLFromISADictionary(
609 const base::DictionaryValue& parent_dictionary,
610 const std::string& parent_key,
611 std::string* url,
612 PP_PNaClOptions* pnacl_options,
613 bool* uses_nonsfi_mode,
614 ErrorInfo* error_info) const {
615 DCHECK(url && pnacl_options && error_info);
617 const base::DictionaryValue* dictionary = nullptr;
618 if (!parent_dictionary.GetDictionaryWithoutPathExpansion(parent_key,
619 &dictionary)) {
620 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
621 error_info->string = std::string("GetURLFromISADictionary failed: ") +
622 parent_key + "'s value is not a json dictionary.";
623 return false;
626 // When the application actually requests a resolved URL, we must have
627 // a matching entry (sandbox_isa_ or portable) for NaCl.
628 ErrorInfo ignored_error_info;
629 if (!IsValidISADictionary(parent_dictionary, parent_key, sandbox_isa_, true,
630 nonsfi_enabled_, &ignored_error_info)) {
631 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
632 error_info->string = "architecture " + sandbox_isa_ +
633 " is not found for file " + parent_key;
634 return false;
637 // The call to IsValidISADictionary() above guarantees that either
638 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the
639 // dictionary.
640 *uses_nonsfi_mode = false;
641 std::string chosen_isa;
642 if (sandbox_isa_ == kPortableKey) {
643 chosen_isa = kPortableKey;
644 } else {
645 std::string nonsfi_isa = GetNonSFIKey(sandbox_isa_);
646 if (nonsfi_enabled_ && dictionary->HasKey(nonsfi_isa)) {
647 chosen_isa = nonsfi_isa;
648 *uses_nonsfi_mode = true;
649 } else if (dictionary->HasKey(sandbox_isa_)) {
650 chosen_isa = sandbox_isa_;
651 } else if (dictionary->HasKey(kPortableKey)) {
652 chosen_isa = kPortableKey;
653 } else {
654 // Should not reach here, because the earlier IsValidISADictionary()
655 // call checked that the manifest covers the current architecture.
656 NOTREACHED();
657 return false;
661 const base::DictionaryValue* isa_spec = nullptr;
662 if (!dictionary->GetDictionaryWithoutPathExpansion(chosen_isa, &isa_spec)) {
663 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
664 error_info->string = std::string("GetURLFromISADictionary failed: ") +
665 chosen_isa + "'s value is not a json dictionary.";
666 return false;
668 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first.
669 // If found, mark that it is a debug URL. Otherwise, fall back to
670 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL.
671 if (pnacl_debug_ && isa_spec->HasKey(kPnaclDebugKey)) {
672 const base::DictionaryValue* pnacl_dict = nullptr;
673 if (!isa_spec->GetDictionaryWithoutPathExpansion(kPnaclDebugKey,
674 &pnacl_dict)) {
675 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
676 error_info->string = std::string("GetURLFromISADictionary failed: ") +
677 kPnaclDebugKey +
678 "'s value is not a json dictionary.";
679 return false;
681 GrabUrlAndPnaclOptions(*pnacl_dict, url, pnacl_options);
682 pnacl_options->is_debug = PP_TRUE;
683 } else if (isa_spec->HasKey(kPnaclTranslateKey)) {
684 const base::DictionaryValue* pnacl_dict = nullptr;
685 if (!isa_spec->GetDictionaryWithoutPathExpansion(kPnaclTranslateKey,
686 &pnacl_dict)) {
687 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
688 error_info->string = std::string("GetURLFromISADictionary failed: ") +
689 kPnaclTranslateKey +
690 "'s value is not a json dictionary.";
691 return false;
693 GrabUrlAndPnaclOptions(*pnacl_dict, url, pnacl_options);
694 } else {
695 // The native NaCl case.
696 if (!isa_spec->GetStringWithoutPathExpansion(kUrlKey, url)) {
697 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
698 error_info->string = std::string("GetURLFromISADictionary failed: ") +
699 kUrlKey + "'s value is not a string.";
700 return false;
702 pnacl_options->translate = PP_FALSE;
705 return true;
708 } // namespace nacl