| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/shell/renderer/web_test/gc_controller.h" |
| |
| #include "base/bind.h" |
| #include "gin/arguments.h" |
| #include "gin/handle.h" |
| #include "gin/object_template_builder.h" |
| #include "third_party/blink/public/web/blink.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "v8/include/v8.h" |
| |
| namespace content { |
| |
| gin::WrapperInfo GCController::kWrapperInfo = {gin::kEmbedderNativeGin}; |
| |
| // static |
| void GCController::Install(blink::WebLocalFrame* frame) { |
| v8::Isolate* isolate = blink::MainThreadIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::Context> context = frame->MainWorldScriptContext(); |
| if (context.IsEmpty()) |
| return; |
| |
| v8::Context::Scope context_scope(context); |
| |
| gin::Handle<GCController> controller = |
| gin::CreateHandle(isolate, new GCController(frame)); |
| if (controller.IsEmpty()) |
| return; |
| v8::Local<v8::Object> global = context->Global(); |
| global |
| ->Set(context, gin::StringToV8(isolate, "GCController"), |
| controller.ToV8()) |
| .Check(); |
| } |
| |
| GCController::GCController(blink::WebLocalFrame* frame) : frame_(frame) {} |
| |
| GCController::~GCController() = default; |
| |
| gin::ObjectTemplateBuilder GCController::GetObjectTemplateBuilder( |
| v8::Isolate* isolate) { |
| return gin::Wrappable<GCController>::GetObjectTemplateBuilder(isolate) |
| .SetMethod("collect", &GCController::Collect) |
| .SetMethod("collectAll", &GCController::CollectAll) |
| .SetMethod("minorCollect", &GCController::MinorCollect) |
| .SetMethod("asyncCollectAll", &GCController::AsyncCollectAll); |
| } |
| |
| void GCController::Collect(const gin::Arguments& args) { |
| args.isolate()->RequestGarbageCollectionForTesting( |
| v8::Isolate::kFullGarbageCollection); |
| } |
| |
| void GCController::CollectAll(const gin::Arguments& args) { |
| for (int i = 0; i < kNumberOfGCsForFullCollection; i++) { |
| args.isolate()->RequestGarbageCollectionForTesting( |
| v8::Isolate::kFullGarbageCollection); |
| } |
| } |
| |
| void GCController::AsyncCollectAll(const gin::Arguments& args) { |
| v8::HandleScope scope(args.isolate()); |
| |
| if (args.PeekNext().IsEmpty() || !args.PeekNext()->IsFunction()) { |
| args.ThrowTypeError( |
| "asyncCollectAll should be called with a callback argument being a " |
| "v8::Function."); |
| return; |
| } |
| v8::UniquePersistent<v8::Function> js_callback( |
| args.isolate(), v8::Local<v8::Function>::Cast(args.PeekNext())); |
| |
| // Bind the js callback into a OnceClosure that will be run asynchronously in |
| // a fresh call stack. |
| base::OnceClosure run_async = |
| base::BindOnce(&GCController::AsyncCollectAllWithEmptyStack, |
| weak_ptr_factory_.GetWeakPtr(), std::move(js_callback)); |
| frame_->GetTaskRunner(blink::TaskType::kInternalTest) |
| ->PostTask(FROM_HERE, std::move(run_async)); |
| } |
| |
| void GCController::AsyncCollectAllWithEmptyStack( |
| v8::UniquePersistent<v8::Function> callback) { |
| v8::Isolate* const isolate = blink::MainThreadIsolate(); |
| |
| for (int i = 0; i < kNumberOfGCsForFullCollection; i++) { |
| isolate->GetEmbedderHeapTracer()->GarbageCollectionForTesting( |
| v8::EmbedderHeapTracer::EmbedderStackState::kEmpty); |
| } |
| |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Function> func = callback.Get(isolate); |
| v8::Local<v8::Context> context = func->CreationContext(); |
| v8::Context::Scope context_scope(context); |
| v8::TryCatch try_catch(isolate); |
| auto result = func->Call(context, context->Global(), 0, nullptr); |
| // Swallow potential exception. |
| ignore_result(result); |
| } |
| |
| void GCController::MinorCollect(const gin::Arguments& args) { |
| args.isolate()->RequestGarbageCollectionForTesting( |
| v8::Isolate::kMinorGarbageCollection); |
| } |
| |
| } // namespace content |