blob: 8b4e5b9de65836f0949a81fe5a5686a5b9642be9 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/nacl/renderer/json_manifest.h"
#include <stddef.h>
#include <stdint.h>
#include <set>
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/macros.h"
#include "components/nacl/common/nacl_types.h"
#include "components/nacl/renderer/nexe_load_manager.h"
#include "url/gurl.h"
namespace nacl {
namespace {
// Top-level section name keys
const char* const kProgramKey = "program";
const char* const kInterpreterKey = "interpreter";
const char* const kFilesKey = "files";
// ISA Dictionary keys
const char* const kX8632Key = "x86-32";
const char* const kX8632NonSFIKey = "x86-32-nonsfi";
const char* const kX8664Key = "x86-64";
const char* const kX8664NonSFIKey = "x86-64-nonsfi";
const char* const kArmKey = "arm";
const char* const kArmNonSFIKey = "arm-nonsfi";
const char* const kPortableKey = "portable";
// Url Resolution keys
const char* const kPnaclDebugKey = "pnacl-debug";
const char* const kPnaclTranslateKey = "pnacl-translate";
const char* const kUrlKey = "url";
// PNaCl keys
const char* const kOptLevelKey = "optlevel";
// Sample NaCl manifest file:
// {
// "program": {
// "x86-32": {"url": "myprogram_x86-32.nexe"},
// "x86-64": {"url": "myprogram_x86-64.nexe"},
// "arm": {"url": "myprogram_arm.nexe"}
// },
// "interpreter": {
// "x86-32": {"url": "interpreter_x86-32.nexe"},
// "x86-64": {"url": "interpreter_x86-64.nexe"},
// "arm": {"url": "interpreter_arm.nexe"}
// },
// "files": {
// "foo.txt": {
// "portable": {"url": "foo.txt"}
// },
// "bar.txt": {
// "x86-32": {"url": "x86-32/bar.txt"},
// "portable": {"url": "bar.txt"}
// },
// "libfoo.so": {
// "x86-64" : { "url": "..." }
// }
// }
// }
// Sample PNaCl manifest file:
// {
// "program": {
// "portable": {
// "pnacl-translate": {
// "url": "myprogram.pexe"
// },
// "pnacl-debug": {
// "url": "myprogram.debug.pexe",
// "opt_level": 0
// }
// }
// },
// "files": {
// "foo.txt": {
// "portable": {"url": "foo.txt"}
// },
// "bar.txt": {
// "portable": {"url": "bar.txt"}
// }
// }
// }
// Returns the key for the architecture in non-SFI mode.
std::string GetNonSFIKey(const std::string& sandbox_isa) {
return sandbox_isa + "-nonsfi";
}
// Looks up |property_name| in the vector |valid_names| with length
// |valid_name_count|. Returns true if |property_name| is found.
bool FindMatchingProperty(const std::string& property_name,
const char** valid_names,
size_t valid_name_count) {
for (size_t i = 0; i < valid_name_count; ++i) {
if (property_name == valid_names[i]) {
return true;
}
}
return false;
}
// Return true if this is a valid dictionary. Having only keys present in
// |valid_keys| and having at least the keys in |required_keys|.
// Error messages will be placed in |error_string|, given that the dictionary
// was the property value of |container_key|.
// E.g., "container_key" : dictionary
bool IsValidDictionary(const base::DictionaryValue& dictionary,
const std::string& container_key,
const std::string& parent_key,
const char** valid_keys,
size_t valid_key_count,
const char** required_keys,
size_t required_key_count,
std::string* error_string) {
// Check for unknown dictionary members.
for (base::DictionaryValue::Iterator it(dictionary); !it.IsAtEnd();
it.Advance()) {
const std::string& property_name = it.key();
if (!FindMatchingProperty(property_name,
valid_keys,
valid_key_count)) {
// For forward compatibility, we do not prohibit other keys being in
// the dictionary.
VLOG(1) << "WARNING: '" << parent_key << "' property '"
<< container_key << "' has unknown key '"
<< property_name << "'.";
}
}
// Check for required members.
for (size_t i = 0; i < required_key_count; ++i) {
if (!dictionary.HasKey(required_keys[i])) {
std::stringstream error_stream;
error_stream << parent_key << " property '" << container_key
<< "' does not have required key: '"
<< required_keys[i] << "'.";
*error_string = error_stream.str();
return false;
}
}
return true;
}
// Validate a "url" dictionary assuming it was resolved from container_key.
// E.g., "container_key" : { "url": "foo.txt" }
bool IsValidUrlSpec(const base::Value& url_spec,
const std::string& container_key,
const std::string& parent_key,
const std::string& sandbox_isa,
std::string* error_string) {
const base::DictionaryValue* url_dict = nullptr;
if (!url_spec.GetAsDictionary(&url_dict)) {
std::stringstream error_stream;
error_stream << parent_key << " property '" << container_key
<< "' is non-dictionary value '" << url_spec << "'.";
*error_string = error_stream.str();
return false;
}
static const char* kManifestUrlSpecRequired[] = {
kUrlKey
};
const char** urlSpecPlusOptional;
size_t urlSpecPlusOptionalLength;
if (sandbox_isa == kPortableKey) {
static const char* kPnaclUrlSpecPlusOptional[] = {
kUrlKey,
kOptLevelKey,
};
urlSpecPlusOptional = kPnaclUrlSpecPlusOptional;
urlSpecPlusOptionalLength = arraysize(kPnaclUrlSpecPlusOptional);
} else {
// URL specifications must not contain "pnacl-translate" keys.
// This prohibits NaCl clients from invoking PNaCl.
if (url_dict->HasKey(kPnaclTranslateKey)) {
std::stringstream error_stream;
error_stream << "PNaCl-like NMF with application/x-nacl mimetype instead "
<< "of x-pnacl mimetype (has " << kPnaclTranslateKey << ").";
*error_string = error_stream.str();
return false;
}
urlSpecPlusOptional = kManifestUrlSpecRequired;
urlSpecPlusOptionalLength = arraysize(kManifestUrlSpecRequired);
}
if (!IsValidDictionary(*url_dict, container_key, parent_key,
urlSpecPlusOptional, urlSpecPlusOptionalLength,
kManifestUrlSpecRequired,
arraysize(kManifestUrlSpecRequired), error_string)) {
return false;
}
// Verify the correct types of the fields if they exist.
const base::Value* url = nullptr;
// URL was already verified above by IsValidDictionary to be required.
url_dict->GetWithoutPathExpansion(kUrlKey, &url);
DCHECK(url);
if (!url->is_string()) {
std::stringstream error_stream;
error_stream << parent_key << " property '" << container_key
<< "' has non-string value '" << *url << "' for key '"
<< kUrlKey << "'.";
*error_string = error_stream.str();
return false;
}
if (url_dict->HasKey(kOptLevelKey)) {
const base::Value* opt_level = nullptr;
url_dict->GetWithoutPathExpansion(kOptLevelKey, &opt_level);
DCHECK(opt_level);
if (!opt_level->is_int()) {
std::stringstream error_stream;
error_stream << parent_key << " property '" << container_key
<< "' has non-numeric value '" << *opt_level << "' for key '"
<< kOptLevelKey << "'.";
*error_string = error_stream.str();
return false;
}
}
return true;
}
// Validate a "pnacl-translate" or "pnacl-debug" dictionary, assuming
// it was resolved from container_key.
// E.g., "container_key" : { "pnacl-translate" : URLSpec }
bool IsValidPnaclTranslateSpec(const base::Value& pnacl_spec,
const std::string& container_key,
const std::string& parent_key,
const std::string& sandbox_isa,
std::string* error_string) {
static const char* kManifestPnaclSpecValid[] = {
kPnaclDebugKey,
kPnaclTranslateKey
};
static const char* kManifestPnaclSpecRequired[] = { kPnaclTranslateKey };
const base::DictionaryValue* pnacl_dict = nullptr;
if (!pnacl_spec.GetAsDictionary(&pnacl_dict)) {
std::stringstream error_stream;
error_stream << parent_key << " property '" << container_key
<< "' is non-dictionary value '" << pnacl_spec << "'.";
*error_string = error_stream.str();
return false;
}
if (!IsValidDictionary(
*pnacl_dict, container_key, parent_key, kManifestPnaclSpecValid,
arraysize(kManifestPnaclSpecValid), kManifestPnaclSpecRequired,
arraysize(kManifestPnaclSpecRequired), error_string)) {
return false;
}
// kPnaclTranslateKey checked to be required above.
const base::Value* url_spec = nullptr;
pnacl_dict->GetWithoutPathExpansion(kPnaclTranslateKey, &url_spec);
DCHECK(url_spec);
return IsValidUrlSpec(*url_spec, kPnaclTranslateKey, container_key,
sandbox_isa, error_string);
}
// Validates that parent_dictionary[parent_key] is a valid ISA dictionary.
// An ISA dictionary is validated to have keys from within the set of
// recognized ISAs. Unknown ISAs are allowed, but ignored and warnings
// are produced. It is also validated that it must have an entry to match the
// ISA specified in |sandbox_isa| or have a fallback 'portable' entry if
// there is no match. Returns true if parent_dictionary[parent_key] is an
// ISA to URL map. Sets |error_info| to something descriptive if it fails.
bool IsValidISADictionary(const base::DictionaryValue& parent_dictionary,
const std::string& parent_key,
const std::string& sandbox_isa,
bool must_find_matching_entry,
bool nonsfi_enabled,
JsonManifest::ErrorInfo* error_info) {
const base::DictionaryValue* dictionary = nullptr;
if (!parent_dictionary.GetDictionaryWithoutPathExpansion(parent_key,
&dictionary)) {
error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
error_info->string = std::string("manifest: ") + parent_key +
" property is not an ISA to URL dictionary";
return false;
}
// Build the set of reserved ISA dictionary keys.
const char** isaProperties;
size_t isaPropertiesLength;
if (sandbox_isa == kPortableKey) {
// The known values for PNaCl ISA dictionaries in the manifest.
static const char* kPnaclManifestISAProperties[] = {
kPortableKey
};
isaProperties = kPnaclManifestISAProperties;
isaPropertiesLength = arraysize(kPnaclManifestISAProperties);
} else {
// The known values for NaCl ISA dictionaries in the manifest.
static const char* kNaClManifestISAProperties[] = {
kX8632Key,
kX8632NonSFIKey,
kX8664Key,
kX8664NonSFIKey,
kArmKey,
kArmNonSFIKey,
// "portable" is here to allow checking that, if present, it can
// only refer to an URL, such as for a data file, and not to
// "pnacl-translate", which would cause the creation of a nexe.
kPortableKey
};
isaProperties = kNaClManifestISAProperties;
isaPropertiesLength = arraysize(kNaClManifestISAProperties);
}
// Check that entries in the dictionary are structurally correct.
for (base::DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd();
it.Advance()) {
const std::string& property_name = it.key();
const base::Value& property_value = it.value();
std::string error_string;
if (FindMatchingProperty(property_name,
isaProperties,
isaPropertiesLength)) {
// For NaCl, arch entries can only be
// "arch/portable" : URLSpec
// For PNaCl arch in "program" dictionary entries can be
// "portable" : { "pnacl-translate": URLSpec }
// or "portable" : { "pnacl-debug": URLSpec }
// For PNaCl arch elsewhere, dictionary entries can only be
// "portable" : URLSpec
if ((sandbox_isa != kPortableKey &&
!IsValidUrlSpec(property_value, property_name, parent_key,
sandbox_isa, &error_string)) ||
(sandbox_isa == kPortableKey &&
parent_key == kProgramKey &&
!IsValidPnaclTranslateSpec(property_value, property_name, parent_key,
sandbox_isa, &error_string)) ||
(sandbox_isa == kPortableKey &&
parent_key != kProgramKey &&
!IsValidUrlSpec(property_value, property_name, parent_key,
sandbox_isa, &error_string))) {
error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
error_info->string = "manifest: " + error_string;
return false;
}
} else {
// For forward compatibility, we do not prohibit other keys being in
// the dictionary, as they may be architectures supported in later
// versions. However, the value of these entries must be an URLSpec.
VLOG(1) << "IsValidISADictionary: unrecognized key '"
<< property_name << "'.";
if (!IsValidUrlSpec(property_value, property_name, parent_key,
sandbox_isa, &error_string)) {
error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
error_info->string = "manifest: " + error_string;
return false;
}
}
}
if (sandbox_isa == kPortableKey) {
if (!dictionary->HasKey(kPortableKey)) {
error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH;
error_info->string = "manifest: no version of " + parent_key +
" given for portable.";
return false;
}
} else if (must_find_matching_entry) {
// TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include
// micro-architectures that can resolve to multiple valid sandboxes.
bool has_isa = dictionary->HasKey(sandbox_isa);
bool has_nonsfi_isa =
nonsfi_enabled && dictionary->HasKey(GetNonSFIKey(sandbox_isa));
bool has_portable = dictionary->HasKey(kPortableKey);
if (!has_isa && !has_nonsfi_isa && !has_portable) {
error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH;
error_info->string = "manifest: no version of " + parent_key +
" given for current arch and no portable version found.";
return false;
}
}
return true;
}
void GrabUrlAndPnaclOptions(const base::DictionaryValue& url_spec,
std::string* url,
PP_PNaClOptions* pnacl_options) {
// url_spec should have been validated as a first pass.
bool get_url_success = url_spec.GetStringWithoutPathExpansion(kUrlKey, url);
DCHECK(get_url_success);
pnacl_options->translate = PP_TRUE;
if (url_spec.HasKey(kOptLevelKey)) {
int32_t opt_raw = 0;
bool get_opt_success =
url_spec.GetIntegerWithoutPathExpansion(kOptLevelKey, &opt_raw);
DCHECK(get_opt_success);
// Currently only allow 0 or 2, since that is what we test.
if (opt_raw <= 0)
pnacl_options->opt_level = 0;
else
pnacl_options->opt_level = 2;
}
}
} // namespace
JsonManifest::JsonManifest(const std::string& manifest_base_url,
const std::string& sandbox_isa,
bool nonsfi_enabled,
bool pnacl_debug)
: manifest_base_url_(manifest_base_url),
sandbox_isa_(sandbox_isa),
nonsfi_enabled_(nonsfi_enabled),
pnacl_debug_(pnacl_debug) { }
JsonManifest::~JsonManifest() {}
bool JsonManifest::Init(const std::string& manifest_json_data,
ErrorInfo* error_info) {
CHECK(error_info);
base::JSONReader json_reader;
int json_read_error_code;
std::string json_read_error_msg;
std::unique_ptr<base::Value> json_data(json_reader.ReadAndReturnError(
manifest_json_data, base::JSON_PARSE_RFC, &json_read_error_code,
&json_read_error_msg));
if (!json_data) {
error_info->error = PP_NACL_ERROR_MANIFEST_PARSING;
error_info->string =
std::string("manifest JSON parsing failed: ") + json_read_error_msg;
return false;
}
// Ensure it's actually a dictionary before capturing as dictionary_.
base::DictionaryValue* json_dict = nullptr;
if (!json_data->GetAsDictionary(&json_dict)) {
error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
error_info->string = "manifest: is not a json dictionary.";
return false;
}
// Can't quite use json_data.swap(dictionary_), since the types are different
// so do this kludgy manual swap.
DCHECK(json_dict == json_data.get());
dictionary_.reset(static_cast<base::DictionaryValue*>(json_data.release()));
// Parse has ensured the string was valid JSON. Check that it matches the
// manifest schema.
return MatchesSchema(error_info);
}
bool JsonManifest::GetProgramURL(std::string* full_url,
PP_PNaClOptions* pnacl_options,
bool* uses_nonsfi_mode,
ErrorInfo* error_info) const {
if (!full_url)
return false;
CHECK(pnacl_options);
CHECK(uses_nonsfi_mode);
CHECK(error_info);
std::string nexe_url;
if (!GetURLFromISADictionary(*dictionary_, kProgramKey, &nexe_url,
pnacl_options, uses_nonsfi_mode, error_info)) {
return false;
}
// The contents of the manifest are resolved relative to the manifest URL.
GURL base_gurl(manifest_base_url_);
if (!base_gurl.is_valid())
return false;
GURL resolved_gurl = base_gurl.Resolve(nexe_url);
if (!resolved_gurl.is_valid()) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string =
"could not resolve url '" + nexe_url +
"' relative to manifest base url '" + manifest_base_url_.c_str() +
"'.";
return false;
}
*full_url = resolved_gurl.possibly_invalid_spec();
return true;
}
void JsonManifest::GetPrefetchableFiles(
std::vector<NaClResourcePrefetchRequest>* out_files) const {
const base::DictionaryValue* files_dict;
if (!dictionary_->GetDictionaryWithoutPathExpansion(kFilesKey, &files_dict))
return;
for (base::DictionaryValue::Iterator it(*files_dict); !it.IsAtEnd();
it.Advance()) {
std::string full_url;
PP_PNaClOptions unused_pnacl_options; // pnacl does not support "files".
const std::string& file_key = it.key();
// We skip invalid entries in "files".
if (GetKeyUrl(*files_dict, file_key, &full_url, &unused_pnacl_options)) {
if (GURL(full_url).SchemeIs("chrome-extension"))
out_files->push_back(NaClResourcePrefetchRequest(file_key, full_url));
}
}
}
bool JsonManifest::ResolveKey(const std::string& key,
std::string* full_url,
PP_PNaClOptions* pnacl_options) const {
if (full_url == NULL || pnacl_options == NULL)
return false;
const base::DictionaryValue* files_dict;
if (!dictionary_->GetDictionaryWithoutPathExpansion(kFilesKey, &files_dict)) {
VLOG(1) << "ResolveKey failed: no \"files\" dictionary";
return false;
}
if (!files_dict->HasKey(key)) {
VLOG(1) << "ResolveKey failed: no such \"files\" entry: " << key;
return false;
}
return GetKeyUrl(*files_dict, key, full_url, pnacl_options);
}
bool JsonManifest::MatchesSchema(ErrorInfo* error_info) {
// The top level dictionary entries valid in the manifest file.
static const char* kManifestTopLevelProperties[] = {
kProgramKey, kInterpreterKey, kFilesKey};
for (base::DictionaryValue::Iterator it(*dictionary_); !it.IsAtEnd();
it.Advance()) {
const std::string& property_name = it.key();
if (!FindMatchingProperty(property_name,
kManifestTopLevelProperties,
arraysize(kManifestTopLevelProperties))) {
VLOG(1) << "JsonManifest::MatchesSchema: WARNING: unknown top-level "
<< "section '" << property_name << "' in manifest.";
}
}
// A manifest file must have a program section.
if (!dictionary_->HasKey(kProgramKey)) {
error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
error_info->string = std::string("manifest: missing '") + kProgramKey +
"' section.";
return false;
}
// Validate the program section.
// There must be a matching (portable or sandbox_isa_) entry for program for
// NaCl.
if (!IsValidISADictionary(*dictionary_, kProgramKey, sandbox_isa_, true,
nonsfi_enabled_, error_info)) {
return false;
}
// Validate the interpreter section (if given).
// There must be a matching (portable or sandbox_isa_) entry for interpreter
// for NaCl.
if (dictionary_->HasKey(kInterpreterKey)) {
if (!IsValidISADictionary(*dictionary_, kInterpreterKey, sandbox_isa_, true,
nonsfi_enabled_, error_info)) {
return false;
}
}
// Validate the file dictionary (if given).
// The "files" key does not require a matching (portable or sandbox_isa_)
// entry at schema validation time for NaCl. This allows manifests to
// specify resources that are only loaded for a particular sandbox_isa.
if (dictionary_->HasKey(kFilesKey)) {
const base::DictionaryValue* files_dictionary = nullptr;
if (!dictionary_->GetDictionaryWithoutPathExpansion(kFilesKey,
&files_dictionary)) {
error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE;
error_info->string = std::string("manifest: '") + kFilesKey +
"' is not a dictionary.";
}
for (base::DictionaryValue::Iterator it(*files_dictionary); !it.IsAtEnd();
it.Advance()) {
const std::string& file_name = it.key();
if (!IsValidISADictionary(*files_dictionary, file_name, sandbox_isa_,
false, nonsfi_enabled_, error_info)) {
return false;
}
}
}
return true;
}
bool JsonManifest::GetKeyUrl(const base::DictionaryValue& dictionary,
const std::string& key,
std::string* full_url,
PP_PNaClOptions* pnacl_options) const {
DCHECK(full_url && pnacl_options);
if (!dictionary.HasKey(key)) {
VLOG(1) << "GetKeyUrl failed: file " << key << " not found in manifest.";
return false;
}
std::string relative_url;
bool uses_nonsfi_mode;
ErrorInfo ignored_error_info;
if (!GetURLFromISADictionary(dictionary, key, &relative_url, pnacl_options,
&uses_nonsfi_mode, &ignored_error_info))
return false;
// The contents of the manifest are resolved relative to the manifest URL.
GURL base_gurl(manifest_base_url_);
if (!base_gurl.is_valid())
return false;
GURL resolved_gurl = base_gurl.Resolve(relative_url);
if (!resolved_gurl.is_valid())
return false;
*full_url = resolved_gurl.possibly_invalid_spec();
return true;
}
bool JsonManifest::GetURLFromISADictionary(
const base::DictionaryValue& parent_dictionary,
const std::string& parent_key,
std::string* url,
PP_PNaClOptions* pnacl_options,
bool* uses_nonsfi_mode,
ErrorInfo* error_info) const {
DCHECK(url && pnacl_options && error_info);
const base::DictionaryValue* dictionary = nullptr;
if (!parent_dictionary.GetDictionaryWithoutPathExpansion(parent_key,
&dictionary)) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string = std::string("GetURLFromISADictionary failed: ") +
parent_key + "'s value is not a json dictionary.";
return false;
}
// When the application actually requests a resolved URL, we must have
// a matching entry (sandbox_isa_ or portable) for NaCl.
ErrorInfo ignored_error_info;
if (!IsValidISADictionary(parent_dictionary, parent_key, sandbox_isa_, true,
nonsfi_enabled_, &ignored_error_info)) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string = "architecture " + sandbox_isa_ +
" is not found for file " + parent_key;
return false;
}
// The call to IsValidISADictionary() above guarantees that either
// sandbox_isa_, its nonsfi mode, or kPortableKey is present in the
// dictionary.
*uses_nonsfi_mode = false;
std::string chosen_isa;
if (sandbox_isa_ == kPortableKey) {
chosen_isa = kPortableKey;
} else {
std::string nonsfi_isa = GetNonSFIKey(sandbox_isa_);
if (nonsfi_enabled_ && dictionary->HasKey(nonsfi_isa)) {
chosen_isa = nonsfi_isa;
*uses_nonsfi_mode = true;
} else if (dictionary->HasKey(sandbox_isa_)) {
chosen_isa = sandbox_isa_;
} else if (dictionary->HasKey(kPortableKey)) {
chosen_isa = kPortableKey;
} else {
// Should not reach here, because the earlier IsValidISADictionary()
// call checked that the manifest covers the current architecture.
NOTREACHED();
return false;
}
}
const base::DictionaryValue* isa_spec = nullptr;
if (!dictionary->GetDictionaryWithoutPathExpansion(chosen_isa, &isa_spec)) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string = std::string("GetURLFromISADictionary failed: ") +
chosen_isa + "'s value is not a json dictionary.";
return false;
}
// If the PNaCl debug flag is turned on, look for pnacl-debug entries first.
// If found, mark that it is a debug URL. Otherwise, fall back to
// checking for pnacl-translate URLs, etc. and don't mark it as a debug URL.
if (pnacl_debug_ && isa_spec->HasKey(kPnaclDebugKey)) {
const base::DictionaryValue* pnacl_dict = nullptr;
if (!isa_spec->GetDictionaryWithoutPathExpansion(kPnaclDebugKey,
&pnacl_dict)) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string = std::string("GetURLFromISADictionary failed: ") +
kPnaclDebugKey +
"'s value is not a json dictionary.";
return false;
}
GrabUrlAndPnaclOptions(*pnacl_dict, url, pnacl_options);
pnacl_options->is_debug = PP_TRUE;
} else if (isa_spec->HasKey(kPnaclTranslateKey)) {
const base::DictionaryValue* pnacl_dict = nullptr;
if (!isa_spec->GetDictionaryWithoutPathExpansion(kPnaclTranslateKey,
&pnacl_dict)) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string = std::string("GetURLFromISADictionary failed: ") +
kPnaclTranslateKey +
"'s value is not a json dictionary.";
return false;
}
GrabUrlAndPnaclOptions(*pnacl_dict, url, pnacl_options);
} else {
// The native NaCl case.
if (!isa_spec->GetStringWithoutPathExpansion(kUrlKey, url)) {
error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL;
error_info->string = std::string("GetURLFromISADictionary failed: ") +
kUrlKey + "'s value is not a string.";
return false;
}
pnacl_options->translate = PP_FALSE;
}
return true;
}
} // namespace nacl