Browse Source

Merge pull request #682 from uWebKit/JME-ATOMIC-UPDATES

Handle WebView JS evaluation with deferred return value, Process CEF messages on Update for headless configurations
JoshEngebretson 9 years ago
parent
commit
4287203b9c

+ 65 - 1
Source/AtomicWebView/Internal/WebAppRenderer.cpp

@@ -68,12 +68,74 @@ public:
         message_router_->OnContextReleased(browser,  frame, context);
     }
 
-    virtual bool OnProcessMessageReceived(
+    virtual bool OnProcessMessageReceived (
             CefRefPtr<WebAppRenderer> app,
             CefRefPtr<CefBrowser> browser,
             CefProcessId source_process,
             CefRefPtr<CefProcessMessage> message) OVERRIDE
     {
+
+        const CefString& message_name = message->GetName();
+
+        if (message_name == "atomic_eval_javascript")
+        {
+            CefRefPtr<CefV8Context> context = browser->GetMainFrame()->GetV8Context();
+            CefRefPtr<CefV8Value> retval;
+            CefRefPtr<CefV8Exception> exception;
+
+            context->Enter();
+
+            CefRefPtr<CefV8Value> json = context->GetGlobal()->GetValue("JSON");
+            if (!json.get() || !json->IsObject())
+            {
+                context->Exit();
+                return false;
+            }
+
+            CefRefPtr<CefV8Value> stringify = json->GetValue("stringify");
+            if (!stringify.get() || !stringify->IsFunction())
+            {
+                context->Exit();
+                return false;
+            }
+
+            unsigned evalID = (unsigned) message->GetArgumentList()->GetInt(0);
+            CefString script = message->GetArgumentList()->GetString(1);
+
+            bool result = context->Eval(script, retval, exception);
+
+            // Create the result message object.
+            CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("atomic_eval_javascript_result");
+
+            // Retrieve the argument list object.
+            CefRefPtr<CefListValue> args = msg->GetArgumentList();
+
+            // Populate the argument values.
+            args->SetInt(0, (int) evalID);
+            args->SetBool(1, result);
+            if (result)
+            {
+                CefV8ValueList stringifyArgs;
+                stringifyArgs.push_back(retval);
+                CefRefPtr<CefV8Value> sjson = stringify->ExecuteFunctionWithContext(context, NULL, stringifyArgs);
+                if (!sjson.get() || !sjson->IsString())
+                {
+                    args->SetString(2, "WebAppRenderer::OnProcessMessageReceived() - Error Getting Return JSON");
+                }
+                else
+                {
+                    args->SetString(2, sjson->GetStringValue());
+                }
+            }
+            else
+                args->SetString(2, exception->GetMessage());
+
+            browser->SendProcessMessage(PID_BROWSER, msg);
+
+            context->Exit();
+            return true;
+        }
+
         return message_router_->OnProcessMessageReceived(
                     browser, source_process, message);
     }
@@ -87,6 +149,7 @@ private:
     IMPLEMENT_REFCOUNTING(WebRenderDelegate);
 };
 
+
 WebAppRenderer::WebAppRenderer() {
 }
 
@@ -151,6 +214,7 @@ 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);

+ 2 - 2
Source/AtomicWebView/WebBrowserHost.cpp

@@ -137,7 +137,7 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 
     RegisterWebSchemeHandlers(this);
 
-    SubscribeToEvent(E_BEGINFRAME, HANDLER(WebBrowserHost, HandleBeginFrame));
+    SubscribeToEvent(E_UPDATE, HANDLER(WebBrowserHost, HandleUpdate));
 
 }
 
@@ -147,7 +147,7 @@ WebBrowserHost::~WebBrowserHost()
     CefShutdown();
 }
 
-void WebBrowserHost::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
+void WebBrowserHost::HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
     CefDoMessageLoopWork();
 }

+ 1 - 1
Source/AtomicWebView/WebBrowserHost.h

@@ -44,7 +44,7 @@ public:
 
 private:
 
-    void HandleBeginFrame(StringHash eventType, VariantMap& eventData);
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);
 
     WebBrowserHostPrivate* d_;
 

+ 78 - 17
Source/AtomicWebView/WebClient.cpp

@@ -149,6 +149,24 @@ public:
 
         CEF_REQUIRE_UI_THREAD();
 
