Files
atom/native/atom_window_controller.mm

295 lines
10 KiB
Plaintext

#import "include/cef_application_mac.h"
#import "include/cef_client.h"
#import "native/atom_cef_client.h"
#import "native/atom_window_controller.h"
#import "native/atom_application.h"
#import <signal.h>
@implementation AtomWindowController
@synthesize splitView=_splitView;
@synthesize webView=_webView;
@synthesize devToolsView=_devToolsView;
@synthesize pathToOpen=_pathToOpen;
- (void)dealloc {
[_splitView release];
[_devToolsView release];
[_webView release];
[_bootstrapScript release];
[_resourcePath release];
[_pathToOpen release];
_cefClient = NULL;
_cefDevToolsClient = NULL;
[super dealloc];
}
- (id)initWithBootstrapScript:(NSString *)bootstrapScript background:(BOOL)background alwaysUseBundleResourcePath:(BOOL)alwaysUseBundleResourcePath {
self = [super initWithWindowNibName:@"AtomWindow"];
_bootstrapScript = [bootstrapScript retain];
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
_resourcePath = [atomApplication.arguments objectForKey:@"resource-path"];
if (!alwaysUseBundleResourcePath && !_resourcePath) {
NSString *defaultRepositoryPath = [@"~/github/atom" stringByStandardizingPath];
if ([defaultRepositoryPath characterAtIndex:0] == '/') {
BOOL isDir = false;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:defaultRepositoryPath isDirectory:&isDir];
if (isDir && exists)
_resourcePath = defaultRepositoryPath;
}
}
if (alwaysUseBundleResourcePath || !_resourcePath) {
_resourcePath = [[NSBundle bundleForClass:self.class] resourcePath];
}
if ([self isDevMode]) {
[self displayDevIcon];
}
_resourcePath = [_resourcePath stringByStandardizingPath];
[_resourcePath retain];
NSArray *paths = [NSArray arrayWithObjects:@"spec", @"benchmark",
@"src/stdlib", @"src/app", @"src/packages", @"src",
@"vendor/packages", @"vendor", @"static", @"themes",
@"node_modules", nil];
NSMutableArray *resourcePaths = [[NSMutableArray alloc] init];
if (_runningSpecs) {
NSString *fixturePackagesDirectory = [NSString stringWithFormat:@"%@/fixtures/packages", _resourcePath];
[resourcePaths addObject:fixturePackagesDirectory];
}
NSString *userPackagesDirectory = [@"~/.atom/packages" stringByStandardizingPath];
[resourcePaths addObject:userPackagesDirectory];
for (int i = 0; i < paths.count; i++) {
NSString *fullPath = [NSString stringWithFormat:@"%@/%@", _resourcePath, [paths objectAtIndex:i]];
[resourcePaths addObject:fullPath];
}
NSString *nodePath = [resourcePaths componentsJoinedByString:@":"];
setenv("NODE_PATH", [nodePath UTF8String], TRUE);
if (!background) {
[self setShouldCascadeWindows:NO];
[self setWindowFrameAutosaveName:@"AtomWindow"];
NSColor *background = [NSColor colorWithDeviceRed:(51.0/255.0) green:(51.0/255.0f) blue:(51.0/255.0f) alpha:1.0];
[self.window setBackgroundColor:background];
[self showWindow:self];
}
return self;
}
- (id)initWithPath:(NSString *)path {
_pathToOpen = [path retain];
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
BOOL useBundleResourcePath = [atomApplication.arguments objectForKey:@"dev"] == nil;
return [self initWithBootstrapScript:@"window-bootstrap" background:NO alwaysUseBundleResourcePath:useBundleResourcePath];
}
- (id)initDevWithPath:(NSString *)path {
_pathToOpen = [path retain];
return [self initWithBootstrapScript:@"window-bootstrap" background:NO alwaysUseBundleResourcePath:false];
}
- (id)initInBackground {
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
BOOL useBundleResourcePath = [atomApplication.arguments objectForKey:@"dev"] == nil;
[self initWithBootstrapScript:@"window-bootstrap" background:YES alwaysUseBundleResourcePath:useBundleResourcePath];
[self.window setFrame:NSMakeRect(0, 0, 0, 0) display:NO];
[self.window setExcludedFromWindowsMenu:YES];
[self.window setCollectionBehavior:NSWindowCollectionBehaviorStationary];
return self;
}
- (id)initSpecsThenExit:(BOOL)exitWhenDone {
_runningSpecs = true;
_exitWhenDone = exitWhenDone;
return [self initWithBootstrapScript:@"spec-bootstrap" background:NO alwaysUseBundleResourcePath:NO];
}
- (id)initBenchmarksThenExit:(BOOL)exitWhenDone {
_runningSpecs = true;
_exitWhenDone = exitWhenDone;
return [self initWithBootstrapScript:@"benchmark-bootstrap" background:NO alwaysUseBundleResourcePath:NO];
}
- (void)addBrowserToView:(NSView *)view url:(const char *)url cefHandler:(CefRefPtr<AtomCefClient>)cefClient {
CefBrowserSettings settings;
[self populateBrowserSettings:settings];
CefWindowInfo window_info;
window_info.SetAsChild(view, 0, 0, view.bounds.size.width, view.bounds.size.height);
CefBrowserHost::CreateBrowser(window_info, cefClient.get(), url, settings);
}
- (NSString *)encodeUrlParam:(NSString *)param {
param = [param stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
param = [param stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
return param;
}
- (void)windowDidLoad {
[self.window setDelegate:self];
[self performSelector:@selector(attachWebView) withObject:nil afterDelay:0];
}
// If this is run directly in windowDidLoad, the web view doesn't
// have the correct initial size based on the frame's last stored size.
// HACK: I hate this and want to place this code directly in windowDidLoad
- (void)attachWebView {
NSURL *url = [[NSBundle bundleForClass:self.class] resourceURL];
NSMutableString *urlString = [NSMutableString string];
[urlString appendString:[[url URLByAppendingPathComponent:@"static/index.html"] absoluteString]];
[urlString appendFormat:@"?bootstrapScript=%@", [self encodeUrlParam:_bootstrapScript]];
[urlString appendFormat:@"&resourcePath=%@", [self encodeUrlParam:_resourcePath]];
if ([self isDevMode])
[urlString appendFormat:@"&devMode=1"];
if (_exitWhenDone)
[urlString appendString:@"&exitWhenDone=1"];
if (_pathToOpen)
[urlString appendFormat:@"&pathToOpen=%@", [self encodeUrlParam:_pathToOpen]];
_cefClient = new AtomCefClient();
[self.webView setHidden:YES];
[self addBrowserToView:self.webView url:[urlString UTF8String] cefHandler:_cefClient];
}
- (void)toggleDevTools {
if (_devToolsView) {
[self hideDevTools];
}
else {
[self showDevTools];
}
}
- (void)showDevTools {
if (_devToolsView) return;
if (_cefClient && _cefClient->GetBrowser()) {
NSRect webViewFrame = _webView.frame;
NSRect devToolsViewFrame = _webView.frame;
devToolsViewFrame.size.height = NSHeight(webViewFrame) / 3;
webViewFrame.size.height = NSHeight(webViewFrame) - NSHeight(devToolsViewFrame);
[_webView setFrame:webViewFrame];
_devToolsView = [[NSView alloc] initWithFrame:devToolsViewFrame];
[_splitView addSubview:_devToolsView];
[_splitView adjustSubviews];
_cefDevToolsClient = new AtomCefClient(true, true);
std::string devtools_url = _cefClient->GetBrowser()->GetHost()->GetDevToolsURL(true);
[self addBrowserToView:_devToolsView url:devtools_url.c_str() cefHandler:_cefDevToolsClient];
}
}
- (void)hideDevTools {
[_devToolsView removeFromSuperview];
[_splitView adjustSubviews];
[_devToolsView release];
_devToolsView = nil;
_cefDevToolsClient = NULL;
_cefClient->GetBrowser()->GetHost()->SetFocus(true);
}
- (void)setPidToKillOnClose:(NSNumber *)pid {
_pidToKillOnClose = [pid retain];
}
# pragma mark NSWindowDelegate
- (void)windowDidResignMain:(NSNotification *)notification {
if (!_runningSpecs) {
[self.window makeFirstResponder:nil];
}
}
- (void)windowDidBecomeMain:(NSNotification *)notification {
if (_cefClient && _cefClient->GetBrowser()) {
_cefClient->GetBrowser()->GetHost()->SetFocus(true);
}
}
- (BOOL)windowShouldClose:(NSNotification *)notification {
if (_cefClient && _cefClient->GetBrowser()) {
_cefClient->GetBrowser()->SendProcessMessage(PID_RENDERER, CefProcessMessage::Create("shutdown"));
}
if (_pidToKillOnClose) kill([_pidToKillOnClose intValue], SIGQUIT);
[self autorelease];
return YES;
}
- (bool)isDevMode {
NSString *bundleResourcePath = [[NSBundle bundleForClass:self.class] resourcePath];
return ![_resourcePath isEqualToString:bundleResourcePath];
}
- (void)displayDevIcon {
NSView *themeFrame = [self.window.contentView superview];
NSButton *fullScreenButton = nil;
for (NSView *view in themeFrame.subviews) {
if (![view isKindOfClass:NSButton.class]) continue;
NSButton *button = (NSButton *)view;
if (button.action != @selector(toggleFullScreen:)) continue;
fullScreenButton = button;
break;
}
NSButton *devButton = [[NSButton alloc] init];
[devButton setTitle:@"\xF0\x9F\x92\x80"];
devButton.autoresizingMask = NSViewMinXMargin | NSViewMinYMargin;
devButton.buttonType = NSMomentaryChangeButton;
devButton.bordered = NO;
[devButton sizeToFit];
devButton.frame = NSMakeRect(fullScreenButton.frame.origin.x - devButton.frame.size.width - 5, fullScreenButton.frame.origin.y, devButton.frame.size.width, devButton.frame.size.height);
[[self.window.contentView superview] addSubview:devButton];
}
- (void)populateBrowserSettings:(CefBrowserSettings &)settings {
CefString(&settings.default_encoding) = "UTF-8";
settings.remote_fonts = STATE_ENABLED;
settings.javascript = STATE_ENABLED;
settings.javascript_open_windows = STATE_ENABLED;
settings.javascript_close_windows = STATE_ENABLED;
settings.javascript_access_clipboard = STATE_ENABLED;
settings.javascript_dom_paste = STATE_DISABLED;
settings.caret_browsing = STATE_DISABLED;
settings.java = STATE_DISABLED;
settings.plugins = STATE_DISABLED;
settings.universal_access_from_file_urls = STATE_DISABLED;
settings.file_access_from_file_urls = STATE_DISABLED;
settings.web_security = STATE_DISABLED;
settings.image_loading = STATE_ENABLED;
settings.image_shrink_standalone_to_fit = STATE_DISABLED;
settings.text_area_resize = STATE_ENABLED;
settings.page_cache = STATE_DISABLED;
settings.tab_to_links = STATE_DISABLED;
settings.author_and_user_styles = STATE_ENABLED;
settings.local_storage = STATE_ENABLED;
settings.databases = STATE_ENABLED;
settings.application_cache = STATE_ENABLED;
settings.webgl = STATE_ENABLED;
settings.accelerated_compositing = STATE_ENABLED;
settings.developer_tools = STATE_ENABLED;
}
@end
@interface GraySplitView : NSSplitView
- (NSColor*)dividerColor;
@end
@implementation GraySplitView
- (NSColor*)dividerColor {
return [NSColor darkGrayColor];
}
@end