Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
danakj | 89f4708 | 2020-09-02 17:53:43 | [diff] [blame] | 5 | #include "content/web_test/renderer/gc_controller.h" |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 6 | |
Avi Drissman | 5d5d48d6 | 2022-01-07 20:23:58 | [diff] [blame] | 7 | #include <tuple> |
| 8 | |
Avi Drissman | adac2199 | 2023-01-11 23:46:39 | [diff] [blame] | 9 | #include "base/functional/bind.h" |
Sean Maher | e672a66 | 2023-01-09 21:42:28 | [diff] [blame] | 10 | #include "base/task/single_thread_task_runner.h" |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 11 | #include "gin/arguments.h" |
| 12 | #include "gin/handle.h" |
| 13 | #include "gin/object_template_builder.h" |
Dave Tapuska | d1aaf8a | 2023-09-12 14:50:34 | [diff] [blame] | 14 | #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h" |
Blink Reformat | a30d423 | 2018-04-07 15:31:06 | [diff] [blame] | 15 | #include "third_party/blink/public/web/web_local_frame.h" |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 16 | #include "v8/include/v8.h" |
| 17 | |
danakj | 741848a | 2020-04-07 22:48:06 | [diff] [blame] | 18 | namespace content { |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 19 | |
| 20 | gin::WrapperInfo GCController::kWrapperInfo = {gin::kEmbedderNativeGin}; |
| 21 | |
| 22 | // static |
danakj | c989ac7 | 2020-04-15 20:31:23 | [diff] [blame] | 23 | void GCController::Install(blink::WebLocalFrame* frame) { |
Dave Tapuska | d1aaf8a | 2023-09-12 14:50:34 | [diff] [blame] | 24 | v8::Isolate* isolate = frame->GetAgentGroupScheduler()->Isolate(); |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 25 | v8::HandleScope handle_scope(isolate); |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 26 | v8::Local<v8::Context> context = frame->MainWorldScriptContext(); |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 27 | if (context.IsEmpty()) |
| 28 | return; |
| 29 | |
| 30 | v8::Context::Scope context_scope(context); |
| 31 | |
| 32 | gin::Handle<GCController> controller = |
danakj | c989ac7 | 2020-04-15 20:31:23 | [diff] [blame] | 33 | gin::CreateHandle(isolate, new GCController(frame)); |
[email protected] | ad4d203 | 2014-04-28 13:50:59 | [diff] [blame] | 34 | if (controller.IsEmpty()) |
| 35 | return; |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 36 | v8::Local<v8::Object> global = context->Global(); |
Dan Elphick | a83be51 | 2019-02-05 15:57:23 | [diff] [blame] | 37 | global |
| 38 | ->Set(context, gin::StringToV8(isolate, "GCController"), |
| 39 | controller.ToV8()) |
| 40 | .Check(); |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 41 | } |
| 42 | |
danakj | c989ac7 | 2020-04-15 20:31:23 | [diff] [blame] | 43 | GCController::GCController(blink::WebLocalFrame* frame) : frame_(frame) {} |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 44 | |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 45 | GCController::~GCController() = default; |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 46 | |
| 47 | gin::ObjectTemplateBuilder GCController::GetObjectTemplateBuilder( |
| 48 | v8::Isolate* isolate) { |
| 49 | return gin::Wrappable<GCController>::GetObjectTemplateBuilder(isolate) |
| 50 | .SetMethod("collect", &GCController::Collect) |
[email protected] | c7d9004 | 2014-02-05 08:25:15 | [diff] [blame] | 51 | .SetMethod("collectAll", &GCController::CollectAll) |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 52 | .SetMethod("minorCollect", &GCController::MinorCollect) |
| 53 | .SetMethod("asyncCollectAll", &GCController::AsyncCollectAll); |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | void GCController::Collect(const gin::Arguments& args) { |
| 57 | args.isolate()->RequestGarbageCollectionForTesting( |
| 58 | v8::Isolate::kFullGarbageCollection); |
| 59 | } |
| 60 | |
[email protected] | c7d9004 | 2014-02-05 08:25:15 | [diff] [blame] | 61 | void GCController::CollectAll(const gin::Arguments& args) { |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 62 | for (int i = 0; i < kNumberOfGCsForFullCollection; i++) { |
[email protected] | c7d9004 | 2014-02-05 08:25:15 | [diff] [blame] | 63 | args.isolate()->RequestGarbageCollectionForTesting( |
| 64 | v8::Isolate::kFullGarbageCollection); |
| 65 | } |
| 66 | } |
| 67 | |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 68 | void GCController::AsyncCollectAll(const gin::Arguments& args) { |
Michael Lippautz | 584c69a8 | 2018-09-25 15:22:51 | [diff] [blame] | 69 | v8::HandleScope scope(args.isolate()); |
| 70 | |
| 71 | if (args.PeekNext().IsEmpty() || !args.PeekNext()->IsFunction()) { |
| 72 | args.ThrowTypeError( |
| 73 | "asyncCollectAll should be called with a callback argument being a " |
| 74 | "v8::Function."); |
| 75 | return; |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 76 | } |
danakj | c989ac7 | 2020-04-15 20:31:23 | [diff] [blame] | 77 | v8::UniquePersistent<v8::Function> js_callback( |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 78 | args.isolate(), v8::Local<v8::Function>::Cast(args.PeekNext())); |
| 79 | |
danakj | c989ac7 | 2020-04-15 20:31:23 | [diff] [blame] | 80 | // Bind the js callback into a OnceClosure that will be run asynchronously in |
| 81 | // a fresh call stack. |
| 82 | base::OnceClosure run_async = |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 83 | base::BindOnce(&GCController::AsyncCollectAllWithEmptyStack, |
danakj | c989ac7 | 2020-04-15 20:31:23 | [diff] [blame] | 84 | weak_ptr_factory_.GetWeakPtr(), std::move(js_callback)); |
| 85 | frame_->GetTaskRunner(blink::TaskType::kInternalTest) |
| 86 | ->PostTask(FROM_HERE, std::move(run_async)); |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | void GCController::AsyncCollectAllWithEmptyStack( |
| 90 | v8::UniquePersistent<v8::Function> callback) { |
Dave Tapuska | d1aaf8a | 2023-09-12 14:50:34 | [diff] [blame] | 91 | v8::Isolate* const isolate = frame_->GetAgentGroupScheduler()->Isolate(); |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 92 | |
| 93 | for (int i = 0; i < kNumberOfGCsForFullCollection; i++) { |
Omer Katz | b65361a4 | 2021-11-22 21:46:49 | [diff] [blame] | 94 | isolate->RequestGarbageCollectionForTesting( |
| 95 | v8::Isolate::kFullGarbageCollection, |
Nico Weber | 2922f369 | 2022-11-29 17:03:46 | [diff] [blame] | 96 | cppgc::EmbedderStackState::kNoHeapPointers); |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | v8::HandleScope scope(isolate); |
| 100 | v8::Local<v8::Function> func = callback.Get(isolate); |
Dominik Inführ | 3af929c | 2024-10-08 05:53:19 | [diff] [blame] | 101 | v8::Local<v8::Context> context = func->GetCreationContextChecked(isolate); |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 102 | v8::Context::Scope context_scope(context); |
Michael Lippautz | 54f67af2 | 2019-01-08 15:41:57 | [diff] [blame] | 103 | v8::TryCatch try_catch(isolate); |
Jochen Eisinger | 68f33286 | 2021-04-29 08:19:20 | [diff] [blame] | 104 | v8::MicrotasksScope microtasks_scope( |
Dave Tapuska | 7ba8750 | 2022-10-14 19:52:27 | [diff] [blame] | 105 | isolate, context->GetMicrotaskQueue(), |
| 106 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
Michael Lippautz | 54f67af2 | 2019-01-08 15:41:57 | [diff] [blame] | 107 | auto result = func->Call(context, context->Global(), 0, nullptr); |
| 108 | // Swallow potential exception. |
Avi Drissman | 5d5d48d6 | 2022-01-07 20:23:58 | [diff] [blame] | 109 | std::ignore = result; |
Michael Lippautz | 5b64e89 | 2018-09-24 11:10:00 | [diff] [blame] | 110 | } |
| 111 | |
[email protected] | 41fba0e | 2014-01-16 18:19:42 | [diff] [blame] | 112 | void GCController::MinorCollect(const gin::Arguments& args) { |
| 113 | args.isolate()->RequestGarbageCollectionForTesting( |
| 114 | v8::Isolate::kMinorGarbageCollection); |
| 115 | } |
| 116 | |
danakj | 741848a | 2020-04-07 22:48:06 | [diff] [blame] | 117 | } // namespace content |