+        const CefString& message_name = message->GetName();
+
+        if (message_name == "atomic_eval_javascript_result")
+        {
+            if (webClient_.Null())
+                return false;
+
+            unsigned evalID = (unsigned) message->GetArgumentList()->GetInt(0);
+            bool result = message->GetArgumentList()->GetBool(1);
+            String value;
+            ConvertCEFString(message->GetArgumentList()->GetString(2), value);
+
+            webClient_->EvalJavaScriptResult(evalID, result, value);
+
+            return true;
+        }
+
+
         if (browserSideRouter_->OnProcessMessageReceived(browser, source_process, message))
         {
             return true;
@@ -314,34 +332,44 @@ public:
 
         Graphics* graphics = webClient_->GetSubsystem<Graphics>();
 
-        SDL_Window* sdlWindow = static_cast<SDL_Window*>(graphics->GetSDLWindow());
-        SDL_SysWMinfo info;
-        SDL_VERSION(&info.version);
-
-        if(SDL_GetWindowWMInfo(sdlWindow, &info))
+        if (graphics)
         {
+            SDL_Window* sdlWindow = static_cast<SDL_Window*>(graphics->GetSDLWindow());
+            SDL_SysWMinfo info;
+            SDL_VERSION(&info.version);
+
+            if(SDL_GetWindowWMInfo(sdlWindow, &info))
+            {
 #ifdef ATOMIC_PLATFORM_OSX
-            NSView* view = (NSView*) GetNSWindowContentView(info.info.cocoa.window);
-            windowInfo.SetAsWindowless(view, false);
+                NSView* view = (NSView*) GetNSWindowContentView(info.info.cocoa.window);
+                windowInfo.SetAsWindowless(view, false);
 #endif
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
-            windowInfo.SetAsWindowless(info.info.win.window, false);
+                windowInfo.SetAsWindowless(info.info.win.window, false);
 #endif
+            }
 
-            webClient_->renderHandler_->SetSize(width, height);
-            CefRefPtr<CefBrowser> browser = CefBrowserHost::CreateBrowserSync(windowInfo, this,
-                                                                              initialURL.CString(), browserSettings, nullptr);
+        }
+        else
+        {
+#ifndef ATOMIC_PLATFORM_LINUX
+            // headless
+            windowInfo.SetAsWindowless(nullptr, false);
+#endif
+        }
 
-            if (!browser.get())
-                return false;
+        webClient_->renderHandler_->SetSize(width, height);
+        CefRefPtr<CefBrowser> browser = CefBrowserHost::CreateBrowserSync(windowInfo, this,
+                                                                          initialURL.CString(), browserSettings, nullptr);
+
+        if (!browser.get())
+            return false;
 
-            browser_ = browser;
+        browser_ = browser;
 
-            return true;
-        }
+        return true;
 
-        return false;
 
     }
 
@@ -598,6 +626,39 @@ void WebClient::ExecuteJavaScript(const String& script)
     d_->browser_->GetMainFrame()->ExecuteJavaScript(CefString(script.CString()), "", 0);
 }
 
+void WebClient::EvalJavaScript(unsigned evalID, const String& script)
+{
+    if (!d_->browser_.get())
+        return;
+
+    // Create the message object.
+    CefRefPtr<CefProcessMessage> msg= CefProcessMessage::Create("atomic_eval_javascript");
+
+    // Retrieve the argument list object.
+    CefRefPtr<CefListValue> args = msg->GetArgumentList();
+
+    // Populate the argument values.
+    args->SetInt(0, (int) evalID);
+    args->SetString(1, CefString(script.CString()));
+
+    // Send the process message to the render process.
+    // Use PID_BROWSER instead when sending a message to the browser process.
+    d_->browser_->SendProcessMessage(PID_RENDERER, msg);
+}
+
+void WebClient::EvalJavaScriptResult(unsigned evalID, bool result, const String& value)
+{
+    using namespace WebViewJSEvalResult;
+
+    VariantMap eventData;
+    eventData[P_CLIENT] = this;
+    eventData[P_EVALID] = evalID;
+    eventData[P_RESULT] = result;
+    eventData[P_VALUE] = value;
+
+    SendEvent(E_WEBVIEWJSEVALRESULT, eventData);
+}
+
 void WebClient::AddMessageHandler(WebMessageHandler* handler, bool first)
 {
     SharedPtr<WebMessageHandler> _handler(handler);

+ 5 - 0
Source/AtomicWebView/WebClient.h

@@ -99,6 +99,9 @@ public:
     /// Execute some JavaScript in the browser
     void ExecuteJavaScript(const String& script);
 
+    /// Eval some JavaScript in the browser (async return value referenced by evalID)
+    void EvalJavaScript(unsigned evalID, const String& script);
+
     // Navigation
 
     /// Returns true if the page is currently loading
@@ -125,6 +128,8 @@ private:
 
     void WasResized();
 
+    void EvalJavaScriptResult(unsigned evalID, bool result, const String& value);
+
     SharedPtr<WebRenderHandler> renderHandler_;
 
     List<SharedPtr<WebMessageHandler>> messageHandlers_;

+ 73 - 4
Source/AtomicWebView/WebMessageHandler.cpp

@@ -92,25 +92,89 @@ public:
         eventData[P_CEFBROWSER] = (void*) browser;
         eventData[P_CEFFRAME] = (void*) frame;
 
+        // defaults to not being deferred
+        eventData[P_DEFERRED] = false;
+
         queryHandled_ = false;
         webMessageHandler_->SendEvent(E_WEBMESSAGE, eventData);
 
-        currentCallback_ = nullptr;
+        bool deferred = eventData[P_DEFERRED].GetBool();
 
-        if (!queryHandled_)
+        if (!deferred)
+        {
+            if (!queryHandled_)
+            {
+                currentCallback_ = nullptr;
+                LOGERROR("WebMessageHandlerPrivate::OnQuery - WebQuery was not handled");
+                return false;
+            }
+        }
+        else
         {
-            LOGERROR("WebMessageHandlerPrivate::OnQuery - WebQuery was not handled");
-            return false;
+            DeferredWebMessage* deferred = new DeferredWebMessage();
+            deferred->browser_ = browser;
+            deferred->frame_  = frame;
+            deferred->queryID_ = query_id;
+            deferred->request_ = request_;
+            deferred->persistent_ = persistent;
+            deferred->callback_ = currentCallback_;
+            deferredWebMessages_.Push(deferred);
         }
 
+        currentCallback_ = nullptr;
+
         return true;
     }
 
