Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make JS bindings work with subprocesses hosting multiple browser instances #1562

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 90 additions & 44 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ namespace CefSharp

void CefAppUnmanagedWrapper::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
{
if (_separatedPopupBoundObjectsEnable)
{
JavascriptRootObject^ rootObj;
_javascriptRootObjects->TryRemove(browser->GetIdentifier(), rootObj);
_javascriptAsyncRootObjects->TryRemove(browser->GetIdentifier(), rootObj);
}

CefBrowserWrapper^ wrapper;
if (_browserWrappers->TryRemove(browser->GetIdentifier(), wrapper))
{
Expand All @@ -60,41 +67,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 rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers;
auto frameId = frame->GetIdentifier();
auto contextCreatedMessage = CefProcessMessage::Create(kOnContextCreatedRequest);

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
{
if (!Object::ReferenceEquals(_javascriptRootObject, nullptr) || !Object::ReferenceEquals(_javascriptAsyncRootObject, nullptr))
{
rootObject->Bind(_javascriptRootObject, _javascriptAsyncRootObject, context->GetGlobal());
}
}
browser->SendProcessMessage(CefProcessId::PID_BROWSER, contextCreatedMessage);
};

void CefAppUnmanagedWrapper::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
Expand Down Expand Up @@ -179,7 +156,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 @@ -215,7 +193,7 @@ namespace CefSharp

return true;
}

//these messages are roughly handled the same way
if (name == kEvaluateJavascriptRequest || name == kJavascriptCallbackRequest)
{
Expand All @@ -242,7 +220,7 @@ namespace CefSharp

JavascriptRootObjectWrapper^ rootObjectWrapper;
browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);

