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"
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"
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";
39 const char* const kOptLevelKey
= "optlevel";
41 // Sample NaCl manifest file:
44 // "x86-32": {"url": "myprogram_x86-32.nexe"},
45 // "x86-64": {"url": "myprogram_x86-64.nexe"},
46 // "arm": {"url": "myprogram_arm.nexe"}
49 // "x86-32": {"url": "interpreter_x86-32.nexe"},
50 // "x86-64": {"url": "interpreter_x86-64.nexe"},
51 // "arm": {"url": "interpreter_arm.nexe"}
55 // "portable": {"url": "foo.txt"}
58 // "x86-32": {"url": "x86-32/bar.txt"},
59 // "portable": {"url": "bar.txt"}
62 // "x86-64" : { "url": "..." }
67 // Sample PNaCl manifest file:
71 // "pnacl-translate": {
72 // "url": "myprogram.pexe"
75 // "url": "myprogram.debug.pexe",
82 // "portable": {"url": "foo.txt"}
85 // "portable": {"url": "bar.txt"}
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
]) {
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();
124 const std::string
& property_name
= it
.key();
125 if (!FindMatchingProperty(property_name
,
128 // For forward compatibility, we do not prohibit other keys being in
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();
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();
164 static const char* kManifestUrlSpecRequired
[] = {
167 const char** urlSpecPlusOptional
;
168 size_t urlSpecPlusOptionalLength
;
169 if (sandbox_isa
== kPortableKey
) {
170 static const char* kPnaclUrlSpecPlusOptional
[] = {
174 urlSpecPlusOptional
= kPnaclUrlSpecPlusOptional
;
175 urlSpecPlusOptionalLength
= arraysize(kPnaclUrlSpecPlusOptional
);
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();
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
)) {
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
);
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 '"
205 *error_string
= error_stream
.str();
208 if (url_dict
->HasKey(kOptLevelKey
)) {
209 const base::Value
* opt_level
= nullptr;
210 url_dict
->GetWithoutPathExpansion(kOptLevelKey
, &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();
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
[] = {
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();
246 if (!IsValidDictionary(
247 *pnacl_dict
, container_key
, parent_key
, kManifestPnaclSpecValid
,
248 arraysize(kManifestPnaclSpecValid
), kManifestPnaclSpecRequired
,
249 arraysize(kManifestPnaclSpecRequired
), error_string
)) {
252 // kPnaclTranslateKey checked to be required above.
253 const base::Value
* url_spec
= nullptr;
254 pnacl_dict
->GetWithoutPathExpansion(kPnaclTranslateKey
, &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
,
272 JsonManifest::ErrorInfo
* error_info
) {
273 const base::DictionaryValue
* dictionary
= nullptr;
274 if (!parent_dictionary
.GetDictionaryWithoutPathExpansion(parent_key
,
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";
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
[] = {
289 isaProperties
= kPnaclManifestISAProperties
;
290 isaPropertiesLength
= arraysize(kPnaclManifestISAProperties
);
292 // The known values for NaCl ISA dictionaries in the manifest.
293 static const char* kNaClManifestISAProperties
[] = {
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.
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();
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
,
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
;
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
;
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.";
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.";
379 void GrabUrlAndPnaclOptions(const base::DictionaryValue
& url_spec
,
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
)) {
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.
393 pnacl_options
->opt_level
= 0;
395 pnacl_options
->opt_level
= 2;
401 JsonManifest::JsonManifest(const std::string
& manifest_base_url
,
402 const std::string
& sandbox_isa
,
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
) {
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
));
423 error_info
->error
= PP_NACL_ERROR_MANIFEST_PARSING
;
425 std::string("manifest JSON parsing failed: ") + json_read_error_msg
;
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.";
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
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 {
450 CHECK(pnacl_options
);
451 CHECK(uses_nonsfi_mode
);
454 std::string nexe_url
;
455 if (!GetURLFromISADictionary(*dictionary_
, kProgramKey
, &nexe_url
,
456 pnacl_options
, uses_nonsfi_mode
, error_info
)) {
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())
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
;
469 "could not resolve url '" + nexe_url
+
470 "' relative to manifest base url '" + manifest_base_url_
.c_str() +
474 *full_url
= resolved_gurl
.possibly_invalid_spec();
478 void JsonManifest::GetPrefetchableFiles(
479 std::vector
<NaClResourcePrefetchRequest
>* out_files
) const {
480 const base::DictionaryValue
* files_dict
;
481 if (!dictionary_
->GetDictionaryWithoutPathExpansion(kFilesKey
, &files_dict
))
484 for (base::DictionaryValue::Iterator
it(*files_dict
); !it
.IsAtEnd();
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
)
503 const base::DictionaryValue
* files_dict
;
504 if (!dictionary_
->GetDictionaryWithoutPathExpansion(kFilesKey
, &files_dict
)) {
505 VLOG(1) << "ResolveKey failed: no \"files\" dictionary";
509 if (!files_dict
->HasKey(key
)) {
510 VLOG(1) << "ResolveKey failed: no such \"files\" entry: " << key
;
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();
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
+
539 // Validate the program section.
540 // There must be a matching (portable or sandbox_isa_) entry for program for
542 if (!IsValidISADictionary(*dictionary_
, kProgramKey
, sandbox_isa_
, true,
543 nonsfi_enabled_
, error_info
)) {
547 // Validate the interpreter section (if given).
548 // There must be a matching (portable or sandbox_isa_) entry for interpreter
550 if (dictionary_
->HasKey(kInterpreterKey
)) {
551 if (!IsValidISADictionary(*dictionary_
, kInterpreterKey
, sandbox_isa_
, true,
552 nonsfi_enabled_
, error_info
)) {
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();
571 const std::string
& file_name
= it
.key();
572 if (!IsValidISADictionary(*files_dictionary
, file_name
, sandbox_isa_
,
573 false, nonsfi_enabled_
, error_info
)) {
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.";
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
))
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())
601 GURL resolved_gurl
= base_gurl
.Resolve(relative_url
);
602 if (!resolved_gurl
.is_valid())
604 *full_url
= resolved_gurl
.possibly_invalid_spec();
608 bool JsonManifest::GetURLFromISADictionary(
609 const base::DictionaryValue
& parent_dictionary
,
610 const std::string
& parent_key
,
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
,
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.";
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
;
637 // The call to IsValidISADictionary() above guarantees that either
638 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the
640 *uses_nonsfi_mode
= false;
641 std::string chosen_isa
;
642 if (sandbox_isa_
== kPortableKey
) {
643 chosen_isa
= kPortableKey
;
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
;
654 // Should not reach here, because the earlier IsValidISADictionary()
655 // call checked that the manifest covers the current architecture.
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.";
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
,
675 error_info
->error
= PP_NACL_ERROR_MANIFEST_RESOLVE_URL
;
676 error_info
->string
= std::string("GetURLFromISADictionary failed: ") +
678 "'s value is not a json dictionary.";
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
,
687 error_info
->error
= PP_NACL_ERROR_MANIFEST_RESOLVE_URL
;
688 error_info
->string
= std::string("GetURLFromISADictionary failed: ") +
690 "'s value is not a json dictionary.";
693 GrabUrlAndPnaclOptions(*pnacl_dict
, url
, pnacl_options
);
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.";
702 pnacl_options
->translate
= PP_FALSE
;