From 8f7692ae6e5c75e26accf49d57f5d8460c81b271 Mon Sep 17 00:00:00 2001 From: amaitland Date: Thu, 26 Apr 2018 10:20:39 +1000 Subject: [PATCH] JSB - Add JavascriptBindingCompleteEventArgs and config options to CefSharp.BindObjectAsync CefSharp.BindObjectAsync can now take an optional config object as first param Include additional information when binding complete Add option for cefSharp.bindObjectAsync (starting with lowercase latters) Add new syntax options - not entirely complete, no variant for the options object Add ignore cache option --- .../CefAppUnmanagedWrapper.cpp | 41 +++-- .../RegisterBoundObjectHandler.h | 147 +++++++++++++++--- CefSharp.Core/Internals/ClientAdapter.cpp | 15 +- CefSharp.Example/Resources/BindingTest.html | 23 ++- CefSharp/CefSharp.csproj | 1 + .../JavascriptBindingCompleteEventArgs.cs | 43 +++++ CefSharp/Event/JavascriptBindingEventArgs.cs | 10 +- CefSharp/IJavascriptObjectRepository.cs | 2 +- .../Internals/JavascriptObjectRepository.cs | 30 ++-- 9 files changed, 239 insertions(+), 73 deletions(-) create mode 100644 CefSharp/Event/JavascriptBindingCompleteEventArgs.cs diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp index 6878bab5e5..9047fdb3bb 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp @@ -86,25 +86,31 @@ namespace CefSharp } auto global = context->GetGlobal(); + auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier()); auto cefSharpObj = CefV8Value::CreateObject(NULL, NULL); global->SetValue("CefSharp", cefSharpObj, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier()); + //We'll support both CefSharp and cefSharp, for those who prefer the JS style + auto cefSharpObjCamelCase = CefV8Value::CreateObject(NULL, NULL); + global->SetValue("cefSharp", cefSharpObjCamelCase, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); //TODO: JSB: Split functions into their own classes //Browser wrapper is only used for BindObjectAsync - auto bindObjAsyncFunction = CefV8Value::CreateFunction("BindObjectAsync", new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, browserWrapper)); - cefSharpObj->SetValue("BindObjectAsync", bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - - auto unBindObFunction = CefV8Value::CreateFunction("DeleteBoundObject", new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, nullptr)); - cefSharpObj->SetValue("DeleteBoundObject", unBindObFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - - auto removeObjectFromCacheFunction = CefV8Value::CreateFunction("RemoveObjectFromCache", new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, nullptr)); - cefSharpObj->SetValue("RemoveObjectFromCache", removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - - auto isObjectCachedFunction = CefV8Value::CreateFunction("IsObjectCached", new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, nullptr)); - cefSharpObj->SetValue("IsObjectCached", isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, browserWrapper)); + auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, nullptr)); + auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, nullptr)); + auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(_registerBoundObjectRegistry, _javascriptObjects, nullptr)); + + cefSharpObj->SetValue(kBindObjectAsync, bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kDeleteBoundObject, unBindObjFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kRemoveObjectFromCache, removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kIsObjectCached, isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + + cefSharpObjCamelCase->SetValue(kBindObjectAsyncCamelCase, bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kDeleteBoundObjectCamelCase, unBindObjFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kRemoveObjectFromCacheCamelCase, removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kIsObjectCachedCamelCase, isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); }; void CefAppUnmanagedWrapper::OnContextReleased(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) @@ -555,15 +561,20 @@ namespace CefSharp auto msg = CefProcessMessage::Create(kJavascriptObjectsBoundInJavascript); auto args = msg->GetArgumentList(); - auto names = CefListValue::Create(); + auto boundObjects = CefListValue::Create(); for (auto i = 0; i < javascriptObjects->Count; i++) { + auto dict = CefDictionaryValue::Create(); auto name = javascriptObjects[i]->JavascriptName; - names->SetString(i, StringUtils::ToNative(name)); + dict->SetString("Name", StringUtils::ToNative(name)); + dict->SetBool("IsCached", false); + dict->SetBool("AlreadyBound", false); + + boundObjects->SetDictionary(i, dict); } - args->SetList(0, names); + args->SetList(0, boundObjects); browser->SendProcessMessage(CefProcessId::PID_BROWSER, msg); } diff --git a/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectHandler.h b/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectHandler.h index 232ef881e0..417299f96d 100644 --- a/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectHandler.h +++ b/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectHandler.h @@ -16,6 +16,15 @@ using namespace CefSharp::Internals::Serialization; namespace CefSharp { + const CefString kIsObjectCached = CefString("IsObjectCached"); + const CefString kIsObjectCachedCamelCase = CefString("isObjectCached"); + const CefString kRemoveObjectFromCache = CefString("RemoveObjectFromCache"); + const CefString kRemoveObjectFromCacheCamelCase = CefString("removeObjectFromCache"); + const CefString kDeleteBoundObject = CefString("DeleteBoundObject"); + const CefString kDeleteBoundObjectCamelCase = CefString("deleteBoundObject"); + const CefString kBindObjectAsync = CefString("BindObjectAsync"); + const CefString kBindObjectAsyncCamelCase = CefString("bindObjectAsync"); + private class RegisterBoundObjectHandler : public CefV8Handler { private: @@ -44,7 +53,7 @@ namespace CefSharp { auto global = context->GetGlobal(); - if (name == "IsObjectCached") + if (name == kIsObjectCached || name == kIsObjectCachedCamelCase) { if (arguments.size() == 0 || arguments.size() > 1) { @@ -60,7 +69,7 @@ namespace CefSharp //Check to see if the object name is within the cache retval = CefV8Value::CreateBool(_javascriptObjects->ContainsKey(managedObjectName)); } - else if (name == "RemoveObjectFromCache") + else if (name == kRemoveObjectFromCache || name == kRemoveObjectFromCacheCamelCase) { if (arguments.size() == 0 || arguments.size() > 1) { @@ -84,8 +93,7 @@ namespace CefSharp } } //TODO: Better name for this function - //TODO: Make this a const - else if (name == "DeleteBoundObject") + else if (name == kDeleteBoundObject || name == kDeleteBoundObject) { if (arguments.size() == 0 || arguments.size() > 1) { @@ -103,7 +111,7 @@ namespace CefSharp retval = CefV8Value::CreateBool(success); } - else if (name == "BindObjectAsync") + else if (name == kBindObjectAsync || name == kBindObjectAsyncCamelCase) { auto promiseCreator = global->GetValue(CefAppUnmanagedWrapper::kPromiseCreatorFunction); @@ -124,29 +132,76 @@ namespace CefSharp auto params = CefListValue::Create(); auto boundObjectRequired = false; + auto notifyIfAlreadyBound = false; + auto ignoreCache = false; auto cachedObjects = gcnew List(); + //TODO: Create object to represent this information + auto objectNamesWithBoundStatus = gcnew List^>(); + auto objectCount = 0; if (arguments.size() > 0) { - for (auto i = 0; i < arguments.size(); i++) + objectCount = (int)arguments.size(); + + //If first argument is an object, we'll see if it contains config values + if (arguments[0]->IsObject()) { - auto objectName = arguments[i]->GetStringValue(); + //TODO: Add camelcase variation + if (arguments[0]->HasValue("NotifyIfAlreadyBound")) + { + auto notify = arguments[0]->GetValue("NotifyIfAlreadyBound"); + if (notify->IsBool()) + { + notifyIfAlreadyBound = notify->GetBoolValue(); + } + } - //Check if the object has already been bound - if (!global->HasValue(objectName)) + if (arguments[0]->HasValue("IgnoreCache")) { - //If no matching object found then we'll add the object name to the list - boundObjectRequired = true; - params->SetString(i, objectName); + auto ignore = arguments[0]->GetValue("IgnoreCache"); + if (ignore->IsBool()) + { + ignoreCache = ignore->GetBoolValue(); + } + } + //If we have a config object then we remove that from the count + objectCount = objectCount - 1; + } + + //Loop through all arguments and ignore anything that's not a string + for (auto i = 0; i < arguments.size(); i++) + { + //Validate arg as being a string + if(arguments[i]->IsString()) + { + auto objectName = arguments[i]->GetStringValue(); auto managedObjectName = StringUtils::ToClr(objectName); + auto alreadyBound = global->HasValue(objectName); + auto cached = false; - JavascriptObject^ obj; - if (_javascriptObjects->TryGetValue(managedObjectName, obj)) + //Check if the object has already been bound + if (alreadyBound) { - cachedObjects->Add(obj); + cached = _javascriptObjects->ContainsKey(managedObjectName); } - } + else + { + //If no matching object found then we'll add the object name to the list + boundObjectRequired = true; + params->SetString(i, objectName); + + JavascriptObject^ obj; + if (_javascriptObjects->TryGetValue(managedObjectName, obj)) + { + cachedObjects->Add(obj); + + cached = true; + } + } + + objectNamesWithBoundStatus->Add(Tuple::Create(managedObjectName, alreadyBound, cached)); + } } } else @@ -155,11 +210,12 @@ namespace CefSharp boundObjectRequired = true; } - if (boundObjectRequired) + if (boundObjectRequired || ignoreCache) { //If the number of cached objects matches the number of args + //(we have a cached copy of all requested objects) //then we'll immediately bind the cached objects - if (cachedObjects->Count == (int)arguments.size()) + if (cachedObjects->Count == objectCount && ignoreCache == false) { auto frame = context->GetFrame(); if (frame.get()) @@ -181,6 +237,7 @@ namespace CefSharp rootObjectWrappers->TryAdd(frame->GetIdentifier(), rootObject); } + //Cached objects only contains a list of objects not already bound rootObject->Bind(cachedObjects, context->GetGlobal()); //Response object has no Accessor or Interceptor @@ -191,21 +248,30 @@ namespace CefSharp response->SetValue("Message", CefV8Value::CreateString("OK"), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); callback->Success(response); + //TODO: This is duplicated //Send message notifying Browser Process of which objects were bound //We do this after the objects have been created in the V8Context to gurantee //they are accessible. auto msg = CefProcessMessage::Create(kJavascriptObjectsBoundInJavascript); auto args = msg->GetArgumentList(); - auto names = CefListValue::Create(); + auto boundObjects = CefListValue::Create(); - for (auto i = 0; i < cachedObjects->Count; i++) + for (auto i = 0; i < objectNamesWithBoundStatus->Count; i++) { - auto name = cachedObjects[i]->JavascriptName; - names->SetString(i, StringUtils::ToNative(name)); + auto dict = CefDictionaryValue::Create(); + + auto name = objectNamesWithBoundStatus[i]->Item1; + auto alreadyBound = objectNamesWithBoundStatus[i]->Item2; + auto isCached = objectNamesWithBoundStatus[i]->Item3; + dict->SetString("Name", StringUtils::ToNative(name)); + dict->SetBool("IsCached", isCached); + dict->SetBool("AlreadyBound", alreadyBound); + + boundObjects->SetDictionary(i, dict); } - args->SetList(0, names); + args->SetList(0, boundObjects); browser->SendProcessMessage(CefProcessId::PID_BROWSER, msg); } @@ -214,7 +280,7 @@ namespace CefSharp } else { - //Obtain a callbackId then send off the Request + //Obtain a callbackId then send off the Request for objects auto callbackId = _callbackRegistry->SaveMethodCallback(callback); argList->SetInt(0, browser->GetIdentifier()); @@ -227,10 +293,11 @@ namespace CefSharp } else { + //Objects already bound or ignore cache + //Response object has no Accessor or Interceptor auto response = CefV8Value::CreateObject(NULL, NULL); - //Objects already bound so we immediately resolve the Promise response->SetValue("Success", CefV8Value::CreateBool(false), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); response->SetValue("Count", CefV8Value::CreateInt(0), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); @@ -241,6 +308,36 @@ namespace CefSharp //If all the requested objects are bound then we immediately execute resolve //with Success true and Count of 0 resolve->ExecuteFunctionWithContext(context, nullptr, returnArgs); + + if (notifyIfAlreadyBound) + { + //TODO: This is duplicated + //Send message notifying Browser Process of which objects were bound + //We do this after the objects have been created in the V8Context to gurantee + //they are accessible. + auto msg = CefProcessMessage::Create(kJavascriptObjectsBoundInJavascript); + auto args = msg->GetArgumentList(); + + auto boundObjects = CefListValue::Create(); + + for (auto i = 0; i < objectNamesWithBoundStatus->Count; i++) + { + auto dict = CefDictionaryValue::Create(); + + auto name = objectNamesWithBoundStatus[i]->Item1; + auto alreadyBound = objectNamesWithBoundStatus[i]->Item2; + auto isCached = objectNamesWithBoundStatus[i]->Item3; + dict->SetString("Name", StringUtils::ToNative(name)); + dict->SetBool("IsCached", isCached); + dict->SetBool("AlreadyBound", alreadyBound); + + boundObjects->SetDictionary(i, dict); + } + + args->SetList(0, boundObjects); + + browser->SendProcessMessage(CefProcessId::PID_BROWSER, msg); + } } } } diff --git a/CefSharp.Core/Internals/ClientAdapter.cpp b/CefSharp.Core/Internals/ClientAdapter.cpp index a675e32839..3df0ae63b1 100644 --- a/CefSharp.Core/Internals/ClientAdapter.cpp +++ b/CefSharp.Core/Internals/ClientAdapter.cpp @@ -1217,14 +1217,19 @@ namespace CefSharp { auto objectRepository = _browserAdapter->JavascriptObjectRepository; - auto objectNames = argList->GetList(0); - auto names = gcnew List(objectNames->GetSize()); - for (auto i = 0; i < objectNames->GetSize(); i++) + auto boundObjects = argList->GetList(0); + auto objs = gcnew List^>(boundObjects->GetSize()); + for (auto i = 0; i < boundObjects->GetSize(); i++) { - names->Add(StringUtils::ToClr(objectNames->GetString(i))); + auto obj = boundObjects->GetDictionary(i); + auto name = obj->GetString("Name"); + auto alreadyBound = obj->GetBool("AlreadyBound"); + auto isCached = obj->GetBool("IsCached"); + + objs->Add(Tuple::Create(StringUtils::ToClr(name), alreadyBound, isCached)); } - objectRepository->ObjectsBound(names); + objectRepository->ObjectsBound(objs); } handled = true; diff --git a/CefSharp.Example/Resources/BindingTest.html b/CefSharp.Example/Resources/BindingTest.html index cbe4e4abad..761fd7418f 100644 --- a/CefSharp.Example/Resources/BindingTest.html +++ b/CefSharp.Example/Resources/BindingTest.html @@ -449,7 +449,8 @@ { var asyncCallback = assert.async(); - CefSharp.BindObjectAsync("boundAsync2").then(function(result) + //Can use both cefSharp.bindObjectAsync and CefSharp.BindObjectAsync, both do the same + cefSharp.bindObjectAsync({ NotifyIfAlreadyBound: true }, "boundAsync2").then(function (result) { boundAsync2.hello('CefSharp').then(function (res) { @@ -465,11 +466,27 @@ }); //Repeat of the previous test to make sure binding a deleted object is working - QUnit.test( "Bind boundAsync2 and call (Hello) Second Attempt:", function( assert ) + QUnit.test("Bind boundAsync2 and call (Hello) Second Attempt:", function (assert) { var asyncCallback = assert.async(); - CefSharp.BindObjectAsync("boundAsync2").then(function(result) + CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, "boundAsync2").then(function (result) + { + boundAsync2.hello('CefSharp').then(function (res) + { + assert.equal(res, "Hello CefSharp") + + asyncCallback(); + }); + }); + }); + + //Repeat of the previous test to make sure binding a deleted object is working + QUnit.test( "Bind boundAsync2 and call (Hello) Third Attempt:", function( assert ) + { + var asyncCallback = assert.async(); + + CefSharp.BindObjectAsync({ NotifyIfAlreadyBound : true, IgnoreCache : true }, "boundAsync2").then(function(result) { boundAsync2.hello('CefSharp').then(function (res) { diff --git a/CefSharp/CefSharp.csproj b/CefSharp/CefSharp.csproj index 7356849346..3ae79a8608 100644 --- a/CefSharp/CefSharp.csproj +++ b/CefSharp/CefSharp.csproj @@ -97,6 +97,7 @@ + diff --git a/CefSharp/Event/JavascriptBindingCompleteEventArgs.cs b/CefSharp/Event/JavascriptBindingCompleteEventArgs.cs new file mode 100644 index 0000000000..90cf125854 --- /dev/null +++ b/CefSharp/Event/JavascriptBindingCompleteEventArgs.cs @@ -0,0 +1,43 @@ +// Copyright © 2010-2017 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; + +namespace CefSharp.Event +{ + /// + /// Event arguments for the event + /// + public class JavascriptBindingCompleteEventArgs : EventArgs + { + /// + /// The javascript object repository, used to register objects + /// + public IJavascriptObjectRepository ObjectRepository { get; private set; } + + /// + /// Name of the object + /// + public string ObjectName { get; private set; } + + /// + /// Was the object already bound. The default is false for the first js call to + /// CefSharp.BindObjectAsync, and subsiquently true if already bound in a given context. + /// + public bool AlreadyBound { get; private set; } + + /// + /// Is the object cached + /// + public bool IsCached { get; private set; } + + public JavascriptBindingCompleteEventArgs(IJavascriptObjectRepository objectRepository, string name, bool alreadyBound, bool isCached) + { + ObjectRepository = objectRepository; + ObjectName = name; + AlreadyBound = alreadyBound; + IsCached = isCached; + } + } +} diff --git a/CefSharp/Event/JavascriptBindingEventArgs.cs b/CefSharp/Event/JavascriptBindingEventArgs.cs index 5c97b4da66..c837d69e2d 100644 --- a/CefSharp/Event/JavascriptBindingEventArgs.cs +++ b/CefSharp/Event/JavascriptBindingEventArgs.cs @@ -2,15 +2,12 @@ // // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. -using CefSharp; using System; -using System.Collections.Generic; namespace CefSharp.Event { /// - /// Event arguments for the and - /// events + /// Event arguments for the event /// public class JavascriptBindingEventArgs : EventArgs { @@ -24,6 +21,11 @@ public class JavascriptBindingEventArgs : EventArgs /// public string ObjectName { get; private set; } + /// + /// Constructor + /// + /// object repository + /// object name public JavascriptBindingEventArgs(IJavascriptObjectRepository objectRepository, string name) { ObjectRepository = objectRepository; diff --git a/CefSharp/IJavascriptObjectRepository.cs b/CefSharp/IJavascriptObjectRepository.cs index f6ce49c510..0ad8febea0 100644 --- a/CefSharp/IJavascriptObjectRepository.cs +++ b/CefSharp/IJavascriptObjectRepository.cs @@ -53,6 +53,6 @@ public interface IJavascriptObjectRepository : IDisposable /// /// Event handler is triggered when a object has been successfully bound on javascript /// - event EventHandler ObjectBoundInJavascript; + event EventHandler ObjectBoundInJavascript; } } diff --git a/CefSharp/Internals/JavascriptObjectRepository.cs b/CefSharp/Internals/JavascriptObjectRepository.cs index a8b34c9db8..4e683f345d 100644 --- a/CefSharp/Internals/JavascriptObjectRepository.cs +++ b/CefSharp/Internals/JavascriptObjectRepository.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Reflection; using System.Threading; -using CefSharp.ModelBinding; using CefSharp.Event; using System.Threading.Tasks; @@ -37,7 +36,7 @@ public class JavascriptObjectRepository : IJavascriptObjectRepository private static long lastId; public event EventHandler ResolveObject; - public event EventHandler ObjectBoundInJavascript; + public event EventHandler ObjectBoundInJavascript; /// /// A hash from assigned object ids to the objects, @@ -67,6 +66,8 @@ public bool IsBound(string name) return objects.Values.Any(x => x.Name == name); } + //Ideally this would internal, unfurtunately it's used in C++ + //and it's hard to expose internals public List GetObjects(List names = null) { //If there are no objects names or the count is 0 then we will raise @@ -96,23 +97,17 @@ public List GetObjects(List names = null) } - public void ObjectsBound(List names) + public void ObjectsBound(List> objs) { - //TODO: JSB Should this be a single event invocation or - // one per object that was bound??? (Currently one per object) - //Execute on Threadpool so we don't unnessicarily block the CEF IO thread var handler = ObjectBoundInJavascript; if(handler != null) { Task.Run(() => { - foreach (var name in names) + foreach (var obj in objs) { - if (handler != null) - { - handler(this, new JavascriptBindingEventArgs(this, name)); - } + handler?.Invoke(this, new JavascriptBindingCompleteEventArgs(this, obj.Item1, obj.Item2, obj.Item3)); } }); } @@ -161,7 +156,7 @@ public void Register(string name, object value, bool isAsync, BindingOptions opt AnalyseObjectForBinding(jsObject, analyseMethods: true, analyseProperties: !isAsync, readPropertyValue: false, camelCaseJavascriptNames: camelCaseJavascriptNames); } - public bool TryCallMethod(long objectId, string name, object[] parameters, out object result, out string exception) + internal bool TryCallMethod(long objectId, string name, object[] parameters, out object result, out string exception) { exception = ""; result = null; @@ -297,7 +292,7 @@ public bool TryCallMethod(long objectId, string name, object[] parameters, out o return false; } - public bool TryGetProperty(long objectId, string name, out object result, out string exception) + internal bool TryGetProperty(long objectId, string name, out object result, out string exception) { exception = ""; result = null; @@ -327,7 +322,7 @@ public bool TryGetProperty(long objectId, string name, out object result, out st return false; } - public bool TrySetProperty(long objectId, string name, object value, out string exception) + internal bool TrySetProperty(long objectId, string name, object value, out string exception) { exception = ""; JavascriptObject obj; @@ -429,12 +424,7 @@ private void AnalyseObjectForBinding(JavascriptObject obj, bool analyseMethods, private void RaiseResolveObjectEvent(string name) { - var handler = ResolveObject; - - if(handler != null) - { - handler(this, new JavascriptBindingEventArgs(this, name)); - } + ResolveObject?.Invoke(this, new JavascriptBindingEventArgs(this, name)); } private static JavascriptMethod CreateJavaScriptMethod(MethodInfo methodInfo, bool camelCaseJavascriptNames)