Chromium Code Reviews| 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 // On Mac, one can't make shortcuts with command-line arguments. Instead, we | 5 // On Mac, one can't make shortcuts with command-line arguments. Instead, we |
| 6 // produce small app bundles which locate the Chromium framework and load it, | 6 // produce small app bundles which locate the Chromium framework and load it, |
| 7 // passing the appropriate data. This is the entry point into the framework for | 7 // passing the appropriate data. This is the entry point into the framework for |
| 8 // those app bundles. | 8 // those app bundles. |
| 9 | 9 |
| 10 #import <Cocoa/Cocoa.h> | 10 #import <Cocoa/Cocoa.h> |
| (...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 473 // A ReplyEventHandler is a helper class to send an Apple Event to a process | 473 // A ReplyEventHandler is a helper class to send an Apple Event to a process |
| 474 // and call a callback when the reply returns. | 474 // and call a callback when the reply returns. |
| 475 // | 475 // |
| 476 // This is used to 'ping' the main Chrome process -- once Chrome has sent back | 476 // This is used to 'ping' the main Chrome process -- once Chrome has sent back |
| 477 // an Apple Event reply, it's guaranteed that it has opened the IPC channel | 477 // an Apple Event reply, it's guaranteed that it has opened the IPC channel |
| 478 // that the app shim will connect to. | 478 // that the app shim will connect to. |
| 479 @interface ReplyEventHandler : NSObject { | 479 @interface ReplyEventHandler : NSObject { |
| 480 base::Callback<void(bool)> onReply_; | 480 base::Callback<void(bool)> onReply_; |
| 481 AEDesc replyEvent_; | 481 AEDesc replyEvent_; |
| 482 } | 482 } |
| 483 // Sends an Apple Event to the process identified by |psn|, and calls |replyFn| | 483 |
| 484 // when the reply is received. Internally this creates a ReplyEventHandler, | |
| 485 // which will delete itself once the reply event has been received. | |
| 486 + (void)pingProcess:(const ProcessSerialNumber&)psn | |
| 487 andCall:(base::Callback<void(bool)>)replyFn; | |
| 488 @end | 484 @end |
| 489 | 485 |
| 490 @interface ReplyEventHandler (PrivateMethods) | 486 @interface ReplyEventHandler (PrivateMethods) |
| 491 // Initialise the reply event handler. Doesn't register any handlers until | 487 // Initialise the reply event handler. Doesn't register any handlers until |
| 492 // |-pingProcess:| is called. |replyFn| is the function to be called when the | 488 // |-pingProcess:| is called. |replyFn| is the function to be called when the |
| 493 // Apple Event reply arrives. | 489 // Apple Event reply arrives. |
| 494 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn; | 490 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn; |
| 495 | 491 |
| 496 // Sends an Apple Event ping to the process identified by |psn| and registers | 492 // Returns the apple event that should be sent to the process. |
| 497 // to listen for a reply. | 493 - (NSAppleEventDescriptor*)appleEventToSendToProcess; |
| 498 - (void)pingProcess:(const ProcessSerialNumber&)psn; | |
| 499 | 494 |
| 500 // Called when a response is received from the target process for the ping sent | 495 // Called when a response is received from the target process for the ping sent |
| 501 // by |-pingProcess:|. | 496 // by |-pingProcess:|. |
| 502 - (void)message:(NSAppleEventDescriptor*)event | 497 - (void)message:(NSAppleEventDescriptor*)event |
| 503 withReply:(NSAppleEventDescriptor*)reply; | 498 withReply:(NSAppleEventDescriptor*)reply; |
| 504 | 499 |
| 505 // Calls |onReply_|, passing it |success| to specify whether the ping was | 500 // Calls |onReply_|, passing it |success| to specify whether the ping was |
| 506 // successful. | 501 // successful. |
| 507 - (void)closeWithSuccess:(bool)success; | 502 - (void)closeWithSuccess:(bool)success; |
| 508 @end | 503 @end |
| 509 | 504 |
| 510 @implementation ReplyEventHandler | 505 @implementation ReplyEventHandler |
| 511 + (void)pingProcess:(const ProcessSerialNumber&)psn | 506 |
| 512 andCall:(base::Callback<void(bool)>)replyFn { | |
| 513 // The object will release itself when the reply arrives, or possibly earlier | |
| 514 // if an unrecoverable error occurs. | |
| 515 ReplyEventHandler* handler = | |
| 516 [[ReplyEventHandler alloc] initWithCallback:replyFn]; | |
| 517 [handler pingProcess:psn]; | |
| 518 } | |
| 519 @end | 507 @end |
| 520 | 508 |
| 521 @implementation ReplyEventHandler (PrivateMethods) | 509 @implementation ReplyEventHandler (PrivateMethods) |
| 522 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn { | 510 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn { |
| 523 if ((self = [super init])) { | 511 if ((self = [super init])) { |
| 524 onReply_ = replyFn; | 512 onReply_ = replyFn; |
| 525 } | 513 } |
| 526 return self; | 514 return self; |
| 527 } | 515 } |
| 528 | 516 |
| 529 - (void)pingProcess:(const ProcessSerialNumber&)psn { | 517 - (NSAppleEventDescriptor*)appleEventToSendToProcess { |
| 530 // Register the reply listener. | 518 // Register the reply listener. |
| 531 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | 519 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
| 532 [em setEventHandler:self | 520 [em setEventHandler:self |
| 533 andSelector:@selector(message:withReply:) | 521 andSelector:@selector(message:withReply:) |
| 534 forEventClass:'aevt' | 522 forEventClass:'aevt' |
| 535 andEventID:'ansr']; | 523 andEventID:'ansr']; |
| 536 // Craft the Apple Event to send. | 524 NSAppleEventDescriptor* initial_event = [NSAppleEventDescriptor |
| 537 NSAppleEventDescriptor* target = [NSAppleEventDescriptor | 525 appleEventWithEventClass:app_mode::kAEChromeAppClass |
| 538 descriptorWithDescriptorType:typeProcessSerialNumber | 526 eventID:app_mode::kAEChromeAppPing |
| 539 bytes:&psn | 527 targetDescriptor:nil |
| 540 length:sizeof(psn)]; | 528 returnID:kAutoGenerateReturnID |
| 541 NSAppleEventDescriptor* initial_event = | 529 transactionID:kAnyTransactionID]; |
| 542 [NSAppleEventDescriptor | 530 return initial_event; |
| 543 appleEventWithEventClass:app_mode::kAEChromeAppClass | |
| 544 eventID:app_mode::kAEChromeAppPing | |
| 545 targetDescriptor:target | |
| 546 returnID:kAutoGenerateReturnID | |
| 547 transactionID:kAnyTransactionID]; | |
| 548 | |
| 549 // Note that AESendMessage effectively ignores kAEDefaultTimeout, because this | |
| 550 // call does not pass kAEWantReceipt (which is deprecated and unsupported on | |
| 551 // Mac). Instead, rely on OnPingChromeTimeout(). | |
| 552 OSStatus status = AESendMessage( | |
| 553 [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout); | |
| 554 if (status != noErr) { | |
| 555 OSSTATUS_LOG(ERROR, status) << "AESendMessage"; | |
| 556 [self closeWithSuccess:false]; | |
| 557 } | |
| 558 } | 531 } |
| 559 | 532 |
| 560 - (void)message:(NSAppleEventDescriptor*)event | 533 - (void)message:(NSAppleEventDescriptor*)event |
| 561 withReply:(NSAppleEventDescriptor*)reply { | 534 withReply:(NSAppleEventDescriptor*)reply { |
| 562 [self closeWithSuccess:true]; | 535 [self closeWithSuccess:true]; |
| 563 } | 536 } |
| 564 | 537 |
| 565 - (void)closeWithSuccess:(bool)success { | 538 - (void)closeWithSuccess:(bool)success { |
| 566 onReply_.Run(success); | 539 onReply_.Run(success); |
| 567 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | 540 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 661 AppShimController controller; | 634 AppShimController controller; |
| 662 base::MessageLoopForUI main_message_loop; | 635 base::MessageLoopForUI main_message_loop; |
| 663 base::PlatformThread::SetName("CrAppShimMain"); | 636 base::PlatformThread::SetName("CrAppShimMain"); |
| 664 | 637 |
| 665 // In tests, launching Chrome does nothing, and we won't get a ping response, | 638 // In tests, launching Chrome does nothing, and we won't get a ping response, |
| 666 // so just assume the socket exists. | 639 // so just assume the socket exists. |
| 667 if (pid == -1 && | 640 if (pid == -1 && |
| 668 !base::CommandLine::ForCurrentProcess()->HasSwitch( | 641 !base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 669 app_mode::kLaunchedForTest)) { | 642 app_mode::kLaunchedForTest)) { |
| 670 // Launch Chrome if it isn't already running. | 643 // Launch Chrome if it isn't already running. |
| 671 ProcessSerialNumber psn; | |
| 672 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); | 644 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| 673 command_line.AppendSwitch(switches::kSilentLaunch); | 645 command_line.AppendSwitch(switches::kSilentLaunch); |
| 674 | 646 |
| 675 // If the shim is the app launcher, pass --show-app-list when starting a new | 647 // If the shim is the app launcher, pass --show-app-list when starting a new |
| 676 // Chrome process to inform startup codepaths and load the correct profile. | 648 // Chrome process to inform startup codepaths and load the correct profile. |
| 677 if (info->app_mode_id == app_mode::kAppListModeId) { | 649 if (info->app_mode_id == app_mode::kAppListModeId) { |
| 678 command_line.AppendSwitch(switches::kShowAppList); | 650 command_line.AppendSwitch(switches::kShowAppList); |
| 679 } else { | 651 } else { |
| 680 command_line.AppendSwitchPath(switches::kProfileDirectory, | 652 command_line.AppendSwitchPath(switches::kProfileDirectory, |
| 681 info->profile_dir); | 653 info->profile_dir); |
| 682 } | 654 } |
| 683 | 655 |
| 656 base::Callback<void(bool)> on_ping_chrome_reply = base::Bind( | |
| 657 &AppShimController::OnPingChromeReply, base::Unretained(&controller)); | |
| 658 | |
| 659 // Self-owned object that will delete itself. | |
| 660 ReplyEventHandler* handler = | |
| 661 [[ReplyEventHandler alloc] initWithCallback:on_ping_chrome_reply]; | |
| 662 // NSAppleEventDescriptor* descriptor = [handler appleEventToSendToProcess]; | |
|
tapted
2016/10/17 04:44:07
I think the rest looks good - we just need to pass
| |
| 663 | |
| 684 base::Process app = base::mac::OpenApplicationWithPath( | 664 base::Process app = base::mac::OpenApplicationWithPath( |
| 685 base::mac::OuterBundlePath(), command_line, NSWorkspaceLaunchDefault); | 665 base::mac::OuterBundlePath(), command_line, NSWorkspaceLaunchDefault); |
| 686 | 666 if (!app.IsValid()) { |
| 687 // TODO(crbug.com/652563): Do not use deprecated GetProcessForPID. Change | 667 [handler closeWithSuccess:false]; |
| 688 // |ReplyEventHandler| to take |pid_t| instead of |ProcessSerialNumber|. | |
| 689 if (!app.IsValid() || GetProcessForPID(app.Pid(), &psn) != noErr) | |
| 690 return 1; | 668 return 1; |
| 691 | 669 } |
| 692 base::Callback<void(bool)> on_ping_chrome_reply = | |
| 693 base::Bind(&AppShimController::OnPingChromeReply, | |
| 694 base::Unretained(&controller)); | |
| 695 | |
| 696 // This code abuses the fact that Apple Events sent before the process is | |
| 697 // fully initialized don't receive a reply until its run loop starts. Once | |
| 698 // the reply is received, Chrome will have opened its IPC port, guaranteed. | |
| 699 [ReplyEventHandler pingProcess:psn | |
| 700 andCall:on_ping_chrome_reply]; | |
| 701 | 670 |
| 702 main_message_loop.task_runner()->PostDelayedTask( | 671 main_message_loop.task_runner()->PostDelayedTask( |
| 703 FROM_HERE, base::Bind(&AppShimController::OnPingChromeTimeout, | 672 FROM_HERE, base::Bind(&AppShimController::OnPingChromeTimeout, |
| 704 base::Unretained(&controller)), | 673 base::Unretained(&controller)), |
| 705 base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds)); | 674 base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds)); |
| 706 } else { | 675 } else { |
| 707 // Chrome already running. Proceed to init. This could still fail if Chrome | 676 // Chrome already running. Proceed to init. This could still fail if Chrome |
| 708 // is still starting up or shutting down, but the process will exit quickly, | 677 // is still starting up or shutting down, but the process will exit quickly, |
| 709 // which is preferable to waiting for the Apple Event to timeout after one | 678 // which is preferable to waiting for the Apple Event to timeout after one |
| 710 // minute. | 679 // minute. |
| 711 main_message_loop.task_runner()->PostTask( | 680 main_message_loop.task_runner()->PostTask( |
| 712 FROM_HERE, | 681 FROM_HERE, |
| 713 base::Bind(&AppShimController::Init, base::Unretained(&controller))); | 682 base::Bind(&AppShimController::Init, base::Unretained(&controller))); |
| 714 } | 683 } |
| 715 | 684 |
| 716 base::RunLoop().Run(); | 685 base::RunLoop().Run(); |
| 717 return 0; | 686 return 0; |
| 718 } | 687 } |
| OLD | NEW |