+    void HandleDeferredResponse(double queryID, bool success, const String& response)
+    {
+        List<DeferredWebMessage*>::Iterator itr = deferredWebMessages_.Begin();
+        while (itr != deferredWebMessages_.End())
+        {
+            if ((*itr)->queryID_ == queryID)
+                break;
+
+            itr++;
+        }
+
+        if (itr == deferredWebMessages_.End())
+        {
+            LOGERRORF("WebMessageHandlerPrivate::HandleDeferredResponse - unable to find queryid: %d", queryID);
+            return;
+        }
+
+        if (success)
+        {
+            (*itr)->callback_->Success(CefString(response.CString()));
+        }
+        else
+        {
+            (*itr)->callback_->Failure(0, CefString(response.CString()));
+        }
+
+        DeferredWebMessage* ptr = (*itr);
+        deferredWebMessages_.Erase(itr);
+        delete ptr;
+
+    }
+
 private:
 
     CefRefPtr<Callback> currentCallback_;
     bool queryHandled_;
 
+    struct DeferredWebMessage
+    {
+        CefRefPtr<CefBrowser> browser_;
+        CefRefPtr<CefFrame> frame_;
+        int64 queryID_;
+        String request_;
+        bool persistent_;
+        CefRefPtr<Callback> callback_;
+    };
+
+    List<DeferredWebMessage*> deferredWebMessages_;
+
     WeakPtr<WebMessageHandler> webMessageHandler_;
 };
 
@@ -125,6 +189,11 @@ WebMessageHandler::~WebMessageHandler()
     d_ = nullptr;
 }
 
+void WebMessageHandler::HandleDeferredResponse(double queryID, bool success, const String& response)
+{
+    d_->HandleDeferredResponse(queryID, success, response);
+}
+
 void WebMessageHandler::Success(const String& response)
 {
     d_->Success(response);

+ 3 - 1
Source/AtomicWebView/WebMessageHandler.h

@@ -42,6 +42,8 @@ public:
     /// Destruct.
     virtual ~WebMessageHandler();
 
+    void HandleDeferredResponse(double queryID, bool success, const String& response);
+
     /// Success callback
     void Success(const String& response = String::EMPTY);
     /// Failure callback
@@ -51,7 +53,7 @@ public:
     WebClient* GetWebClient() { return client_; }
 
     /// Set the WebClient associated with this WebMessageHandler
-    void SetWebClient(WebClient* client) { client_ = client; }
+    void SetWebClient(WebClient* client) { client_ = client; }       
 
     /// Get the CefMessageRouterBrowserSide::Handler* as a opaque void*
     void* GetCefHandler() { return (void*) d_; }

+ 12 - 0
Source/AtomicWebView/WebViewEvents.h

@@ -64,6 +64,16 @@ EVENT(E_WEBVIEWTITLECHANGE, WebViewTitleChange)
     PARAM(P_TITLE, Title);   // String
 }
 
+/// WebView title change
+EVENT(E_WEBVIEWJSEVALRESULT, WebViewJSEvalResult)
+{
+    PARAM(P_CLIENT, Client);   // WebClient*
+    PARAM(P_EVALID, EvalID);   // unsigned
+    PARAM(P_RESULT, Result);   // boolean (true: success, false: error)
+    PARAM(P_VALUE, Value);   // String (sucess: eval's value, error: exception message)
+}
+
+
 /// WebView title change
 EVENT(E_WEBMESSAGE, WebMessage)
 {
@@ -74,6 +84,8 @@ EVENT(E_WEBMESSAGE, WebMessage)
 
     PARAM(P_CEFBROWSER, Browser);       // CefBrowser*
     PARAM(P_CEFFRAME, Frame);           // CefFrame*
+
+    PARAM(P_DEFERRED, Deferred);        // Return Value: Bool
 }