Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(448)

Side by Side Diff: ppapi/native_client/src/trusted/plugin/json_manifest.cc

Issue 177113009: Support non-SFI mode in NaCl manifest file. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be 3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file. 4 * found in the LICENSE file.
5 */ 5 */
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "ppapi/native_client/src/trusted/plugin/json_manifest.h" 9 #include "ppapi/native_client/src/trusted/plugin/json_manifest.h"
10 10
(...skipping 14 matching lines...) Expand all
25 namespace plugin { 25 namespace plugin {
26 26
27 namespace { 27 namespace {
28 // Top-level section name keys 28 // Top-level section name keys
29 const char* const kProgramKey = "program"; 29 const char* const kProgramKey = "program";
30 const char* const kInterpreterKey = "interpreter"; 30 const char* const kInterpreterKey = "interpreter";
31 const char* const kFilesKey = "files"; 31 const char* const kFilesKey = "files";
32 32
33 // ISA Dictionary keys 33 // ISA Dictionary keys
34 const char* const kX8632Key = "x86-32"; 34 const char* const kX8632Key = "x86-32";
35 const char* const kX8632NonSFIKey = "x86-32-nonsfi";
35 const char* const kX8664Key = "x86-64"; 36 const char* const kX8664Key = "x86-64";
37 const char* const kX8664NonSFIKey = "x86-64-nonsfi";
36 const char* const kArmKey = "arm"; 38 const char* const kArmKey = "arm";
39 const char* const kArmNonSFIKey = "arm-nonsfi";
37 const char* const kPortableKey = "portable"; 40 const char* const kPortableKey = "portable";
38 41
39 // Url Resolution keys 42 // Url Resolution keys
40 const char* const kPnaclDebugKey = "pnacl-debug"; 43 const char* const kPnaclDebugKey = "pnacl-debug";
41 const char* const kPnaclTranslateKey = "pnacl-translate"; 44 const char* const kPnaclTranslateKey = "pnacl-translate";
42 const char* const kUrlKey = "url"; 45 const char* const kUrlKey = "url";
43 46
44 // PNaCl keys 47 // PNaCl keys
45 const char* const kOptLevelKey = "optlevel"; 48 const char* const kOptLevelKey = "optlevel";
46 49
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 // "files": { 89 // "files": {
87 // "foo.txt": { 90 // "foo.txt": {
88 // "portable": {"url": "foo.txt"} 91 // "portable": {"url": "foo.txt"}
89 // }, 92 // },
90 // "bar.txt": { 93 // "bar.txt": {
91 // "portable": {"url": "bar.txt"} 94 // "portable": {"url": "bar.txt"}
92 // } 95 // }
93 // } 96 // }
94 // } 97 // }
95 98
99 // Returns the key for the architecture in non-SFI mode.
100 nacl::string GetNonSFIKey(const nacl::string& sandbox_isa) {
101 return sandbox_isa + "-nonsfi";
102 }
103
96 // Looks up |property_name| in the vector |valid_names| with length 104 // Looks up |property_name| in the vector |valid_names| with length
97 // |valid_name_count|. Returns true if |property_name| is found. 105 // |valid_name_count|. Returns true if |property_name| is found.
98 bool FindMatchingProperty(const nacl::string& property_name, 106 bool FindMatchingProperty(const nacl::string& property_name,
99 const char** valid_names, 107 const char** valid_names,
100 size_t valid_name_count) { 108 size_t valid_name_count) {
101 for (size_t i = 0; i < valid_name_count; ++i) { 109 for (size_t i = 0; i < valid_name_count; ++i) {
102 if (property_name == valid_names[i]) { 110 if (property_name == valid_names[i]) {
103 return true; 111 return true;
104 } 112 }
105 } 113 }
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 // is validated to have keys from within the set of recognized ISAs. Unknown 260 // is validated to have keys from within the set of recognized ISAs. Unknown
253 // ISAs are allowed, but ignored and warnings are produced. It is also validated 261 // ISAs are allowed, but ignored and warnings are produced. It is also validated
254 // that it must have an entry to match the ISA specified in |sandbox_isa| or 262 // that it must have an entry to match the ISA specified in |sandbox_isa| or
255 // have a fallback 'portable' entry if there is no match. Returns true if 263 // have a fallback 'portable' entry if there is no match. Returns true if
256 // |dictionary| is an ISA to URL map. Sets |error_info| to something 264 // |dictionary| is an ISA to URL map. Sets |error_info| to something
257 // descriptive if it fails. 265 // descriptive if it fails.
258 bool IsValidISADictionary(const Json::Value& dictionary, 266 bool IsValidISADictionary(const Json::Value& dictionary,
259 const nacl::string& parent_key, 267 const nacl::string& parent_key,
260 const nacl::string& sandbox_isa, 268 const nacl::string& sandbox_isa,
261 bool must_find_matching_entry, 269 bool must_find_matching_entry,
270 bool nonsfi_enabled,
262 ErrorInfo* error_info) { 271 ErrorInfo* error_info) {
263 if (error_info == NULL) return false; 272 if (error_info == NULL) return false;
264 273
265 // An ISA to URL dictionary has to be an object. 274 // An ISA to URL dictionary has to be an object.
266 if (!dictionary.isObject()) { 275 if (!dictionary.isObject()) {
267 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, 276 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE,
268 nacl::string("manifest: ") + parent_key + 277 nacl::string("manifest: ") + parent_key +
269 " property is not an ISA to URL dictionary"); 278 " property is not an ISA to URL dictionary");
270 return false; 279 return false;
271 } 280 }
272 // Build the set of reserved ISA dictionary keys. 281 // Build the set of reserved ISA dictionary keys.
273 const char** isaProperties; 282 const char** isaProperties;
274 size_t isaPropertiesLength; 283 size_t isaPropertiesLength;
275 if (sandbox_isa == kPortableKey) { 284 if (sandbox_isa == kPortableKey) {
276 // The known values for PNaCl ISA dictionaries in the manifest. 285 // The known values for PNaCl ISA dictionaries in the manifest.
277 static const char* kPnaclManifestISAProperties[] = { 286 static const char* kPnaclManifestISAProperties[] = {
278 kPortableKey 287 kPortableKey
279 }; 288 };
280 isaProperties = kPnaclManifestISAProperties; 289 isaProperties = kPnaclManifestISAProperties;
281 isaPropertiesLength = NACL_ARRAY_SIZE(kPnaclManifestISAProperties); 290 isaPropertiesLength = NACL_ARRAY_SIZE(kPnaclManifestISAProperties);
282 } else { 291 } else {
283 // The known values for NaCl ISA dictionaries in the manifest. 292 // The known values for NaCl ISA dictionaries in the manifest.
284 static const char* kNaClManifestISAProperties[] = { 293 static const char* kNaClManifestISAProperties[] = {
285 kX8632Key, 294 kX8632Key,
295 kX8632NonSFIKey,
286 kX8664Key, 296 kX8664Key,
297 kX8664NonSFIKey,
287 kArmKey, 298 kArmKey,
299 kArmNonSFIKey,
288 // "portable" is here to allow checking that, if present, it can 300 // "portable" is here to allow checking that, if present, it can
289 // only refer to an URL, such as for a data file, and not to 301 // only refer to an URL, such as for a data file, and not to
290 // "pnacl-translate", which would cause the creation of a nexe. 302 // "pnacl-translate", which would cause the creation of a nexe.
291 kPortableKey 303 kPortableKey
292 }; 304 };
293 isaProperties = kNaClManifestISAProperties; 305 isaProperties = kNaClManifestISAProperties;
294 isaPropertiesLength = NACL_ARRAY_SIZE(kNaClManifestISAProperties); 306 isaPropertiesLength = NACL_ARRAY_SIZE(kNaClManifestISAProperties);
295 } 307 }
296 // Check that entries in the dictionary are structurally correct. 308 // Check that entries in the dictionary are structurally correct.
297 Json::Value::Members members = dictionary.getMemberNames(); 309 Json::Value::Members members = dictionary.getMemberNames();
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 error_info->SetReport( 358 error_info->SetReport(
347 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH, 359 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH,
348 nacl::string("manifest: no version of ") + parent_key + 360 nacl::string("manifest: no version of ") + parent_key +
349 " given for portable."); 361 " given for portable.");
350 return false; 362 return false;
351 } 363 }
352 } else if (must_find_matching_entry) { 364 } else if (must_find_matching_entry) {
353 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include 365 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include
354 // micro-architectures that can resolve to multiple valid sandboxes. 366 // micro-architectures that can resolve to multiple valid sandboxes.
355 bool has_isa = dictionary.isMember(sandbox_isa); 367 bool has_isa = dictionary.isMember(sandbox_isa);
368 bool has_nonsfi_isa =
369 nonsfi_enabled && dictionary.isMember(GetNonSFIKey(sandbox_isa));
356 bool has_portable = dictionary.isMember(kPortableKey); 370 bool has_portable = dictionary.isMember(kPortableKey);
357 371
358 if (!has_isa && !has_portable) { 372 if (!has_isa && !has_nonsfi_isa && !has_portable) {
359 error_info->SetReport( 373 error_info->SetReport(
360 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH, 374 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH,
361 nacl::string("manifest: no version of ") + parent_key + 375 nacl::string("manifest: no version of ") + parent_key +
362 " given for current arch and no portable version found."); 376 " given for current arch and no portable version found.");
363 return false; 377 return false;
364 } 378 }
365 } 379 }
366 380
367 return true; 381 return true;
368 } 382 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 return false; 446 return false;
433 } 447 }
434 448
435 // Validate the program section. 449 // Validate the program section.
436 // There must be a matching (portable or sandbox_isa_) entry for program for 450 // There must be a matching (portable or sandbox_isa_) entry for program for
437 // NaCl. 451 // NaCl.
438 if (!IsValidISADictionary(dictionary_[kProgramKey], 452 if (!IsValidISADictionary(dictionary_[kProgramKey],
439 kProgramKey, 453 kProgramKey,
440 sandbox_isa_, 454 sandbox_isa_,
441 true, 455 true,
456 nonsfi_enabled_,
442 error_info)) { 457 error_info)) {
443 return false; 458 return false;
444 } 459 }
445 460
446 // Validate the interpreter section (if given). 461 // Validate the interpreter section (if given).
447 // There must be a matching (portable or sandbox_isa_) entry for interpreter 462 // There must be a matching (portable or sandbox_isa_) entry for interpreter
448 // for NaCl. 463 // for NaCl.
449 if (dictionary_.isMember(kInterpreterKey)) { 464 if (dictionary_.isMember(kInterpreterKey)) {
450 if (!IsValidISADictionary(dictionary_[kInterpreterKey], 465 if (!IsValidISADictionary(dictionary_[kInterpreterKey],
451 kInterpreterKey, 466 kInterpreterKey,
452 sandbox_isa_, 467 sandbox_isa_,
453 true, 468 true,
469 nonsfi_enabled_,
454 error_info)) { 470 error_info)) {
455 return false; 471 return false;
456 } 472 }
457 } 473 }
458 474
459 // Validate the file dictionary (if given). 475 // Validate the file dictionary (if given).
460 // The "files" key does not require a matching (portable or sandbox_isa_) 476 // The "files" key does not require a matching (portable or sandbox_isa_)
461 // entry at schema validation time for NaCl. This allows manifests to specify 477 // entry at schema validation time for NaCl. This allows manifests to specify
462 // resources that are only loaded for a particular sandbox_isa. 478 // resources that are only loaded for a particular sandbox_isa.
463 if (dictionary_.isMember(kFilesKey)) { 479 if (dictionary_.isMember(kFilesKey)) {
464 const Json::Value& files = dictionary_[kFilesKey]; 480 const Json::Value& files = dictionary_[kFilesKey];
465 if (!files.isObject()) { 481 if (!files.isObject()) {
466 error_info->SetReport( 482 error_info->SetReport(
467 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, 483 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE,
468 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary."); 484 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary.");
469 } 485 }
470 Json::Value::Members members = files.getMemberNames(); 486 Json::Value::Members members = files.getMemberNames();
471 for (size_t i = 0; i < members.size(); ++i) { 487 for (size_t i = 0; i < members.size(); ++i) {
472 nacl::string file_name = members[i]; 488 nacl::string file_name = members[i];
473 if (!IsValidISADictionary(files[file_name], 489 if (!IsValidISADictionary(files[file_name],
474 file_name, 490 file_name,
475 sandbox_isa_, 491 sandbox_isa_,
476 false, 492 false,
493 nonsfi_enabled_,
477 error_info)) { 494 error_info)) {
478 return false; 495 return false;
479 } 496 }
480 } 497 }
481 } 498 }
482 499
483 return true; 500 return true;
484 } 501 }
485 502
486 bool JsonManifest::GetURLFromISADictionary(const Json::Value& dictionary, 503 bool JsonManifest::GetURLFromISADictionary(const Json::Value& dictionary,
487 const nacl::string& parent_key, 504 const nacl::string& parent_key,
488 nacl::string* url, 505 nacl::string* url,
489 PnaclOptions* pnacl_options, 506 PnaclOptions* pnacl_options,
507 bool* uses_nonsfi_mode,
490 ErrorInfo* error_info) const { 508 ErrorInfo* error_info) const {
491 DCHECK(url != NULL && pnacl_options != NULL && error_info != NULL); 509 DCHECK(url != NULL && pnacl_options != NULL && error_info != NULL);
492 510
493 // When the application actually requests a resolved URL, we must have 511 // When the application actually requests a resolved URL, we must have
494 // a matching entry (sandbox_isa_ or portable) for NaCl. 512 // a matching entry (sandbox_isa_ or portable) for NaCl.
495 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa_, true, 513 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa_, true,
496 error_info)) { 514 nonsfi_enabled_, error_info)) {
497 error_info->SetReport(PP_NACL_ERROR_MANIFEST_RESOLVE_URL, 515 error_info->SetReport(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
498 "architecture " + sandbox_isa_ + 516 "architecture " + sandbox_isa_ +
499 " is not found for file " + parent_key); 517 " is not found for file " + parent_key);
500 return false; 518 return false;
501 } 519 }
502 520
503 *url = "";
504
505 // The call to IsValidISADictionary() above guarantees that either 521 // The call to IsValidISADictionary() above guarantees that either
506 // sandbox_isa_ or kPortableKey is present in the dictionary. 522 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the
507 bool has_portable = dictionary.isMember(kPortableKey); 523 // dictionary.
508 bool has_isa = dictionary.isMember(sandbox_isa_); 524 *uses_nonsfi_mode = false;
509 nacl::string chosen_isa; 525 nacl::string chosen_isa;
510 if ((sandbox_isa_ == kPortableKey) || (has_portable && !has_isa)) { 526 if (sandbox_isa_ == kPortableKey) {
511 chosen_isa = kPortableKey; 527 chosen_isa = kPortableKey;
512 } else { 528 } else {
513 chosen_isa = sandbox_isa_; 529 nacl::string nonsfi_isa = GetNonSFIKey(sandbox_isa_);
530 if (nonsfi_enabled_ && dictionary.isMember(nonsfi_isa)) {
531 chosen_isa = nonsfi_isa;
532 *uses_nonsfi_mode = true;
533 } else if (dictionary.isMember(sandbox_isa_)) {
534 chosen_isa = sandbox_isa_;
535 } else if (dictionary.isMember(kPortableKey)) {
536 chosen_isa = kPortableKey;
537 } else {
538 // Should not reach here, because the earlier IsValidISADictionary()
539 // call checked that the manifest covers the current architecture.
540 DCHECK(false);
541 }
514 } 542 }
543
515 const Json::Value& isa_spec = dictionary[chosen_isa]; 544 const Json::Value& isa_spec = dictionary[chosen_isa];
516 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first. 545 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first.
517 // If found, mark that it is a debug URL. Otherwise, fall back to 546 // If found, mark that it is a debug URL. Otherwise, fall back to
518 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL. 547 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL.
519 if (pnacl_debug_ && isa_spec.isMember(kPnaclDebugKey)) { 548 if (pnacl_debug_ && isa_spec.isMember(kPnaclDebugKey)) {
520 GrabUrlAndPnaclOptions(isa_spec[kPnaclDebugKey], url, pnacl_options); 549 GrabUrlAndPnaclOptions(isa_spec[kPnaclDebugKey], url, pnacl_options);
521 pnacl_options->set_debug(true); 550 pnacl_options->set_debug(true);
522 } else if (isa_spec.isMember(kPnaclTranslateKey)) { 551 } else if (isa_spec.isMember(kPnaclTranslateKey)) {
523 GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options); 552 GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options);
524 } else { 553 } else {
(...skipping 11 matching lines...) Expand all
536 PnaclOptions* pnacl_options, 565 PnaclOptions* pnacl_options,
537 ErrorInfo* error_info) const { 566 ErrorInfo* error_info) const {
538 DCHECK(full_url != NULL && pnacl_options != NULL && error_info != NULL); 567 DCHECK(full_url != NULL && pnacl_options != NULL && error_info != NULL);
539 if (!dictionary.isMember(key)) { 568 if (!dictionary.isMember(key)) {
540 error_info->SetReport(PP_NACL_ERROR_MANIFEST_RESOLVE_URL, 569 error_info->SetReport(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
541 "file key not found in manifest"); 570 "file key not found in manifest");
542 return false; 571 return false;
543 } 572 }
544 const Json::Value& isa_dict = dictionary[key]; 573 const Json::Value& isa_dict = dictionary[key];
545 nacl::string relative_url; 574 nacl::string relative_url;
575 bool uses_nonsfi_mode;
546 if (!GetURLFromISADictionary(isa_dict, key, &relative_url, 576 if (!GetURLFromISADictionary(isa_dict, key, &relative_url,
547 pnacl_options, error_info)) { 577 pnacl_options, &uses_nonsfi_mode, error_info)) {
548 return false; 578 return false;
549 } 579 }
550 return ResolveURL(relative_url, full_url, error_info); 580 return ResolveURL(relative_url, full_url, error_info);
551 } 581 }
552 582
553 bool JsonManifest::ResolveURL(const nacl::string& relative_url, 583 bool JsonManifest::ResolveURL(const nacl::string& relative_url,
554 nacl::string* full_url, 584 nacl::string* full_url,
555 ErrorInfo* error_info) const { 585 ErrorInfo* error_info) const {
556 // The contents of the manifest are resolved relative to the manifest URL. 586 // The contents of the manifest are resolved relative to the manifest URL.
557 CHECK(url_util_ != NULL); 587 CHECK(url_util_ != NULL);
558 pp::Var resolved_url = 588 pp::Var resolved_url =
559 url_util_->ResolveRelativeToURL(pp::Var(manifest_base_url_), 589 url_util_->ResolveRelativeToURL(pp::Var(manifest_base_url_),
560 relative_url); 590 relative_url);
561 if (!resolved_url.is_string()) { 591 if (!resolved_url.is_string()) {
562 error_info->SetReport( 592 error_info->SetReport(
563 PP_NACL_ERROR_MANIFEST_RESOLVE_URL, 593 PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
564 "could not resolve url '" + relative_url + 594 "could not resolve url '" + relative_url +
565 "' relative to manifest base url '" + manifest_base_url_.c_str() + 595 "' relative to manifest base url '" + manifest_base_url_.c_str() +
566 "'."); 596 "'.");
567 return false; 597 return false;
568 } 598 }
569 *full_url = resolved_url.AsString(); 599 *full_url = resolved_url.AsString();
570 return true; 600 return true;
571 } 601 }
572 602
573 bool JsonManifest::GetProgramURL(nacl::string* full_url, 603 bool JsonManifest::GetProgramURL(nacl::string* full_url,
574 PnaclOptions* pnacl_options, 604 PnaclOptions* pnacl_options,
605 bool* uses_nonsfi_mode,
575 ErrorInfo* error_info) const { 606 ErrorInfo* error_info) const {
576 if (full_url == NULL || pnacl_options == NULL || error_info == NULL) 607 if (full_url == NULL || pnacl_options == NULL || error_info == NULL)
577 return false; 608 return false;
578 609
579 Json::Value program = dictionary_[kProgramKey]; 610 const Json::Value& program = dictionary_[kProgramKey];
580 611
581 nacl::string nexe_url; 612 nacl::string nexe_url;
582 nacl::string error_string; 613 nacl::string error_string;
583 614
584 if (!GetURLFromISADictionary(program, 615 if (!GetURLFromISADictionary(program,
585 kProgramKey, 616 kProgramKey,
586 &nexe_url, 617 &nexe_url,
587 pnacl_options, 618 pnacl_options,
619 uses_nonsfi_mode,
588 error_info)) { 620 error_info)) {
589 return false; 621 return false;
590 } 622 }
591 623
592 return ResolveURL(nexe_url, full_url, error_info); 624 return ResolveURL(nexe_url, full_url, error_info);
593 } 625 }
594 626
595 bool JsonManifest::GetFileKeys(std::set<nacl::string>* keys) const { 627 bool JsonManifest::GetFileKeys(std::set<nacl::string>* keys) const {
596 if (!dictionary_.isMember(kFilesKey)) { 628 if (!dictionary_.isMember(kFilesKey)) {
597 // trivial success: no keys when there is no "files" section. 629 // trivial success: no keys when there is no "files" section.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
648 if (!files.isMember(rest)) { 680 if (!files.isMember(rest)) {
649 error_info->SetReport( 681 error_info->SetReport(
650 PP_NACL_ERROR_MANIFEST_RESOLVE_URL, 682 PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
651 nacl::string("ResolveKey: no such \"files\" entry: ") + key); 683 nacl::string("ResolveKey: no such \"files\" entry: ") + key);
652 return false; 684 return false;
653 } 685 }
654 return GetKeyUrl(files, rest, full_url, pnacl_options, error_info); 686 return GetKeyUrl(files, rest, full_url, pnacl_options, error_info);
655 } 687 }
656 688
657 } // namespace plugin 689 } // namespace plugin
OLDNEW
« no previous file with comments | « ppapi/native_client/src/trusted/plugin/json_manifest.h ('k') | ppapi/native_client/src/trusted/plugin/manifest.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698