1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Cocoa/Cocoa.h>
#include <utility>
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/path_service.h"
#include "content/public/browser/context_factory.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/result_codes.h"
#include "content/shell/browser/shell_application_mac.h"
#include "content/shell/browser/shell_browser_context.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views_content_client/views_content_client.h"
#include "ui/views_content_client/views_content_client_main_parts.h"
// A simple NSApplicationDelegate that provides a basic mainMenu and can
// activate a task when the application has finished loading.
@interface ViewsContentClientAppController : NSObject <NSApplicationDelegate> {
@private
base::OnceClosure _onApplicationDidFinishLaunching;
}
// Set the task to run after receiving -applicationDidFinishLaunching:.
- (void)setOnApplicationDidFinishLaunching:(base::OnceClosure)task;
@end
namespace ui {
namespace {
class ViewsContentClientMainPartsMac : public ViewsContentClientMainParts {
public:
explicit ViewsContentClientMainPartsMac(
ViewsContentClient* views_content_client);
ViewsContentClientMainPartsMac(const ViewsContentClientMainPartsMac&) =
delete;
ViewsContentClientMainPartsMac& operator=(
const ViewsContentClientMainPartsMac&) = delete;
~ViewsContentClientMainPartsMac() override;
// content::BrowserMainParts:
int PreMainMessageLoopRun() override;
private:
ViewsContentClientAppController* __strong app_controller_;
};
ViewsContentClientMainPartsMac::ViewsContentClientMainPartsMac(
ViewsContentClient* views_content_client)
: ViewsContentClientMainParts(views_content_client) {
// Cache the child process path to avoid triggering an AssertIOAllowed.
base::FilePath child_process_exe;
base::PathService::Get(content::CHILD_PROCESS_EXE, &child_process_exe);
app_controller_ = [[ViewsContentClientAppController alloc] init];
NSApplication.sharedApplication.delegate = app_controller_;
}
int ViewsContentClientMainPartsMac::PreMainMessageLoopRun() {
ViewsContentClientMainParts::PreMainMessageLoopRun();
views_delegate()->set_context_factory(content::GetContextFactory());
// On Mac, the task must be deferred to applicationDidFinishLaunching. If not,
// the widget can activate, but (even if configured) the mainMenu won't be
// ready to switch over in the OSX UI, so it will look strange.
NSWindow* window_context = nil;
[app_controller_
setOnApplicationDidFinishLaunching:
base::BindOnce(&ViewsContentClient::OnPreMainMessageLoopRun,
base::Unretained(views_content_client()),
base::Unretained(browser_context()),
base::Unretained(window_context))];
return content::RESULT_CODE_NORMAL_EXIT;
}
ViewsContentClientMainPartsMac::~ViewsContentClientMainPartsMac() {
NSApplication.sharedApplication.delegate = nil;
}
} // namespace
// static
std::unique_ptr<ViewsContentClientMainParts>
ViewsContentClientMainParts::Create(ViewsContentClient* views_content_client) {
return std::make_unique<ViewsContentClientMainPartsMac>(views_content_client);
}
// static
void ViewsContentClientMainParts::PreBrowserMain() {
// Simply instantiating an instance of ShellCrApplication serves to register
// it as the application class. Do make sure that no other code has done this
// first, though.
CHECK_EQ(NSApp, nil);
[ShellCrApplication sharedApplication];
}
} // namespace ui
@implementation ViewsContentClientAppController
- (void)setOnApplicationDidFinishLaunching:(base::OnceClosure)task {
_onApplicationDidFinishLaunching = std::move(task);
}
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
// To get key events, the application needs to have an activation policy.
// Unbundled apps (i.e. those without an Info.plist) default to
// NSApplicationActivationPolicyProhibited.
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
// Create a basic mainMenu object using the executable filename.
NSMenu* mainMenu = [[NSMenu alloc] initWithTitle:@""];
NSMenuItem* appMenuItem = [mainMenu addItemWithTitle:@""
action:nullptr
keyEquivalent:@""];
[NSApp setMainMenu:mainMenu];
NSMenu* appMenu = [[NSMenu alloc] initWithTitle:@""];
NSString* appName = NSProcessInfo.processInfo.processName;
// TODO(tapted): Localize "Quit" if this is ever used for a released binary.
// At the time of writing, ui_strings.grd has "Close" but not "Quit".
NSString* quitTitle = [@"Quit " stringByAppendingString:appName];
[appMenu addItemWithTitle:quitTitle
action:@selector(terminate:)
keyEquivalent:@"q"];
[appMenuItem setSubmenu:appMenu];
CHECK([NSApp isKindOfClass:[ShellCrApplication class]]);
std::move(_onApplicationDidFinishLaunching).Run();
}
@end
|