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 "content/browser/renderer_host/sandbox_ipc_linux.h" 6 7#include <fcntl.h> 8#include <fontconfig/fontconfig.h> 9#include <sys/poll.h> 10#include <sys/socket.h> 11#include <sys/stat.h> 12 13#include "base/command_line.h" 14#include "base/files/scoped_file.h" 15#include "base/linux_util.h" 16#include "base/memory/scoped_vector.h" 17#include "base/memory/shared_memory.h" 18#include "base/posix/eintr_wrapper.h" 19#include "base/posix/unix_domain_socket_linux.h" 20#include "base/process/launch.h" 21#include "base/strings/string_number_conversions.h" 22#include "content/common/font_config_ipc_linux.h" 23#include "content/common/sandbox_linux/sandbox_linux.h" 24#include "content/common/set_process_title.h" 25#include "content/public/common/content_switches.h" 26#include "ppapi/c/trusted/ppb_browser_font_trusted.h" 27#include "third_party/WebKit/public/platform/linux/WebFontInfo.h" 28#include "third_party/WebKit/public/web/WebKit.h" 29#include "third_party/npapi/bindings/npapi_extensions.h" 30#include "third_party/skia/include/ports/SkFontConfigInterface.h" 31#include "ui/gfx/font_render_params_linux.h" 32 33using blink::WebCString; 34using blink::WebFontInfo; 35using blink::WebUChar; 36using blink::WebUChar32; 37 38namespace { 39 40// MSCharSetToFontconfig translates a Microsoft charset identifier to a 41// fontconfig language set by appending to |langset|. 42// Returns true if |langset| is Latin/Greek/Cyrillic. 43bool MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) { 44 // We have need to translate raw fdwCharSet values into terms that 45 // fontconfig can understand. (See the description of fdwCharSet in the MSDN 46 // documentation for CreateFont: 47 // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx ) 48 // 49 // Although the argument is /called/ 'charset', the actual values conflate 50 // character sets (which are sets of Unicode code points) and character 51 // encodings (which are algorithms for turning a series of bits into a 52 // series of code points.) Sometimes the values will name a language, 53 // sometimes they'll name an encoding. In the latter case I'm assuming that 54 // they mean the set of code points in the domain of that encoding. 55 // 56 // fontconfig deals with ISO 639-1 language codes: 57 // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes 58 // 59 // So, for each of the documented fdwCharSet values I've had to take a 60 // guess at the set of ISO 639-1 languages intended. 61 62 bool is_lgc = false; 63 switch (fdwCharSet) { 64 case NPCharsetAnsi: 65 // These values I don't really know what to do with, so I'm going to map 66 // them to English also. 67 case NPCharsetDefault: 68 case NPCharsetMac: 69 case NPCharsetOEM: 70 case NPCharsetSymbol: 71 is_lgc = true; 72 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en")); 73 break; 74 case NPCharsetBaltic: 75 // The three baltic languages. 76 is_lgc = true; 77 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et")); 78 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv")); 79 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt")); 80 break; 81 case NPCharsetChineseBIG5: 82 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-tw")); 83 break; 84 case NPCharsetGB2312: 85 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-cn")); 86 break; 87 case NPCharsetEastEurope: 88 // A scattering of eastern European languages. 89 is_lgc = true; 90 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl")); 91 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs")); 92 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk")); 93 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu")); 94 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr")); 95 break; 96 case NPCharsetGreek: 97 is_lgc = true; 98 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el")); 99 break; 100 case NPCharsetHangul: 101 case NPCharsetJohab: 102 // Korean 103 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko")); 104 break; 105 case NPCharsetRussian: 106 is_lgc = true; 107 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru")); 108 break; 109 case NPCharsetShiftJIS: 110 // Japanese 111 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja")); 112 break; 113 case NPCharsetTurkish: 114 is_lgc = true; 115 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr")); 116 break; 117 case NPCharsetVietnamese: 118 is_lgc = true; 119 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi")); 120 break; 121 case NPCharsetArabic: 122 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar")); 123 break; 124 case NPCharsetHebrew: 125 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he")); 126 break; 127 case NPCharsetThai: 128 FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th")); 129 break; 130 // default: 131 // Don't add any languages in that case that we don't recognise the 132 // constant. 133 } 134 return is_lgc; 135} 136 137} // namespace 138 139namespace content { 140 141SandboxIPCHandler::SandboxIPCHandler(int lifeline_fd, int browser_socket) 142 : lifeline_fd_(lifeline_fd), browser_socket_(browser_socket) { 143 // FontConfig doesn't provide a standard property to control subpixel 144 // positioning, so we pass the current setting through to WebKit. 145 WebFontInfo::setSubpixelPositioning( 146 gfx::GetDefaultWebkitSubpixelPositioning()); 147} 148 149void SandboxIPCHandler::Run() { 150 struct pollfd pfds[2]; 151 pfds[0].fd = lifeline_fd_; 152 pfds[0].events = POLLIN; 153 pfds[1].fd = browser_socket_; 154 pfds[1].events = POLLIN; 155 156 int failed_polls = 0; 157 for (;;) { 158 const int r = 159 HANDLE_EINTR(poll(pfds, arraysize(pfds), -1 /* no timeout */)); 160 // '0' is not a possible return value with no timeout. 161 DCHECK_NE(0, r); 162 if (r < 0) { 163 PLOG(WARNING) << "poll"; 164 if (failed_polls++ == 3) { 165 LOG(FATAL) << "poll(2) failing. SandboxIPCHandler aborting."; 166 return; 167 } 168 continue; 169 } 170 171 failed_polls = 0; 172 173 // The browser process will close the other end of this pipe on shutdown, 174 // so we should exit. 175 if (pfds[0].revents) { 176 break; 177 } 178 179 // If poll(2) reports an error condition in this fd, 180 // we assume the zygote is gone and we exit the loop. 181 if (pfds[1].revents & (POLLERR | POLLHUP)) { 182 break; 183 } 184 185 if (pfds[1].revents & POLLIN) { 186 HandleRequestFromRenderer(browser_socket_); 187 } 188 } 189 190 VLOG(1) << "SandboxIPCHandler stopping."; 191} 192 193void SandboxIPCHandler::HandleRequestFromRenderer(int fd) { 194 ScopedVector<base::ScopedFD> fds; 195 196 // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength 197 // bytes long (this is the largest message type). 198 // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC 199 // error for a maximum length message. 200 char buf[FontConfigIPC::kMaxFontFamilyLength + 128]; 201 202 const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); 203 if (len == -1) { 204 // TODO: should send an error reply, or the sender might block forever. 205 NOTREACHED() << "Sandbox host message is larger than kMaxFontFamilyLength"; 206 return; 207 } 208 if (fds.empty()) 209 return; 210 211 Pickle pickle(buf, len); 212 PickleIterator iter(pickle); 213 214 int kind; 215 if (!pickle.ReadInt(&iter, &kind)) 216 return; 217 218 if (kind == FontConfigIPC::METHOD_MATCH) { 219 HandleFontMatchRequest(fd, pickle, iter, fds.get()); 220 } else if (kind == FontConfigIPC::METHOD_OPEN) { 221 HandleFontOpenRequest(fd, pickle, iter, fds.get()); 222 } else if (kind == LinuxSandbox::METHOD_GET_FALLBACK_FONT_FOR_CHAR) { 223 HandleGetFallbackFontForChar(fd, pickle, iter, fds.get()); 224 } else if (kind == LinuxSandbox::METHOD_LOCALTIME) { 225 HandleLocaltime(fd, pickle, iter, fds.get()); 226 } else if (kind == LinuxSandbox::METHOD_GET_STYLE_FOR_STRIKE) { 227 HandleGetStyleForStrike(fd, pickle, iter, fds.get()); 228 } else if (kind == LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT) { 229 HandleMakeSharedMemorySegment(fd, pickle, iter, fds.get()); 230 } else if (kind == LinuxSandbox::METHOD_MATCH_WITH_FALLBACK) { 231 HandleMatchWithFallback(fd, pickle, iter, fds.get()); 232 } 233} 234 235int SandboxIPCHandler::FindOrAddPath(const SkString& path) { 236 int count = paths_.count(); 237 for (int i = 0; i < count; ++i) { 238 if (path == *paths_[i]) 239 return i; 240 } 241 *paths_.append() = new SkString(path); 242 return count; 243} 244 245void SandboxIPCHandler::HandleFontMatchRequest( 246 int fd, 247 const Pickle& pickle, 248 PickleIterator iter, 249 const std::vector<base::ScopedFD*>& fds) { 250 uint32_t requested_style; 251 std::string family; 252 if (!pickle.ReadString(&iter, &family) || 253 !pickle.ReadUInt32(&iter, &requested_style)) 254 return; 255 256 SkFontConfigInterface::FontIdentity result_identity; 257 SkString result_family; 258 SkTypeface::Style result_style; 259 SkFontConfigInterface* fc = 260 SkFontConfigInterface::GetSingletonDirectInterface(); 261 const bool r = 262 fc->matchFamilyName(family.c_str(), 263 static_cast<SkTypeface::Style>(requested_style), 264 &result_identity, 265 &result_family, 266 &result_style); 267 268 Pickle reply; 269 if (!r) { 270 reply.WriteBool(false); 271 } else { 272 // Stash away the returned path, so we can give it an ID (index) 273 // which will later be given to us in a request to open the file. 274 int index = FindOrAddPath(result_identity.fString); 275 result_identity.fID = static_cast<uint32_t>(index); 276 277 reply.WriteBool(true); 278 skia::WriteSkString(&reply, result_family); 279 skia::WriteSkFontIdentity(&reply, result_identity); 280 reply.WriteUInt32(result_style); 281 } 282 SendRendererReply(fds, reply, -1); 283} 284 285void SandboxIPCHandler::HandleFontOpenRequest( 286 int fd, 287 const Pickle& pickle, 288 PickleIterator iter, 289 const std::vector<base::ScopedFD*>& fds) { 290 uint32_t index; 291 if (!pickle.ReadUInt32(&iter, &index)) 292 return; 293 if (index >= static_cast<uint32_t>(paths_.count())) 294 return; 295 const int result_fd = open(paths_[index]->c_str(), O_RDONLY); 296 297 Pickle reply; 298 if (result_fd == -1) { 299 reply.WriteBool(false); 300 } else { 301 reply.WriteBool(true); 302 } 303 304 // The receiver will have its own access to the file, so we will close it 305 // after this send. 306 SendRendererReply(fds, reply, result_fd); 307 308 if (result_fd >= 0) { 309 int err = IGNORE_EINTR(close(result_fd)); 310 DCHECK(!err); 311 } 312} 313 314void SandboxIPCHandler::HandleGetFallbackFontForChar( 315 int fd, 316 const Pickle& pickle, 317 PickleIterator iter, 318 const std::vector<base::ScopedFD*>& fds) { 319 // The other side of this call is 320 // content/common/child_process_sandbox_support_impl_linux.cc 321 322 EnsureWebKitInitialized(); 323 WebUChar32 c; 324 if (!pickle.ReadInt(&iter, &c)) 325 return; 326 327 std::string preferred_locale; 328 if (!pickle.ReadString(&iter, &preferred_locale)) 329 return; 330 331 blink::WebFallbackFont fallbackFont; 332 WebFontInfo::fallbackFontForChar(c, preferred_locale.c_str(), &fallbackFont); 333 334 Pickle reply; 335 if (fallbackFont.name.data()) { 336 reply.WriteString(fallbackFont.name.data()); 337 } else { 338 reply.WriteString(std::string()); 339 } 340 if (fallbackFont.filename.data()) { 341 reply.WriteString(fallbackFont.filename.data()); 342 } else { 343 reply.WriteString(std::string()); 344 } 345 reply.WriteInt(fallbackFont.ttcIndex); 346 reply.WriteBool(fallbackFont.isBold); 347 reply.WriteBool(fallbackFont.isItalic); 348 SendRendererReply(fds, reply, -1); 349} 350 351void SandboxIPCHandler::HandleGetStyleForStrike( 352 int fd, 353 const Pickle& pickle, 354 PickleIterator iter, 355 const std::vector<base::ScopedFD*>& fds) { 356 std::string family; 357 int sizeAndStyle; 358 359 if (!pickle.ReadString(&iter, &family) || 360 !pickle.ReadInt(&iter, &sizeAndStyle)) { 361 return; 362 } 363 364 EnsureWebKitInitialized(); 365 blink::WebFontRenderStyle style; 366 WebFontInfo::renderStyleForStrike(family.c_str(), sizeAndStyle, &style); 367 368 Pickle reply; 369 reply.WriteInt(style.useBitmaps); 370 reply.WriteInt(style.useAutoHint); 371 reply.WriteInt(style.useHinting); 372 reply.WriteInt(style.hintStyle); 373 reply.WriteInt(style.useAntiAlias); 374 reply.WriteInt(style.useSubpixelRendering); 375 reply.WriteInt(style.useSubpixelPositioning); 376 377 SendRendererReply(fds, reply, -1); 378} 379 380void SandboxIPCHandler::HandleLocaltime( 381 int fd, 382 const Pickle& pickle, 383 PickleIterator iter, 384 const std::vector<base::ScopedFD*>& fds) { 385 // The other side of this call is in zygote_main_linux.cc 386 387 std::string time_string; 388 if (!pickle.ReadString(&iter, &time_string) || 389 time_string.size() != sizeof(time_t)) { 390 return; 391 } 392 393 time_t time; 394 memcpy(&time, time_string.data(), sizeof(time)); 395 // We use localtime here because we need the tm_zone field to be filled 396 // out. Since we are a single-threaded process, this is safe. 397 const struct tm* expanded_time = localtime(&time); 398 399 std::string result_string; 400 const char* time_zone_string = ""; 401 if (expanded_time != NULL) { 402 result_string = std::string(reinterpret_cast<const char*>(expanded_time), 403 sizeof(struct tm)); 404 time_zone_string = expanded_time->tm_zone; 405 } 406 407 Pickle reply; 408 reply.WriteString(result_string); 409 reply.WriteString(time_zone_string); 410 SendRendererReply(fds, reply, -1); 411} 412 413void SandboxIPCHandler::HandleMakeSharedMemorySegment( 414 int fd, 415 const Pickle& pickle, 416 PickleIterator iter, 417 const std::vector<base::ScopedFD*>& fds) { 418 base::SharedMemoryCreateOptions options; 419 uint32_t size; 420 if (!pickle.ReadUInt32(&iter, &size)) 421 return; 422 options.size = size; 423 if (!pickle.ReadBool(&iter, &options.executable)) 424 return; 425 int shm_fd = -1; 426 base::SharedMemory shm; 427 if (shm.Create(options)) 428 shm_fd = shm.handle().fd; 429 Pickle reply; 430 SendRendererReply(fds, reply, shm_fd); 431} 432 433void SandboxIPCHandler::HandleMatchWithFallback( 434 int fd, 435 const Pickle& pickle, 436 PickleIterator iter, 437 const std::vector<base::ScopedFD*>& fds) { 438 // Unlike the other calls, for which we are an indirection in front of 439 // WebKit or Skia, this call is always made via this sandbox helper 440 // process. Therefore the fontconfig code goes in here directly. 441 442 std::string face; 443 bool is_bold, is_italic; 444 uint32 charset, fallback_family; 445 446 if (!pickle.ReadString(&iter, &face) || face.empty() || 447 !pickle.ReadBool(&iter, &is_bold) || 448 !pickle.ReadBool(&iter, &is_italic) || 449 !pickle.ReadUInt32(&iter, &charset) || 450 !pickle.ReadUInt32(&iter, &fallback_family)) { 451 return; 452 } 453 454 FcLangSet* langset = FcLangSetCreate(); 455 bool is_lgc = MSCharSetToFontconfig(langset, charset); 456 457 FcPattern* pattern = FcPatternCreate(); 458 FcPatternAddString( 459 pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(face.c_str())); 460 461 // TODO(thestig) Check if we can access Chrome's per-script font preference 462 // here and select better default fonts for non-LGC case. 463 std::string generic_font_name; 464 if (is_lgc) { 465 switch (fallback_family) { 466 case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF: 467 generic_font_name = "Times New Roman"; 468 break; 469 case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF: 470 generic_font_name = "Arial"; 471 break; 472 case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE: 473 generic_font_name = "Courier New"; 474 break; 475 } 476 } 477 if (!generic_font_name.empty()) { 478 const FcChar8* fc_generic_font_name = 479 reinterpret_cast<const FcChar8*>(generic_font_name.c_str()); 480 FcPatternAddString(pattern, FC_FAMILY, fc_generic_font_name); 481 } 482 483 if (is_bold) 484 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); 485 if (is_italic) 486 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); 487 FcPatternAddLangSet(pattern, FC_LANG, langset); 488 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); 489 FcConfigSubstitute(NULL, pattern, FcMatchPattern); 490 FcDefaultSubstitute(pattern); 491 492 FcResult result; 493 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); 494 int font_fd = -1; 495 int good_enough_index = -1; 496 bool good_enough_index_set = false; 497 498 if (font_set) { 499 for (int i = 0; i < font_set->nfont; ++i) { 500 FcPattern* current = font_set->fonts[i]; 501 502 // Older versions of fontconfig have a bug where they cannot select 503 // only scalable fonts so we have to manually filter the results. 504 FcBool is_scalable; 505 if (FcPatternGetBool(current, FC_SCALABLE, 0, &is_scalable) != 506 FcResultMatch || 507 !is_scalable) { 508 continue; 509 } 510 511 FcChar8* c_filename; 512 if (FcPatternGetString(current, FC_FILE, 0, &c_filename) != 513 FcResultMatch) { 514 continue; 515 } 516 517 // We only want to return sfnt (TrueType) based fonts. We don't have a 518 // very good way of detecting this so we'll filter based on the 519 // filename. 520 bool is_sfnt = false; 521 static const char kSFNTExtensions[][5] = {".ttf", ".otc", ".TTF", ".ttc", 522 ""}; 523 const size_t filename_len = strlen(reinterpret_cast<char*>(c_filename)); 524 for (unsigned j = 0;; j++) { 525 if (kSFNTExtensions[j][0] == 0) { 526 // None of the extensions matched. 527 break; 528 } 529 const size_t ext_len = strlen(kSFNTExtensions[j]); 530 if (filename_len > ext_len && 531 memcmp(c_filename + filename_len - ext_len, 532 kSFNTExtensions[j], 533 ext_len) == 0) { 534 is_sfnt = true; 535 break; 536 } 537 } 538 539 if (!is_sfnt) 540 continue; 541 542 // This font is good enough to pass muster, but we might be able to do 543 // better with subsequent ones. 544 if (!good_enough_index_set) { 545 good_enough_index = i; 546 good_enough_index_set = true; 547 } 548 549 FcValue matrix; 550 bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0; 551 552 if (is_italic && have_matrix) { 553 // we asked for an italic font, but fontconfig is giving us a 554 // non-italic font with a transformation matrix. 555 continue; 556 } 557 558 FcValue embolden; 559 const bool have_embolden = 560 FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0; 561 562 if (is_bold && have_embolden) { 563 // we asked for a bold font, but fontconfig gave us a non-bold font 564 // and asked us to apply fake bolding. 565 continue; 566 } 567 568 font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY); 569 if (font_fd >= 0) 570 break; 571 } 572 } 573 574 if (font_fd == -1 && good_enough_index_set) { 575 // We didn't find a font that we liked, so we fallback to something 576 // acceptable. 577 FcPattern* current = font_set->fonts[good_enough_index]; 578 FcChar8* c_filename; 579 FcPatternGetString(current, FC_FILE, 0, &c_filename); 580 font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY); 581 } 582 583 if (font_set) 584 FcFontSetDestroy(font_set); 585 FcPatternDestroy(pattern); 586 587 Pickle reply; 588 SendRendererReply(fds, reply, font_fd); 589 590 if (font_fd >= 0) { 591 if (IGNORE_EINTR(close(font_fd)) < 0) 592 PLOG(ERROR) << "close"; 593 } 594} 595 596void SandboxIPCHandler::SendRendererReply( 597 const std::vector<base::ScopedFD*>& fds, 598 const Pickle& reply, 599 int reply_fd) { 600 struct msghdr msg; 601 memset(&msg, 0, sizeof(msg)); 602 struct iovec iov = {const_cast<void*>(reply.data()), reply.size()}; 603 msg.msg_iov = &iov; 604 msg.msg_iovlen = 1; 605 606 char control_buffer[CMSG_SPACE(sizeof(int))]; 607 608 if (reply_fd != -1) { 609 struct stat st; 610 if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) { 611 LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC"; 612 // We must never send directory descriptors to a sandboxed process 613 // because they can use openat with ".." elements in the path in order 614 // to escape the sandbox and reach the real filesystem. 615 } 616 617 struct cmsghdr* cmsg; 618 msg.msg_control = control_buffer; 619 msg.msg_controllen = sizeof(control_buffer); 620 cmsg = CMSG_FIRSTHDR(&msg); 621 cmsg->cmsg_level = SOL_SOCKET; 622 cmsg->cmsg_type = SCM_RIGHTS; 623 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 624 memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd)); 625 msg.msg_controllen = cmsg->cmsg_len; 626 } 627 628 if (HANDLE_EINTR(sendmsg(fds[0]->get(), &msg, MSG_DONTWAIT)) < 0) 629 PLOG(ERROR) << "sendmsg"; 630} 631 632SandboxIPCHandler::~SandboxIPCHandler() { 633 paths_.deleteAll(); 634 if (webkit_platform_support_) 635 blink::shutdownWithoutV8(); 636 637 if (IGNORE_EINTR(close(lifeline_fd_)) < 0) 638 PLOG(ERROR) << "close"; 639 if (IGNORE_EINTR(close(browser_socket_)) < 0) 640 PLOG(ERROR) << "close"; 641} 642 643void SandboxIPCHandler::EnsureWebKitInitialized() { 644 if (webkit_platform_support_) 645 return; 646 webkit_platform_support_.reset(new BlinkPlatformImpl); 647 blink::initializeWithoutV8(webkit_platform_support_.get()); 648} 649 650} // namespace content 651