My application has it's own unique file type. When the file is double-clicked by the user and the application was not running, the application starts but reports the following error:
My application base on Chromium
(CEF). I neither use application:applicationWillFinishLaunching
nor application:applicationDidFinishLaunching
for any setup parts but it seems that application:openFile did get called before the application is fully launched.
My AppDelegate implementation is as follow:
// ****************************************************************************
// application:openFile
// ****************************************************************************
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
return [self processFile:filename];
}
// ****************************************************************************
// processFile
// ****************************************************************************
- (BOOL)processFile:(NSString *)file
{
std::string fileName([file UTF8String]);
au::arcwork::Handler* handler = au::arcwork::Handler::GetInstance();
handler->OnOpenFile(fileName);
return YES;
}
After lot of research and debugging I came up with a solution how to implement application:openFile
for Chromium based applications on OS X. First of all, there are 3 parts/layers which have to be solved.
application:openFile
Beginning with 1:
The Apple documentation for application:openFile already describes in the Discussion part, that application:openFile
is called before applicationDidFinishLaunching
. This means, that If you rely on a full initialized client (I can't imagine how's not?), you have to store the URL of the file somewhere, e.g. ivar/property in application:openFile
or like in my example below in a std::vector
of my Chromium handler
. Only if the application if fully initialized and a browser window is displayed, then you're able to directly call the Chromium handler method
that in turn calls the appropriate JavaScript function!
// ***
// application:openFile
// ***
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
AUApplication * clientApp = (AUApplication *)theApplication;
NSWindow* targetWindow = [clientApp findTargetWindow];
//Check if browser window is up and running
if (targetWindow) {
[self processFile:filename];
}
else {
//This method saves the file URL to open the file
//when the application+JavaScript is fully initialized
au::test::Handler* handler = au::test::Handler::GetInstance();
handler->AddPendingFile([filename UTF8String]);
}
return YES;
}
// ***
// processFile
// ***
- (BOOL)processFile:(NSString *)file
{
//This method calls the JavaScript function to open the file
std::string fileName([file UTF8String]);
au::test::Handler* handler = au::test::Handler::GetInstance();
handler->OnOpenFile(fileName);
return YES;
}
The 2 part:
On Chromium side
you have to store the file URL in a appropriate structure. I'm using std::vector
for that. The file URL is saved in the Chromium method OnLoadEnd
. Here Chromium have loaded your HTML+JavaScript parts in the browser. But be careful. The initialization of JavaScript is not done yet! In my example below I have to assign a JavaScript property to store the file URL.
// ***
// Handler::OnLoadEnd
// ***
void Handler::OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) {
if (!m_pendingOpenFiles.empty()) {
std::string file(m_pendingOpenFiles[0]);
std::cout << "Pending file for later opening: " << file << std::endl;
//This method below is calling a JavaScript function to assign
//a property `pendingFile`
OnPendingFile(file);
//Don't forget to pop the file URL afterwards
//or use another store container instead of `std::vector`
//if you don't plan to implement `application:openFiles` as well!!!
m_pendingOpenFiles.pop_back();
}
}
The 3 part:
On JavaScript side you have to check after the application initialization if the property pendingFile
is assigned or not to open the file appropriately.