Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #382 from fungl164/osx-live

Fix Live Development issues with Chrome OSX (round 2)
  • Loading branch information...
commit 59006d5d74395117ca2cb29a49b328fa24c93277 2 parents 2f543d2 + 96756ce
@redmunds redmunds authored
View
6 appshell.gyp
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
+ # Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
@@ -204,6 +204,8 @@
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/ScriptingBridge.framework',
'$(CONFIGURATION)/libcef.dylib',
],
},
@@ -359,6 +361,8 @@
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/ScriptingBridge.framework',
'$(CONFIGURATION)/libcef.dylib',
],
},
View
191 appshell/GoogleChrome.h
@@ -0,0 +1,191 @@
+/*
+ * GoogleChrome.h
+ */
+
+#import <AppKit/AppKit.h>
+#import <ScriptingBridge/ScriptingBridge.h>
+
+
+@class GoogleChromeApplication, GoogleChromeWindow, GoogleChromeTab, GoogleChromeBookmarkFolder, GoogleChromeBookmarkItem;
+
+
+
+/*
+ * Standard Suite
+ */
+
+// The application's top-level scripting object.
+@interface GoogleChromeApplication : SBApplication
+
+- (SBElementArray *) windows;
+
+@property (copy, readonly) NSString *name; // The name of the application.
+@property (readonly) BOOL frontmost; // Is this the frontmost (active) application?
+@property (copy, readonly) NSString *version; // The version of the application.
+
+- (void) open:(NSArray *)x; // Open a document.
+- (void) quit; // Quit the application.
+- (BOOL) exists:(id)x; // Verify if an object exists.
+
+@end
+
+// A window.
+@interface GoogleChromeWindow : SBObject
+
+- (SBElementArray *) tabs;
+
+@property (copy, readonly) NSString *name; // The full title of the window.
+- (NSInteger) id; // The unique identifier of the window.
+@property NSInteger index; // The index of the window, ordered front to back.
+@property NSRect bounds; // The bounding rectangle of the window.
+@property (readonly) BOOL closeable; // Whether the window has a close box.
+@property (readonly) BOOL minimizable; // Whether the window can be minimized.
+@property BOOL minimized; // Whether the window is currently minimized.
+@property (readonly) BOOL resizable; // Whether the window can be resized.
+@property BOOL visible; // Whether the window is currently visible.
+@property (readonly) BOOL zoomable; // Whether the window can be zoomed.
+@property BOOL zoomed; // Whether the window is currently zoomed.
+@property (copy, readonly) GoogleChromeTab *activeTab; // Returns the currently selected tab
+@property (copy) NSString *mode; // Represents the mode of the window which can be 'normal' or 'incognito', can be set only once during creation of the window.
+@property NSInteger activeTabIndex; // The index of the active tab.
+
+- (void) saveIn:(NSURL *)in_ as:(NSString *)as; // Save an object.
+- (void) close; // Close a window.
+- (void) delete; // Delete an object.
+- (SBObject *) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location.
+- (SBObject *) moveTo:(SBObject *)to; // Move object(s) to a new location.
+- (void) print; // Print an object.
+- (void) reload; // Reload a tab.
+- (void) goBack; // Go Back (If Possible).
+- (void) goForward; // Go Forward (If Possible).
+- (void) selectAll; // Select all.
+- (void) cutSelection; // Cut selected text (If Possible).
+- (void) copySelection; // Copy text.
+- (void) pasteSelection; // Paste text (If Possible).
+- (void) undo; // Undo the last change.
+- (void) redo; // Redo the last change.
+- (void) stop; // Stop the current tab from loading.
+- (void) viewSource; // View the HTML source of the tab.
+- (id) executeJavascript:(NSString *)javascript; // Execute a piece of javascript.
+- (void) enterPresentationMode; // Enter presentation mode in window.
+- (void) exitPresentationMode; // Exit presentation mode in window.
+
+@end
+
+
+
+/*
+ * Chromium Suite
+ */
+
+// The application's top-level scripting object.
+@interface GoogleChromeApplication (ChromiumSuite)
+
+- (SBElementArray *) bookmarkFolders;
+
+@property (copy, readonly) GoogleChromeBookmarkFolder *bookmarksBar; // The bookmarks bar bookmark folder.
+@property (copy, readonly) GoogleChromeBookmarkFolder *otherBookmarks; // The other bookmarks bookmark folder.
+
+@end
+
+// A tab.
+@interface GoogleChromeTab : SBObject
+
+- (NSInteger) id; // Unique ID of the tab.
+@property (copy, readonly) NSString *title; // The title of the tab.
+@property (copy) NSString *URL; // The url visible to the user.
+@property (readonly) BOOL loading; // Is loading?
+
+- (void) saveIn:(NSURL *)in_ as:(NSString *)as; // Save an object.
+- (void) close; // Close a window.
+- (void) delete; // Delete an object.
+- (SBObject *) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location.
+- (SBObject *) moveTo:(SBObject *)to; // Move object(s) to a new location.
+- (void) print; // Print an object.
+- (void) reload; // Reload a tab.
+- (void) goBack; // Go Back (If Possible).
+- (void) goForward; // Go Forward (If Possible).
+- (void) selectAll; // Select all.
+- (void) cutSelection; // Cut selected text (If Possible).
+- (void) copySelection; // Copy text.
+- (void) pasteSelection; // Paste text (If Possible).
+- (void) undo; // Undo the last change.
+- (void) redo; // Redo the last change.
+- (void) stop; // Stop the current tab from loading.
+- (void) viewSource; // View the HTML source of the tab.
+- (id) executeJavascript:(NSString *)javascript; // Execute a piece of javascript.
+- (void) enterPresentationMode; // Enter presentation mode in window.
+- (void) exitPresentationMode; // Exit presentation mode in window.
+
+@end
+
+// A bookmarks folder that contains other bookmarks folder and bookmark items.
+@interface GoogleChromeBookmarkFolder : SBObject
+
+- (SBElementArray *) bookmarkFolders;
+- (SBElementArray *) bookmarkItems;
+
+- (NSNumber *) id; // Unique ID of the bookmark folder.
+@property (copy) NSString *title; // The title of the folder.
+@property (copy, readonly) NSNumber *index; // Returns the index with respect to its parent bookmark folder
+
+- (void) saveIn:(NSURL *)in_ as:(NSString *)as; // Save an object.
+- (void) close; // Close a window.
+- (void) delete; // Delete an object.
+- (SBObject *) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location.
+- (SBObject *) moveTo:(SBObject *)to; // Move object(s) to a new location.
+- (void) print; // Print an object.
+- (void) reload; // Reload a tab.
+- (void) goBack; // Go Back (If Possible).
+- (void) goForward; // Go Forward (If Possible).
+- (void) selectAll; // Select all.
+- (void) cutSelection; // Cut selected text (If Possible).
+- (void) copySelection; // Copy text.
+- (void) pasteSelection; // Paste text (If Possible).
+- (void) undo; // Undo the last change.
+- (void) redo; // Redo the last change.
+- (void) stop; // Stop the current tab from loading.
+- (void) viewSource; // View the HTML source of the tab.
+- (id) executeJavascript:(NSString *)javascript; // Execute a piece of javascript.
+- (void) enterPresentationMode; // Enter presentation mode in window.
+- (void) exitPresentationMode; // Exit presentation mode in window.
+
+@end
+
+// An item consists of an URL and the title of a bookmark
+@interface GoogleChromeBookmarkItem : SBObject
+
+- (NSInteger) id; // Unique ID of the bookmark item.
+@property (copy) NSString *title; // The title of the bookmark item.
+@property (copy) NSString *URL; // The URL of the bookmark.
+@property (copy, readonly) NSNumber *index; // Returns the index with respect to its parent bookmark folder
+
+- (void) saveIn:(NSURL *)in_ as:(NSString *)as; // Save an object.
+- (void) close; // Close a window.
+- (void) delete; // Delete an object.
+- (SBObject *) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location.
+- (SBObject *) moveTo:(SBObject *)to; // Move object(s) to a new location.
+- (void) print; // Print an object.
+- (void) reload; // Reload a tab.
+- (void) goBack; // Go Back (If Possible).
+- (void) goForward; // Go Forward (If Possible).
+- (void) selectAll; // Select all.
+- (void) cutSelection; // Cut selected text (If Possible).
+- (void) copySelection; // Copy text.
+- (void) pasteSelection; // Paste text (If Possible).
+- (void) undo; // Undo the last change.
+- (void) redo; // Redo the last change.
+- (void) stop; // Stop the current tab from loading.
+- (void) viewSource; // View the HTML source of the tab.
+- (id) executeJavascript:(NSString *)javascript; // Execute a piece of javascript.
+- (void) enterPresentationMode; // Enter presentation mode in window.
+- (void) exitPresentationMode; // Exit presentation mode in window.
+
+@end
+
+@interface GoogleChromeWindow (ChromiumSuite)
+
+@property (readonly) BOOL presenting; // Whether the window is in presentation mode.
+
+@end
+
View
430 appshell/appshell_extensions_mac.mm
@@ -25,7 +25,10 @@
#include "appshell_extensions.h"
#include "native_menu_model.h"
+#include "GoogleChrome.h"
+
#include <Cocoa/Cocoa.h>
+#include <sys/sysctl.h>
NSMutableArray* pendingOpenFiles;
@@ -34,10 +37,18 @@ - (void)appTerminated:(NSNotification *)note;
- (void)timeoutTimer:(NSTimer*)timer;
@end
+// LiveBrowser helper functions
+NSRunningApplication* GetLiveBrowserApp(NSString *bundleId, int debugPort);
+
// App ID for either Chrome or Chrome Canary (commented out)
NSString *const appId = @"com.google.Chrome";
//NSString *const appId = @"com.google.Chrome.canary";
+// Live Development browser debug paramaters
+int const debugPort = 9222;
+NSString* debugPortCommandlineArguments = [NSString stringWithFormat:@"--remote-debugging-port=%d", debugPort];
+NSString* debugProfilePath = [NSString stringWithFormat:@"--user-data-dir=%s/live-dev-profile", ClientApp::AppGetSupportDirectory().ToString().c_str()];
+
///////////////////////////////////////////////////////////////////////////////
// LiveBrowserMgrMac
@@ -51,11 +62,16 @@ - (void)timeoutTimer:(NSTimer*)timer;
void CheckForChromeRunning();
void CheckForChromeRunningTimeout();
+ void SetWorkspaceNotifications();
+ void RemoveWorkspaceNotifications();
+
void CloseLiveBrowserKillTimers();
void CloseLiveBrowserFireCallback(int valToSend);
ChromeWindowsTerminatedObserver* GetTerminateObserver() { return m_chromeTerminateObserver; }
CefRefPtr<CefProcessMessage> GetCloseCallback() { return m_closeLiveBrowserCallback; }
+ NSRunningApplication* GetLiveBrowser() { return GetLiveBrowserApp(appId, debugPort); }
+ int GetLiveBrowserPid() { return m_liveBrowserPid; }
void SetCloseTimeoutTimer(NSTimer* closeLiveBrowserTimeoutTimer)
{ m_closeLiveBrowserTimeoutTimer = closeLiveBrowserTimeoutTimer; }
@@ -65,7 +81,8 @@ void SetCloseCallback(CefRefPtr<CefProcessMessage> response)
{ m_closeLiveBrowserCallback = response; }
void SetBrowser(CefRefPtr<CefBrowser> browser)
{ m_browser = browser; }
-
+ void SetLiveBrowserPid(int pid)
+ { m_liveBrowserPid = pid; }
private:
// private so this class cannot be instantiated externally
@@ -76,14 +93,16 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
CefRefPtr<CefProcessMessage> m_closeLiveBrowserCallback;
CefRefPtr<CefBrowser> m_browser;
ChromeWindowsTerminatedObserver* m_chromeTerminateObserver;
+ int m_liveBrowserPid;
- static LiveBrowserMgrMac* s_instance;
+ static LiveBrowserMgrMac* s_instance;
};
LiveBrowserMgrMac::LiveBrowserMgrMac()
: m_closeLiveBrowserTimeoutTimer(nil)
, m_chromeTerminateObserver(nil)
+ , m_liveBrowserPid(ERR_PID_NOT_FOUND)
{
}
@@ -91,12 +110,15 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
{
if (s_instance)
s_instance->CloseLiveBrowserKillTimers();
+
+ RemoveWorkspaceNotifications();
}
LiveBrowserMgrMac* LiveBrowserMgrMac::GetInstance()
{
if (!s_instance)
s_instance = new LiveBrowserMgrMac();
+
return s_instance;
}
@@ -108,14 +130,7 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
bool LiveBrowserMgrMac::IsChromeRunning()
{
- NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:appId];
- for (NSUInteger i = 0; i < apps.count; i++) {
- NSRunningApplication* curApp = [apps objectAtIndex:i];
- if( curApp && !curApp.terminated ) {
- return true;
- }
- }
- return false;
+ return GetLiveBrowser() ? true : false;
}
void LiveBrowserMgrMac::CloseLiveBrowserKillTimers()
@@ -125,26 +140,27 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
[m_closeLiveBrowserTimeoutTimer release];
m_closeLiveBrowserTimeoutTimer = nil;
}
-
- if (m_chromeTerminateObserver) {
- [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:m_chromeTerminateObserver];
- [m_chromeTerminateObserver release];
- m_chromeTerminateObserver = nil;
- }
}
void LiveBrowserMgrMac::CloseLiveBrowserFireCallback(int valToSend)
{
- CefRefPtr<CefListValue> responseArgs = m_closeLiveBrowserCallback->GetArgumentList();
-
// kill the timers
CloseLiveBrowserKillTimers();
-
- // Set common response args (callbackId and error)
- responseArgs->SetInt(1, valToSend);
-
- // Send response
- m_browser->SendProcessMessage(PID_RENDERER, m_closeLiveBrowserCallback);
+
+ // Stop listening for ws shutdown notifications
+ RemoveWorkspaceNotifications();
+
+ // Prepare response
+ if (m_closeLiveBrowserCallback && m_browser) {
+
+ CefRefPtr<CefListValue> responseArgs = m_closeLiveBrowserCallback->GetArgumentList();
+
+ // Set common response args (callbackId and error)
+ responseArgs->SetInt(1, valToSend);
+
+ // Send response
+ m_browser->SendProcessMessage(PID_RENDERER, m_closeLiveBrowserCallback);
+ }
// Clear state
m_closeLiveBrowserCallback = NULL;
@@ -156,6 +172,10 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
if (IsChromeRunning())
return;
+ // Unset the LiveBrowser pid
+ m_liveBrowserPid = ERR_PID_NOT_FOUND;
+
+ // Fire callback to browser
CloseLiveBrowserFireCallback(NO_ERROR);
}
@@ -167,55 +187,120 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
CloseLiveBrowserFireCallback(retVal);
}
+void LiveBrowserMgrMac::SetWorkspaceNotifications()
+{
+ if (!GetTerminateObserver()) {
+ //register an observer to watch for the app terminations
+ SetTerminateObserver([[ChromeWindowsTerminatedObserver alloc] init]);
+
+ [[[NSWorkspace sharedWorkspace] notificationCenter]
+ addObserver:GetTerminateObserver()
+ selector:@selector(appTerminated:)
+ name:NSWorkspaceDidTerminateApplicationNotification
+ object:nil
+ ];
+ }
+}
+
+void LiveBrowserMgrMac::RemoveWorkspaceNotifications()
+{
+ if (m_chromeTerminateObserver) {
+ [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:m_chromeTerminateObserver];
+ [m_chromeTerminateObserver release];
+ m_chromeTerminateObserver = nil;
+ }
+}
+
LiveBrowserMgrMac* LiveBrowserMgrMac::s_instance = NULL;
// Forward declarations for functions defined later in this file
void NSArrayToCefList(NSArray* array, CefRefPtr<CefListValue>& list);
int32 ConvertNSErrorCode(NSError* error, bool isReading);
+GoogleChromeApplication* GetGoogleChromeApplicationWithPid(int PID)
+{
+ try {
+ // Ensure we have a valid process id before invoking ScriptingBridge.
+ // We need this because negative pids (e.g ERR_PID_NOT_FOUND) will not
+ // throw an exception, but rather will return a non-nil junk object
+ // that causes Brackets to hang on close
+ GoogleChromeApplication* app = PID < 0 ? nil : [SBApplication applicationWithProcessIdentifier:PID];
+
+ // Second check before returning
+ return [app respondsToSelector:@selector(name)] && [app.name isEqualToString:@"Google Chrome"] ? app : nil;
+ }
+ catch (...) {
+ return nil;
+ }
+}
int32 OpenLiveBrowser(ExtensionString argURL, bool enableRemoteDebugging)
{
+ LiveBrowserMgrMac* liveBrowserMgr = LiveBrowserMgrMac::GetInstance();
+
// Parse the arguments
NSString *urlString = [NSString stringWithUTF8String:argURL.c_str()];
- NSURL *url = [NSURL URLWithString:urlString];
// Find instances of the Browser
- NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:appId];
- NSWorkspace * ws = [NSWorkspace sharedWorkspace];
- NSUInteger launchOptions = NSWorkspaceLaunchDefault | NSWorkspaceLaunchWithoutActivation;
+ NSRunningApplication* liveBrowser = liveBrowserMgr->GetLiveBrowser();
+
+ // Get the corresponding chromeApp scriptable browser object
+ GoogleChromeApplication* chromeApp = !liveBrowser ? nil : GetGoogleChromeApplicationWithPid([liveBrowser processIdentifier]);
// Launch Browser
- if(apps.count == 0) {
-
- // Create the configuration dictionary for launching with custom parameters.
- NSArray *parameters = nil;
- if (enableRemoteDebugging) {
- parameters = [NSArray arrayWithObjects:
- @"--remote-debugging-port=9222",
- @"--allow-file-access-from-files",
- nil];
- }
- else {
- parameters = [NSArray arrayWithObjects:
- @"--allow-file-access-from-files",
- nil];
- }
-
- NSMutableDictionary* appConfig = [NSDictionary dictionaryWithObject:parameters forKey:NSWorkspaceLaunchConfigurationArguments];
-
- NSURL *appURL = [ws URLForApplicationWithBundleIdentifier:appId];
+ if (!chromeApp) {
+ NSURL* appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:appId];
if( !appURL ) {
return ERR_NOT_FOUND; //Chrome not installed
}
- NSError *error = nil;
- if( ![ws launchApplicationAtURL:appURL options:launchOptions configuration:appConfig error:&error] ) {
+
+ // Create the configuration dictionary for launching with custom parameters.
+ NSArray *parameters = [NSArray arrayWithObjects:
+ @"--no-first-run",
+ @"--no-default-browser-check",
+ debugPortCommandlineArguments,
+ debugProfilePath,
+ urlString,
+ nil];
+
+ NSDictionary* appConfig = [NSDictionary dictionaryWithObject:parameters forKey:NSWorkspaceLaunchConfigurationArguments];
+ NSUInteger launchOptions = NSWorkspaceLaunchDefault | NSWorkspaceLaunchWithoutActivation | NSWorkspaceLaunchNewInstance;
+
+ liveBrowser = [[NSWorkspace sharedWorkspace] launchApplicationAtURL:appURL options:launchOptions configuration:appConfig error:nil];
+ if (!liveBrowser) {
return ERR_UNKNOWN;
}
+
+ liveBrowserMgr->SetLiveBrowserPid([liveBrowser processIdentifier]);
+ liveBrowserMgr->SetWorkspaceNotifications();
+
+ return NO_ERROR;
}
-
+
+ // Check for existing tab with url already loaded
+ for (GoogleChromeWindow* chromeWindow in [chromeApp windows]) {
+ for (GoogleChromeTab* tab in [chromeWindow tabs]) {
+ if ([tab.URL isEqualToString:urlString]) {
+ // Found and open tab with url already loaded
+ return NO_ERROR;
+ }
+ }
+ }
+
// Tell the Browser to load the url
- [ws openURLs:[NSArray arrayWithObject:url] withAppBundleIdentifier:appId options:launchOptions additionalEventParamDescriptor:nil launchIdentifiers:nil];
-
+ GoogleChromeWindow* chromeWindow = [[chromeApp windows] objectAtIndex:0];
+ if (!chromeWindow || [[chromeWindow tabs] count] == 0) {
+ // Create new Window
+ GoogleChromeWindow* chromeWindow = [[[chromeApp classForScriptingClass:@"window"] alloc] init];
+ [[chromeApp windows] addObject:chromeWindow];
+ chromeWindow.activeTab.URL = urlString;
+ [chromeWindow release];
+ } else {
+ // Create new Tab
+ GoogleChromeTab* chromeTab = [[[chromeApp classForScriptingClass:@"tab"] alloc] initWithProperties:@{@"URL": urlString}];
+ [[chromeWindow tabs] addObject:chromeTab];
+ [chromeTab release];
+ }
+
return NO_ERROR;
}
@@ -229,40 +314,46 @@ void CloseLiveBrowser(CefRefPtr<CefBrowser> browser, CefRefPtr<CefProcessMessage
liveBrowserMgr->CloseLiveBrowserFireCallback(ERR_UNKNOWN);
}
+ // Set up new Brackets CloseLiveBrowser callbacks
liveBrowserMgr->SetBrowser(browser);
liveBrowserMgr->SetCloseCallback(response);
- // Find instances of the Browser and terminate them
- NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:appId];
-
- if (apps.count == 0) {
- // No instances of Chrome found. Fire callback immediately.
+ // Get the currently active LiveBrowser session
+ NSRunningApplication* liveBrowser = liveBrowserMgr->GetLiveBrowser();
+ if (!liveBrowser) {
+ // No active LiveBrowser found
liveBrowserMgr->CloseLiveBrowserFireCallback(NO_ERROR);
return;
- } else if (apps.count > 0 && !LiveBrowserMgrMac::GetInstance()->GetTerminateObserver()) {
- //register an observer to watch for the app terminations
- LiveBrowserMgrMac::GetInstance()->SetTerminateObserver([[ChromeWindowsTerminatedObserver alloc] init]);
-
- [[[NSWorkspace sharedWorkspace] notificationCenter]
- addObserver:LiveBrowserMgrMac::GetInstance()->GetTerminateObserver()
- selector:@selector(appTerminated:)
- name:NSWorkspaceDidTerminateApplicationNotification
- object:nil
- ];
}
-
- // Iterate over open browser intances and terminate
- for (NSUInteger i = 0; i < apps.count; i++) {
- NSRunningApplication* curApp = [apps objectAtIndex:i];
- if( curApp && !curApp.terminated ) {
- [curApp terminate];
- }
+
+ GoogleChromeApplication* chromeApp = GetGoogleChromeApplicationWithPid([liveBrowser processIdentifier]);
+ if (!chromeApp) {
+ // No corresponding scriptable browser object found
+ liveBrowserMgr->CloseLiveBrowserFireCallback(NO_ERROR);
+ return;
+ }
+
+ // Technically at this point we would locate the LiveBrowser window and
+ // close all tabs; however, the LiveDocument tab was already closed by Inspector!
+ // and there is no way to find which window to close.
+
+ // Do not close other windows
+ if ([[chromeApp windows] count] > 0 || [[[[chromeApp windows] objectAtIndex:0] tabs] count] > 0) {
+ liveBrowserMgr->CloseLiveBrowserFireCallback(NO_ERROR);
+ return;
}
- //start a timeout timer
+ // Set up workspace shutdown notifications
+ liveBrowserMgr->SetLiveBrowserPid([liveBrowser processIdentifier]);
+ liveBrowserMgr->SetWorkspaceNotifications();
+
+ // No more open windows found, so quit Chrome
+ [chromeApp quit];
+
+ // Set timeout timer
liveBrowserMgr->SetCloseTimeoutTimer([[NSTimer
scheduledTimerWithTimeInterval:(3 * 60)
- target:LiveBrowserMgrMac::GetInstance()->GetTerminateObserver()
+ target:liveBrowserMgr->GetTerminateObserver()
selector:@selector(timeoutTimer:)
userInfo:nil repeats:NO] retain]
);
@@ -666,6 +757,16 @@ @implementation ChromeWindowsTerminatedObserver
- (void) appTerminated:(NSNotification *)note
{
+ // Not Chrome? Not interested.
+ if ( ![[[note userInfo] objectForKey:@"NSApplicationBundleIdentifier"] isEqualToString:appId] ) {
+ return;
+ }
+
+ // Not LiveBrowser instance? Not interested.
+ if ( ![[[note userInfo] objectForKey:@"NSApplicationProcessIdentifier"] isEqualToNumber:[NSNumber numberWithInt:LiveBrowserMgrMac::GetInstance()->GetLiveBrowserPid()]] ) {
+ return;
+ }
+
LiveBrowserMgrMac::GetInstance()->CheckForChromeRunning();
}
@@ -1136,3 +1237,180 @@ void DragWindow(CefRefPtr<CefBrowser> browser)
[win displayIfNeeded];
}
}
+
+int32 GetArgvFromProcessID(int pid, NSString **argv);
+NSRunningApplication* GetLiveBrowserApp(NSString *bundleId, int debugPort)
+{
+
+ NSArray* appList = [NSRunningApplication runningApplicationsWithBundleIdentifier: bundleId];
+
+ // Search list of running apps with bundleId + debug port
+ for (NSRunningApplication* currApp in appList) {
+
+ int PID = [currApp processIdentifier];
+ NSString* args = nil;
+
+ // Check for process arguments
+ if (GetArgvFromProcessID(PID, &args) != NO_ERROR) {
+ continue;
+ }
+
+ // Check debug port (e.g. --remote-debugging-port=9222)
+ if ([args rangeOfString:debugPortCommandlineArguments].location != NSNotFound) {
+ return currApp;
+ }
+ }
+ return nil;
+}
+
+// Extracted & Modified from https://gist.github.com/nonowarn/770696
+int32 GetArgvFromProcessID(int pid, NSString **argv)
+{
+ int mib[3], argmax, nargs, c = 0;
+ size_t size;
+ char *procargs, *sp, *np, *cp;
+ int show_args = 1;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARGMAX;
+
+ size = sizeof(argmax);
+ if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) {
+ goto ERROR_A;
+ }
+
+ /* Allocate space for the arguments. */
+ procargs = (char *)malloc(argmax);
+ if (procargs == NULL) {
+ goto ERROR_A;
+ }
+
+
+ /*
+ * Make a sysctl() call to get the raw argument space of the process.
+ * The layout is documented in start.s, which is part of the Csu
+ * project. In summary, it looks like:
+ *
+ * /---------------\ 0x00000000
+ * : :
+ * : :
+ * |---------------|
+ * | argc |
+ * |---------------|
+ * | arg[0] |
+ * |---------------|
+ * : :
+ * : :
+ * |---------------|
+ * | arg[argc - 1] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | env[0] |
+ * |---------------|
+ * : :
+ * : :
+ * |---------------|
+ * | env[n] |
+ * |---------------|
+ * | 0 |
+ * |---------------| <-- Beginning of data returned by sysctl() is here.
+ * | argc |
+ * |---------------|
+ * | exec_path |
+ * |:::::::::::::::|
+ * | |
+ * | String area. |
+ * | |
+ * |---------------| <-- Top of stack.
+ * : :
+ * : :
+ * \---------------/ 0xffffffff
+ */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROCARGS2;
+ mib[2] = pid;
+
+ size = (size_t)argmax;
+ if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
+ goto ERROR_B;
+ }
+
+ memcpy(&nargs, procargs, sizeof(nargs));
+ cp = procargs + sizeof(nargs);
+
+ /* Skip the saved exec_path. */
+ for (; cp < &procargs[size]; cp++) {
+ if (*cp == '\0') {
+ /* End of exec_path reached. */
+ break;
+ }
+ }
+ if (cp == &procargs[size]) {
+ goto ERROR_B;
+ }
+
+ /* Skip trailing '\0' characters. */
+ for (; cp < &procargs[size]; cp++) {
+ if (*cp != '\0') {
+ /* Beginning of first argument reached. */
+ break;
+ }
+ }
+ if (cp == &procargs[size]) {
+ goto ERROR_B;
+ }
+ /* Save where the argv[0] string starts. */
+ sp = cp;
+
+ /*
+ * Iterate through the '\0'-terminated strings and convert '\0' to ' '
+ * until a string is found that has a '=' character in it (or there are
+ * no more strings in procargs). There is no way to deterministically
+ * know where the command arguments end and the environment strings
+ * start, which is why the '=' character is searched for as a heuristic.
+ */
+ for (np = NULL; c < nargs && cp < &procargs[size]; cp++) {
+ if (*cp == '\0') {
+ c++;
+ if (np != NULL) {
+ /* Convert previous '\0'. */
+ *np = ' ';
+ } else {
+ /* *argv0len = cp - sp; */
+ }
+ /* Note location of current '\0'. */
+ np = cp;
+
+ if (!show_args) {
+ /*
+ * Don't convert '\0' characters to ' '.
+ * However, we needed to know that the
+ * command name was terminated, which we
+ * now know.
+ */
+ break;
+ }
+ }
+ }
+
+ /*
+ * sp points to the beginning of the arguments/environment string, and
+ * np should point to the '\0' terminator for the string.
+ */
+ if (np == NULL || np == sp) {
+ /* Empty or unterminated string. */
+ goto ERROR_B;
+ }
+
+ /* Clean up. */
+ free(procargs);
+
+ *argv = [NSString stringWithCString:sp encoding:NSUTF8StringEncoding];
+ return NO_ERROR;
+
+ERROR_B:
+ free(procargs);
+ERROR_A:
+ return ERR_UNKNOWN;
+}
View
1  appshell/appshell_extensions_platform.h
@@ -44,6 +44,7 @@ static const int ERR_NOT_FILE = 8;
static const int ERR_NOT_DIRECTORY = 9;
static const int ERR_FILE_EXISTS = 10;
static const int ERR_BROWSER_NOT_INSTALLED = 11;
+static const int ERR_PID_NOT_FOUND = -9999; // negative int to avoid confusion with real PIDs
#if defined(OS_WIN)
typedef std::wstring ExtensionString;
Please sign in to comment.
Something went wrong with that request. Please try again.