Search code examples
objective-cmacoscocoachromium-embedded

Chromium entry point for fully initialized CEF OS X client


I have a Chromium client implementation for OS X. I would like to support double-click action on a data file which can be read and displayed inside my chromium client. For that I need to implement the method application:openFile. This method is called long time before the application is fully initialized. For that I need to call application:openFile after the chromium client is fully initialized and a NSWindow is present.

Which method is automatically called after chromium is fully initialized and a chrome window is available?


Solution

  • The class CefLoadHandler has a method called OnLoadEnd. This method is called, when the web-app is loaded by the browser. But be careful! At this time the JavaScript part of the web-app must not be already initialized. So if you are relying on full initialized JavaScript (all your JavaScript objects etc.) then you have to check this inside your JavaScript code.

    See the full answer how to implement application:openFile for Chromium Clients:

    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.

    1. Cocoa part with application:openFile
    2. Chromium part with browser initialization code
    3. JavaScript part with its initialization part

    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.