Commit 55ff2549 authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

Separate AppShimController and AppShimDelegate classes

Cut-and-paste these classes into separate files, because their current
location is getting crowded.

Bug: 859152
Change-Id: I9bc9fe8225589f91ba1cbc9daaccfbe58288197f
Reviewed-on: https://chromium-review.googlesource.com/c/1277581Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Commit-Queue: ccameron <ccameron@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599335}
parent c7542cef
...@@ -11,6 +11,10 @@ import("//build/util/branding.gni") ...@@ -11,6 +11,10 @@ import("//build/util/branding.gni")
# Framework bundle. # Framework bundle.
source_set("app_shim") { source_set("app_shim") {
sources = [ sources = [
"app_shim_controller.h",
"app_shim_controller.mm",
"app_shim_delegate.h",
"app_shim_delegate.mm",
"chrome_main_app_mode_mac.mm", "chrome_main_app_mode_mac.mm",
] ]
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_APP_SHIM_APP_SHIM_CONTROLLER_H_
#define CHROME_APP_SHIM_APP_SHIM_CONTROLLER_H_
#include "base/files/file_path.h"
#include "base/mac/scoped_nsobject.h"
#include "chrome/common/mac/app_mode_common.h"
#include "chrome/common/mac/app_shim.mojom.h"
#include "chrome/common/mac/app_shim_param_traits.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/isolated_connection.h"
@class AppShimDelegate;
// The AppShimController is responsible for communication with the main Chrome
// process, and generally controls the lifetime of the app shim process.
class AppShimController : public chrome::mojom::AppShim {
public:
explicit AppShimController(const app_mode::ChromeAppModeInfo* app_mode_info);
~AppShimController() override;
// Called when the main Chrome process responds to the Apple Event ping that
// was sent, or when the ping fails (if |success| is false).
void OnPingChromeReply(bool success);
// Called |kPingChromeTimeoutSeconds| after startup, to allow a timeout on the
// ping event to be detected.
void OnPingChromeTimeout();
// Connects to Chrome and sends a LaunchApp message.
void Init();
chrome::mojom::AppShimHost* host() const { return host_.get(); }
// Called when the app is activated, e.g. by clicking on it in the dock, by
// dropping a file on the dock icon, or by Cmd+Tabbing to it.
// Returns whether the message was sent.
bool SendFocusApp(apps::AppShimFocusType focus_type,
const std::vector<base::FilePath>& files);
private:
// Create a channel from |socket_path| and send a LaunchApp message.
void CreateChannelAndSendLaunchApp(const base::FilePath& socket_path);
// Builds main menu bar items.
void SetUpMenu();
void ChannelError(uint32_t custom_reason, const std::string& description);
// chrome::mojom::AppShim implementation.
void LaunchAppDone(apps::AppShimLaunchResult result) override;
void CreateViewsBridgeFactory(
views_bridge_mac::mojom::BridgeFactoryAssociatedRequest request) override;
void CreateContentNSViewBridgeFactory(
content::mojom::NSViewBridgeFactoryAssociatedRequest request) override;
void Hide() override;
void UnhideWithoutActivation() override;
void SetUserAttention(apps::AppShimAttentionType attention_type) override;
// Terminates the app shim process.
void Close();
const app_mode::ChromeAppModeInfo* const app_mode_info_;
base::FilePath user_data_dir_;
mojo::IsolatedConnection mojo_connection_;
mojo::Binding<chrome::mojom::AppShim> shim_binding_;
chrome::mojom::AppShimHostPtr host_;
base::scoped_nsobject<AppShimDelegate> delegate_;
bool launch_app_done_;
bool ping_chrome_reply_received_;
NSInteger attention_request_id_;
DISALLOW_COPY_AND_ASSIGN(AppShimController);
};
#endif // CHROME_APP_SHIM_APP_SHIM_CONTROLLER_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/app_shim/app_shim_controller.h"
#import <Cocoa/Cocoa.h>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/app_shim/app_shim_delegate.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/ns_view_bridge_factory_impl.h"
#include "content/public/common/ns_view_bridge_factory.mojom.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views_bridge_mac/bridge_factory_impl.h"
#include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h"
AppShimController::AppShimController(
const app_mode::ChromeAppModeInfo* app_mode_info)
: app_mode_info_(app_mode_info),
shim_binding_(this),
delegate_([[AppShimDelegate alloc] init]),
launch_app_done_(false),
ping_chrome_reply_received_(false),
attention_request_id_(0) {
// Since AppShimController is created before the main message loop starts,
// NSApp will not be set, so use sharedApplication.
NSApplication* sharedApplication = [NSApplication sharedApplication];
[sharedApplication setDelegate:delegate_];
}
AppShimController::~AppShimController() {
// Un-set the delegate since NSApplication does not retain it.
NSApplication* sharedApplication = [NSApplication sharedApplication];
[sharedApplication setDelegate:nil];
}
void AppShimController::OnPingChromeReply(bool success) {
ping_chrome_reply_received_ = true;
if (!success) {
[NSApp terminate:nil];
return;
}
Init();
}
void AppShimController::OnPingChromeTimeout() {
if (!ping_chrome_reply_received_)
[NSApp terminate:nil];
}
void AppShimController::Init() {
ui::WindowResizeHelperMac::Get()->Init(base::ThreadTaskRunnerHandle::Get());
SetUpMenu();
// Chrome will relaunch shims when relaunching apps.
[NSApp disableRelaunchOnLogin];
// The user_data_dir for shims actually contains the app_data_path.
// I.e. <user_data_dir>/<profile_dir>/Web Applications/_crx_extensionid/
user_data_dir_ = app_mode_info_->user_data_dir.DirName().DirName().DirName();
CHECK(!user_data_dir_.empty());
base::FilePath symlink_path =
user_data_dir_.Append(app_mode::kAppShimSocketSymlinkName);
base::FilePath socket_path;
if (!base::ReadSymbolicLink(symlink_path, &socket_path)) {
// The path in the user data dir is not a symlink, try connecting directly.
CreateChannelAndSendLaunchApp(symlink_path);
return;
}
app_mode::VerifySocketPermissions(socket_path);
CreateChannelAndSendLaunchApp(socket_path);
}
void AppShimController::CreateChannelAndSendLaunchApp(
const base::FilePath& socket_path) {
mojo::ScopedMessagePipeHandle message_pipe = mojo_connection_.Connect(
mojo::NamedPlatformChannel::ConnectToServer(socket_path.value()));
host_ = chrome::mojom::AppShimHostPtr(
chrome::mojom::AppShimHostPtrInfo(std::move(message_pipe), 0));
chrome::mojom::AppShimPtr app_shim_ptr;
shim_binding_.Bind(mojo::MakeRequest(&app_shim_ptr),
ui::WindowResizeHelperMac::Get()->task_runner());
shim_binding_.set_connection_error_with_reason_handler(
base::BindOnce(&AppShimController::ChannelError, base::Unretained(this)));
bool launched_by_chrome = base::CommandLine::ForCurrentProcess()->HasSwitch(
app_mode::kLaunchedByChromeProcessId);
apps::AppShimLaunchType launch_type =
launched_by_chrome ? apps::APP_SHIM_LAUNCH_REGISTER_ONLY
: apps::APP_SHIM_LAUNCH_NORMAL;
[delegate_ setController:this];
std::vector<base::FilePath> files;
[delegate_ getFilesToOpenAtStartup:&files];
host_->LaunchApp(std::move(app_shim_ptr), app_mode_info_->profile_dir,
app_mode_info_->app_mode_id, launch_type, files);
}
void AppShimController::SetUpMenu() {
NSString* title = base::SysUTF16ToNSString(app_mode_info_->app_mode_name);
// Create a main menu since [NSApp mainMenu] is nil.
base::scoped_nsobject<NSMenu> main_menu([[NSMenu alloc] initWithTitle:title]);
// The title of the first item is replaced by OSX with the name of the app and
// bold styling. Create a dummy item for this and make it hidden.
NSMenuItem* dummy_item =
[main_menu addItemWithTitle:title action:nil keyEquivalent:@""];
base::scoped_nsobject<NSMenu> dummy_submenu(
[[NSMenu alloc] initWithTitle:title]);
[dummy_item setSubmenu:dummy_submenu];
[dummy_item setHidden:YES];
// Construct an unbolded app menu, to match how it appears in the Chrome menu
// bar when the app is focused.
NSMenuItem* item =
[main_menu addItemWithTitle:title action:nil keyEquivalent:@""];
base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:title]);
[item setSubmenu:submenu];
// Add a quit entry.
NSString* quit_localized_string =
l10n_util::GetNSStringF(IDS_EXIT_MAC, app_mode_info_->app_mode_name);
[submenu addItemWithTitle:quit_localized_string
action:@selector(terminate:)
keyEquivalent:@"q"];
// Add File, Edit, and Window menus. These are just here to make the
// transition smoother, i.e. from another application to the shim then to
// Chrome.
[main_menu addItemWithTitle:l10n_util::GetNSString(IDS_FILE_MENU_MAC)
action:nil
keyEquivalent:@""];
[main_menu addItemWithTitle:l10n_util::GetNSString(IDS_EDIT_MENU_MAC)
action:nil
keyEquivalent:@""];
[main_menu addItemWithTitle:l10n_util::GetNSString(IDS_WINDOW_MENU_MAC)
action:nil
keyEquivalent:@""];
[NSApp setMainMenu:main_menu];
}
void AppShimController::ChannelError(uint32_t custom_reason,
const std::string& description) {
LOG(ERROR) << "Channel error custom_reason:" << custom_reason
<< " description: " << description;
Close();
}
void AppShimController::LaunchAppDone(apps::AppShimLaunchResult result) {
if (result != apps::APP_SHIM_LAUNCH_SUCCESS) {
Close();
return;
}
std::vector<base::FilePath> files;
if ([delegate_ getFilesToOpenAtStartup:&files])
SendFocusApp(apps::APP_SHIM_FOCUS_OPEN_FILES, files);
launch_app_done_ = true;
}
void AppShimController::CreateViewsBridgeFactory(
views_bridge_mac::mojom::BridgeFactoryAssociatedRequest request) {
views_bridge_mac::BridgeFactoryImpl::Get()->BindRequest(std::move(request));
}
void AppShimController::CreateContentNSViewBridgeFactory(
content::mojom::NSViewBridgeFactoryAssociatedRequest request) {
content::NSViewBridgeFactoryImpl::Get()->BindRequest(std::move(request));
}
void AppShimController::Hide() {
[NSApp hide:nil];
}
void AppShimController::UnhideWithoutActivation() {
[NSApp unhideWithoutActivation];
}
void AppShimController::SetUserAttention(
apps::AppShimAttentionType attention_type) {
switch (attention_type) {
case apps::APP_SHIM_ATTENTION_CANCEL:
[NSApp cancelUserAttentionRequest:attention_request_id_];
attention_request_id_ = 0;
break;
case apps::APP_SHIM_ATTENTION_CRITICAL:
attention_request_id_ = [NSApp requestUserAttention:NSCriticalRequest];
break;
case apps::APP_SHIM_ATTENTION_INFORMATIONAL:
attention_request_id_ =
[NSApp requestUserAttention:NSInformationalRequest];
break;
case apps::APP_SHIM_ATTENTION_NUM_TYPES:
NOTREACHED();
}
}
void AppShimController::Close() {
[delegate_ terminateNow];
}
bool AppShimController::SendFocusApp(apps::AppShimFocusType focus_type,
const std::vector<base::FilePath>& files) {
if (launch_app_done_) {
host_->FocusApp(focus_type, files);
return true;
}
return false;
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_APP_SHIM_APP_SHIM_DELEGATE_H_
#define CHROME_APP_SHIM_APP_SHIM_DELEGATE_H_
#import <Cocoa/Cocoa.h>
#include <vector>
#include "base/files/file_path.h"
class AppShimController;
// An application delegate to catch user interactions and send the appropriate
// IPC messages to Chrome.
@interface AppShimDelegate : NSObject<NSApplicationDelegate> {
@private
AppShimController* appShimController_; // Weak, initially NULL.
BOOL terminateNow_;
BOOL terminateRequested_;
std::vector<base::FilePath> filesToOpenAtStartup_;
}
// The controller is initially NULL. Setting it indicates to the delegate that
// the controller has finished initialization.
- (void)setController:(AppShimController*)controller;
// Gets files that were queued because the controller was not ready.
// Returns whether any FilePaths were added to |out|.
- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out;
// If the controller is ready, this sends a FocusApp with the files to open.
// Otherwise, this adds the files to |filesToOpenAtStartup_|.
// Takes an array of NSString*.
- (void)openFiles:(NSArray*)filename;
// Terminate immediately. This is necessary as we override terminate: to send
// a QuitApp message.
- (void)terminateNow;
@end
#endif // CHROME_APP_SHIM_APP_SHIM_DELEGATE_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/app_shim/app_shim_delegate.h"
#include "base/mac/foundation_util.h"
#include "chrome/app_shim/app_shim_controller.h"
@implementation AppShimDelegate
- (BOOL)getFilesToOpenAtStartup:(std::vector<base::FilePath>*)out {
if (filesToOpenAtStartup_.empty())
return NO;
out->insert(out->end(), filesToOpenAtStartup_.begin(),
filesToOpenAtStartup_.end());
filesToOpenAtStartup_.clear();
return YES;
}
- (void)setController:(AppShimController*)controller {
appShimController_ = controller;
}
- (void)openFiles:(NSArray*)filenames {
std::vector<base::FilePath> filePaths;
for (NSString* filename in filenames)
filePaths.push_back(base::mac::NSStringToFilePath(filename));
// If the AppShimController is ready, try to send a FocusApp. If that fails,
// (e.g. if launching has not finished), enqueue the files.
if (appShimController_ && appShimController_->SendFocusApp(
apps::APP_SHIM_FOCUS_OPEN_FILES, filePaths)) {
return;
}
filesToOpenAtStartup_.insert(filesToOpenAtStartup_.end(), filePaths.begin(),
filePaths.end());
}
- (BOOL)application:(NSApplication*)app openFile:(NSString*)filename {
[self openFiles:@[ filename ]];
return YES;
}
- (void)application:(NSApplication*)app openFiles:(NSArray*)filenames {
[self openFiles:filenames];
[app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
}
- (BOOL)applicationOpenUntitledFile:(NSApplication*)app {
if (appShimController_) {
return appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_REOPEN,
std::vector<base::FilePath>());
}
return NO;
}
- (void)applicationWillBecomeActive:(NSNotification*)notification {
if (appShimController_) {
appShimController_->SendFocusApp(apps::APP_SHIM_FOCUS_NORMAL,
std::vector<base::FilePath>());
}
}
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication*)sender {
if (terminateNow_ || !appShimController_)
return NSTerminateNow;
appShimController_->host()->QuitApp();
// Wait for the channel to close before terminating.
terminateRequested_ = YES;
return NSTerminateLater;
}
- (void)applicationWillHide:(NSNotification*)notification {
if (appShimController_)
appShimController_->host()->SetAppHidden(true);
}
- (void)applicationWillUnhide:(NSNotification*)notification {
if (appShimController_)
appShimController_->host()->SetAppHidden(false);
}
- (void)terminateNow {
if (terminateRequested_) {
[NSApp replyToApplicationShouldTerminate:NSTerminateNow];
return;
}
terminateNow_ = YES;
[NSApp terminate:nil];
}
@end
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment