callback.cc 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright (c) 2019 GitHub, Inc. All rights reserved.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/common/gin_helper/callback.h"
  5. #include "base/cxx17_backports.h"
  6. #include "content/public/browser/browser_thread.h"
  7. #include "gin/dictionary.h"
  8. namespace gin_helper {
  9. namespace {
  10. struct TranslaterHolder {
  11. explicit TranslaterHolder(v8::Isolate* isolate)
  12. : handle(isolate, v8::External::New(isolate, this)) {
  13. handle.SetWeak(this, &GC, v8::WeakCallbackType::kFinalizer);
  14. }
  15. ~TranslaterHolder() {
  16. if (!handle.IsEmpty()) {
  17. handle.ClearWeak();
  18. handle.Reset();
  19. }
  20. }
  21. static void GC(const v8::WeakCallbackInfo<TranslaterHolder>& data) {
  22. delete data.GetParameter();
  23. }
  24. v8::Global<v8::External> handle;
  25. Translater translater;
  26. };
  27. // Cached JavaScript version of |CallTranslater|.
  28. v8::Persistent<v8::FunctionTemplate> g_call_translater;
  29. void CallTranslater(v8::Local<v8::External> external,
  30. v8::Local<v8::Object> state,
  31. gin::Arguments* args) {
  32. // Whether the callback should only be called once.
  33. v8::Isolate* isolate = args->isolate();
  34. auto context = isolate->GetCurrentContext();
  35. bool one_time =
  36. state->Has(context, gin::StringToSymbol(isolate, "oneTime")).ToChecked();
  37. // Check if the callback has already been called.
  38. if (one_time) {
  39. auto called_symbol = gin::StringToSymbol(isolate, "called");
  40. if (state->Has(context, called_symbol).ToChecked()) {
  41. args->ThrowTypeError("One-time callback was called more than once");
  42. return;
  43. } else {
  44. state->Set(context, called_symbol, v8::Boolean::New(isolate, true))
  45. .ToChecked();
  46. }
  47. }
  48. auto* holder = static_cast<TranslaterHolder*>(external->Value());
  49. holder->translater.Run(args);
  50. // Free immediately for one-time callback.
  51. if (one_time)
  52. delete holder;
  53. }
  54. } // namespace
  55. // Destroy the class on UI thread when possible.
  56. struct DeleteOnUIThread {
  57. template <typename T>
  58. static void Destruct(const T* x) {
  59. if (gin_helper::Locker::IsBrowserProcess() &&
  60. !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  61. content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
  62. x);
  63. } else {
  64. delete x;
  65. }
  66. }
  67. };
  68. // Like v8::Global, but ref-counted.
  69. template <typename T>
  70. class RefCountedGlobal
  71. : public base::RefCountedThreadSafe<RefCountedGlobal<T>, DeleteOnUIThread> {
  72. public:
  73. RefCountedGlobal(v8::Isolate* isolate, v8::Local<v8::Value> value)
  74. : handle_(isolate, value.As<T>()) {}
  75. bool IsAlive() const { return !handle_.IsEmpty(); }
  76. v8::Local<T> NewHandle(v8::Isolate* isolate) const {
  77. return v8::Local<T>::New(isolate, handle_);
  78. }
  79. private:
  80. v8::Global<T> handle_;
  81. DISALLOW_COPY_AND_ASSIGN(RefCountedGlobal);
  82. };
  83. SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value)
  84. : v8_function_(new RefCountedGlobal<v8::Function>(isolate, value)) {}
  85. SafeV8Function::SafeV8Function(const SafeV8Function& other) = default;
  86. SafeV8Function::~SafeV8Function() = default;
  87. bool SafeV8Function::IsAlive() const {
  88. return v8_function_.get() && v8_function_->IsAlive();
  89. }
  90. v8::Local<v8::Function> SafeV8Function::NewHandle(v8::Isolate* isolate) const {
  91. return v8_function_->NewHandle(isolate);
  92. }
  93. v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
  94. const Translater& translater,
  95. bool one_time) {
  96. // The FunctionTemplate is cached.
  97. if (g_call_translater.IsEmpty())
  98. g_call_translater.Reset(
  99. isolate,
  100. CreateFunctionTemplate(isolate, base::BindRepeating(&CallTranslater)));
  101. v8::Local<v8::FunctionTemplate> call_translater =
  102. v8::Local<v8::FunctionTemplate>::New(isolate, g_call_translater);
  103. auto* holder = new TranslaterHolder(isolate);
  104. holder->translater = translater;
  105. gin::Dictionary state = gin::Dictionary::CreateEmpty(isolate);
  106. if (one_time)
  107. state.Set("oneTime", true);
  108. auto context = isolate->GetCurrentContext();
  109. return BindFunctionWith(
  110. isolate, context, call_translater->GetFunction(context).ToLocalChecked(),
  111. holder->handle.Get(isolate), gin::ConvertToV8(isolate, state));
  112. }
  113. // func.bind(func, arg1).
  114. // NB(zcbenz): Using C++11 version crashes VS.
  115. v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
  116. v8::Local<v8::Context> context,
  117. v8::Local<v8::Function> func,
  118. v8::Local<v8::Value> arg1,
  119. v8::Local<v8::Value> arg2) {
  120. v8::MaybeLocal<v8::Value> bind =
  121. func->Get(context, gin::StringToV8(isolate, "bind"));
  122. CHECK(!bind.IsEmpty());
  123. v8::Local<v8::Function> bind_func = bind.ToLocalChecked().As<v8::Function>();
  124. v8::Local<v8::Value> converted[] = {func, arg1, arg2};
  125. return bind_func->Call(context, func, base::size(converted), converted)
  126. .ToLocalChecked();
  127. }
  128. } // namespace gin_helper