//NOTE: In the rare case when when OnContextCreated hasn't been called we need to manually create the rootObjectWrapper
//It appears that OnContextCreated is only called for pages that have javascript on them, which makes sense
//as without javascript there is no need for a context.
Expand All @@ -261,14 +239,14 @@ namespace CefSharp
if (frame.get())
{
auto context = frame->GetV8Context();

if (context.get() && context->Enter())
{
try
{
CefRefPtr<CefV8Exception> exception;
success = context->Eval(script, result, exception);

//we need to do this here to be able to store the v8context
if (success)
{
Expand Down Expand Up @@ -317,14 +295,14 @@ namespace CefSharp
{
auto context = callbackWrapper->GetContext();
auto value = callbackWrapper->GetValue();

if (context.get() && context->Enter())
{
try
{
auto parameterList = argList->GetList(3);
CefV8ValueList params;

//Needs to be called within the context as for Dictionary (mapped to struct)
//a V8Object will be created
for (CefV8ValueList::size_type i = 0; i < parameterList->GetSize(); i++)
Expand All @@ -334,7 +312,7 @@ namespace CefSharp

result = value->ExecuteFunction(nullptr, params);
success = result.get() != nullptr;

//we need to do this here to be able to store the v8context
if (success)
{
Expand Down Expand Up @@ -386,15 +364,26 @@ namespace CefSharp
}
else if (name == kJavascriptRootObjectRequest)
{
_javascriptAsyncRootObject = DeserializeJsRootObject(argList, 0);
_javascriptRootObject = DeserializeJsRootObject(argList, 1);
if (_separatedPopupBoundObjectsEnable)
{
_javascriptAsyncRootObjects->default[browser->GetIdentifier()] = DeserializeJsRootObject(argList, 0);
_javascriptRootObjects->default[browser->GetIdentifier()] = DeserializeJsRootObject(argList, 1);
}
else
{
_javascriptAsyncRootObject = DeserializeJsRootObject(argList, 0);
_javascriptRootObject = DeserializeJsRootObject(argList, 1);
}

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

handled = true;
}
else if (name == kJavascriptAsyncMethodCallResponse)
{
auto frameId = GetInt64(argList, 0);
auto callbackId = GetInt64(argList, 1);

JavascriptRootObjectWrapper^ rootObjectWrapper;
browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frameId, rootObjectWrapper);

Expand All @@ -418,6 +407,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 @@ -454,4 +453,51 @@ 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())
{
auto javascriptRootObject = static_cast<JavascriptRootObject^>(_javascriptRootObject);
auto javascriptAsyncRootObject = static_cast<JavascriptRootObject^>(_javascriptAsyncRootObject);
if (_separatedPopupBoundObjectsEnable)
{
_javascriptRootObjects->TryGetValue(browser->GetIdentifier(), javascriptRootObject);
_javascriptAsyncRootObjects->TryGetValue(browser->GetIdentifier(), javascriptAsyncRootObject);
}
if (javascriptRootObject != nullptr ||
javascriptAsyncRootObject != nullptr)
{
rootObject->Bind(javascriptRootObject, javascriptAsyncRootObject, context->GetGlobal());
}
}
}
finally
{
context->Exit();
}
}
}
}
}
14 changes: 13 additions & 1 deletion CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,36 @@ namespace CefSharp
gcroot<List<CefExtension^>^> _extensions;
gcroot<List<CefCustomScheme^>^> _schemes;
bool _focusedNodeChangedEnabled;
bool _separatedPopupBoundObjectsEnable;

// The serialized registered object data waiting to be used (only contains methods and bound async).
gcroot<JavascriptRootObject^> _javascriptAsyncRootObject;

// The serialized registered object data waiting to be used.
gcroot<JavascriptRootObject^> _javascriptRootObject;

// The serialized registered object data waiting to be used (only contains methods and bound async) when
// separate bound objects are enabled.
gcroot<ConcurrentDictionary<int, JavascriptRootObject^>^> _javascriptAsyncRootObjects;

// The serialized registered object data waiting to be used when separate bound objects are enabled.
gcroot<ConcurrentDictionary<int, JavascriptRootObject^>^> _javascriptRootObjects;

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

CefAppUnmanagedWrapper(List<CefCustomScheme^>^ schemes, bool enableFocusedNodeChanged, Action<CefBrowserWrapper^>^ onBrowserCreated, Action<CefBrowserWrapper^>^ onBrowserDestoryed)
CefAppUnmanagedWrapper(List<CefCustomScheme^>^ schemes, bool enableFocusedNodeChanged, bool enableSeparatedPopupBoundObjects, Action<CefBrowserWrapper^>^ onBrowserCreated, Action<CefBrowserWrapper^>^ onBrowserDestoryed)
{
_onBrowserCreated = onBrowserCreated;
_onBrowserDestroyed = onBrowserDestoryed;
_browserWrappers = gcnew ConcurrentDictionary<int, CefBrowserWrapper^>();
_extensions = gcnew List<CefExtension^>();
_schemes = schemes;
_focusedNodeChangedEnabled = enableFocusedNodeChanged;
_separatedPopupBoundObjectsEnable = enableSeparatedPopupBoundObjects;
_javascriptRootObjects = gcnew ConcurrentDictionary<int, JavascriptRootObject^>();
_javascriptAsyncRootObjects = gcnew ConcurrentDictionary<int, JavascriptRootObject^>();
}

~CefAppUnmanagedWrapper()
Expand Down
3 changes: 2 additions & 1 deletion CefSharp.BrowserSubprocess.Core/SubProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ namespace CefSharp
auto onBrowserDestroyed = gcnew Action<CefBrowserWrapper^>(this, &SubProcess::OnBrowserDestroyed);
auto schemes = CefCustomScheme::ParseCommandLineArguments(args);
auto enableFocusedNodeChanged = CommandLineArgsParser::HasArgument(args, CefSharpArguments::FocusedNodeChangedEnabledArgument);
auto enableSeparatedBoundObjects = CommandLineArgsParser::HasArgument(args, CefSharpArguments::SeparateBoundObjectsArgument);

_cefApp = new CefAppUnmanagedWrapper(schemes, enableFocusedNodeChanged, onBrowserCreated, onBrowserDestroyed);
_cefApp = new CefAppUnmanagedWrapper(schemes, enableFocusedNodeChanged, enableSeparatedBoundObjects, onBrowserCreated, onBrowserDestroyed);
}

!SubProcess()
Expand Down
3 changes: 0 additions & 3 deletions CefSharp.Core/CefSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ namespace CefSharp
//Automatically discovered and load a system-wide installation of Pepper Flash.
_cefCommandLineArgs->Add("enable-system-flash", "1");

//Temp workaround for https://github.com/cefsharp/CefSharp/issues/1203
_cefCommandLineArgs->Add("process-per-tab", "1");

_focusedNodeChangedEnabled = false;
}

Expand Down
5 changes: 5 additions & 0 deletions CefSharp.Core/Internals/CefSharpApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ namespace CefSharp
commandLine->AppendArgument(StringUtils::ToNative(CefSharpArguments::WcfEnabledArgument));
}

if (CefSharpSettings::SeparateBoundObjects)
{
commandLine->AppendArgument(StringUtils::ToNative(CefSharpArguments::SeparateBoundObjectsArgument));
}

if (_cefSettings->_cefCustomSchemes->Count > 0)
{
String^ argument = "=";
Expand Down
Loading