Browse Source

Adding Web message handling, separate process app setup, etc

Josh Engebretson 10 years ago
parent
commit
dd342a49b8

+ 6 - 0
Data/AtomicEditor/CodeEditor/Editor.html

@@ -44,6 +44,12 @@
     editor.setTheme("ace/theme/monokai");
     editor.session.setMode("ace/mode/typescript");
 
+    editor.getSession().on('change', function(e) {
+      window.atomicQuery({request: 'change',
+      persistent: false,
+      onSuccess: function(response) { },
+      onFailure: function(error_code, error_message) {console.log("Error on change")}});
+    });
   }
 
   function loadCode(codeUrl)

+ 1 - 1
Source/AtomicEditor/CMakeLists.txt

@@ -76,7 +76,7 @@ if(OS_MACOSX)
   # Helper executable target.
   add_executable(${CEF_HELPER_TARGET} MACOSX_BUNDLE "${CMAKE_CURRENT_SOURCE_DIR}/WebView/WebProcessHelperMac.cpp")
   add_dependencies(${CEF_HELPER_TARGET} libcef_dll_wrapper)
-  target_link_libraries(${CEF_HELPER_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})
+  target_link_libraries(${CEF_HELPER_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS} Atomic AtomicWebView ${ATOMIC_LINK_LIBRARIES})
   set_target_properties(${CEF_HELPER_TARGET} PROPERTIES
     MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/Build/CMake/Modules/AtomicEditor-HelperInfo.plist.template"
     )

+ 21 - 0
Source/AtomicEditor/Editors/JSResourceEditor.cpp

@@ -17,6 +17,7 @@
 #include <AtomicWebView/WebViewEvents.h>
 #include <AtomicWebView/UIWebView.h>
 #include <AtomicWebView/WebClient.h>
+#include <AtomicWebView/WebMessageHandler.h>
 
 #include "JSResourceEditor.h"
 
@@ -45,8 +46,12 @@ JSResourceEditor ::JSResourceEditor(Context* context, const String &fullpath, UI
     String url = "file:///Users/josh/Dev/atomic/AtomicGameEngine/Data/AtomicEditor/CodeEditor/Editor.html";
     webView_ = new UIWebView(context_, url);
     webClient_ = webView_->GetWebClient();
+    messageHandler_ = new WebMessageHandler(context_);
+    webClient_->AddMessageHandler(messageHandler_);
 
     SubscribeToEvent(webClient_, E_WEBVIEWLOADEND, HANDLER(JSResourceEditor, HandleWebViewLoadEnd));
+    SubscribeToEvent(messageHandler_, E_WEBMESSAGE, HANDLER(JSResourceEditor, HandleWebMessage));
+
 
     c->AddChild(webView_->GetInternalWidget());
 
@@ -66,6 +71,22 @@ void JSResourceEditor::HandleWebViewLoadEnd(StringHash eventType, VariantMap& ev
     webClient_->ExecuteJavaScript(ToString("loadCode(\"atomic://resources/%s\");", fullpath_.CString()));
 }
 
+void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventData)
+{
+    using namespace WebMessage;
+
+    const String& request = eventData[P_REQUEST].GetString();
+    WebMessageHandler* handler = static_cast<WebMessageHandler*>(eventData[P_HANDLER].GetPtr());
+
+    if (request == "change")
+    {
+        SetModified(true);
+    }
+
+    handler->Success();
+
+}
+
 void JSResourceEditor::FormatCode()
 {
 

+ 3 - 2
Source/AtomicEditor/Editors/JSResourceEditor.h

@@ -16,6 +16,7 @@ namespace Atomic
 {
     class UIWebView;
     class WebClient;
+    class WebMessageHandler;
 }
 
 namespace AtomicEditor
@@ -48,13 +49,13 @@ public:
 private:
 
     void HandleWebViewLoadEnd(StringHash eventType, VariantMap& eventData);
+    void HandleWebMessage(StringHash eventType, VariantMap& eventData);
 
     bool BeautifyJavascript(const char* source, String& output);
 
     SharedPtr<UIWebView> webView_;
     WeakPtr<WebClient> webClient_;
-
-
+    WeakPtr<WebMessageHandler> messageHandler_;
 };
 
 }

+ 23 - 5
Source/AtomicEditor/WebView/WebProcessHelperMac.cpp

@@ -2,13 +2,31 @@
 // reserved. Use of this source code is governed by a BSD-style license that can
 // be found in the LICENSE file.
 
+#include <AtomicWebView/Internal/WebAppOther.h>
+#include <AtomicWebView/Internal/WebAppRenderer.h>
+
+using namespace Atomic;
+
 #include "include/cef_app.h"
 
 // Entry point function for sub-processes.
