Skip to content

Commit

Permalink
Modified binding so it's always triggered after the bound objects has…
Browse files Browse the repository at this point in the history
… been transmitted to the subprocess
  • Loading branch information
cefsharp-ms committed Aug 29, 2016
1 parent 9727753 commit 48b1258
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 60 deletions.
98 changes: 60 additions & 38 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,45 +71,11 @@ namespace CefSharp

void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
//Send a message to the browser processing signaling that OnContextCreated has been called
//only param is the FrameId. Currently an IPC message is only sent for the main frame - will see
//how viable this solution is and if it's worth expanding to sub/child frames.
if (frame->IsMain())
{
auto contextCreatedMessage = CefProcessMessage::Create(kOnContextCreatedRequest);

SetInt64(contextCreatedMessage->GetArgumentList(), 0, frame->GetIdentifier());

browser->SendProcessMessage(CefProcessId::PID_BROWSER, contextCreatedMessage);
}

auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier(), true);
auto contextCreatedMessage = CefProcessMessage::Create(kOnContextCreatedRequest);

auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers;
auto frameId = frame->GetIdentifier();

JavascriptRootObjectWrapper^ rootObject;
if (!rootObjectWrappers->TryGetValue(frameId, rootObject))
{
rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier(), browserWrapper->BrowserProcess);
rootObjectWrappers->TryAdd(frameId, rootObject);
}
SetInt64(contextCreatedMessage->GetArgumentList(), 0, frame->GetIdentifier());

if (rootObject->IsBound)
{
LOG(WARNING) << "A context has been created for the same browser / frame without context released called previously";
}
else
{
JavascriptRootObject^ syncRootObject;
JavascriptRootObject^ asyncRootObject;
_javascriptRootObjects->TryGetValue(browser->GetIdentifier(), syncRootObject);
_javascriptAsyncRootObjects->TryGetValue(browser->GetIdentifier(), asyncRootObject);
if (syncRootObject != nullptr || asyncRootObject != nullptr)
{
rootObject->Bind(syncRootObject, asyncRootObject, context->GetGlobal());
}
}
browser->SendProcessMessage(CefProcessId::PID_BROWSER, contextCreatedMessage);
};

void CefAppUnmanagedWrapper::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
Expand Down Expand Up @@ -194,7 +160,8 @@ namespace CefSharp
{
if (name == kJavascriptCallbackDestroyRequest ||
name == kJavascriptRootObjectRequest ||
name == kJavascriptAsyncMethodCallResponse)
name == kJavascriptAsyncMethodCallResponse ||
name == kStartJavascriptBindingRequest)
{
//If we can't find the browser wrapper then we'll just
//ignore this as it's likely already been disposed of
Expand Down Expand Up @@ -413,6 +380,8 @@ namespace CefSharp
_mainJavascriptRootObject = syncRoot;
}

browser->SendProcessMessage(CefProcessId::PID_BROWSER, CefProcessMessage::Create(kJavascriptRootObjectResponse));

handled = true;
}
else if (name == kJavascriptAsyncMethodCallResponse)
Expand Down Expand Up @@ -443,6 +412,16 @@ namespace CefSharp
}
handled = true;
}
else if (name == kStartJavascriptBindingRequest)
{
auto frameList = argList->GetList(0);

for (auto i = 0; i < frameList->GetSize(); i++)
{
auto frameId = GetInt64(frameList, i);
BindObjectForFrame(browser, browserWrapper, frameId);
}
}

return handled;
};
Expand Down Expand Up @@ -479,4 +458,47 @@ namespace CefSharp
registrar->AddCustomScheme(StringUtils::ToNative(scheme->SchemeName), scheme->IsStandard, scheme->IsLocal, scheme->IsDisplayIsolated);
}
}

void CefAppUnmanagedWrapper::BindObjectForFrame(const CefRefPtr<CefBrowser> &browser, CefBrowserWrapper^ wrapper, int64 frameId)
{
auto frame = browser->GetFrame(frameId);
auto rootObjectWrappers = wrapper->JavascriptRootObjectWrappers;

if (frame.get())
{
JavascriptRootObjectWrapper^ rootObject;
if (!rootObjectWrappers->TryGetValue(frameId, rootObject))
{
rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier(), wrapper->BrowserProcess);
rootObjectWrappers->TryAdd(frameId, rootObject);
}

if (rootObject->IsBound)
{
LOG(WARNING) << "A context has been created for the same browser / frame without context released called previously";
}
else
{
auto context = frame->GetV8Context();
try
{
if (context.get() && context->IsValid() && context->Enter())
{
JavascriptRootObject^ syncRootObject;
JavascriptRootObject^ asyncRootObject;
_javascriptRootObjects->TryGetValue(browser->GetIdentifier(), syncRootObject);
_javascriptAsyncRootObjects->TryGetValue(browser->GetIdentifier(), asyncRootObject);
if (syncRootObject != nullptr || asyncRootObject != nullptr)
{
rootObject->Bind(syncRootObject, asyncRootObject, context->GetGlobal());
}
}
}
finally
{
context->Exit();
}
}
}
}
}
2 changes: 2 additions & 0 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ namespace CefSharp
// Store the js binding information for the non-popup browser in this subprocess
gcroot<JavascriptRootObject^> _mainJavascriptRootObject;
gcroot<JavascriptRootObject^> _mainAsyncJavascriptRootObject;

