RegisterBoundObjectHandler.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright © 2010-2017 The CefSharp Authors. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
  4. #pragma once
  5. #include "include/cef_v8.h"
  6. #include "RegisterBoundObjectRegistry.h"
  7. #include "..\CefSharp.Core\Internals\Messaging\Messages.h"
  8. #include "..\CefSharp.Core\Internals\Serialization\Primitives.h"
  9. using namespace System;
  10. using namespace CefSharp::Internals::Messaging;
  11. using namespace CefSharp::Internals::Serialization;
  12. namespace CefSharp
  13. {
  14. private class RegisterBoundObjectHandler : public CefV8Handler
  15. {
  16. private:
  17. gcroot<RegisterBoundObjectRegistry^> _callbackRegistry;
  18. gcroot<Dictionary<String^, JavascriptObject^>^> _javascriptObjects;
  19. gcroot<CefBrowserWrapper^> _browserWrapper;
  20. public:
  21. RegisterBoundObjectHandler(RegisterBoundObjectRegistry^ callbackRegistery, Dictionary<String^, JavascriptObject^>^ javascriptObjects, CefBrowserWrapper^ browserWrapper)
  22. {
  23. _callbackRegistry = callbackRegistery;
  24. _javascriptObjects = javascriptObjects;
  25. _browserWrapper = browserWrapper;
  26. }
  27. bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE
  28. {
  29. auto context = CefV8Context::GetCurrentContext();
  30. if (context.get())
  31. {
  32. auto browser = context->GetBrowser();
  33. if (context.get() && context->Enter())
  34. {
  35. try
  36. {
  37. auto global = context->GetGlobal();
  38. if (name == "IsObjectCached")
  39. {
  40. if (arguments.size() == 0 || arguments.size() > 1)
  41. {
  42. //TODO: Improve error message
  43. exception = "Must specify the name of a single bound object to check the cache for";
  44. return true;
  45. }
  46. auto objectName = arguments[0]->GetStringValue();
  47. auto managedObjectName = StringUtils::ToClr(objectName);
  48. //Check to see if the object name is within the cache
  49. retval = CefV8Value::CreateBool(_javascriptObjects->ContainsKey(managedObjectName));
  50. }
  51. else if (name == "RemoveObjectFromCache")
  52. {
  53. if (arguments.size() == 0 || arguments.size() > 1)
  54. {
  55. //TODO: Improve error message
  56. exception = "Must specify the name of a single bound object to remove from cache";
  57. return true;
  58. }
  59. auto objectName = arguments[0]->GetStringValue();
  60. auto managedObjectName = StringUtils::ToClr(objectName);
  61. if (_javascriptObjects->ContainsKey(managedObjectName))
  62. {
  63. //Remove object from cache
  64. retval = CefV8Value::CreateBool(_javascriptObjects->Remove(managedObjectName));
  65. }
  66. else
  67. {
  68. retval = CefV8Value::CreateBool(false);
  69. }
  70. }
  71. //TODO: Better name for this function
  72. //TODO: Make this a const
  73. else if (name == "DeleteBoundObject")
  74. {
  75. if (arguments.size() == 0 || arguments.size() > 1)
  76. {
  77. //TODO: Improve error message
  78. exception = "Must specify the name of a bound object to unbind, one object at a time.";
  79. return true;
  80. }
  81. auto objectName = arguments[0]->GetStringValue();
  82. auto global = context->GetGlobal();
  83. auto success = global->DeleteValue(objectName);
  84. retval = CefV8Value::CreateBool(success);
  85. }
  86. else if (name == "BindObjectAsync")
  87. {
  88. auto promiseCreator = global->GetValue(CefAppUnmanagedWrapper::kPromiseCreatorFunction);
  89. //this will create a promise and give us the reject/resolve functions {p: Promise, res: resolve(), rej: reject()}
  90. auto promiseData = promiseCreator->ExecuteFunctionWithContext(context, nullptr, CefV8ValueList());
  91. //return the promose
  92. retval = promiseData->GetValue("p");
  93. //References to the promise resolve and reject methods
  94. auto resolve = promiseData->GetValue("res");
  95. auto reject = promiseData->GetValue("rej");
  96. auto callback = gcnew JavascriptAsyncMethodCallback(context, resolve, reject);
  97. auto request = CefProcessMessage::Create(kJavascriptRootObjectRequest);
  98. auto argList = request->GetArgumentList();
  99. auto params = CefListValue::Create();
  100. auto boundObjectRequired = false;
  101. auto cachedObjects = gcnew List<JavascriptObject^>();
  102. if (arguments.size() > 0)
  103. {
  104. for (auto i = 0; i < arguments.size(); i++)
  105. {
  106. auto objectName = arguments[i]->GetStringValue();
  107. //Check if the object has already been bound
  108. if (!global->HasValue(objectName))
  109. {
  110. //If no matching object found then we'll add the object name to the list
  111. boundObjectRequired = true;
  112. params->SetString(i, objectName);
  113. auto managedObjectName = StringUtils::ToClr(objectName);
  114. JavascriptObject^ obj;
  115. if (_javascriptObjects->TryGetValue(managedObjectName, obj))
  116. {
  117. cachedObjects->Add(obj);
  118. }
  119. }
  120. }
  121. }
  122. else
  123. {
  124. //No objects names were specified so we default to makeing the request
  125. boundObjectRequired = true;
  126. }
  127. if (boundObjectRequired)
  128. {
  129. //If the number of cached objects matches the number of args
  130. //then we'll immediately bind the cached objects
  131. if (cachedObjects->Count == (int)arguments.size())
  132. {
  133. auto frame = context->GetFrame();
  134. if (frame.get())
  135. {
  136. if (Object::ReferenceEquals(_browserWrapper, nullptr))
  137. {
  138. callback->Fail("Browser wrapper is null and unable to bind objects");
  139. }
  140. else
  141. {
  142. //TODO: JSB This code is almost exactly duplicated in CefAppUnmangedWrapper
  143. //Need to extract into a common method
  144. auto rootObjectWrappers = _browserWrapper->JavascriptRootObjectWrappers;
  145. JavascriptRootObjectWrapper^ rootObject;
  146. if (!rootObjectWrappers->TryGetValue(frame->GetIdentifier(), rootObject))
  147. {
  148. rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier(), _browserWrapper->BrowserProcess);
  149. rootObjectWrappers->TryAdd(frame->GetIdentifier(), rootObject);
  150. }
  151. rootObject->Bind(cachedObjects, context->GetGlobal());
  152. //Response object has no Accessor or Interceptor
  153. auto response = CefV8Value::CreateObject(NULL, NULL);
  154. response->SetValue("Count", CefV8Value::CreateInt(cachedObjects->Count), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY);
  155. response->SetValue("Success", CefV8Value::CreateBool(true), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY);
  156. response->SetValue("Message", CefV8Value::CreateString("OK"), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY);
  157. callback->Success(response);
  158. //Send message notifying Browser Process of which objects were bound
  159. //We do this after the objects have been created in the V8Context to gurantee
  160. //they are accessible.
  161. auto msg = CefProcessMessage::Create(kJavascriptObjectsBoundInJavascript);
  162. auto args = msg->GetArgumentList();
  163. auto names = CefListValue::Create();
  164. for (auto i = 0; i < cachedObjects->Count; i++)
  165. {
  166. auto name = cachedObjects[i]->JavascriptName;
  167. names->SetString(i, StringUtils::ToNative(name));
  168. }
  169. args->SetList(0, names);
  170. browser->SendProcessMessage(CefProcessId::PID_BROWSER, msg);
  171. }
  172. }
  173. }
  174. else
  175. {
  176. //Obtain a callbackId then send off the Request
  177. auto callbackId = _callbackRegistry->SaveMethodCallback(callback);
  178. argList->SetInt(0, browser->GetIdentifier());
  179. SetInt64(argList, 1, context->GetFrame()->GetIdentifier());
  180. SetInt64(argList, 2, callbackId);
  181. argList->SetList(3, params);
  182. browser->SendProcessMessage(CefProcessId::PID_BROWSER, request);
  183. }
  184. }
  185. else
  186. {
  187. //Response object has no Accessor or Interceptor
  188. auto response = CefV8Value::CreateObject(NULL, NULL);
  189. //Objects already bound so we immediately resolve the Promise
  190. response->SetValue("Success", CefV8Value::CreateBool(false), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY);
  191. response->SetValue("Count", CefV8Value::CreateInt(0), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY);
  192. response->SetValue("Message", CefV8Value::CreateString("Object(s) already bound"), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY);
  193. CefV8ValueList returnArgs;
  194. returnArgs.push_back(response);
  195. //If all the requested objects are bound then we immediately execute resolve
  196. //with Success true and Count of 0
  197. resolve->ExecuteFunctionWithContext(context, nullptr, returnArgs);
  198. }
  199. }
  200. }
  201. finally
  202. {
  203. context->Exit();
  204. }
  205. }
  206. else
  207. {
  208. exception = "Unable to Enter Context";
  209. }
  210. }
  211. else
  212. {
  213. exception = "Unable to get current context";
  214. }
  215. return true;
  216. }
  217. IMPLEMENT_REFCOUNTING(RegisterBoundObjectHandler)
  218. };
  219. }