| 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| | |
| 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 | 483 @end |
| 489 | 484 |
| 490 @interface ReplyEventHandler (PrivateMethods) | 485 @interface ReplyEventHandler (PrivateMethods) |
| 491 // Initialise the reply event handler. Doesn't register any handlers until | 486 // Initialise the reply event handler. Doesn't register any handlers until |
| 492 // |-pingProcess:| is called. |replyFn| is the function to be called when the | 487 // |-pingProcess:| is called. |replyFn| is the function to be called when the |
| 493 // Apple Event reply arrives. | 488 // Apple Event reply arrives. |
| 494 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn; | 489 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn; |
| 495 | 490 |
| 496 // Sends an Apple Event ping to the process identified by |psn| and registers | 491 // Returns the apple event that should be sent to the process. |
| 497 // to listen for a reply. | 492 - (NSAppleEventDescriptor*)appleEventToSendToProcess; |
| 498 - (void)pingProcess:(const ProcessSerialNumber&)psn; | |
| 499 | 493 |
| 500 // Called when a response is received from the target process for the ping sent | 494 // Called when a response is received from the target process for the ping sent |
| 501 // by |-pingProcess:|. | 495 // by |-pingProcess:|. |
| 502 - (void)message:(NSAppleEventDescriptor*)event | 496 - (void)message:(NSAppleEventDescriptor*)event |
| 503 withReply:(NSAppleEventDescriptor*)reply; | 497 withReply:(NSAppleEventDescriptor*)reply; |
| 504 | 498 |
| 505 // Calls |onReply_|, passing it |success| to specify whether the ping was | 499 // Calls |onReply_|, passing it |success| to specify whether the ping was |
| 506 // successful. | 500 // successful. |
| 507 - (void)closeWithSuccess:(bool)success; | 501 - (void)closeWithSuccess:(bool)success; |
| 508 @end | 502 @end |
| 509 | 503 |
| 510 @implementation ReplyEventHandler | |
| 511 + (void)pingProcess:(const ProcessSerialNumber&)psn | |
| 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 | |
| 520 | |
| 521 @implementation ReplyEventHandler (PrivateMethods) | 504 @implementation ReplyEventHandler (PrivateMethods) |
| 522 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn { | 505 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn { |
| 523 if ((self = [super init])) { | 506 if ((self = [super init])) { |
| 524 onReply_ = replyFn; | 507 onReply_ = replyFn; |
| 525 } | 508 } |
| 526 return self; | 509 return self; |
| 527 } | 510 } |
| 528 | 511 |
| 529 - (void)pingProcess:(const ProcessSerialNumber&)psn { | 512 - (NSAppleEventDescriptor*)appleEventToSendToProcess { |
| 530 // Register the reply listener. | 513 // Register the reply listener. |
| 531 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | 514 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
| 532 [em setEventHandler:self | 515 [em setEventHandler:self |
| 533 andSelector:@selector(message:withReply:) | 516 andSelector:@selector(message:withReply:) |
| 534 forEventClass:'aevt' | 517 forEventClass:'aevt' |
| 535 andEventID:'ansr']; | 518 andEventID:'ansr']; |
| 536 // Craft the Apple Event to send. | 519 NSAppleEventDescriptor* initial_event = [NSAppleEventDescriptor |
| 537 NSAppleEventDescriptor* target = [NSAppleEventDescriptor | 520 appleEventWithEventClass:app_mode::kAEChromeAppClass |
| 538 descriptorWithDescriptorType:typeProcessSerialNumber | 521 eventID:app_mode::kAEChromeAppPing |
| 539 bytes:&psn | 522 targetDescriptor:nil |
| 540 length:sizeof(psn)]; | 523 returnID:kAutoGenerateReturnID |
| 541 NSAppleEventDescriptor* initial_event = | 524 transactionID:kAnyTransactionID]; |
| 542 [NSAppleEventDescriptor | 525 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 } | 526 } |
| 559 | 527 |
| 560 - (void)message:(NSAppleEventDescriptor*)event | 528 - (void)message:(NSAppleEventDescriptor*)event |
| 561 withReply:(NSAppleEventDescriptor*)reply { | 529 withReply:(NSAppleEventDescriptor*)reply { |
| 562 [self closeWithSuccess:true]; | 530 [self closeWithSuccess:true]; |
| 563 } | 531 } |
| 564 | 532 |
| 565 - (void)closeWithSuccess:(bool)success { | 533 - (void)closeWithSuccess:(bool)success { |
| 566 onReply_.Run(success); | 534 onReply_.Run(success); |
| 567 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | 535 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 661 AppShimController controller; | 629 AppShimController controller; |
| 662 base::MessageLoopForUI main_message_loop; | 630 base::MessageLoopForUI main_message_loop; |
| 663 base::PlatformThread::SetName("CrAppShimMain"); | 631 base::PlatformThread::SetName("CrAppShimMain"); |
| 664 | 632 |
| 665 // In tests, launching Chrome does nothing, and we won't get a ping response, | 633 // In tests, launching Chrome does nothing, and we won't get a ping response, |
| 666 // so just assume the socket exists. | 634 // so just assume the socket exists. |
| 667 if (pid == -1 && | 635 if (pid == -1 && |
| 668 !base::CommandLine::ForCurrentProcess()->HasSwitch( | 636 !base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 669 app_mode::kLaunchedForTest)) { | 637 app_mode::kLaunchedForTest)) { |
| 670 // Launch Chrome if it isn't already running. | 638 // Launch Chrome if it isn't already running. |
| 671 ProcessSerialNumber psn; | |
| 672 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); | 639 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| 673 command_line.AppendSwitch(switches::kSilentLaunch); | 640 command_line.AppendSwitch(switches::kSilentLaunch); |
| 674 | 641 |
| 675 // If the shim is the app launcher, pass --show-app-list when starting a new | 642 // 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. | 643 // Chrome process to inform startup codepaths and load the correct profile. |
| 677 if (info->app_mode_id == app_mode::kAppListModeId) { | 644 if (info->app_mode_id == app_mode::kAppListModeId) { |
| 678 command_line.AppendSwitch(switches::kShowAppList); | 645 command_line.AppendSwitch(switches::kShowAppList); |
| 679 } else { | 646 } else { |
| 680 command_line.AppendSwitchPath(switches::kProfileDirectory, | 647 command_line.AppendSwitchPath(switches::kProfileDirectory, |
| 681 info->profile_dir); | 648 info->profile_dir); |
| 682 } | 649 } |
| 683 | 650 |
| 684 bool success = | 651 base::Callback<void(bool)> on_ping_chrome_reply = base::Bind( |
| 685 base::mac::OpenApplicationWithPath(base::mac::OuterBundlePath(), | 652 &AppShimController::OnPingChromeReply, base::Unretained(&controller)); |
| 686 command_line, | 653 |
| 687 kLSLaunchDefaults, | 654 // Self-owned object that will delete itself. |
| 688 &psn); | 655 ReplyEventHandler* handler = |
| 689 if (!success) | 656 [[ReplyEventHandler alloc] initWithCallback:on_ping_chrome_reply]; |
| 657 NSAppleEventDescriptor* descriptor = [handler appleEventToSendToProcess]; |
| 658 |
| 659 bool success = base::mac::OpenApplicationWithPath( |
| 660 base::mac::OuterBundlePath(), command_line, NSWorkspaceLaunchDefault, |
| 661 descriptor, nullptr); |
| 662 if (!success) { |
| 663 [handler closeWithSuccess:false]; |
| 690 return 1; | 664 return 1; |
| 691 | 665 } |
| 692 base::Callback<void(bool)> on_ping_chrome_reply = | |
| 693 base::Bind(&AppShimController::OnPingChromeReply, | |
| 694 base::Unretained(&controller)); | |
| 695 | 666 |
| 696 // This code abuses the fact that Apple Events sent before the process is | 667 // 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 | 668 // 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. | 669 // the reply is received, Chrome will have opened its IPC port, guaranteed. |
| 699 [ReplyEventHandler pingProcess:psn | |
| 700 andCall:on_ping_chrome_reply]; | |
| 701 | |
| 702 main_message_loop.task_runner()->PostDelayedTask( | 670 main_message_loop.task_runner()->PostDelayedTask( |
| 703 FROM_HERE, base::Bind(&AppShimController::OnPingChromeTimeout, | 671 FROM_HERE, base::Bind(&AppShimController::OnPingChromeTimeout, |
| 704 base::Unretained(&controller)), | 672 base::Unretained(&controller)), |
| 705 base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds)); | 673 base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds)); |
| 706 } else { | 674 } else { |
| 707 // Chrome already running. Proceed to init. This could still fail if Chrome | 675 // 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, | 676 // 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 | 677 // which is preferable to waiting for the Apple Event to timeout after one |
| 710 // minute. | 678 // minute. |
| 711 main_message_loop.task_runner()->PostTask( | 679 main_message_loop.task_runner()->PostTask( |
| 712 FROM_HERE, | 680 FROM_HERE, |
| 713 base::Bind(&AppShimController::Init, base::Unretained(&controller))); | 681 base::Bind(&AppShimController::Init, base::Unretained(&controller))); |
| 714 } | 682 } |
| 715 | 683 |
| 716 base::RunLoop().Run(); | 684 base::RunLoop().Run(); |
| 717 return 0; | 685 return 0; |
| 718 } | 686 } |
| OLD | NEW |