void BindObjectForFrame(const CefRefPtr<CefBrowser> &browser, CefSharp::CefBrowserWrapper^ wrapper, int64 frameId);
public:
static const CefString kPromiseCreatorFunction;

Expand Down
99 changes: 77 additions & 22 deletions CefSharp.Core/Internals/ClientAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,21 +536,6 @@ namespace CefSharp

void ClientAdapter::OnRenderViewReady(CefRefPtr<CefBrowser> browser)
{
if (!Object::ReferenceEquals(_browserAdapter, nullptr) && !_browserAdapter->IsDisposed && !browser->IsPopup())
{
auto objectRepository = _browserAdapter->JavascriptObjectRepository;

if (objectRepository->HasBoundObjects)
{
//transmit async bound objects
auto jsRootObjectMessage = CefProcessMessage::Create(kJavascriptRootObjectRequest);
auto argList = jsRootObjectMessage->GetArgumentList();
SerializeJsObject(objectRepository->AsyncRootObject, argList, 0);
SerializeJsObject(objectRepository->RootObject, argList, 1);
browser->SendProcessMessage(CefProcessId::PID_RENDERER, jsRootObjectMessage);
}
}

auto handler = _browserControl->RequestHandler;

if (handler != nullptr)
Expand Down Expand Up @@ -696,19 +681,57 @@ namespace CefSharp

cef_return_value_t ClientAdapter::OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, CefRefPtr<CefRequestCallback> callback)
{
auto handler = _browserControl->RequestHandler;

if (handler == nullptr)
if (!_boundObjectTransmitHelper->Completed)
{
return cef_return_value_t::RV_CONTINUE;
}
auto canBind = false;

if (!Object::ReferenceEquals(_browserAdapter, nullptr) && !_browserAdapter->IsDisposed)
{
auto objectRepository = _browserAdapter->JavascriptObjectRepository;

if (objectRepository->HasBoundObjects)
{
canBind = true;
}
}

if (canBind)
{
auto objectRepository = _browserAdapter->JavascriptObjectRepository;
//transmit async bound objects
auto jsRootObjectMessage = CefProcessMessage::Create(kJavascriptRootObjectRequest);
auto argList = jsRootObjectMessage->GetArgumentList();
SerializeJsObject(objectRepository->AsyncRootObject, argList, 0);
SerializeJsObject(objectRepository->RootObject, argList, 1);
browser->SendProcessMessage(CefProcessId::PID_RENDERER, jsRootObjectMessage);
}
else
{
_boundObjectTransmitHelper->CompleteSync();
}
}

auto handler = _browserControl->RequestHandler;

auto frameWrapper = gcnew CefFrameWrapper(frame);
auto browserWrapper = GetBrowserWrapper(browser->GetIdentifier(), browser->IsPopup());
auto requestWrapper = gcnew CefRequestWrapper(request);
auto requestCallback = gcnew CefRequestCallbackWrapper(callback, frameWrapper, requestWrapper);

return (cef_return_value_t)handler->OnBeforeResourceLoad(_browserControl, browserWrapper, frameWrapper, requestWrapper, requestCallback);
if (!_boundObjectTransmitHelper->Completed)
{
auto val = CefReturnValue::Continue;
if (handler != nullptr)
{
val = handler->OnBeforeResourceLoad(_browserControl, browserWrapper, frameWrapper, requestWrapper, _boundObjectTransmitHelper->PassableCallback);
}
return (cef_return_value_t)_boundObjectTransmitHelper->DoYield(requestCallback, val);
}
else if(handler != nullptr)
{
return (cef_return_value_t)handler->OnBeforeResourceLoad(_browserControl, browserWrapper, frameWrapper, requestWrapper, requestCallback);
}
return cef_return_value_t::RV_CONTINUE;
}

bool ClientAdapter::GetAuthCredentials(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, bool isProxy,
Expand Down Expand Up @@ -1033,12 +1056,20 @@ namespace CefSharp

if (name == kOnContextCreatedRequest)
{
auto frameId = GetInt64(argList, 0);
if (_boundObjectTransmitHelper->Completed)
{
//start binding for the corresponding frame
vector<int64> frameIds{ frameId };
SendJavascriptBindMessage(browser, frameIds);
}

auto handler = _browserControl->RenderProcessMessageHandler;

if (handler != nullptr)
{
auto browserWrapper = GetBrowserWrapper(browser->GetIdentifier(), browser->IsPopup());
CefFrameWrapper frameWrapper(browser->GetFrame(GetInt64(argList, 0)));
CefFrameWrapper frameWrapper(browser->GetFrame(frameId));

handler->OnContextCreated(_browserControl, browserWrapper, %frameWrapper);
}
Expand Down Expand Up @@ -1122,6 +1153,17 @@ namespace CefSharp

handled = true;
}
else if (name == kJavascriptRootObjectResponse && !_boundObjectTransmitHelper->Completed)
{
vector<int64> frameIdentifiers;
browser->GetFrameIdentifiers(frameIdentifiers);
//let's try to initiate bindings for all frames
SendJavascriptBindMessage(browser, frameIdentifiers);

_boundObjectTransmitHelper->CompleteAsync();

handled = true;
}

return handled;
}
Expand Down Expand Up @@ -1187,5 +1229,18 @@ namespace CefSharp
}
}
}

void ClientAdapter::SendJavascriptBindMessage(const CefRefPtr<CefBrowser> &browser, const vector<int64> &frameIdentifiers)
{
auto message = CefProcessMessage::Create(kStartJavascriptBindingRequest);
auto args = message->GetArgumentList();
auto frameIdList = CefListValue::Create();
for (auto i = 0; i < frameIdentifiers.size(); i++)
{
SetInt64(frameIdList, i, frameIdentifiers.at(i));
}
args->SetList(0, frameIdList);
browser->SendProcessMessage(CefProcessId::PID_RENDERER, message);
}
}
}
3 changes: 3 additions & 0 deletions CefSharp.Core/Internals/ClientAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace CefSharp
HWND _browserHwnd;
CefRefPtr<CefBrowser> _cefBrowser;

gcroot<BoundObjectTransmitHelper^> _boundObjectTransmitHelper;
gcroot<IBrowser^> _browser;
gcroot<Dictionary<int, IBrowser^>^> _popupBrowsers;
gcroot<String^> _tooltip;
Expand All @@ -52,11 +53,13 @@ namespace CefSharp

IBrowser^ GetBrowserWrapper(int browserId, bool isPopup);

void SendJavascriptBindMessage(const CefRefPtr<CefBrowser> &browser, const std::vector<int64> &frameIdentifiers);
public:
ClientAdapter(IWebBrowserInternal^ browserControl, IBrowserAdapter^ browserAdapter) :
_browserControl(browserControl),
_popupBrowsers(gcnew Dictionary<int, IBrowser^>()),
_pendingTaskRepository(gcnew PendingTaskRepository<JavascriptResponse^>()),
_boundObjectTransmitHelper(gcnew BoundObjectTransmitHelper()),
_browserAdapter(browserAdapter),
_browserHwnd(NULL)
{
Expand Down
4 changes: 4 additions & 0 deletions CefSharp.Core/Internals/Messaging/Messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ namespace CefSharp
const CefString kJavascriptCallbackResponse = "JavascriptCallbackDoneResponse";
//Message containing a js root object for js bindings
const CefString kJavascriptRootObjectRequest = "JavascriptRootObjectRequest";
//Message to ack js root object for js bindings
const CefString kJavascriptRootObjectResponse = "JavascriptRootObjectResponse";
//Message from the render process to request a method invocation on a bound object
const CefString kJavascriptAsyncMethodCallRequest = "JavascriptAsyncMethodCallRequest";
//Message from the browser process containing the result of a bound method invocation
const CefString kJavascriptAsyncMethodCallResponse = "JavascriptAsyncMethodCallResponse";
//Message that signals a new V8Context has been created
const CefString kOnContextCreatedRequest = "OnContextCreated";
//Message that initiates javacript bindings in the subprocess
const CefString kStartJavascriptBindingRequest = "StartJavascriptBindingRequest";
// Message from the render process that an element (or nothing) has
// gotten focus. This message is only sent if specified as an
// optional message via command line argument when the subprocess is
Expand Down
1 change: 1 addition & 0 deletions CefSharp/CefSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
<Compile Include="IFindHandler.cs" />
<Compile Include="INavigationEntryVisitor.cs" />
<Compile Include="Internals\CefTimeUtils.cs" />
<Compile Include="Internals\BoundObjectTransmitHelper.cs" />
<Compile Include="Internals\CommandLineArgsParser.cs" />
<Compile Include="Internals\MethodParameter.cs" />
<Compile Include="IPluginHandler.cs" />
Expand Down
Loading

0 comments on commit 48b1258

Please sign in to comment.