Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #371 from fungl164/fungl164/chrome-live-osx-2

Fix Brackets Live Development Issues with Chrome OSX
  • Loading branch information...
commit 305dadc954d58cbb8f20df07f7cc95d88d9f980f 2 parents 7d19cdc + bcd2784
Randy Edmunds redmunds authored
6 appshell.gyp
View
@@ -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',
],
},
191 appshell/GoogleChrome.h
View
@@ -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
+
443 appshell/appshell_extensions_mac.mm
View
@@ -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,20 @@ - (void)appTerminated:(NSNotification *)note;
- (void)timeoutTimer:(NSTimer*)timer;
@end
+// LiveBrowser helper functions
+NSRunningApplication* GetLiveBrowserApp(NSString *bundleId, int debugPort);
+NSString* GetUserProfilePath() {
+ return [NSString stringWithFormat:@"%s%@", ClientApp::AppGetSupportDirectory().ToString().c_str(), @"/live-dev-profile"];
+}
+
// 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 debug port
+int const debugPort = 9222;
+NSString* debugPortCommandlineArguments = [NSString stringWithFormat:@"--remote-debugging-port=%d", debugPort];
+
///////////////////////////////////////////////////////////////////////////////
// LiveBrowserMgrMac
@@ -51,11 +64,19 @@ - (void)timeoutTimer:(NSTimer*)timer;
void CheckForChromeRunning();
void CheckForChromeRunningTimeout();
+ void SetWorkspaceNotifications(int pid);
+ void RemoveWorkspaceNotifications();
+
void CloseLiveBrowserKillTimers();
void CloseLiveBrowserFireCallback(int valToSend);
+ int IncrementOpenRetryCount() { return m_openLiveBrowserRetryCount++; }
+ void ResetOpenRetryCount() { m_openLiveBrowserRetryCount = 0; }
+
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,25 +86,30 @@ 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
LiveBrowserMgrMac();
virtual ~LiveBrowserMgrMac();
+ int m_openLiveBrowserRetryCount;
NSTimer* m_closeLiveBrowserTimeoutTimer;
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_openLiveBrowserRetryCount(0)
+, m_closeLiveBrowserTimeoutTimer(nil)
+, m_chromeTerminateObserver(nil)
+, m_liveBrowserPid(ERR_PID_NOT_FOUND)
{
}
@@ -91,12 +117,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 +137,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,12 +147,6 @@ 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)
@@ -139,7 +155,10 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
// kill the timers
CloseLiveBrowserKillTimers();
-
+
+ // Stop listening for ws shutdown notifications
+ RemoveWorkspaceNotifications();
+
// Set common response args (callbackId and error)
responseArgs->SetInt(1, valToSend);
@@ -156,6 +175,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 +190,143 @@ void SetBrowser(CefRefPtr<CefBrowser> browser)
CloseLiveBrowserFireCallback(retVal);
}
+void LiveBrowserMgrMac::SetWorkspaceNotifications(int PID)
+{
+
+ // Cache the LiveBrowser pid for fast lookups + matching during termination
+ SetLiveBrowserPid(PID);
+
+ // Set up workspace notifications for asynchronous Browser shutdowns
+ 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 {
+ return [SBApplication applicationWithProcessIdentifier:PID];
+ }
+ 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();
- // Launch Browser
- if(apps.count == 0) {
+ // Launch Browser (if not running)
+ if (!liveBrowser) {
- // 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];
-
+ NSWorkspace * ws = [NSWorkspace sharedWorkspace];
NSURL *appURL = [ws URLForApplicationWithBundleIdentifier:appId];
if( !appURL ) {
return ERR_NOT_FOUND; //Chrome not installed
}
+
+ // Create the configuration dictionary for launching with custom parameters.
+ NSArray *parameters = nil;
+ NSString *profilePath = [NSString stringWithFormat:@"--user-data-dir=%@", GetUserProfilePath()];
+
+ parameters = [NSArray arrayWithObjects:
+ debugPortCommandlineArguments,
+ @"--allow-file-access-from-files",
+ @"--no-first-run",
+ @"--no-default-browser-check",
+ @"--temp-profile",
+ profilePath,
+ urlString,
+ nil];
+
+ NSDictionary* appConfig = [NSDictionary dictionaryWithObject:parameters forKey:NSWorkspaceLaunchConfigurationArguments];
+
NSError *error = nil;
- if( ![ws launchApplicationAtURL:appURL options:launchOptions configuration:appConfig error:&error] ) {
+ NSUInteger launchOptions = NSWorkspaceLaunchDefault | NSWorkspaceLaunchWithoutActivation | NSWorkspaceLaunchNewInstance;
+ liveBrowser = [ws launchApplicationAtURL:appURL options:launchOptions configuration:appConfig error:&error];
+
+ // Set up workspace notifications for asynchronous Browser shutdowns
+ if (liveBrowser) {
+ liveBrowserMgr->SetWorkspaceNotifications([liveBrowser processIdentifier]);
+ }
+
+ return liveBrowser ? NO_ERROR : ERR_UNKNOWN;
+ }
+
+ // A running instance of LiveBrowser was found. Let's get the corresponding GoogleChromeApplication object
+ GoogleChromeApplication *chromeApp = GetGoogleChromeApplicationWithPid([liveBrowser processIdentifier]);
+
+ // Sanity check
+ if (!chromeApp) {
+ // Failed to retrieve the LiveBrowser's instance as a GoogleChromeApplication object.
+ // It's very likely that Chrome has been shutdown asynchronously.
+
+ // So let's try to open a new instance recursively, first making sure we limit the
+ // number of retries in order to prevent infinite looping...
+ if (liveBrowserMgr->IncrementOpenRetryCount() > 3) {
+ liveBrowserMgr->ResetOpenRetryCount();
return ERR_UNKNOWN;
}
+
+ return OpenLiveBrowser(argURL, enableRemoteDebugging);
+ }
+
+ liveBrowserMgr->ResetOpenRetryCount();
+
+ // Set up workspace notifications for asynchronous Browser shutdowns
+ liveBrowserMgr->SetWorkspaceNotifications([liveBrowser processIdentifier]);
+
+ // Check for existing tab (with interstitial page) in all open Chrome windows
+ for (GoogleChromeWindow* chromeWindow in [chromeApp windows]){
+ for (GoogleChromeTab* tab in [chromeWindow tabs]) {
+ if ([tab.URL isEqualToString:urlString]) {
+ // Found and open tab with interstitial page loaded
+ return NO_ERROR;
+ }
+ }
+ }
+
+ GoogleChromeWindow* chromeWindow = [[chromeApp windows] objectAtIndex:0];
+ if(!chromeWindow){
+ // Create new LiveBrowser Window
+ GoogleChromeWindow* chromeWindow = [[[chromeApp classForScriptingClass:@"window"] alloc] init];
+ [[chromeApp windows] addObject:chromeWindow];
+ chromeWindow.activeTab.URL = urlString;
+ [chromeWindow release];
+ } else {
+ // Create new Tab in LiveBrowser window
+ GoogleChromeTab* chromeTab = [[[chromeApp classForScriptingClass:@"tab"] alloc] initWithProperties:@{@"URL": urlString}];
+ [[chromeWindow tabs] addObject:chromeTab];
+ [chromeTab release];
}
-
- // Tell the Browser to load the url
- [ws openURLs:[NSArray arrayWithObject:url] withAppBundleIdentifier:appId options:launchOptions additionalEventParamDescriptor:nil launchIdentifiers:nil];
-
return NO_ERROR;
}
@@ -229,40 +340,45 @@ 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];
+ // Set up workspace shutdown notifications (in case they are not currently setup
+ // or have been since disabled by a previous browser shutdown notification)
+ liveBrowserMgr->SetWorkspaceNotifications(liveBrowserMgr->GetLiveBrowserPid());
- if (apps.count == 0) {
- // No instances of Chrome found. Fire callback immediately.
+ // Check chrome
+ if (!liveBrowserMgr->IsChromeRunning()) {
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];
- }
+ // Get the currently active LiveBrowser session
+ GoogleChromeApplication* chromeApp = GetGoogleChromeApplicationWithPid(liveBrowserMgr->GetLiveBrowserPid());
+ if (!chromeApp) {
+ // No active LiveBrowser found
+ liveBrowserMgr->CloseLiveBrowserFireCallback(NO_ERROR);
+ return;
+ }
+
+ // Technically at this point we would locate the LiveBrowser window and
+ // close all tabs; however, the LiveDocument tab 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
+ // 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 +782,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 +1262,184 @@ 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;
+ }
+
+ /* Make a copy of the string. */
+ //printf("From function: %s\n", sp);
+
+ /* Clean up. */
+ free(procargs);
+
+ *argv = [NSString stringWithCString:sp encoding:NSUTF8StringEncoding];
+ return NO_ERROR;
+
+ERROR_B:
+ free(procargs);
+ERROR_A:
+ //fprintf(stderr, "Sorry, failed\n");
+ return ERR_UNKNOWN;
+}
1  appshell/appshell_extensions_platform.h
View
@@ -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.