OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/nacl/renderer/ppb_nacl_private_impl.h" | 5 #include "components/nacl/renderer/ppb_nacl_private_impl.h" |
6 | 6 |
7 #include <numeric> | 7 #include <numeric> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
14 #include "base/containers/scoped_ptr_hash_map.h" | 14 #include "base/containers/scoped_ptr_hash_map.h" |
15 #include "base/cpu.h" | 15 #include "base/cpu.h" |
16 #include "base/lazy_instance.h" | 16 #include "base/lazy_instance.h" |
17 #include "base/logging.h" | 17 #include "base/logging.h" |
18 #include "base/rand_util.h" | 18 #include "base/rand_util.h" |
19 #include "components/nacl/common/nacl_host_messages.h" | 19 #include "components/nacl/common/nacl_host_messages.h" |
20 #include "components/nacl/common/nacl_messages.h" | 20 #include "components/nacl/common/nacl_messages.h" |
21 #include "components/nacl/common/nacl_nonsfi_util.h" | 21 #include "components/nacl/common/nacl_nonsfi_util.h" |
22 #include "components/nacl/common/nacl_switches.h" | 22 #include "components/nacl/common/nacl_switches.h" |
23 #include "components/nacl/common/nacl_types.h" | 23 #include "components/nacl/common/nacl_types.h" |
| 24 #include "components/nacl/renderer/file_downloader.h" |
24 #include "components/nacl/renderer/histogram.h" | 25 #include "components/nacl/renderer/histogram.h" |
25 #include "components/nacl/renderer/json_manifest.h" | 26 #include "components/nacl/renderer/json_manifest.h" |
26 #include "components/nacl/renderer/manifest_downloader.h" | 27 #include "components/nacl/renderer/manifest_downloader.h" |
27 #include "components/nacl/renderer/manifest_service_channel.h" | 28 #include "components/nacl/renderer/manifest_service_channel.h" |
28 #include "components/nacl/renderer/nexe_load_manager.h" | 29 #include "components/nacl/renderer/nexe_load_manager.h" |
29 #include "components/nacl/renderer/pnacl_translation_resource_host.h" | 30 #include "components/nacl/renderer/pnacl_translation_resource_host.h" |
30 #include "components/nacl/renderer/progress_event.h" | 31 #include "components/nacl/renderer/progress_event.h" |
31 #include "components/nacl/renderer/sandbox_arch.h" | 32 #include "components/nacl/renderer/sandbox_arch.h" |
32 #include "components/nacl/renderer/trusted_plugin_channel.h" | 33 #include "components/nacl/renderer/trusted_plugin_channel.h" |
33 #include "content/public/common/content_client.h" | 34 #include "content/public/common/content_client.h" |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 bool result = PP_ToBool(manifest_service_.Quit(user_data_)); | 249 bool result = PP_ToBool(manifest_service_.Quit(user_data_)); |
249 DCHECK(!result); | 250 DCHECK(!result); |
250 user_data_ = NULL; | 251 user_data_ = NULL; |
251 } | 252 } |
252 | 253 |
253 PPP_ManifestService manifest_service_; | 254 PPP_ManifestService manifest_service_; |
254 void* user_data_; | 255 void* user_data_; |
255 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy); | 256 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy); |
256 }; | 257 }; |
257 | 258 |
| 259 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document, |
| 260 const GURL& gurl) { |
| 261 blink::WebURLLoaderOptions options; |
| 262 options.untrustedHTTP = true; |
| 263 |
| 264 // Options settings here follow the original behavior in the trusted |
| 265 // plugin and PepperURLLoaderHost. |
| 266 if (document.securityOrigin().canRequest(gurl)) { |
| 267 options.allowCredentials = true; |
| 268 } else { |
| 269 // Allow CORS. |
| 270 options.crossOriginRequestPolicy = |
| 271 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; |
| 272 } |
| 273 return document.frame()->createAssociatedURLLoader(options); |
| 274 } |
| 275 |
| 276 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document, |
| 277 const GURL& gurl) { |
| 278 blink::WebURLRequest request; |
| 279 request.initialize(); |
| 280 request.setURL(gurl); |
| 281 request.setFirstPartyForCookies(document.firstPartyForCookies()); |
| 282 return request; |
| 283 } |
| 284 |
258 // Launch NaCl's sel_ldr process. | 285 // Launch NaCl's sel_ldr process. |
259 void LaunchSelLdr(PP_Instance instance, | 286 void LaunchSelLdr(PP_Instance instance, |
260 PP_Bool main_service_runtime, | 287 PP_Bool main_service_runtime, |
261 const char* alleged_url, | 288 const char* alleged_url, |
262 PP_Bool uses_irt, | 289 PP_Bool uses_irt, |
263 PP_Bool uses_ppapi, | 290 PP_Bool uses_ppapi, |
264 PP_Bool uses_nonsfi_mode, | 291 PP_Bool uses_nonsfi_mode, |
265 PP_Bool enable_ppapi_dev, | 292 PP_Bool enable_ppapi_dev, |
266 PP_Bool enable_dyncode_syscalls, | 293 PP_Bool enable_dyncode_syscalls, |
267 PP_Bool enable_exception_handling, | 294 PP_Bool enable_exception_handling, |
(...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
661 uint64_t loaded_bytes, | 688 uint64_t loaded_bytes, |
662 uint64_t total_bytes) { | 689 uint64_t total_bytes) { |
663 ProgressEvent event(event_type, | 690 ProgressEvent event(event_type, |
664 resource_url, | 691 resource_url, |
665 PP_ToBool(length_is_computable), | 692 PP_ToBool(length_is_computable), |
666 loaded_bytes, | 693 loaded_bytes, |
667 total_bytes); | 694 total_bytes); |
668 DispatchProgressEvent(instance, event); | 695 DispatchProgressEvent(instance, event); |
669 } | 696 } |
670 | 697 |
671 void NexeFileDidOpen(PP_Instance instance, | |
672 int32_t pp_error, | |
673 int32_t fd, | |
674 int32_t http_status, | |
675 int64_t nexe_bytes_read, | |
676 const char* url, | |
677 int64_t time_since_open) { | |
678 NexeLoadManager* load_manager = GetNexeLoadManager(instance); | |
679 if (load_manager) { | |
680 load_manager->NexeFileDidOpen(pp_error, | |
681 fd, | |
682 http_status, | |
683 nexe_bytes_read, | |
684 url, | |
685 time_since_open); | |
686 } | |
687 } | |
688 | |
689 void ReportLoadSuccess(PP_Instance instance, | 698 void ReportLoadSuccess(PP_Instance instance, |
690 const char* url, | 699 const char* url, |
691 uint64_t loaded_bytes, | 700 uint64_t loaded_bytes, |
692 uint64_t total_bytes) { | 701 uint64_t total_bytes) { |
693 NexeLoadManager* load_manager = GetNexeLoadManager(instance); | 702 NexeLoadManager* load_manager = GetNexeLoadManager(instance); |
694 if (load_manager) | 703 if (load_manager) |
695 load_manager->ReportLoadSuccess(url, loaded_bytes, total_bytes); | 704 load_manager->ReportLoadSuccess(url, loaded_bytes, total_bytes); |
696 } | 705 } |
697 | 706 |
698 void ReportLoadError(PP_Instance instance, | 707 void ReportLoadError(PP_Instance instance, |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
912 struct PP_Var* out_data, | 921 struct PP_Var* out_data, |
913 base::Time start_time, | 922 base::Time start_time, |
914 PP_NaClError pp_nacl_error, | 923 PP_NaClError pp_nacl_error, |
915 const std::string& data); | 924 const std::string& data); |
916 | 925 |
917 void DownloadManifestToBuffer(PP_Instance instance, | 926 void DownloadManifestToBuffer(PP_Instance instance, |
918 struct PP_Var* out_data, | 927 struct PP_Var* out_data, |
919 struct PP_CompletionCallback callback) { | 928 struct PP_CompletionCallback callback) { |
920 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance); | 929 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance); |
921 DCHECK(load_manager); | 930 DCHECK(load_manager); |
922 if (!load_manager) { | 931 content::PepperPluginInstance* plugin_instance = |
| 932 content::PepperPluginInstance::Get(instance); |
| 933 if (!load_manager || !plugin_instance) { |
923 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask( | 934 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask( |
924 FROM_HERE, | 935 FROM_HERE, |
925 base::Bind(callback.func, callback.user_data, | 936 base::Bind(callback.func, callback.user_data, |
926 static_cast<int32_t>(PP_ERROR_FAILED))); | 937 static_cast<int32_t>(PP_ERROR_FAILED))); |
927 } | 938 } |
| 939 const blink::WebDocument& document = |
| 940 plugin_instance->GetContainer()->element().document(); |
928 | 941 |
929 const GURL& gurl = load_manager->manifest_base_url(); | 942 const GURL& gurl = load_manager->manifest_base_url(); |
930 | 943 scoped_ptr<blink::WebURLLoader> url_loader( |
931 content::PepperPluginInstance* plugin_instance = | 944 CreateWebURLLoader(document, gurl)); |
932 content::PepperPluginInstance::Get(instance); | 945 blink::WebURLRequest request = CreateWebURLRequest(document, gurl); |
933 blink::WebURLLoaderOptions options; | |
934 options.untrustedHTTP = true; | |
935 | |
936 blink::WebSecurityOrigin security_origin = | |
937 plugin_instance->GetContainer()->element().document().securityOrigin(); | |
938 // Options settings here follow the original behavior in the trusted | |
939 // plugin and PepperURLLoaderHost. | |
940 if (security_origin.canRequest(gurl)) { | |
941 options.allowCredentials = true; | |
942 } else { | |
943 // Allow CORS. | |
944 options.crossOriginRequestPolicy = | |
945 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; | |
946 } | |
947 | |
948 blink::WebFrame* frame = | |
949 plugin_instance->GetContainer()->element().document().frame(); | |
950 blink::WebURLLoader* url_loader = frame->createAssociatedURLLoader(options); | |
951 blink::WebURLRequest request; | |
952 request.initialize(); | |
953 request.setURL(gurl); | |
954 request.setFirstPartyForCookies(frame->document().firstPartyForCookies()); | |
955 | 946 |
956 // ManifestDownloader deletes itself after invoking the callback. | 947 // ManifestDownloader deletes itself after invoking the callback. |
957 ManifestDownloader* client = new ManifestDownloader( | 948 ManifestDownloader* manifest_downloader = new ManifestDownloader( |
| 949 url_loader.Pass(), |
958 load_manager->is_installed(), | 950 load_manager->is_installed(), |
959 base::Bind(DownloadManifestToBufferCompletion, | 951 base::Bind(DownloadManifestToBufferCompletion, |
960 instance, callback, out_data, base::Time::Now())); | 952 instance, callback, out_data, base::Time::Now())); |
961 url_loader->loadAsynchronously(request, client); | 953 manifest_downloader->Load(request); |
962 } | 954 } |
963 | 955 |
964 void DownloadManifestToBufferCompletion(PP_Instance instance, | 956 void DownloadManifestToBufferCompletion(PP_Instance instance, |
965 struct PP_CompletionCallback callback, | 957 struct PP_CompletionCallback callback, |
966 struct PP_Var* out_data, | 958 struct PP_Var* out_data, |
967 base::Time start_time, | 959 base::Time start_time, |
968 PP_NaClError pp_nacl_error, | 960 PP_NaClError pp_nacl_error, |
969 const std::string& data) { | 961 const std::string& data) { |
970 base::TimeDelta download_time = base::Time::Now() - start_time; | 962 base::TimeDelta download_time = base::Time::Now() - start_time; |
971 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload", | 963 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload", |
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1275 } | 1267 } |
1276 | 1268 |
1277 void PostMessageToJavaScript(PP_Instance instance, const char* message) { | 1269 void PostMessageToJavaScript(PP_Instance instance, const char* message) { |
1278 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask( | 1270 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask( |
1279 FROM_HERE, | 1271 FROM_HERE, |
1280 base::Bind(&PostMessageToJavaScriptMainThread, | 1272 base::Bind(&PostMessageToJavaScriptMainThread, |
1281 instance, | 1273 instance, |
1282 std::string(message))); | 1274 std::string(message))); |
1283 } | 1275 } |
1284 | 1276 |
| 1277 // Encapsulates some of the state for a call to DownloadNexe to prevent |
| 1278 // argument lists from getting too long. |
| 1279 struct DownloadNexeRequest { |
| 1280 PP_Instance instance; |
| 1281 std::string url; |
| 1282 PP_CompletionCallback callback; |
| 1283 base::Time start_time; |
| 1284 }; |
| 1285 |
| 1286 // A utility class to ensure that we don't send progress events more often than |
| 1287 // every 10ms for a given file. |
| 1288 class ProgressEventRateLimiter { |
| 1289 public: |
| 1290 explicit ProgressEventRateLimiter(PP_Instance instance) |
| 1291 : instance_(instance) { } |
| 1292 |
| 1293 void ReportProgress(const std::string& url, |
| 1294 int64_t total_bytes_received, |
| 1295 int64_t total_bytes_to_be_received) { |
| 1296 base::Time now = base::Time::Now(); |
| 1297 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) { |
| 1298 DispatchProgressEvent(instance_, |
| 1299 ProgressEvent(PP_NACL_EVENT_PROGRESS, |
| 1300 url, |
| 1301 total_bytes_to_be_received >= 0, |
| 1302 total_bytes_received, |
| 1303 total_bytes_to_be_received)); |
| 1304 last_event_ = now; |
| 1305 } |
| 1306 } |
| 1307 |
| 1308 private: |
| 1309 PP_Instance instance_; |
| 1310 base::Time last_event_; |
| 1311 }; |
| 1312 |
| 1313 void DownloadNexeCompletion(const DownloadNexeRequest& request, |
| 1314 base::PlatformFile target_file, |
| 1315 PP_FileHandle* out_handle, |
| 1316 FileDownloader::Status status, |
| 1317 int http_status); |
| 1318 |
| 1319 void DownloadNexe(PP_Instance instance, |
| 1320 const char* url, |
| 1321 PP_FileHandle* out_handle, |
| 1322 PP_CompletionCallback callback) { |
| 1323 CHECK(url); |
| 1324 CHECK(out_handle); |
| 1325 DownloadNexeRequest request; |
| 1326 request.instance = instance; |
| 1327 request.url = url; |
| 1328 request.callback = callback; |
| 1329 request.start_time = base::Time::Now(); |
| 1330 |
| 1331 // Try the fast path for retrieving the file first. |
| 1332 uint64_t file_token_lo = 0; |
| 1333 uint64_t file_token_hi = 0; |
| 1334 PP_FileHandle file_handle = OpenNaClExecutable(instance, |
| 1335 url, |
| 1336 &file_token_lo, |
| 1337 &file_token_hi); |
| 1338 if (file_handle != PP_kInvalidFileHandle) { |
| 1339 DownloadNexeCompletion(request, |
| 1340 file_handle, |
| 1341 out_handle, |
| 1342 FileDownloader::SUCCESS, |
| 1343 200); |
| 1344 return; |
| 1345 } |
| 1346 |
| 1347 // The fast path didn't work, we'll fetch the file using URLLoader and write |
| 1348 // it to local storage. |
| 1349 base::PlatformFile target_file = CreateTemporaryFile(instance); |
| 1350 GURL gurl(url); |
| 1351 |
| 1352 content::PepperPluginInstance* plugin_instance = |
| 1353 content::PepperPluginInstance::Get(instance); |
| 1354 if (!plugin_instance) { |
| 1355 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask( |
| 1356 FROM_HERE, |
| 1357 base::Bind(callback.func, callback.user_data, |
| 1358 static_cast<int32_t>(PP_ERROR_FAILED))); |
| 1359 } |
| 1360 const blink::WebDocument& document = |
| 1361 plugin_instance->GetContainer()->element().document(); |
| 1362 scoped_ptr<blink::WebURLLoader> url_loader( |
| 1363 CreateWebURLLoader(document, gurl)); |
| 1364 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl); |
| 1365 |
| 1366 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance); |
| 1367 |
| 1368 // FileDownloader deletes itself after invoking DownloadNexeCompletion. |
| 1369 FileDownloader* file_downloader = new FileDownloader( |
| 1370 url_loader.Pass(), |
| 1371 target_file, |
| 1372 base::Bind(&DownloadNexeCompletion, request, target_file, out_handle), |
| 1373 base::Bind(&ProgressEventRateLimiter::ReportProgress, |
| 1374 base::Owned(tracker), url)); |
| 1375 file_downloader->Load(url_request); |
| 1376 } |
| 1377 |
| 1378 void DownloadNexeCompletion(const DownloadNexeRequest& request, |
| 1379 base::PlatformFile target_file, |
| 1380 PP_FileHandle* out_handle, |
| 1381 FileDownloader::Status status, |
| 1382 int http_status) { |
| 1383 int32_t pp_error; |
| 1384 switch (status) { |
| 1385 case FileDownloader::SUCCESS: |
| 1386 *out_handle = target_file; |
| 1387 pp_error = PP_OK; |
| 1388 break; |
| 1389 case FileDownloader::ACCESS_DENIED: |
| 1390 pp_error = PP_ERROR_NOACCESS; |
| 1391 break; |
| 1392 case FileDownloader::FAILED: |
| 1393 pp_error = PP_ERROR_FAILED; |
| 1394 break; |
| 1395 default: |
| 1396 NOTREACHED(); |
| 1397 return; |
| 1398 } |
| 1399 |
| 1400 int64_t bytes_read = -1; |
| 1401 if (pp_error == PP_OK && target_file != base::kInvalidPlatformFileValue) { |
| 1402 base::PlatformFileInfo info; |
| 1403 if (GetPlatformFileInfo(target_file, &info)) |
| 1404 bytes_read = info.size; |
| 1405 } |
| 1406 |
| 1407 if (bytes_read == -1) { |
| 1408 base::ClosePlatformFile(target_file); |
| 1409 pp_error = PP_ERROR_FAILED; |
| 1410 } |
| 1411 |
| 1412 base::TimeDelta download_time = base::Time::Now() - request.start_time; |
| 1413 |
| 1414 NexeLoadManager* load_manager = GetNexeLoadManager(request.instance); |
| 1415 if (load_manager) { |
| 1416 load_manager->NexeFileDidOpen(pp_error, |
| 1417 target_file, |
| 1418 http_status, |
| 1419 bytes_read, |
| 1420 request.url, |
| 1421 download_time); |
| 1422 } |
| 1423 |
| 1424 request.callback.func(request.callback.user_data, pp_error); |
| 1425 } |
| 1426 |
1285 const PPB_NaCl_Private nacl_interface = { | 1427 const PPB_NaCl_Private nacl_interface = { |
1286 &LaunchSelLdr, | 1428 &LaunchSelLdr, |
1287 &StartPpapiProxy, | 1429 &StartPpapiProxy, |
1288 &UrandomFD, | 1430 &UrandomFD, |
1289 &Are3DInterfacesDisabled, | 1431 &Are3DInterfacesDisabled, |
1290 &BrokerDuplicateHandle, | 1432 &BrokerDuplicateHandle, |
1291 &GetReadonlyPnaclFD, | 1433 &GetReadonlyPnaclFD, |
1292 &CreateTemporaryFile, | 1434 &CreateTemporaryFile, |
1293 &GetNumberOfProcessors, | 1435 &GetNumberOfProcessors, |
1294 &PPIsNonSFIModeEnabled, | 1436 &PPIsNonSFIModeEnabled, |
1295 &GetNexeFd, | 1437 &GetNexeFd, |
1296 &ReportTranslationFinished, | 1438 &ReportTranslationFinished, |
1297 &OpenNaClExecutable, | 1439 &OpenNaClExecutable, |
1298 &DispatchEvent, | 1440 &DispatchEvent, |
1299 &NexeFileDidOpen, | |
1300 &ReportLoadSuccess, | 1441 &ReportLoadSuccess, |
1301 &ReportLoadError, | 1442 &ReportLoadError, |
1302 &ReportLoadAbort, | 1443 &ReportLoadAbort, |
1303 &NexeDidCrash, | 1444 &NexeDidCrash, |
1304 &InstanceCreated, | 1445 &InstanceCreated, |
1305 &InstanceDestroyed, | 1446 &InstanceDestroyed, |
1306 &NaClDebugEnabledForURL, | 1447 &NaClDebugEnabledForURL, |
1307 &GetSandboxArch, | 1448 &GetSandboxArch, |
1308 &LogToConsole, | 1449 &LogToConsole, |
1309 &GetNaClReadyState, | 1450 &GetNaClReadyState, |
1310 &GetIsInstalled, | 1451 &GetIsInstalled, |
1311 &GetExitStatus, | 1452 &GetExitStatus, |
1312 &SetExitStatus, | 1453 &SetExitStatus, |
1313 &Vlog, | 1454 &Vlog, |
1314 &InitializePlugin, | 1455 &InitializePlugin, |
1315 &GetNexeSize, | 1456 &GetNexeSize, |
1316 &RequestNaClManifest, | 1457 &RequestNaClManifest, |
1317 &GetManifestBaseURL, | 1458 &GetManifestBaseURL, |
1318 &ResolvesRelativeToPluginBaseURL, | 1459 &ResolvesRelativeToPluginBaseURL, |
1319 &ProcessNaClManifest, | 1460 &ProcessNaClManifest, |
1320 &GetManifestURLArgument, | 1461 &GetManifestURLArgument, |
1321 &DevInterfacesEnabled, | 1462 &DevInterfacesEnabled, |
1322 &CreatePNaClManifest, | 1463 &CreatePNaClManifest, |
1323 &CreateJsonManifest, | 1464 &CreateJsonManifest, |
1324 &DestroyManifest, | 1465 &DestroyManifest, |
1325 &ManifestGetProgramURL, | 1466 &ManifestGetProgramURL, |
1326 &ManifestResolveKey, | 1467 &ManifestResolveKey, |
1327 &GetPNaClResourceInfo, | 1468 &GetPNaClResourceInfo, |
1328 &GetCpuFeatureAttrs, | 1469 &GetCpuFeatureAttrs, |
1329 &PostMessageToJavaScript | 1470 &PostMessageToJavaScript, |
| 1471 &DownloadNexe |
1330 }; | 1472 }; |
1331 | 1473 |
1332 } // namespace | 1474 } // namespace |
1333 | 1475 |
1334 const PPB_NaCl_Private* GetNaClPrivateInterface() { | 1476 const PPB_NaCl_Private* GetNaClPrivateInterface() { |
1335 return &nacl_interface; | 1477 return &nacl_interface; |
1336 } | 1478 } |
1337 | 1479 |
1338 } // namespace nacl | 1480 } // namespace nacl |
OLD | NEW |