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, | 484 // Sends an Apple Event to the process identified by the outer bundle |
| 485 // which will delete itself once the reply event has been received. | 485 // identifier, and calls |replyFn| when the reply is received. Internally this |
| 486 + (void)pingProcess:(const ProcessSerialNumber&)psn | 486 // creates a ReplyEventHandler, which will delete itself once the reply event |
| 487 andCall:(base::Callback<void(bool)>)replyFn; | 487 // has been received. |
| 488 + (void)pingProcessAndCall:(base::Callback<void(bool)>)replyFn; | |
| 489 | |
| 488 @end | 490 @end |
| 489 | 491 |
| 490 @interface ReplyEventHandler (PrivateMethods) | 492 @interface ReplyEventHandler (PrivateMethods) |
| 491 // Initialise the reply event handler. Doesn't register any handlers until | 493 // Initialise the reply event handler. Doesn't register any handlers until |
| 492 // |-pingProcess:| is called. |replyFn| is the function to be called when the | 494 // |-pingProcess:| is called. |replyFn| is the function to be called when the |
| 493 // Apple Event reply arrives. | 495 // Apple Event reply arrives. |
| 494 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn; | 496 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn; |
| 495 | 497 |
| 496 // Sends an Apple Event ping to the process identified by |psn| and registers | 498 // Returns the apple event that should be sent to the process. |
| 497 // to listen for a reply. | 499 - (NSAppleEventDescriptor*)appleEventToSendToProcess; |
| 498 - (void)pingProcess:(const ProcessSerialNumber&)psn; | 500 |
| 501 // Sends an Apple Event ping to the process identified by the bundle | |
| 502 // identifier and registers to listen for a reply. | |
| 503 - (void)pingProcess; | |
| 499 | 504 |
| 500 // Called when a response is received from the target process for the ping sent | 505 // Called when a response is received from the target process for the ping sent |
| 501 // by |-pingProcess:|. | 506 // by |-pingProcess:|. |
| 502 - (void)message:(NSAppleEventDescriptor*)event | 507 - (void)message:(NSAppleEventDescriptor*)event |
| 503 withReply:(NSAppleEventDescriptor*)reply; | 508 withReply:(NSAppleEventDescriptor*)reply; |
| 504 | 509 |
| 505 // Calls |onReply_|, passing it |success| to specify whether the ping was | 510 // Calls |onReply_|, passing it |success| to specify whether the ping was |
| 506 // successful. | 511 // successful. |
| 507 - (void)closeWithSuccess:(bool)success; | 512 - (void)closeWithSuccess:(bool)success; |
| 513 | |
| 508 @end | 514 @end |
| 509 | 515 |
| 510 @implementation ReplyEventHandler | 516 @implementation ReplyEventHandler |
| 511 + (void)pingProcess:(const ProcessSerialNumber&)psn | 517 |
| 512 andCall:(base::Callback<void(bool)>)replyFn { | 518 + (void)pingProcessAndCall:(base::Callback<void(bool)>)replyFn { |
| 513 // The object will release itself when the reply arrives, or possibly earlier | 519 // The object will release itself when the reply arrives, or possibly earlier |
| 514 // if an unrecoverable error occurs. | 520 // if an unrecoverable error occurs. |
| 515 ReplyEventHandler* handler = | 521 ReplyEventHandler* handler = |
| 516 [[ReplyEventHandler alloc] initWithCallback:replyFn]; | 522 [[ReplyEventHandler alloc] initWithCallback:replyFn]; |
| 517 [handler pingProcess:psn]; | 523 [handler pingProcess]; |
| 518 } | 524 } |
| 525 | |
| 519 @end | 526 @end |
| 520 | 527 |
| 521 @implementation ReplyEventHandler (PrivateMethods) | 528 @implementation ReplyEventHandler (PrivateMethods) |
| 522 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn { | 529 - (id)initWithCallback:(base::Callback<void(bool)>)replyFn { |
| 523 if ((self = [super init])) { | 530 if ((self = [super init])) { |
| 524 onReply_ = replyFn; | 531 onReply_ = replyFn; |
| 525 } | 532 } |
| 526 return self; | 533 return self; |
| 527 } | 534 } |
| 528 | 535 |
| 529 - (void)pingProcess:(const ProcessSerialNumber&)psn { | 536 - (void)pingProcess { |
| 530 // Register the reply listener. | 537 // Register the reply listener. |
| 531 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | 538 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
| 532 [em setEventHandler:self | 539 [em setEventHandler:self |
| 533 andSelector:@selector(message:withReply:) | 540 andSelector:@selector(message:withReply:) |
| 534 forEventClass:'aevt' | 541 forEventClass:kCoreEventClass |
| 535 andEventID:'ansr']; | 542 andEventID:kAEAnswer]; |
| 543 | |
| 536 // Craft the Apple Event to send. | 544 // Craft the Apple Event to send. |
| 545 NSString* chromeBundleId = [base::mac::OuterBundle() bundleIdentifier]; | |
| 537 NSAppleEventDescriptor* target = [NSAppleEventDescriptor | 546 NSAppleEventDescriptor* target = [NSAppleEventDescriptor |
| 538 descriptorWithDescriptorType:typeProcessSerialNumber | 547 descriptorWithDescriptorType:typeApplicationBundleID |
| 539 bytes:&psn | 548 data:[chromeBundleId |
| 540 length:sizeof(psn)]; | 549 dataUsingEncoding:NSUTF8StringEncoding]]; |
| 541 NSAppleEventDescriptor* initial_event = | 550 |
| 551 NSAppleEventDescriptor* initialEvent = | |
| 542 [NSAppleEventDescriptor | 552 [NSAppleEventDescriptor |
| 543 appleEventWithEventClass:app_mode::kAEChromeAppClass | 553 appleEventWithEventClass:app_mode::kAEChromeAppClass |
| 544 eventID:app_mode::kAEChromeAppPing | 554 eventID:app_mode::kAEChromeAppPing |
| 545 targetDescriptor:target | 555 targetDescriptor:target |
| 546 returnID:kAutoGenerateReturnID | 556 returnID:kAutoGenerateReturnID |
| 547 transactionID:kAnyTransactionID]; | 557 transactionID:kAnyTransactionID]; |
| 548 | 558 |
| 549 // Note that AESendMessage effectively ignores kAEDefaultTimeout, because this | 559 // Note that AESendMessage effectively ignores kAEDefaultTimeout, because this |
| 550 // call does not pass kAEWantReceipt (which is deprecated and unsupported on | 560 // call does not pass kAEWantReceipt (which is deprecated and unsupported on |
| 551 // Mac). Instead, rely on OnPingChromeTimeout(). | 561 // Mac). Instead, rely on OnPingChromeTimeout(). |
| 552 OSStatus status = AESendMessage( | 562 OSStatus status = AESendMessage( |
| 553 [initial_event aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout); | 563 [initialEvent aeDesc], &replyEvent_, kAEQueueReply, kAEDefaultTimeout); |
| 554 if (status != noErr) { | 564 if (status != noErr) { |
| 555 OSSTATUS_LOG(ERROR, status) << "AESendMessage"; | 565 OSSTATUS_LOG(ERROR, status) << "AESendMessage"; |
| 556 [self closeWithSuccess:false]; | 566 [self closeWithSuccess:false]; |
| 557 } | 567 } |
| 558 } | 568 } |
| 559 | 569 |
| 570 - (NSAppleEventDescriptor*)appleEventToSendToProcess { | |
| 571 // Register the reply listener. | |
| 572 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | |
| 573 [em setEventHandler:self | |
| 574 andSelector:@selector(message:withReply:) | |
| 575 forEventClass:kCoreEventClass | |
| 576 andEventID:kAEAnswer]; | |
| 577 NSAppleEventDescriptor* initial_event = [NSAppleEventDescriptor | |
| 578 appleEventWithEventClass:app_mode::kAEChromeAppClass | |
| 579 eventID:app_mode::kAEChromeAppPing | |
| 580 targetDescriptor:nil | |
|
tapted
2016/10/26 06:28:29
It sounds like maybe this apple event simply isn't
| |
| 581 returnID:kAutoGenerateReturnID | |
| 582 transactionID:kAnyTransactionID]; | |
| 583 return initial_event; | |
| 584 } | |
| 585 | |
| 560 - (void)message:(NSAppleEventDescriptor*)event | 586 - (void)message:(NSAppleEventDescriptor*)event |
| 561 withReply:(NSAppleEventDescriptor*)reply { | 587 withReply:(NSAppleEventDescriptor*)reply { |
| 562 [self closeWithSuccess:true]; | 588 [self closeWithSuccess:true]; |
| 563 } | 589 } |
| 564 | 590 |
| 565 - (void)closeWithSuccess:(bool)success { | 591 - (void)closeWithSuccess:(bool)success { |
| 566 onReply_.Run(success); | 592 onReply_.Run(success); |
| 567 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | 593 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
| 568 [em removeEventHandlerForEventClass:'aevt' andEventID:'ansr']; | 594 [em removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEAnswer]; |
| 569 [self release]; | 595 [self release]; |
| 570 } | 596 } |
| 597 | |
| 571 @end | 598 @end |
| 572 | 599 |
| 573 //----------------------------------------------------------------------------- | 600 //----------------------------------------------------------------------------- |
| 574 | 601 |
| 575 extern "C" { | 602 extern "C" { |
| 576 | 603 |
| 577 // |ChromeAppModeStart()| is the point of entry into the framework from the app | 604 // |ChromeAppModeStart()| is the point of entry into the framework from the app |
| 578 // mode loader. There are cases where the Chromium framework may have changed in | 605 // mode loader. There are cases where the Chromium framework may have changed in |
| 579 // a way that is incompatible with an older shim (e.g. change to libc++ library | 606 // a way that is incompatible with an older shim (e.g. change to libc++ library |
| 580 // linking). The function name is versioned to provide a way to force shim | 607 // linking). The function name is versioned to provide a way to force shim |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 657 if ([existing_chrome count] > 0) | 684 if ([existing_chrome count] > 0) |
| 658 pid = [[existing_chrome objectAtIndex:0] processIdentifier]; | 685 pid = [[existing_chrome objectAtIndex:0] processIdentifier]; |
| 659 } | 686 } |
| 660 | 687 |
| 661 AppShimController controller; | 688 AppShimController controller; |
| 662 base::MessageLoopForUI main_message_loop; | 689 base::MessageLoopForUI main_message_loop; |
| 663 base::PlatformThread::SetName("CrAppShimMain"); | 690 base::PlatformThread::SetName("CrAppShimMain"); |
| 664 | 691 |
| 665 // In tests, launching Chrome does nothing, and we won't get a ping response, | 692 // In tests, launching Chrome does nothing, and we won't get a ping response, |
| 666 // so just assume the socket exists. | 693 // so just assume the socket exists. |
| 667 if (pid == -1 && | 694 if (pid == -1 && |
|
tapted
2016/10/26 06:28:29
So I just got another crazy idea: I think we can j
| |
| 668 !base::CommandLine::ForCurrentProcess()->HasSwitch( | 695 !base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 669 app_mode::kLaunchedForTest)) { | 696 app_mode::kLaunchedForTest)) { |
| 670 // Launch Chrome if it isn't already running. | 697 // Launch Chrome if it isn't already running. |
| 671 ProcessSerialNumber psn; | |
| 672 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); | 698 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| 673 command_line.AppendSwitch(switches::kSilentLaunch); | 699 command_line.AppendSwitch(switches::kSilentLaunch); |
| 674 | 700 |
| 675 // If the shim is the app launcher, pass --show-app-list when starting a new | 701 // 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. | 702 // Chrome process to inform startup codepaths and load the correct profile. |
| 677 if (info->app_mode_id == app_mode::kAppListModeId) { | 703 if (info->app_mode_id == app_mode::kAppListModeId) { |
| 678 command_line.AppendSwitch(switches::kShowAppList); | 704 command_line.AppendSwitch(switches::kShowAppList); |
| 679 } else { | 705 } else { |
| 680 command_line.AppendSwitchPath(switches::kProfileDirectory, | 706 command_line.AppendSwitchPath(switches::kProfileDirectory, |
| 681 info->profile_dir); | 707 info->profile_dir); |
| 682 } | 708 } |
| 683 | 709 |
| 710 base::Callback<void(bool)> on_ping_chrome_reply = base::Bind( | |
| 711 &AppShimController::OnPingChromeReply, base::Unretained(&controller)); | |
| 712 | |
| 713 // Self-owned object that will delete itself. | |
| 714 ReplyEventHandler* handler = | |
| 715 [[ReplyEventHandler alloc] initWithCallback:on_ping_chrome_reply]; | |
| 716 NSAppleEventDescriptor* descriptor = [handler appleEventToSendToProcess]; | |
| 717 | |
| 684 base::Process app = base::mac::OpenApplicationWithPath( | 718 base::Process app = base::mac::OpenApplicationWithPath( |
| 685 base::mac::OuterBundlePath(), command_line, NSWorkspaceLaunchDefault); | 719 base::mac::OuterBundlePath(), command_line, NSWorkspaceLaunchDefault, |
| 686 | 720 descriptor); |
| 687 // TODO(crbug.com/652563): Do not use deprecated GetProcessForPID. Change | 721 if (!app.IsValid()) { |
| 688 // |ReplyEventHandler| to take |pid_t| instead of |ProcessSerialNumber|. | 722 [handler closeWithSuccess:false]; |
| 689 if (!app.IsValid() || GetProcessForPID(app.Pid(), &psn) != noErr) | |
| 690 return 1; | 723 return 1; |
| 691 | 724 } |
| 692 base::Callback<void(bool)> on_ping_chrome_reply = | |
| 693 base::Bind(&AppShimController::OnPingChromeReply, | |
| 694 base::Unretained(&controller)); | |
| 695 | 725 |
| 696 // This code abuses the fact that Apple Events sent before the process is | 726 // 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 | 727 // 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. | 728 // the reply is received, Chrome will have opened its IPC port, guaranteed. |
| 699 [ReplyEventHandler pingProcess:psn | 729 [ReplyEventHandler pingProcessAndCall:on_ping_chrome_reply]; |
| 700 andCall:on_ping_chrome_reply]; | |
| 701 | 730 |
| 702 main_message_loop.task_runner()->PostDelayedTask( | 731 main_message_loop.task_runner()->PostDelayedTask( |
| 703 FROM_HERE, base::Bind(&AppShimController::OnPingChromeTimeout, | 732 FROM_HERE, base::Bind(&AppShimController::OnPingChromeTimeout, |
| 704 base::Unretained(&controller)), | 733 base::Unretained(&controller)), |
| 705 base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds)); | 734 base::TimeDelta::FromSeconds(kPingChromeTimeoutSeconds)); |
| 706 } else { | 735 } else { |
| 707 // Chrome already running. Proceed to init. This could still fail if Chrome | 736 // 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, | 737 // 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 | 738 // which is preferable to waiting for the Apple Event to timeout after one |
| 710 // minute. | 739 // minute. |
| 711 main_message_loop.task_runner()->PostTask( | 740 main_message_loop.task_runner()->PostTask( |
| 712 FROM_HERE, | 741 FROM_HERE, |
| 713 base::Bind(&AppShimController::Init, base::Unretained(&controller))); | 742 base::Bind(&AppShimController::Init, base::Unretained(&controller))); |
| 714 } | 743 } |
| 715 | 744 |
| 716 base::RunLoop().Run(); | 745 base::RunLoop().Run(); |
| 717 return 0; | 746 return 0; |
| 718 } | 747 } |
| OLD | NEW |