Mime sniffer: reduce table from 256 bytes to 4
[chromium-blink-merge.git] / gin / v8_initializer.cc
blob8f79d0d8693b23b01820e9906dad5da48a64bb77
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
3 // found in the LICENSE file.
5 #include "gin/v8_initializer.h"
7 #include "base/basictypes.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/rand_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/time.h"
17 #include "crypto/sha2.h"
19 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
20 #if defined(OS_MACOSX)
21 #include "base/mac/foundation_util.h"
22 #endif // OS_MACOSX
23 #include "base/path_service.h"
24 #endif // V8_USE_EXTERNAL_STARTUP_DATA
26 namespace gin {
28 namespace {
30 base::MemoryMappedFile* g_mapped_natives = nullptr;
31 base::MemoryMappedFile* g_mapped_snapshot = nullptr;
33 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
34 #if !defined(OS_MACOSX)
35 const int kV8SnapshotBasePathKey =
36 #if defined(OS_ANDROID)
37 base::DIR_ANDROID_APP_DATA;
38 #elif defined(OS_POSIX)
39 base::DIR_EXE;
40 #elif defined(OS_WIN)
41 base::DIR_MODULE;
42 #endif // OS_ANDROID
43 #endif // !OS_MACOSX
45 const char kNativesFileName[] = "natives_blob.bin";
46 const char kSnapshotFileName[] = "snapshot_blob.bin";
48 // Constants for snapshot loading retries taken from:
49 // https://support.microsoft.com/en-us/kb/316609.
50 const int kMaxOpenAttempts = 5;
51 const int kOpenRetryDelayMillis = 250;
53 void GetV8FilePaths(base::FilePath* natives_path_out,
54 base::FilePath* snapshot_path_out) {
55 #if !defined(OS_MACOSX)
56 base::FilePath data_path;
57 PathService::Get(kV8SnapshotBasePathKey, &data_path);
58 DCHECK(!data_path.empty());
60 *natives_path_out = data_path.AppendASCII(kNativesFileName);
61 *snapshot_path_out = data_path.AppendASCII(kSnapshotFileName);
62 #else // !defined(OS_MACOSX)
63 base::ScopedCFTypeRef<CFStringRef> natives_file_name(
64 base::SysUTF8ToCFStringRef(kNativesFileName));
65 *natives_path_out =
66 base::mac::PathForFrameworkBundleResource(natives_file_name);
67 base::ScopedCFTypeRef<CFStringRef> snapshot_file_name(
68 base::SysUTF8ToCFStringRef(kSnapshotFileName));
69 *snapshot_path_out =
70 base::mac::PathForFrameworkBundleResource(snapshot_file_name);
71 DCHECK(!natives_path_out->empty());
72 DCHECK(!snapshot_path_out->empty());
73 #endif // !defined(OS_MACOSX)
76 static bool MapV8Files(base::File natives_file,
77 base::File snapshot_file,
78 base::MemoryMappedFile::Region natives_region =
79 base::MemoryMappedFile::Region::kWholeFile,
80 base::MemoryMappedFile::Region snapshot_region =
81 base::MemoryMappedFile::Region::kWholeFile) {
82 g_mapped_natives = new base::MemoryMappedFile;
83 if (!g_mapped_natives->IsValid()) {
84 if (!g_mapped_natives->Initialize(natives_file.Pass(), natives_region)) {
85 delete g_mapped_natives;
86 g_mapped_natives = NULL;
87 LOG(FATAL) << "Couldn't mmap v8 natives data file";
88 return false;
92 g_mapped_snapshot = new base::MemoryMappedFile;
93 if (!g_mapped_snapshot->IsValid()) {
94 if (!g_mapped_snapshot->Initialize(snapshot_file.Pass(), snapshot_region)) {
95 delete g_mapped_snapshot;
96 g_mapped_snapshot = NULL;
97 LOG(ERROR) << "Couldn't mmap v8 snapshot data file";
98 return false;
102 return true;
105 static bool OpenV8File(const base::FilePath& path,
106 int flags,
107 base::File& file) {
108 // Re-try logic here is motivated by http://crbug.com/479537
109 // for A/V on Windows (https://support.microsoft.com/en-us/kb/316609).
111 // These match tools/metrics/histograms.xml
112 enum OpenV8FileResult {
113 OPENED = 0,
114 OPENED_RETRY,
115 FAILED_IN_USE,
116 FAILED_OTHER,
117 MAX_VALUE
120 OpenV8FileResult result = OpenV8FileResult::FAILED_IN_USE;
121 for (int attempt = 0; attempt < kMaxOpenAttempts; attempt++) {
122 file.Initialize(path, flags);
123 if (file.IsValid()) {
124 if (attempt == 0) {
125 result = OpenV8FileResult::OPENED;
126 break;
127 } else {
128 result = OpenV8FileResult::OPENED_RETRY;
129 break;
131 } else if (file.error_details() != base::File::FILE_ERROR_IN_USE) {
132 result = OpenV8FileResult::FAILED_OTHER;
133 break;
134 } else if (kMaxOpenAttempts - 1 != attempt) {
135 base::PlatformThread::Sleep(
136 base::TimeDelta::FromMilliseconds(kOpenRetryDelayMillis));
140 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.OpenV8File.Result",
141 result,
142 OpenV8FileResult::MAX_VALUE);
144 return result == OpenV8FileResult::OPENED
145 || result == OpenV8FileResult::OPENED_RETRY;
148 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
149 bool VerifyV8SnapshotFile(base::MemoryMappedFile* snapshot_file,
150 const unsigned char* fingerprint) {
151 unsigned char output[crypto::kSHA256Length];
152 crypto::SHA256HashString(
153 base::StringPiece(reinterpret_cast<const char*>(snapshot_file->data()),
154 snapshot_file->length()),
155 output, sizeof(output));
156 return !memcmp(fingerprint, output, sizeof(output));
158 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
159 #endif // V8_USE_EXTERNAL_STARTUP_DATA
161 bool GenerateEntropy(unsigned char* buffer, size_t amount) {
162 base::RandBytes(buffer, amount);
163 return true;
166 } // namespace
168 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
169 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
170 // Defined in gen/gin/v8_snapshot_fingerprint.cc
171 extern const unsigned char g_natives_fingerprint[];
172 extern const unsigned char g_snapshot_fingerprint[];
173 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
175 // static
176 bool V8Initializer::LoadV8Snapshot() {
178 enum LoadV8SnapshotResult {
179 SUCCESS = 0,
180 FAILED_OPEN,
181 FAILED_MAP,
182 FAILED_VERIFY,
183 MAX_VALUE
186 if (g_mapped_natives && g_mapped_snapshot)
187 return true;
189 base::FilePath natives_data_path;
190 base::FilePath snapshot_data_path;
191 GetV8FilePaths(&natives_data_path, &snapshot_data_path);
193 base::File natives_file;
194 base::File snapshot_file;
195 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
197 LoadV8SnapshotResult result;
198 if (!OpenV8File(natives_data_path, flags, natives_file) ||
199 !OpenV8File(snapshot_data_path, flags, snapshot_file)) {
200 result = LoadV8SnapshotResult::FAILED_OPEN;
201 } else if (!MapV8Files(natives_file.Pass(), snapshot_file.Pass())) {
202 result = LoadV8SnapshotResult::FAILED_MAP;
203 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
204 } else if (!VerifyV8SnapshotFile(g_mapped_natives, g_natives_fingerprint) ||
205 !VerifyV8SnapshotFile(g_mapped_snapshot, g_snapshot_fingerprint)) {
206 result = LoadV8SnapshotResult::FAILED_VERIFY;
207 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
208 } else {
209 result = LoadV8SnapshotResult::SUCCESS;
212 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result",
213 result,
214 LoadV8SnapshotResult::MAX_VALUE);
215 return result == LoadV8SnapshotResult::SUCCESS;
218 // static
219 bool V8Initializer::LoadV8SnapshotFromFD(base::PlatformFile natives_pf,
220 int64 natives_offset,
221 int64 natives_size,
222 base::PlatformFile snapshot_pf,
223 int64 snapshot_offset,
224 int64 snapshot_size) {
225 if (g_mapped_natives && g_mapped_snapshot)
226 return true;
228 base::MemoryMappedFile::Region natives_region =
229 base::MemoryMappedFile::Region::kWholeFile;
230 if (natives_size != 0 || natives_offset != 0) {
231 natives_region =
232 base::MemoryMappedFile::Region(natives_offset, natives_size);
235 base::MemoryMappedFile::Region snapshot_region =
236 base::MemoryMappedFile::Region::kWholeFile;
237 if (natives_size != 0 || natives_offset != 0) {
238 snapshot_region =
239 base::MemoryMappedFile::Region(snapshot_offset, snapshot_size);
242 return MapV8Files(base::File(natives_pf), base::File(snapshot_pf),
243 natives_region, snapshot_region);
246 // static
247 bool V8Initializer::OpenV8FilesForChildProcesses(
248 base::PlatformFile* natives_fd_out,
249 base::PlatformFile* snapshot_fd_out) {
250 base::FilePath natives_data_path;
251 base::FilePath snapshot_data_path;
252 GetV8FilePaths(&natives_data_path, &snapshot_data_path);
254 base::File natives_data_file;
255 base::File snapshot_data_file;
256 int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
258 bool success = OpenV8File(natives_data_path, file_flags, natives_data_file) &&
259 OpenV8File(snapshot_data_path, file_flags, snapshot_data_file);
260 if (success) {
261 *natives_fd_out = natives_data_file.TakePlatformFile();
262 *snapshot_fd_out = snapshot_data_file.TakePlatformFile();
264 return success;
267 #endif // V8_USE_EXTERNAL_STARTUP_DATA
269 // static
270 void V8Initializer::Initialize(gin::IsolateHolder::ScriptMode mode) {
271 static bool v8_is_initialized = false;
272 if (v8_is_initialized)
273 return;
275 v8::V8::InitializePlatform(V8Platform::Get());
277 if (gin::IsolateHolder::kStrictMode == mode) {
278 static const char use_strict[] = "--use_strict";
279 v8::V8::SetFlagsFromString(use_strict, sizeof(use_strict) - 1);
282 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
283 v8::StartupData natives;
284 natives.data = reinterpret_cast<const char*>(g_mapped_natives->data());
285 natives.raw_size = static_cast<int>(g_mapped_natives->length());
286 v8::V8::SetNativesDataBlob(&natives);
288 v8::StartupData snapshot;
289 snapshot.data = reinterpret_cast<const char*>(g_mapped_snapshot->data());
290 snapshot.raw_size = static_cast<int>(g_mapped_snapshot->length());
291 v8::V8::SetSnapshotDataBlob(&snapshot);
292 #endif // V8_USE_EXTERNAL_STARTUP_DATA
294 v8::V8::SetEntropySource(&GenerateEntropy);
295 v8::V8::Initialize();
297 v8_is_initialized = true;
300 // static
301 void V8Initializer::GetV8ExternalSnapshotData(const char** natives_data_out,
302 int* natives_size_out,
303 const char** snapshot_data_out,
304 int* snapshot_size_out) {
305 if (!g_mapped_natives || !g_mapped_snapshot) {
306 *natives_data_out = *snapshot_data_out = NULL;
307 *natives_size_out = *snapshot_size_out = 0;
308 return;
310 *natives_data_out = reinterpret_cast<const char*>(g_mapped_natives->data());
311 *snapshot_data_out = reinterpret_cast<const char*>(g_mapped_snapshot->data());
312 *natives_size_out = static_cast<int>(g_mapped_natives->length());
313 *snapshot_size_out = static_cast<int>(g_mapped_snapshot->length());
316 } // namespace gin