-int main(int argc, char* argv[]) {
-  // Provide CEF with command-line arguments.
-  CefMainArgs main_args(argc, argv);
+int main(int argc, char* argv[])
+{
+    // Provide CEF with command-line arguments.
+    CefMainArgs main_args(argc, argv);
+
+    // Parse command-line arguments.
+    CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+    command_line->InitFromArgv(argc, argv);
+
+    // Create a ClientApp of the correct type.
+    CefRefPtr<CefApp> app;
+    WebApp::ProcessType process_type = WebApp::GetProcessType(command_line);
+    if (process_type == WebApp::RendererProcess)
+        app = new WebAppRenderer();
+    else if (process_type == WebApp::OtherProcess)
+        app = new WebAppOther();
 
-  // Execute the sub-process.
-  return CefExecuteProcess(main_args, NULL, NULL);
+    // Execute the secondary process.
+    return CefExecuteProcess(main_args, app, NULL);
 }

+ 1 - 1
Source/AtomicWebView/CMakeLists.txt

@@ -1,7 +1,7 @@
 
 include_directories (${CMAKE_SOURCE_DIR}/Source/ThirdParty/)
 
-file (GLOB SOURCE_FILES *.cpp *.h)
+file (GLOB_RECURSE SOURCE_FILES *.cpp *.h)
 
 file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/WebView/*.cpp)
 

+ 45 - 0
Source/AtomicWebView/Internal/WebApp.cpp

@@ -0,0 +1,45 @@
+
+#include <include/cef_client.h>
+
+#include "WebApp.h"
+#include "../WebString.h"
+
+namespace Atomic
+{
+
+// These flags must match the Chromium values.
+static const char kProcessType[] = "type";
+static const char kRendererProcess[] = "renderer";
+#if defined(OS_LINUX)
+static const char kZygoteProcess[] = "zygote";
+#endif
+
+WebApp::WebApp()
+{
+
+}
+
+WebApp::ProcessType WebApp::GetProcessType(CefRefPtr<CefCommandLine> command_line)
+{
+    // The command-line flag won't be specified for the browser process.
+    if (!command_line->HasSwitch(kProcessType))
+        return BrowserProcess;
+
+    String processType;
+    ConvertCEFString(command_line->GetSwitchValue(kProcessType), processType);
+
+    if (processType == kRendererProcess)
+        return RendererProcess;
+
+#if defined(OS_LINUX)
+    else if (processType == kZygoteProcess)
+        return ZygoteProcess;
+#endif
+
+    return OtherProcess;
+}
+
+
+
+
+}

+ 38 - 0
Source/AtomicWebView/Internal/WebApp.h

@@ -0,0 +1,38 @@
+
+#pragma once
+
+#include <include/cef_app.h>
+#include "WebApp.h"
+
+namespace Atomic
+{
+
+// Base class for customizing process-type-based behavior.
+class WebApp : public CefApp
+{
+
+public:
+
+    enum ProcessType
+    {
+        BrowserProcess,
+        RendererProcess,
+        ZygoteProcess,
+        OtherProcess,
+    };
+
+    WebApp();
+
+    // Determine the process type based on command-line arguments.
+    static ProcessType GetProcessType(CefRefPtr<CefCommandLine> command_line);
+
+private:
+
+    DISALLOW_COPY_AND_ASSIGN(WebApp);
+
+
+};
+
+
+}
+

+ 25 - 0
Source/AtomicWebView/Internal/WebAppBrowser.cpp

@@ -0,0 +1,25 @@
+
+
+#include "WebAppBrowser.h"
+
+namespace Atomic
+{
+
+WebAppBrowser::WebAppBrowser()
+{
+
+}
+
+void WebAppBrowser::OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line)
+{
+    CefApp::OnBeforeCommandLineProcessing(process_type, command_line);
+}
+
+// CefBrowserProcessHandler methods.
+void WebAppBrowser::OnContextInitialized()
+{
+    CefBrowserProcessHandler::OnContextInitialized();
+}
+
+
+}

+ 36 - 0
Source/AtomicWebView/Internal/WebAppBrowser.h

@@ -0,0 +1,36 @@
+
+#pragma once
+
+#include "WebApp.h"
+
+namespace Atomic
+{
+
+class WebAppBrowser : public WebApp, public CefBrowserProcessHandler
+{
+
+public:
+
+    WebAppBrowser();
+
+    // CefApp methods.
+    void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line) OVERRIDE;
+
+    CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE
+    {
+        return this;
+    }
+
+    // CefBrowserProcessHandler methods.
+    void OnContextInitialized() OVERRIDE;
+
+
+private:
+
+    DISALLOW_COPY_AND_ASSIGN(WebAppBrowser);
+    IMPLEMENT_REFCOUNTING(WebAppBrowser);
+
+};
+
+}
+

+ 13 - 0
Source/AtomicWebView/Internal/WebAppOther.cpp

@@ -0,0 +1,13 @@
+
+
+#include "WebAppOther.h"
+
+namespace Atomic
+{
+
+WebAppOther::WebAppOther()
+{
+
+}
+
+}

+ 24 - 0
Source/AtomicWebView/Internal/WebAppOther.h

@@ -0,0 +1,24 @@
+
+#pragma once
+
+#include "WebApp.h"
+
+namespace Atomic
+{
+
+class WebAppOther : public WebApp
+{
+
+public:
+
+    WebAppOther();
+
+private:
+
+    DISALLOW_COPY_AND_ASSIGN(WebAppOther);
+    IMPLEMENT_REFCOUNTING(WebAppOther);
+
+};
+
+}
+

+ 186 - 0
Source/AtomicWebView/Internal/WebAppRenderer.cpp

@@ -0,0 +1,186 @@
+
+
+#include <include/wrapper/cef_message_router.h>
+
+#include "WebAppRenderer.h"
+
+namespace Atomic
+{
+
+// Must match the value in client_handler.cc.
+static const char kFocusedNodeChangedMessage[] = "ClientRenderer.FocusedNodeChanged";
+
+class WebRenderDelegate : public WebAppRenderer::Delegate
+{
+
+public:
+
+    WebRenderDelegate()
+        : last_node_is_editable_(false)
+    {
+
+    }
+
+    virtual void OnWebKitInitialized(CefRefPtr<WebAppRenderer> app) OVERRIDE
+    {
+        // Create the renderer-side router for query handling.
+        CefMessageRouterConfig config;
+        config.js_query_function = "atomicQuery";
+        config.js_cancel_function = "atomicQueryCancel";
+
+        message_router_ = CefMessageRouterRendererSide::Create(config);
+    }
+
+    virtual void OnContextCreated(CefRefPtr<WebAppRenderer> app,
+                                  CefRefPtr<CefBrowser> browser,
+                                  CefRefPtr<CefFrame> frame,
+                                  CefRefPtr<CefV8Context> context) OVERRIDE
+    {
+        message_router_->OnContextCreated(browser,  frame, context);
+    }
+
+    virtual void OnContextReleased(CefRefPtr<WebAppRenderer> app,
+                                   CefRefPtr<CefBrowser> browser,
+                                   CefRefPtr<CefFrame> frame,
+                                   CefRefPtr<CefV8Context> context) OVERRIDE
+    {
+        message_router_->OnContextReleased(browser,  frame, context);
+    }
+
+    virtual bool OnProcessMessageReceived(
+            CefRefPtr<WebAppRenderer> app,
+            CefRefPtr<CefBrowser> browser,
+            CefProcessId source_process,
+            CefRefPtr<CefProcessMessage> message) OVERRIDE
+    {
+        return message_router_->OnProcessMessageReceived(
+                    browser, source_process, message);
+    }
+
+private:
+    bool last_node_is_editable_;
+
+    // Handles the renderer side of query routing.
+    CefRefPtr<CefMessageRouterRendererSide> message_router_;
+
+    IMPLEMENT_REFCOUNTING(WebRenderDelegate);
+};
+
+WebAppRenderer::WebAppRenderer() {
+}
+
+void WebAppRenderer::OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info)
+{
+    delegates_.Push(new WebRenderDelegate());
+
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnRenderThreadCreated(this, extra_info);
+}
+
+void WebAppRenderer::OnWebKitInitialized()
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnWebKitInitialized(this);
+}
+
+void WebAppRenderer::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnBrowserCreated(this, browser);
+}
+
+void WebAppRenderer::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnBrowserDestroyed(this, browser);
+}
+
+CefRefPtr<CefLoadHandler> WebAppRenderer::GetLoadHandler()
+{
+    CefRefPtr<CefLoadHandler> load_handler;
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End() && !load_handler.get(); ++it)
+        load_handler = (*it)->GetLoadHandler(this);
+
+    return load_handler;
+}
+
+bool WebAppRenderer::OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefRefPtr<CefRequest> request,
+                                        NavigationType navigation_type,
+                                        bool is_redirect)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it) {
+        if ((*it)->OnBeforeNavigation(this, browser, frame, request, navigation_type, is_redirect))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void WebAppRenderer::OnContextCreated(CefRefPtr<CefBrowser> browser,
+                                      CefRefPtr<CefFrame> frame,
+                                      CefRefPtr<CefV8Context> context)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnContextCreated(this, browser, frame, context);
+}
+
+void WebAppRenderer::OnContextReleased(CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       CefRefPtr<CefV8Context> context)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnContextReleased(this, browser, frame, context);
+}
+
+void WebAppRenderer::OnUncaughtException(
+        CefRefPtr<CefBrowser> browser,
+        CefRefPtr<CefFrame> frame,
+        CefRefPtr<CefV8Context> context,
+        CefRefPtr<CefV8Exception> exception,
+        CefRefPtr<CefV8StackTrace> stackTrace)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+    {
+        (*it)->OnUncaughtException(this, browser, frame, context, exception, stackTrace);
+    }
+}
+
+void WebAppRenderer::OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
+                                          CefRefPtr<CefFrame> frame,
+                                          CefRefPtr<CefDOMNode> node)
+{
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End(); ++it)
+        (*it)->OnFocusedNodeChanged(this, browser, frame, node);
+}
+
+bool WebAppRenderer::OnProcessMessageReceived(
+        CefRefPtr<CefBrowser> browser,
+        CefProcessId source_process,
+        CefRefPtr<CefProcessMessage> message)
+{
+    DCHECK_EQ(source_process, PID_BROWSER);
+
+    bool handled = false;
+
+    DelegateSet::Iterator it = delegates_.Begin();
+    for (; it != delegates_.End() && !handled; ++it) {
+        handled = (*it)->OnProcessMessageReceived(this, browser, source_process, message);
+    }
+
+    return handled;
+}
+}

+ 146 - 0
Source/AtomicWebView/Internal/WebAppRenderer.h

@@ -0,0 +1,146 @@
+
+#pragma once
+
+#include <Atomic/Container/List.h>
+#include "WebApp.h"
+
+namespace Atomic
+{
+
+// Web app implementation for the renderer process.
+class WebAppRenderer : public WebApp, public CefRenderProcessHandler
+{
+
+public:
+    // Interface for renderer delegates. All Delegates must be returned via
+    // CreateDelegates. Do not perform work in the Delegate
+    // constructor. See CefRenderProcessHandler for documentation.
+    class Delegate : public virtual CefBase
+    {
+    public:
+        virtual void OnRenderThreadCreated(CefRefPtr<WebAppRenderer> app,
+                                           CefRefPtr<CefListValue> extra_info) {}
+
+        virtual void OnWebKitInitialized(CefRefPtr<WebAppRenderer> app) {}
+
+        virtual void OnBrowserCreated(CefRefPtr<WebAppRenderer> app,
+                                      CefRefPtr<CefBrowser> browser) {}
+
+        virtual void OnBrowserDestroyed(CefRefPtr<WebAppRenderer> app,
+                                        CefRefPtr<CefBrowser> browser) {}
+
+        virtual CefRefPtr<CefLoadHandler> GetLoadHandler(CefRefPtr<WebAppRenderer> app)
+        {
+            return NULL;
+        }
+
+        virtual bool OnBeforeNavigation(CefRefPtr<WebAppRenderer> app,
+                                        CefRefPtr<CefBrowser> browser,
+                                        CefRefPtr<CefFrame> frame,
+                                        CefRefPtr<CefRequest> request,
+                                        cef_navigation_type_t navigation_type,
+                                        bool is_redirect)
+        {
+            return false;
+        }
+
+        virtual void OnContextCreated(CefRefPtr<WebAppRenderer> app,
+                                      CefRefPtr<CefBrowser> browser,
+                                      CefRefPtr<CefFrame> frame,
+                                      CefRefPtr<CefV8Context> context) {}
+
+        virtual void OnContextReleased(CefRefPtr<WebAppRenderer> app,
+                                       CefRefPtr<CefBrowser> browser,
+                                       CefRefPtr<CefFrame> frame,
+                                       CefRefPtr<CefV8Context> context) {}
+
+        virtual void OnUncaughtException(CefRefPtr<WebAppRenderer> app,
+                                         CefRefPtr<CefBrowser> browser,
+                                         CefRefPtr<CefFrame> frame,
+                                         CefRefPtr<CefV8Context> context,
+                                         CefRefPtr<CefV8Exception> exception,
+                                         CefRefPtr<CefV8StackTrace> stackTrace) {}
+
+        virtual void OnFocusedNodeChanged(CefRefPtr<WebAppRenderer> app,
+                                          CefRefPtr<CefBrowser> browser,
+                                          CefRefPtr<CefFrame> frame,
+                                          CefRefPtr<CefDOMNode> node) {}
+
+        // Called when a process message is received. Return true if the message was
+        // handled and should not be passed on to other handlers. Delegates
+        // should check for unique message names to avoid interfering with each
+        // other.
+        virtual bool OnProcessMessageReceived(
+                CefRefPtr<WebAppRenderer> app,
+                CefRefPtr<CefBrowser> browser,
+                CefProcessId source_process,
+                CefRefPtr<CefProcessMessage> message)
+        {
+            return false;
+        }
+    };
+
+    typedef List<CefRefPtr<Delegate>> DelegateSet;
+
+    WebAppRenderer();
+
+private:
+    // Creates all of the Delegate objects. Implemented by cefclient in
+    // client_app_delegates_renderer.cc
+    static void CreateDelegates(DelegateSet& delegates);
+
+    // CefApp methods.
+    CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE {
+        return this;
+    }
+
+    // CefRenderProcessHandler methods.
+
+    void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) OVERRIDE;
+
+    void OnWebKitInitialized() OVERRIDE;
+
+    void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+    void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE;
+
+    CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE;
+
+    bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
+                            CefRefPtr<CefFrame> frame,
+                            CefRefPtr<CefRequest> request,
+                            NavigationType navigation_type,
+                            bool is_redirect) OVERRIDE;
+
+    void OnContextCreated(CefRefPtr<CefBrowser> browser,
+                          CefRefPtr<CefFrame> frame,
+                          CefRefPtr<CefV8Context> context) OVERRIDE;
+
+    void OnContextReleased(CefRefPtr<CefBrowser> browser,
+                           CefRefPtr<CefFrame> frame,
+                           CefRefPtr<CefV8Context> context) OVERRIDE;
+
+    void OnUncaughtException(CefRefPtr<CefBrowser> browser,
+                             CefRefPtr<CefFrame> frame,
+                             CefRefPtr<CefV8Context> context,
+                             CefRefPtr<CefV8Exception> exception,
+                             CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE;
+
+    void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
+                              CefRefPtr<CefFrame> frame,
+                              CefRefPtr<CefDOMNode> node) OVERRIDE;
+
+    bool OnProcessMessageReceived(
+            CefRefPtr<CefBrowser> browser,
+            CefProcessId source_process,
+            CefRefPtr<CefProcessMessage> message) OVERRIDE;
+
+private:
+    // Set of supported Delegates.
+    DelegateSet delegates_;
+
+    IMPLEMENT_REFCOUNTING(WebAppRenderer);
+    DISALLOW_COPY_AND_ASSIGN(WebAppRenderer);
+};
+}
+

+ 11 - 4
Source/AtomicWebView/WebBrowserHost.cpp

@@ -14,6 +14,8 @@
 
 #include <Atomic/Graphics/Graphics.h>
 
+#include "Internal/WebAppBrowser.h"
+
 #include "WebSchemeHandler.h"
 #include "WebClient.h"
 #include "WebBrowserHost.h"
@@ -32,6 +34,7 @@ public:
     {
 
         host_ = host;
+        app_ = new WebAppBrowser();
     }
 
     virtual ~WebBrowserHostPrivate()
@@ -42,6 +45,7 @@ public:
 private:
 
     WeakPtr<WebBrowserHost> host_;
+    CefRefPtr<CefApp> app_;
 
 };
 
@@ -50,6 +54,9 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 
     const Vector<String>& arguments = GetArguments();
 
+    // IMPORTANT: Cef::App contains virtual void OnBeforeCommandLineProcessing(), which should make it possible
+    // to setup args on Windows
+
 #ifdef ATOMIC_PLATFORM_OSX
     const char* _argv[3] = { "", "--enable-media-stream", "--enable-usermedia-screen-capturing" };
     CefMainArgs args(3, (char**) &_argv);
@@ -60,16 +67,16 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
     CefSettings settings;
     settings.windowless_rendering_enabled = true;
 
+    d_ = new WebBrowserHostPrivate(this);
+
     // If losing OSX system menu, it means we're calling this
     // before initializing graphics subsystem
-    if (!CefInitialize(args, settings, nullptr, nullptr))
+    if (!CefInitialize(args, settings, d_->app_, nullptr))
     {
         LOGERROR("CefInitialize - Error");
     }
 
-    RegisterWebSchemeHandlers(this);
-
-    d_ = new WebBrowserHostPrivate(this);
+    RegisterWebSchemeHandlers(this);    
 
     SubscribeToEvent(E_BEGINFRAME, HANDLER(WebBrowserHost, HandleBeginFrame));
 

+ 2 - 2
Source/AtomicWebView/WebBrowserHost.h

@@ -1,8 +1,8 @@
 
-#include <Atomic/Core/Object.h>
-
 #pragma once
 
+#include <Atomic/Core/Object.h>
+
 namespace Atomic
 {
 

+ 112 - 15
Source/AtomicWebView/WebClient.cpp

@@ -8,6 +8,7 @@
 #include <include/wrapper/cef_helpers.h>
 #include <include/base/cef_bind.h>
 #include <include/wrapper/cef_closure_task.h>
+#include "include/wrapper/cef_message_router.h"
 
 #include <Atomic/Core/ProcessUtils.h>
 #include <Atomic/Core/CoreEvents.h>
@@ -17,6 +18,7 @@
 #include <Atomic/Graphics/Graphics.h>
 
 #include "WebBrowserHost.h"
+#include "WebMessageHandler.h"
 #include "WebClient.h"
 #include "WebKeyboard.h"
 #include "WebViewEvents.h"
@@ -29,7 +31,7 @@ namespace Atomic
 void* GetNSWindowContentView(void* window);
 #endif
 
-class WebClientPrivate : public CefClient, public CefLifeSpanHandler, public CefLoadHandler, public CefDisplayHandler
+class WebClientPrivate : public CefClient, public CefLifeSpanHandler, public CefLoadHandler, public CefDisplayHandler, public CefRequestHandler
 {
     friend class WebClient;
 
@@ -40,6 +42,16 @@ public:
 
         webClient_ = client;
         webBrowserHost_ = webClient_->GetSubsystem<WebBrowserHost>();
+
+        CefMessageRouterConfig config;
+        config.js_query_function = "atomicQuery";
+        config.js_cancel_function = "atomicQueryCancel";
+        browserSideRouter_ = CefMessageRouterBrowserSide::Create(config);
+
+    }
+
+    virtual ~WebClientPrivate()
+    {
     }
 
     CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE
@@ -52,19 +64,63 @@ public:
 
     }
 
-    virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE
+    CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE
     {
         return this;
     }
 
-    virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE {
+    CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE
+    {
         return this;
     }
 
-    virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE {
+    CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE
+    {
         return this;
     }
 
+    CefRefPtr<CefRequestHandler> GetRequestHandler() OVERRIDE
+    {
+        return this;
+    }
+
+
+    // CefRequestHandler methods
+    bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
+                        CefRefPtr<CefFrame> frame,
+                        CefRefPtr<CefRequest> request,
+                        bool is_redirect) OVERRIDE
+    {
+        CEF_REQUIRE_UI_THREAD();
+
+        browserSideRouter_->OnBeforeBrowse(browser, frame);
+        return false;
+
+    }
+
+    bool OnProcessMessageReceived(
+            CefRefPtr<CefBrowser> browser,
+            CefProcessId source_process,
+            CefRefPtr<CefProcessMessage> message) OVERRIDE
+    {
+
+        CEF_REQUIRE_UI_THREAD();
+
+        if (browserSideRouter_->OnProcessMessageReceived(browser, source_process, message))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
+                                   TerminationStatus status) OVERRIDE
+    {
+        CEF_REQUIRE_UI_THREAD();
+        browserSideRouter_->OnRenderProcessTerminated(browser);
+    }
 
     // CefLoadHandler
 
@@ -195,15 +251,6 @@ public:
         return false;
     }
 
-
-
-    bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
-                                  CefProcessId source_process,
-                                  CefRefPtr<CefProcessMessage> message) OVERRIDE
-    {
-        return false;
-    }
-
     bool CreateBrowser(const String& initialURL, int width, int height)
     {
         if (webClient_->renderHandler_.Null())
@@ -256,6 +303,7 @@ public:
     }
 
     // CefLifeSpanHandler methods:
+
     virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE
     {
         CEF_REQUIRE_UI_THREAD();
@@ -270,8 +318,17 @@ public:
     {
         CEF_REQUIRE_UI_THREAD();
 
-        if (browser->IsSame(browser_))
-            browser_ = nullptr;
+        List<SharedPtr<WebMessageHandler>>::Iterator itr = webClient_->messageHandlers_.Begin();
+        while (itr != webClient_->messageHandlers_.End())
+        {
+            CefMessageRouterBrowserSide::Handler* handler = static_cast<CefMessageRouterBrowserSide::Handler*>((*itr)->GetCefHandler());
+            browserSideRouter_->RemoveHandler(handler);
+            itr++;
+        }
+
+        webClient_->messageHandlers_.Clear();
+
+        browser_ = nullptr;
 
     }
 
@@ -299,6 +356,7 @@ private:
     CefRefPtr<CefBrowser> browser_;
     WeakPtr<WebBrowserHost> webBrowserHost_;
     WeakPtr<WebClient> webClient_;
+    CefRefPtr<CefMessageRouterBrowserSide> browserSideRouter_;
 
 };
 
@@ -472,6 +530,45 @@ void WebClient::ExecuteJavaScript(const String& script)
     d_->browser_->GetMainFrame()->ExecuteJavaScript(CefString(script.CString()), "", 0);
 }
 
+void WebClient::AddMessageHandler(WebMessageHandler* handler, bool first)
+{
+    SharedPtr<WebMessageHandler> _handler(handler);
+
+    if (handler->GetWebClient())
+    {
+        LOGWARNING("WebClient::AddMessageHandler - message handler already added to another client");
+        return;
+    }
+
+    if (messageHandlers_.Contains(_handler))
+    {
+        LOGWARNING("WebClient::AddMessageHandler - message handler already added to this client");
+        return;
+    }
+
+    _handler->SetWebClient(this);
+    messageHandlers_.Push(_handler);
+    d_->browserSideRouter_->AddHandler(static_cast<CefMessageRouterBrowserSide::Handler*>(handler->GetCefHandler()), first);
+
+}
+
+void WebClient::RemoveMessageHandler(WebMessageHandler* handler)
+{
+
+    SharedPtr<WebMessageHandler> _handler(handler);
+
+    List<SharedPtr<WebMessageHandler>>::Iterator itr = messageHandlers_.Find(_handler);
+
+    if (itr == messageHandlers_.End())
+    {
+        LOGWARNING("WebClient::RemoveMessageHandler - message handler not found");
+        return;
+    }
+
+    d_->browserSideRouter_->RemoveHandler(static_cast<CefMessageRouterBrowserSide::Handler*>(handler->GetCefHandler()));
+    messageHandlers_.Erase(itr);
+}
+
 // Navigation
 
 void WebClient::LoadURL(const String& url)

+ 9 - 4
Source/AtomicWebView/WebClient.h

@@ -1,15 +1,16 @@
+#pragma once
 
+#include <Atomic/Container/List.h>
 #include <Atomic/Core/Object.h>
 #include "WebRenderHandler.h"
 
-#pragma once
-
 class CefClient;
 
 namespace Atomic
 {
 
 class WebClientPrivate;
+class WebMessageHandler;
 
 class ATOMIC_API WebClient : public Object
 {
@@ -32,8 +33,6 @@ public:
 
     void SetWebRenderHandler(WebRenderHandler* handler);
 
-    CefClient* GetCefClient();
-
     void SendMouseClickEvent(int x, int y, unsigned button, bool mouseUp, unsigned modifier) const;
     void SendMousePressEvent(int x, int y, unsigned button = 0, unsigned modifier = 0) const;
     void SendMouseMoveEvent(int x, int y, unsigned modifier, bool mouseLeave = false) const;
@@ -52,6 +51,8 @@ public:
     void ShortcutRedo();
     void ShortcutDelete();
 
+    void AddMessageHandler(WebMessageHandler* handler, bool first = false);
+    void RemoveMessageHandler(WebMessageHandler* handler);
     void ExecuteJavaScript(const String& script);
 
     // Navigation
@@ -65,12 +66,16 @@ public:
 
     void Reload();
 
+    CefClient* GetCefClient();
+
 private:
 
     void WasResized();
 
     SharedPtr<WebRenderHandler> renderHandler_;
 
+    List<SharedPtr<WebMessageHandler>> messageHandlers_;
+
     WebClientPrivate* d_;
 
 };

+ 118 - 0
Source/AtomicWebView/WebMessageHandler.cpp

@@ -0,0 +1,118 @@
+
+#include "include/wrapper/cef_message_router.h"
+
+#include <Atomic/IO/Log.h>
+
+#include "WebViewEvents.h"
+#include "WebString.h"
+#include "WebClient.h"
+#include "WebMessageHandler.h"
+
+namespace Atomic
+{
+
+class WebMessageHandlerPrivate : public CefMessageRouterBrowserSide::Handler
+{
+public:
+
+    WebMessageHandlerPrivate(WebMessageHandler* webMessageHandler) : webMessageHandler_(webMessageHandler), queryHandled_(false)
+    {
+
+    }
+
+    void Success(const String& response)
+    {
+        if (!currentCallback_)
+        {
+            LOGERROR("WebMessageHandlerPrivate::Success - Called with no current callback");
+            return;
+        }
+
+        queryHandled_ = true;
+        currentCallback_->Success(CefString(response.CString()));
+
+    }
+
+    void Failure(int errorCode, const String& errorMessage)
+    {
+        if (!currentCallback_)
+        {
+            LOGERROR("WebMessageHandlerPrivate::Failure - Called with no current callback");
+            return;
+        }
+
+        queryHandled_ = true;
+        currentCallback_->Failure(errorCode, CefString(errorMessage.CString()));
+
+    }
+
+    // Called due to atomicQuery execution.
+    bool OnQuery(CefRefPtr<CefBrowser> browser,
+                 CefRefPtr<CefFrame> frame,
+                 int64 query_id,
+                 const CefString& request,
+                 bool persistent,
+                 CefRefPtr<Callback> callback) OVERRIDE
+    {
+        currentCallback_ = callback;
+
+        VariantMap eventData;
+        using namespace WebMessage;
+
+        String request_;
+        ConvertCEFString(request, request_);
+
+        eventData[P_HANDLER] = webMessageHandler_;
+
+        eventData[P_QUERYID] = (double) query_id;
+        eventData[P_REQUEST] = request_;
+        eventData[P_PERSISTENT] = persistent;
+
+        eventData[P_CEFBROWSER] = (void*) browser;
+        eventData[P_CEFFRAME] = (void*) frame;
+
+        queryHandled_ = false;
+        webMessageHandler_->SendEvent(E_WEBMESSAGE, eventData);
+
+        currentCallback_ = nullptr;
+
+        if (!queryHandled_)
+        {
+            LOGERROR("WebMessageHandlerPrivate::OnQuery - WebQuery was not handled");
+            return false;
+        }
+
+        return true;
+    }
+
+private:
+
+    CefRefPtr<Callback> currentCallback_;
+    bool queryHandled_;
+
+    WeakPtr<WebMessageHandler> webMessageHandler_;
+};
+
+WebMessageHandler::WebMessageHandler(Context* context) : Object(context)
+{
+    d_ = new WebMessageHandlerPrivate(this);
+}
+
+WebMessageHandler::~WebMessageHandler()
+{
+    delete d_;
+    d_ = nullptr;
+}
+
+void WebMessageHandler::Success(const String& response)
+{
+    d_->Success(response);
+}
+
+void WebMessageHandler::Failure(int errorCode, const String& errorMessage)
+{
+    d_->Failure(errorCode, errorMessage);
+}
+
+
+}

+ 40 - 0
Source/AtomicWebView/WebMessageHandler.h

@@ -0,0 +1,40 @@
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+namespace Atomic
+{
+
+class WebClient;
+class WebMessageHandlerPrivate;
+
+class WebMessageHandler : public Object
+{
+
+    OBJECT(WebMessageHandler);
+
+public:
+
+    /// Construct.
+    WebMessageHandler(Context* context);
+    /// Destruct.
+    virtual ~WebMessageHandler();
+
+    void Success(const String& response = String::EMPTY);
+    void Failure(int errorCode, const String& errorMessage);
+
+    WebClient* GetWebClient() { return client_; }
+    void SetWebClient(WebClient* client) { client_ = client; }
+
+    /// Get the CefMessageRouterBrowserSide::Handler* as a opaque void*
+    void* GetCefHandler() { return (void*) d_; }
+
+private:
+
+    WeakPtr<WebClient> client_;
+    WebMessageHandlerPrivate* d_;
+
+};
+
+}

+ 0 - 1
Source/AtomicWebView/WebRenderHandler.cpp

@@ -9,7 +9,6 @@
 namespace Atomic
 {
 
-
 WebRenderHandler::WebRenderHandler(Context* context) : Object (context)
 {
 }

+ 2 - 2
Source/AtomicWebView/WebRenderHandler.h

@@ -1,9 +1,9 @@
 
+#pragma once
+
 #include <Atomic/Graphics/Texture2D.h>
 #include <Atomic/Graphics/Material.h>
 
-#pragma once
-
 class CefRenderHandler;
 
 namespace Atomic

+ 7 - 0
Source/AtomicWebView/WebSchemeHandler.cpp

@@ -75,6 +75,9 @@ public:
 
         if (ext == ".js")
             response->SetMimeType("text/javascript");
+        else if (ext == ".ts")
+            response->SetMimeType("text/typescript");
+
 
         response->SetStatus(200);
 
@@ -105,8 +108,12 @@ public:
 
             bytes_read = transfer_size;
             has_data = true;
+
+            if (offset_ >= fileLength_)
+                file_->Close();
         }
 
+
         return has_data;
     }
 

+ 0 - 1
Source/AtomicWebView/WebSchemeHandler.h

@@ -1,7 +1,6 @@
 
 #pragma once
 
-
 namespace Atomic
 {
 

+ 0 - 1
Source/AtomicWebView/WebString.h

@@ -1,7 +1,6 @@
 
 #pragma once
 
-
 #include <Atomic/Container/Str.h>
 
 namespace Atomic

+ 2 - 2
Source/AtomicWebView/WebTexture2D.h

@@ -1,11 +1,11 @@
 
+#pragma once
+
 #include <Atomic/Graphics/Texture2D.h>
 #include <Atomic/Graphics/Material.h>
 
 #include "WebRenderHandler.h"
 
-#pragma once
-
 namespace Atomic
 {
 

+ 12 - 0
Source/AtomicWebView/WebViewEvents.h

@@ -43,5 +43,17 @@ EVENT(E_WEBVIEWTITLECHANGE, WebViewTitleChange)
     PARAM(P_TITLE, Title);   // String
 }
 
+/// WebView title change
+EVENT(E_WEBMESSAGE, WebMessage)
+{
+    PARAM(P_HANDLER, Handler);          // WebMessageHandler*
+    PARAM(P_QUERYID, QueryID);          // Double (Int64 CEF Side)
+    PARAM(P_REQUEST, Request);          // String
+    PARAM(P_PERSISTENT, Persistent);    // Bool
+
+    PARAM(P_CEFBROWSER, Browser);       // CefBrowser*
+    PARAM(P_CEFFRAME, Frame);           // CefFrame*
+}
+
 
 }