Long time lurker, but happy to be here. I'm attempting to bind a basic javascript binding to the cefSimple application.
I'm working on an up-to-date Macbook as well as an up-to-date iMac.
Numerous searches have resulted in some basic mistakes I was making- but I seem to have rectified them all and it still work go.
Specifically, I have made certain to have my SimpleApp class inherit from CefRenderProcessHandler (atleast, I think it's inheriting. The syntax is still a bit new to new)
I have also envoked GetRenderProcessHandler in that class aswell.
The other main suggestion I've seen is to be sure to pass the SimpleApp instance to CefExecuteProcess- which I have done.
Here is my code:
simple_app.mm
// Copyright (c) 2013 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Cocoa/Cocoa.h>
#include "include/cef_application_mac.h"
#include "include/wrapper/cef_helpers.h"
#include "include/wrapper/cef_library_loader.h"
#include "tests/cefsimple/simple_app.h"
#include "tests/cefsimple/simple_handler.h"
// Receives notifications from the application.
@interface SimpleAppDelegate : NSObject <NSApplicationDelegate>
- (void)createApplication:(id)object;
- (void)tryToTerminateApplication:(NSApplication*)app;
@end
// Provide the CefAppProtocol implementation required by CEF.
@interface SimpleApplication : NSApplication <CefAppProtocol> {
@private
BOOL handlingSendEvent_;
}
@end
@implementation SimpleApplication
- (BOOL)isHandlingSendEvent {
return handlingSendEvent_;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
handlingSendEvent_ = handlingSendEvent;
}
- (void)sendEvent:(NSEvent*)event {
CefScopedSendingEvent sendingEventScoper;
[super sendEvent:event];
}
// |-terminate:| is the entry point for orderly "quit" operations in Cocoa. This
// includes the application menu's quit menu item and keyboard equivalent, the
// application's dock icon menu's quit menu item, "quit" (not "force quit") in
// the Activity Monitor, and quits triggered by user logout and system restart
// and shutdown.
//
// The default |-terminate:| implementation ends the process by calling exit(),
// and thus never leaves the main run loop. This is unsuitable for Chromium
// since Chromium depends on leaving the main run loop to perform an orderly
// shutdown. We support the normal |-terminate:| interface by overriding the
// default implementation. Our implementation, which is very specific to the
// needs of Chromium, works by asking the application delegate to terminate
// using its |-tryToTerminateApplication:| method.
//
// |-tryToTerminateApplication:| differs from the standard
// |-applicationShouldTerminate:| in that no special event loop is run in the
// case that immediate termination is not possible (e.g., if dialog boxes
// allowing the user to cancel have to be shown). Instead, this method tries to
// close all browsers by calling CloseBrowser(false) via
// ClientHandler::CloseAllBrowsers. Calling CloseBrowser will result in a call
// to ClientHandler::DoClose and execution of |-performClose:| on the NSWindow.
// DoClose sets a flag that is used to differentiate between new close events
// (e.g., user clicked the window close button) and in-progress close events
// (e.g., user approved the close window dialog). The NSWindowDelegate
// |-windowShouldClose:| method checks this flag and either calls
// CloseBrowser(false) in the case of a new close event or destructs the
// NSWindow in the case of an in-progress close event.
// ClientHandler::OnBeforeClose will be called after the CEF NSView hosted in
// the NSWindow is dealloc'ed.
//
// After the final browser window has closed ClientHandler::OnBeforeClose will
// begin actual tear-down of the application by calling CefQuitMessageLoop.
// This ends the NSApplication event loop and execution then returns to the
// main() function for cleanup before application termination.
//
// The standard |-applicationShouldTerminate:| is not supported, and code paths
// leading to it must be redirected.
- (void)terminate:(id)sender {
SimpleAppDelegate* delegate =
static_cast<SimpleAppDelegate*>([NSApp delegate]);
[delegate tryToTerminateApplication:self];
// Return, don't exit. The application is responsible for exiting on its own.
}
@end
@implementation SimpleAppDelegate
// Create the application on the UI thread.
- (void)createApplication:(id)object {
[NSApplication sharedApplication];
[[NSBundle mainBundle] loadNibNamed:@"MainMenu"
owner:NSApp
topLevelObjects:nil];
// Set the delegate for application events.
[[NSApplication sharedApplication] setDelegate:self];
}
- (void)tryToTerminateApplication:(NSApplication*)app {
SimpleHandler* handler = SimpleHandler::GetInstance();
if (handler && !handler->IsClosing())
handler->CloseAllBrowsers(false);
}
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication*)sender {
return NSTerminateNow;
}
@end
// Entry point function for the browser process.
int main(int argc, char* argv[]) {
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
CefScopedLibraryLoader library_loader;
if (!library_loader.LoadInMain())
return 1;
// Provide CEF with command-line arguments.
CefMainArgs main_args(argc, argv);
@autoreleasepool {
// Initialize the SimpleApplication instance.
[SimpleApplication sharedApplication];
// Specify CEF global settings here.
CefSettings settings;
// When generating projects with CMake the CEF_USE_SANDBOX value will be
// defined automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line
// to disable use of the sandbox.
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
// SimpleApp implements application-level callbacks for the browser process.
// It will create the first browser instance in OnContextInitialized() after
// CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
CefExecuteProcess(main_args, app, nullptr);
// Initialize CEF for the browser process.
CefInitialize(main_args, settings, app.get(), NULL);
// Create the application delegate.
NSObject* delegate = [[SimpleAppDelegate alloc] init];
[delegate performSelectorOnMainThread:@selector(createApplication:)
withObject:nil
waitUntilDone:NO];
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
// Release the delegate.
#if !__has_feature(objc_arc)
[delegate release];
#endif // !__has_feature(objc_arc)
delegate = nil;
} // @autoreleasepool
return 0;
}
simple_app.h
// Copyright (c) 2013 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.
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#include "include/cef_app.h"
// Implement application-level callbacks for the browser process.
class SimpleApp : public CefApp,
public CefBrowserProcessHandler,
public CefRenderProcessHandler {
public:
SimpleApp();
// CefApp methods:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
OVERRIDE {
return this;
}
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler()
OVERRIDE {
return this;
}
// CefBrowserProcessHandler methods:
virtual void OnContextInitialized() OVERRIDE;
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE;
private:
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleApp);
};
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
simple_app.cc
// Copyright (c) 2013 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.
#include "tests/cefsimple/simple_app.h"
#include <string>
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/cefsimple/simple_handler.h"
namespace {
// When using the Views framework this object provides the delegate
// implementation for the CefWindow that hosts the Views-based browser.
class SimpleWindowDelegate : public CefWindowDelegate {
public:
explicit SimpleWindowDelegate(CefRefPtr<CefBrowserView> browser_view)
: browser_view_(browser_view) {}
void OnWindowCreated(CefRefPtr<CefWindow> window) OVERRIDE {
// Add the browser view and show the window.
window->AddChildView(browser_view_);
window->Show();
// Give keyboard focus to the browser view.
browser_view_->RequestFocus();
}
void OnWindowDestroyed(CefRefPtr<CefWindow> window) OVERRIDE {
browser_view_ = NULL;
}
bool CanClose(CefRefPtr<CefWindow> window) OVERRIDE {
// Allow the window to close if the browser says it's OK.
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
if (browser)
return browser->GetHost()->TryCloseBrowser();
return true;
}
CefSize GetPreferredSize(CefRefPtr<CefView> view) OVERRIDE {
return CefSize(800, 600);
}
private:
CefRefPtr<CefBrowserView> browser_view_;
IMPLEMENT_REFCOUNTING(SimpleWindowDelegate);
DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
};
} // namespace
SimpleApp::SimpleApp() {}
void SimpleApp::OnContextInitialized() {
CEF_REQUIRE_UI_THREAD();
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();
#if defined(OS_WIN) || defined(OS_LINUX)
// Create the browser using the Views framework if "--use-views" is specified
// via the command-line. Otherwise, create the browser using the native
// platform framework. The Views framework is currently only supported on
// Windows and Linux.
const bool use_views = command_line->HasSwitch("use-views");
#else
const bool use_views = false;
#endif
// SimpleHandler implements browser-level callbacks.
CefRefPtr<SimpleHandler> handler(new SimpleHandler(use_views));
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
std::string url;
// Check if a "--url=" value was provided via the command-line. If so, use
// that instead of the default URL.
url = command_line->GetSwitchValue("url");
if (url.empty())
url = "http://www.google.com";
if (use_views) {
// Create the BrowserView.
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
handler, url, browser_settings, NULL, NULL, NULL);
// Create the Window. It will show itself after creation.
CefWindow::CreateTopLevelWindow(new SimpleWindowDelegate(browser_view));
} else {
// Information used when creating the native window.
CefWindowInfo window_info;
#if defined(OS_WIN)
// On Windows we need to specify certain flags that will be passed to
// CreateWindowEx().
window_info.SetAsPopup(NULL, "cefsimple");
#endif
// Create the first browser window.
CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings,
NULL, NULL);
}
}
void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
// Create a new V8 string value. See the "Basic JS Types" section below.
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
// Add the string to the window object as "window.myval". See the "JS Objects" section below.
object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}
And of course, the HTML to check if it's working.
CEFTest.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<span id="thespan"></span>
<script type="text/javascript">
function SetValue(argument) {
document.getElementById( 'thespan' ).innerHTML = window.myval + 'hello'
}
window.addEventListener( 'load', SetValue )
</script>
</body>
</html>
My understanding of Cef is only just now starting to really take shape. What it looks like I've had to do is pass CefRenderProcessHandler to the SimpleApp class in order to allow it access to all of the RenderProcessHandler's methods. Then I had to override OnContextCreated with my own version (as the original version is simply a placeholder function)
Calling CefExecuteProcess serves, I think, to actually force the process to start. I could use some clarification on that.