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