Don't preload rarely seen large images
[chromium-blink-merge.git] / components / nacl / renderer / json_manifest.cc
blob6cab9b41039eefbc06b269011185f01612a5ee54
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/logging.h"
10 #include "base/macros.h"
11 #include "components/nacl/common/nacl_types.h"
12 #include "components/nacl/renderer/nexe_load_manager.h"
13 #include "third_party/jsoncpp/source/include/json/reader.h"
14 #include "third_party/jsoncpp/source/include/json/value.h"
15 #include "url/gurl.h"
17 namespace nacl {
19 namespace {
20 // Top-level section name keys
21 const char* const kProgramKey = "program";
22 const char* const kInterpreterKey = "interpreter";
23 const char* const kFilesKey = "files";
25 // ISA Dictionary keys
26 const char* const kX8632Key = "x86-32";
27 const char* const kX8632NonSFIKey = "x86-32-nonsfi";
28 const char* const kX8664Key = "x86-64";
29 const char* const kX8664NonSFIKey = "x86-64-nonsfi";
30 const char* const kArmKey = "arm";
31 const char* const kArmNonSFIKey = "arm-nonsfi";
32 const char* const kPortableKey = "portable";
34 // Url Resolution keys
35 const char* const kPnaclDebugKey = "pnacl-debug";
36 const char* const kPnaclTranslateKey = "pnacl-translate";
37 const char* const kUrlKey = "url";
39 // PNaCl keys
40 const char* const kOptLevelKey = "optlevel";
42 // Sample NaCl manifest file:
43 // {
44 // "program": {
45 // "x86-32": {"url": "myprogram_x86-32.nexe"},
46 // "x86-64": {"url": "myprogram_x86-64.nexe"},
47 // "arm": {"url": "myprogram_arm.nexe"}
48 // },
49 // "interpreter": {
50 // "x86-32": {"url": "interpreter_x86-32.nexe"},
51 // "x86-64": {"url": "interpreter_x86-64.nexe"},
52 // "arm": {"url": "interpreter_arm.nexe"}
53 // },
54 // "files": {
55 // "foo.txt": {
56 // "portable": {"url": "foo.txt"}
57 // },
58 // "bar.txt": {
59 // "x86-32": {"url": "x86-32/bar.txt"},
60 // "portable": {"url": "bar.txt"}
61 // },
62 // "libfoo.so": {
63 // "x86-64" : { "url": "..." }
64 // }
65 // }
66 // }
68 // Sample PNaCl manifest file:
69 // {
70 // "program": {
71 // "portable": {
72 // "pnacl-translate": {
73 // "url": "myprogram.pexe"
74 // },
75 // "pnacl-debug": {
76 // "url": "myprogram.debug.pexe",
77 // "opt_level": 0
78 // }
79 // }
80 // },
81 // "files": {
82 // "foo.txt": {
83 // "portable": {"url": "foo.txt"}
84 // },
85 // "bar.txt": {
86 // "portable": {"url": "bar.txt"}
87 // }
88 // }
89 // }
91 // Returns the key for the architecture in non-SFI mode.
92 std::string GetNonSFIKey(const std::string& sandbox_isa) {
93 return sandbox_isa + "-nonsfi";
96 // Looks up |property_name| in the vector |valid_names| with length
97 // |valid_name_count|. Returns true if |property_name| is found.
98 bool FindMatchingProperty(const std::string& property_name,
99 const char** valid_names,
100 size_t valid_name_count) {
101 for (size_t i = 0; i < valid_name_count; ++i) {
102 if (property_name == valid_names[i]) {
103 return true;
106 return false;
109 // Return true if this is a valid dictionary. Having only keys present in
110 // |valid_keys| and having at least the keys in |required_keys|.
111 // Error messages will be placed in |error_string|, given that the dictionary
112 // was the property value of |container_key|.
113 // E.g., "container_key" : dictionary
114 bool IsValidDictionary(const Json::Value& dictionary,
115 const std::string& container_key,
116 const std::string& parent_key,
117 const char** valid_keys,
118 size_t valid_key_count,
119 const char** required_keys,
120 size_t required_key_count,
121 std::string* error_string) {
122 if (!dictionary.isObject()) {
123 std::stringstream error_stream;
124 error_stream << parent_key << " property '" << container_key
125 << "' is non-dictionary value '"
126 << dictionary.toStyledString() << "'.";
127 *error_string = error_stream.str();
128 return false;
130 // Check for unknown dictionary members.
131 Json::Value::Members members = dictionary.getMemberNames();
132 for (size_t i = 0; i < members.size(); ++i) {
133 std::string property_name = members[i];
134 if (!FindMatchingProperty(property_name,
135 valid_keys,
136 valid_key_count)) {
137 // For forward compatibility, we do not prohibit other keys being in
138 // the dictionary.
139 VLOG(1) << "WARNING: '" << parent_key << "' property '"
140 << container_key << "' has unknown key '"
141 << property_name << "'.";
144 // Check for required members.
145 for (size_t i = 0; i < required_key_count; ++i) {
146 if (!dictionary.isMember(required_keys[i])) {
147 std::stringstream error_stream;
148 error_stream << parent_key << " property '" << container_key
149 << "' does not have required key: '"
150 << required_keys[i] << "'.";
151 *error_string = error_stream.str();
152 return false;
155 return true;
158 // Validate a "url" dictionary assuming it was resolved from container_key.
159 // E.g., "container_key" : { "url": "foo.txt" }
160 bool IsValidUrlSpec(const Json::Value& url_spec,
161 const std::string& container_key,
162 const std::string& parent_key,
163 const std::string& sandbox_isa,
164 std::string* error_string) {
165 static const char* kManifestUrlSpecRequired[] = {
166 kUrlKey
168 const char** urlSpecPlusOptional;
169 size_t urlSpecPlusOptionalLength;
170 if (sandbox_isa == kPortableKey) {
171 static const char* kPnaclUrlSpecPlusOptional[] = {
172 kUrlKey,
173 kOptLevelKey,
175 urlSpecPlusOptional = kPnaclUrlSpecPlusOptional;
176 urlSpecPlusOptionalLength = arraysize(kPnaclUrlSpecPlusOptional);
177 } else {
178 // URL specifications must not contain "pnacl-translate" keys.
179 // This prohibits NaCl clients from invoking PNaCl.
180 if (url_spec.isMember(kPnaclTranslateKey)) {
181 std::stringstream error_stream;
182 error_stream << "PNaCl-like NMF with application/x-nacl mimetype instead "
183 << "of x-pnacl mimetype (has " << kPnaclTranslateKey << ").";
184 *error_string = error_stream.str();
185 return false;
187 urlSpecPlusOptional = kManifestUrlSpecRequired;
188 urlSpecPlusOptionalLength = arraysize(kManifestUrlSpecRequired);
190 if (!IsValidDictionary(url_spec, container_key, parent_key,
191 urlSpecPlusOptional,
192 urlSpecPlusOptionalLength,
193 kManifestUrlSpecRequired,
194 arraysize(kManifestUrlSpecRequired),
195 error_string)) {
196 return false;
198 // Verify the correct types of the fields if they exist.
199 Json::Value url = url_spec[kUrlKey];
200 if (!url.isString()) {
201 std::stringstream error_stream;
202 error_stream << parent_key << " property '" << container_key <<
203 "' has non-string value '" << url.toStyledString() <<
204 "' for key '" << kUrlKey << "'.";
205 *error_string = error_stream.str();
206 return false;
208 Json::Value opt_level = url_spec[kOptLevelKey];
209 if (!opt_level.empty() && !opt_level.isNumeric()) {
210 std::stringstream error_stream;
211 error_stream << parent_key << " property '" << container_key <<
212 "' has non-numeric value '" << opt_level.toStyledString() <<
213 "' for key '" << kOptLevelKey << "'.";
214 *error_string = error_stream.str();
215 return false;
217 return true;
220 // Validate a "pnacl-translate" or "pnacl-debug" dictionary, assuming
221 // it was resolved from container_key.
222 // E.g., "container_key" : { "pnacl-translate" : URLSpec }
223 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec,
224 const std::string& container_key,
225 const std::string& parent_key,
226 const std::string& sandbox_isa,
227 std::string* error_string) {
228 static const char* kManifestPnaclSpecValid[] = {
229 kPnaclDebugKey,
230 kPnaclTranslateKey
232 static const char* kManifestPnaclSpecRequired[] = { kPnaclTranslateKey };
233 if (!IsValidDictionary(pnacl_spec, container_key, parent_key,
234 kManifestPnaclSpecValid,
235 arraysize(kManifestPnaclSpecValid),
236 kManifestPnaclSpecRequired,
237 arraysize(kManifestPnaclSpecRequired),
238 error_string)) {
239 return false;
241 Json::Value url_spec = pnacl_spec[kPnaclTranslateKey];
242 return IsValidUrlSpec(url_spec, kPnaclTranslateKey,
243 container_key, sandbox_isa, error_string);
246 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary
247 // is validated to have keys from within the set of recognized ISAs. Unknown
248 // ISAs are allowed, but ignored and warnings are produced. It is also
249 // validated
250 // that it must have an entry to match the ISA specified in |sandbox_isa| or
251 // have a fallback 'portable' entry if there is no match. Returns true if
252 // |dictionary| is an ISA to URL map. Sets |error_info| to something
253 // descriptive if it fails.
254 bool IsValidISADictionary(const Json::Value& dictionary,
255 const std::string& parent_key,
256 const std::string& sandbox_isa,
257 bool must_find_matching_entry,
258 bool nonsfi_enabled,
259 JsonManifest::ErrorInfo* error_info) {
260 // An ISA to URL dictionary has to be an object.
261 if (!dictionary.isObject()) {
262 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
263 error_info->string = std::string("manifest: ") + parent_key +
264 " property is not an ISA to URL dictionary";
265 return false;
267 // Build the set of reserved ISA dictionary keys.
268 const char** isaProperties;
269 size_t isaPropertiesLength;
270 if (sandbox_isa == kPortableKey) {
271 // The known values for PNaCl ISA dictionaries in the manifest.
272 static const char* kPnaclManifestISAProperties[] = {
273 kPortableKey
275 isaProperties = kPnaclManifestISAProperties;
276 isaPropertiesLength = arraysize(kPnaclManifestISAProperties);
277 } else {
278 // The known values for NaCl ISA dictionaries in the manifest.
279 static const char* kNaClManifestISAProperties[] = {
280 kX8632Key,
281 kX8632NonSFIKey,
282 kX8664Key,
283 kX8664NonSFIKey,
284 kArmKey,
285 kArmNonSFIKey,
286 // "portable" is here to allow checking that, if present, it can
287 // only refer to an URL, such as for a data file, and not to
288 // "pnacl-translate", which would cause the creation of a nexe.
289 kPortableKey
291 isaProperties = kNaClManifestISAProperties;
292 isaPropertiesLength = arraysize(kNaClManifestISAProperties);
294 // Check that entries in the dictionary are structurally correct.
295 Json::Value::Members members = dictionary.getMemberNames();
296 for (size_t i = 0; i < members.size(); ++i) {
297 std::string property_name = members[i];
298 Json::Value property_value = dictionary[property_name];
299 std::string error_string;
300 if (FindMatchingProperty(property_name,
301 isaProperties,
302 isaPropertiesLength)) {
303 // For NaCl, arch entries can only be
304 // "arch/portable" : URLSpec
305 // For PNaCl arch in "program" dictionary entries can be
306 // "portable" : { "pnacl-translate": URLSpec }
307 // or "portable" : { "pnacl-debug": URLSpec }
308 // For PNaCl arch elsewhere, dictionary entries can only be
309 // "portable" : URLSpec
310 if ((sandbox_isa != kPortableKey &&
311 !IsValidUrlSpec(property_value, property_name, parent_key,
312 sandbox_isa, &error_string)) ||
313 (sandbox_isa == kPortableKey &&
314 parent_key == kProgramKey &&
315 !IsValidPnaclTranslateSpec(property_value, property_name, parent_key,
316 sandbox_isa, &error_string)) ||
317 (sandbox_isa == kPortableKey &&
318 parent_key != kProgramKey &&
319 !IsValidUrlSpec(property_value, property_name, parent_key,
320 sandbox_isa, &error_string))) {
321 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
322 error_info->string = "manifest: " + error_string;
323 return false;
325 } else {
326 // For forward compatibility, we do not prohibit other keys being in
327 // the dictionary, as they may be architectures supported in later
328 // versions. However, the value of these entries must be an URLSpec.
329 VLOG(1) << "IsValidISADictionary: unrecognized key '"
330 << property_name << "'.";
331 if (!IsValidUrlSpec(property_value, property_name, parent_key,
332 sandbox_isa, &error_string)) {
333 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
334 error_info->string = "manifest: " + error_string;
335 return false;
340 if (sandbox_isa == kPortableKey) {
341 if (!dictionary.isMember(kPortableKey)) {
342 error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH;
343 error_info->string = "manifest: no version of " + parent_key +
344 " given for portable.";
345 return false;
347 } else if (must_find_matching_entry) {
348 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include
349 // micro-architectures that can resolve to multiple valid sandboxes.
350 bool has_isa = dictionary.isMember(sandbox_isa);
351 bool has_nonsfi_isa =
352 nonsfi_enabled && dictionary.isMember(GetNonSFIKey(sandbox_isa));
353 bool has_portable = dictionary.isMember(kPortableKey);
355 if (!has_isa && !has_nonsfi_isa && !has_portable) {
356 error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH;
357 error_info->string = "manifest: no version of " + parent_key +
358 " given for current arch and no portable version found.";
359 return false;
362 return true;
365 void GrabUrlAndPnaclOptions(const Json::Value& url_spec,
366 std::string* url,
367 PP_PNaClOptions* pnacl_options) {
368 *url = url_spec[kUrlKey].asString();
369 pnacl_options->translate = PP_TRUE;
370 if (url_spec.isMember(kOptLevelKey)) {
371 int32_t opt_raw = url_spec[kOptLevelKey].asInt();
372 // Currently only allow 0 or 2, since that is what we test.
373 if (opt_raw <= 0)
374 pnacl_options->opt_level = 0;
375 else
376 pnacl_options->opt_level = 2;
380 } // namespace
382 JsonManifest::JsonManifest(const std::string& manifest_base_url,
383 const std::string& sandbox_isa,
384 bool nonsfi_enabled,
385 bool pnacl_debug)
386 : manifest_base_url_(manifest_base_url),
387 sandbox_isa_(sandbox_isa),
388 nonsfi_enabled_(nonsfi_enabled),
389 pnacl_debug_(pnacl_debug) { }
391 bool JsonManifest::Init(const std::string& manifest_json,
392 ErrorInfo* error_info) {
393 CHECK(error_info);
395 Json::Reader reader;
396 if (!reader.parse(manifest_json, dictionary_)) {
397 std::string json_error = reader.getFormattedErrorMessages();
398 error_info->error = PP_NACL_ERROR_MANIFEST_PARSING;
399 error_info->string = "manifest JSON parsing failed: " + json_error;
400 return false;
402 // Parse has ensured the string was valid JSON. Check that it matches the
403 // manifest schema.
404 return MatchesSchema(error_info);
407 bool JsonManifest::GetProgramURL(std::string* full_url,
408 PP_PNaClOptions* pnacl_options,
409 bool* uses_nonsfi_mode,
410 ErrorInfo* error_info) const {
411 if (!full_url)
412 return false;
413 CHECK(pnacl_options);
414 CHECK(uses_nonsfi_mode);
415 CHECK(error_info);
417 const Json::Value& program = dictionary_[kProgramKey];
418 std::string nexe_url;
419 if (!GetURLFromISADictionary(program,
420 kProgramKey,
421 &nexe_url,
422 pnacl_options,
423 uses_nonsfi_mode,
424 error_info)) {
425 return false;
428 // The contents of the manifest are resolved relative to the manifest URL.
429 GURL base_gurl(manifest_base_url_);
430 if (!base_gurl.is_valid())
431 return false;
433 GURL resolved_gurl = base_gurl.Resolve(nexe_url);
434 if (!resolved_gurl.is_valid()) {
435 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
436 error_info->string =
437 "could not resolve url '" + nexe_url +
438 "' relative to manifest base url '" + manifest_base_url_.c_str() +
439 "'.";
440 return false;
442 *full_url = resolved_gurl.possibly_invalid_spec();
443 return true;
446 void JsonManifest::GetPrefetchableFiles(
447 std::vector<NaClResourcePrefetchRequest>* out_files) const {
448 const Json::Value& files = dictionary_[kFilesKey];
449 if (!files.isObject())
450 return;
452 Json::Value::Members keys = files.getMemberNames();
453 for (size_t i = 0; i < keys.size(); ++i) {
454 std::string full_url;
455 PP_PNaClOptions unused_pnacl_options; // pnacl does not support "files".
456 // We skip invalid entries in "files".
457 if (GetKeyUrl(files, keys[i], &full_url, &unused_pnacl_options)) {
458 if (GURL(full_url).SchemeIs("chrome-extension"))
459 out_files->push_back(NaClResourcePrefetchRequest(keys[i], full_url));
464 bool JsonManifest::ResolveKey(const std::string& key,
465 std::string* full_url,
466 PP_PNaClOptions* pnacl_options) const {
467 if (full_url == NULL || pnacl_options == NULL)
468 return false;
470 const Json::Value& files = dictionary_[kFilesKey];
471 if (!files.isObject()) {
472 VLOG(1) << "ResolveKey failed: no \"files\" dictionary";
473 return false;
476 if (!files.isMember(key)) {
477 VLOG(1) << "ResolveKey failed: no such \"files\" entry: " << key;
478 return false;
480 return GetKeyUrl(files, key, full_url, pnacl_options);
483 bool JsonManifest::MatchesSchema(ErrorInfo* error_info) {
484 if (!dictionary_.isObject()) {
485 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
486 error_info->string = "manifest: is not a json dictionary.";
487 return false;
489 Json::Value::Members members = dictionary_.getMemberNames();
490 for (size_t i = 0; i < members.size(); ++i) {
491 // The top level dictionary entries valid in the manifest file.
492 static const char* kManifestTopLevelProperties[] = { kProgramKey,
493 kInterpreterKey,
494 kFilesKey };
495 std::string property_name = members[i];
496 if (!FindMatchingProperty(property_name,
497 kManifestTopLevelProperties,
498 arraysize(kManifestTopLevelProperties))) {
499 VLOG(1) << "JsonManifest::MatchesSchema: WARNING: unknown top-level "
500 << "section '" << property_name << "' in manifest.";
504 // A manifest file must have a program section.
505 if (!dictionary_.isMember(kProgramKey)) {
506 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
507 error_info->string = std::string("manifest: missing '") + kProgramKey +
508 "' section.";
509 return false;
512 // Validate the program section.
513 // There must be a matching (portable or sandbox_isa_) entry for program for
514 // NaCl.
515 if (!IsValidISADictionary(dictionary_[kProgramKey],
516 kProgramKey,
517 sandbox_isa_,
518 true,
519 nonsfi_enabled_,
520 error_info)) {
521 return false;
524 // Validate the interpreter section (if given).
525 // There must be a matching (portable or sandbox_isa_) entry for interpreter
526 // for NaCl.
527 if (dictionary_.isMember(kInterpreterKey)) {
528 if (!IsValidISADictionary(dictionary_[kInterpreterKey],
529 kInterpreterKey,
530 sandbox_isa_,
531 true,
532 nonsfi_enabled_,
533 error_info)) {
534 return false;
538 // Validate the file dictionary (if given).
539 // The "files" key does not require a matching (portable or sandbox_isa_)
540 // entry at schema validation time for NaCl. This allows manifests to
541 // specify resources that are only loaded for a particular sandbox_isa.
542 if (dictionary_.isMember(kFilesKey)) {
543 const Json::Value& files = dictionary_[kFilesKey];
544 if (!files.isObject()) {
545 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
546 error_info->string = std::string("manifest: '") + kFilesKey +
547 "' is not a dictionary.";
549 Json::Value::Members members = files.getMemberNames();
550 for (size_t i = 0; i < members.size(); ++i) {
551 std::string file_name = members[i];
552 if (!IsValidISADictionary(files[file_name],
553 file_name,
554 sandbox_isa_,
555 false,
556 nonsfi_enabled_,
557 error_info)) {
558 return false;
562 return true;
565 bool JsonManifest::GetKeyUrl(const Json::Value& dictionary,
566 const std::string& key,
567 std::string* full_url,
568 PP_PNaClOptions* pnacl_options) const {
569 DCHECK(full_url && pnacl_options);
570 if (!dictionary.isMember(key)) {
571 VLOG(1) << "GetKeyUrl failed: file " << key << " not found in manifest.";
572 return false;
574 const Json::Value& isa_dict = dictionary[key];
575 std::string relative_url;
576 bool uses_nonsfi_mode;
577 ErrorInfo ignored_error_info;
578 if (!GetURLFromISADictionary(isa_dict, key, &relative_url,
579 pnacl_options, &uses_nonsfi_mode,
580 &ignored_error_info))
581 return false;
583 // The contents of the manifest are resolved relative to the manifest URL.
584 GURL base_gurl(manifest_base_url_);
585 if (!base_gurl.is_valid())
586 return false;
587 GURL resolved_gurl = base_gurl.Resolve(relative_url);
588 if (!resolved_gurl.is_valid())
589 return false;
590 *full_url = resolved_gurl.possibly_invalid_spec();
591 return true;
594 bool JsonManifest::GetURLFromISADictionary(const Json::Value& dictionary,
595 const std::string& parent_key,
596 std::string* url,
597 PP_PNaClOptions* pnacl_options,
598 bool* uses_nonsfi_mode,
599 ErrorInfo* error_info) const {
600 DCHECK(url && pnacl_options && error_info);
602 // When the application actually requests a resolved URL, we must have
603 // a matching entry (sandbox_isa_ or portable) for NaCl.
604 ErrorInfo ignored_error_info;
605 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa_, true,
606 nonsfi_enabled_, &ignored_error_info)) {
607 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
608 error_info->string = "architecture " + sandbox_isa_ +
609 " is not found for file " + parent_key;
610 return false;
613 // The call to IsValidISADictionary() above guarantees that either
614 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the
615 // dictionary.
616 *uses_nonsfi_mode = false;
617 std::string chosen_isa;
618 if (sandbox_isa_ == kPortableKey) {
619 chosen_isa = kPortableKey;
620 } else {
621 std::string nonsfi_isa = GetNonSFIKey(sandbox_isa_);
622 if (nonsfi_enabled_ && dictionary.isMember(nonsfi_isa)) {
623 chosen_isa = nonsfi_isa;
624 *uses_nonsfi_mode = true;
625 } else if (dictionary.isMember(sandbox_isa_)) {
626 chosen_isa = sandbox_isa_;
627 } else if (dictionary.isMember(kPortableKey)) {
628 chosen_isa = kPortableKey;
629 } else {
630 // Should not reach here, because the earlier IsValidISADictionary()
631 // call checked that the manifest covers the current architecture.
632 DCHECK(false);
633 return false;
637 const Json::Value& isa_spec = dictionary[chosen_isa];
638 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first.
639 // If found, mark that it is a debug URL. Otherwise, fall back to
640 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL.
641 if (pnacl_debug_ && isa_spec.isMember(kPnaclDebugKey)) {
642 GrabUrlAndPnaclOptions(isa_spec[kPnaclDebugKey], url, pnacl_options);
643 pnacl_options->is_debug = PP_TRUE;
644 } else if (isa_spec.isMember(kPnaclTranslateKey)) {
645 GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options);
646 } else {
647 // NaCl
648 *url = isa_spec[kUrlKey].asString();
649 pnacl_options->translate = PP_FALSE;
652 return true;
655 } // namespace nacl