test_js_runner.cc 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/bindings/test_js_runner.h"
  5. #include <ostream>
  6. #include "base/bind.h"
  7. #include "extensions/renderer/bindings/api_binding_test_util.h"
  8. namespace extensions {
  9. namespace {
  10. // NOTE(devlin): These aren't thread-safe. If we have multi-threaded unittests,
  11. // we'll need to expand these.
  12. bool g_allow_errors = false;
  13. bool g_suspended = false;
  14. } // namespace
  15. TestJSRunner::Scope::Scope(std::unique_ptr<JSRunner> runner)
  16. : runner_(std::move(runner)),
  17. old_runner_(JSRunner::GetInstanceForTesting()) {
  18. DCHECK_NE(runner_.get(), old_runner_);
  19. JSRunner::SetInstanceForTesting(runner_.get());
  20. }
  21. TestJSRunner::Scope::~Scope() {
  22. DCHECK_EQ(runner_.get(), JSRunner::GetInstanceForTesting());
  23. JSRunner::SetInstanceForTesting(old_runner_);
  24. }
  25. TestJSRunner::AllowErrors::AllowErrors() {
  26. DCHECK(!g_allow_errors) << "Nested AllowErrors() blocks are not allowed.";
  27. g_allow_errors = true;
  28. }
  29. TestJSRunner::AllowErrors::~AllowErrors() {
  30. DCHECK(g_allow_errors);
  31. g_allow_errors = false;
  32. }
  33. TestJSRunner::Suspension::Suspension() {
  34. DCHECK(!g_suspended) << "Nested Suspension() blocks are not allowed.";
  35. g_suspended = true;
  36. }
  37. TestJSRunner::Suspension::~Suspension() {
  38. DCHECK(g_suspended);
  39. g_suspended = false;
  40. TestJSRunner* test_runner =
  41. static_cast<TestJSRunner*>(JSRunner::GetInstanceForTesting());
  42. DCHECK(test_runner);
  43. test_runner->Flush();
  44. }
  45. TestJSRunner::PendingCall::PendingCall() {}
  46. TestJSRunner::PendingCall::~PendingCall() = default;
  47. TestJSRunner::PendingCall::PendingCall(PendingCall&& other) = default;
  48. TestJSRunner::TestJSRunner() = default;
  49. TestJSRunner::TestJSRunner(const base::RepeatingClosure& will_call_js)
  50. : will_call_js_(will_call_js) {}
  51. TestJSRunner::~TestJSRunner() = default;
  52. void TestJSRunner::RunJSFunction(v8::Local<v8::Function> function,
  53. v8::Local<v8::Context> context,
  54. int argc,
  55. v8::Local<v8::Value> argv[],
  56. ResultCallback callback) {
  57. if (g_suspended) {
  58. // Script is suspended. Queue up the call and return.
  59. v8::Isolate* isolate = context->GetIsolate();
  60. PendingCall call;
  61. call.isolate = isolate;
  62. call.function.Reset(isolate, function);
  63. call.context.Reset(isolate, context);
  64. call.arguments.reserve(argc);
  65. call.callback = std::move(callback);
  66. for (int i = 0; i < argc; ++i)
  67. call.arguments.push_back(v8::Global<v8::Value>(isolate, argv[i]));
  68. pending_calls_.push_back(std::move(call));
  69. return;
  70. }
  71. // Functions should always run in the scope of the context.
  72. v8::Context::Scope context_scope(context);
  73. if (will_call_js_)
  74. will_call_js_.Run();
  75. v8::MaybeLocal<v8::Value> result;
  76. if (g_allow_errors) {
  77. result = function->Call(context, context->Global(), argc, argv);
  78. } else {
  79. result = RunFunctionOnGlobal(function, context, argc, argv);
  80. }
  81. if (callback)
  82. std::move(callback).Run(context, result);
  83. }
  84. v8::MaybeLocal<v8::Value> TestJSRunner::RunJSFunctionSync(
  85. v8::Local<v8::Function> function,
  86. v8::Local<v8::Context> context,
  87. int argc,
  88. v8::Local<v8::Value> argv[]) {
  89. // Note: deliberately circumvent g_suspension, since this should only be used
  90. // in response to JS interaction.
  91. if (will_call_js_)
  92. will_call_js_.Run();
  93. if (g_allow_errors) {
  94. v8::MaybeLocal<v8::Value> result =
  95. function->Call(context, context->Global(), argc, argv);
  96. return result;
  97. }
  98. return RunFunctionOnGlobal(function, context, argc, argv);
  99. }
  100. void TestJSRunner::Flush() {
  101. // Move pending_calls_ in case running any pending calls results in more calls
  102. // into the JSRunner.
  103. std::vector<PendingCall> calls = std::move(pending_calls_);
  104. pending_calls_.clear();
  105. for (auto& call : calls) {
  106. v8::Isolate* isolate = call.isolate;
  107. v8::Local<v8::Context> context = call.context.Get(isolate);
  108. v8::Context::Scope context_scope(context);
  109. std::vector<v8::Local<v8::Value>> local_arguments;
  110. local_arguments.reserve(call.arguments.size());
  111. for (auto& arg : call.arguments)
  112. local_arguments.push_back(arg.Get(isolate));
  113. v8::MaybeLocal<v8::Value> result =
  114. RunJSFunctionSync(call.function.Get(isolate), context,
  115. local_arguments.size(), local_arguments.data());
  116. if (call.callback)
  117. std::move(call.callback).Run(context, result);
  118. }
  119. }
  120. } // namespace extensions