#include "extensions/renderer/wake_event_page.h"
#include <memory>
#include <utility>
#include "base/atomic_sequence_num.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/lazy_instance.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/worker_thread.h"
#include "extensions/common/extension_messages.h"
#include "extensions/renderer/object_backed_native_handler.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/v8_helpers.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "v8/include/v8-context.h"
#include "v8/include/v8-function.h"
#include "v8/include/v8-persistent-handle.h"
#include "v8/include/v8-primitive.h"
namespace extensions {
namespace {
base::LazyInstance<WakeEventPage>::DestructorAtExit g_wake_event_page_instance =
LAZY_INSTANCE_INITIALIZER;
constexpr char kWakeEventPageFunctionName[] = "WakeEventPage";
}
class WakeEventPage::WakeEventPageNativeHandler
: public ObjectBackedNativeHandler {
public:
WakeEventPageNativeHandler(ScriptContext* context,
const MakeRequestCallback& make_request)
: ObjectBackedNativeHandler(context), make_request_(make_request) {
context->AddInvalidationObserver(base::BindOnce(
&WakeEventPageNativeHandler::DeleteSelf, base::Unretained(this)));
}
void AddRoutes() override {
RouteHandlerFunction(
kWakeEventPageFunctionName,
base::BindRepeating(&WakeEventPageNativeHandler::DoWakeEventPage,
base::Unretained(this)));
}
WakeEventPageNativeHandler(const WakeEventPageNativeHandler&) = delete;
WakeEventPageNativeHandler& operator=(const WakeEventPageNativeHandler&) =
delete;
~WakeEventPageNativeHandler() override {}
private:
void DeleteSelf() {
Invalidate();
delete this;
}
void DoWakeEventPage(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_EQ(1, args.Length());
CHECK(args[0]->IsFunction());
v8::Global<v8::Function> callback(args.GetIsolate(),
args[0].As<v8::Function>());
const std::string& extension_id = context()->GetExtensionID();
CHECK(!extension_id.empty());
make_request_.Run(
extension_id,
base::BindOnce(&WakeEventPageNativeHandler::OnEventPageIsAwake,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void OnEventPageIsAwake(v8::Global<v8::Function> callback, bool success) {
v8::Isolate* isolate = context()->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Value> args[] = {
v8::Boolean::New(isolate, success),
};
context()->SafeCallFunction(v8::Local<v8::Function>::New(isolate, callback),
std::size(args), args);
}
MakeRequestCallback make_request_;
base::WeakPtrFactory<WakeEventPageNativeHandler> weak_ptr_factory_{this};
};
WakeEventPage* WakeEventPage::Get() {
return g_wake_event_page_instance.Pointer();
}
void WakeEventPage::Init(content::RenderThread* render_thread) {
DCHECK(render_thread);
DCHECK_EQ(content::RenderThread::Get(), render_thread);
DCHECK(!message_filter_);
message_filter_ = render_thread->GetSyncMessageFilter();
render_thread->AddObserver(this);
}
v8::Local<v8::Function> WakeEventPage::GetForContext(ScriptContext* context) {
DCHECK(message_filter_);
v8::Isolate* isolate = context->isolate();
v8::EscapableHandleScope handle_scope(isolate);
v8::Local<v8::Context> v8_context = context->v8_context();
v8::Context::Scope context_scope(v8_context);
v8::Local<v8::Private> kWakeEventPageKey = v8::Private::ForApi(
isolate, v8_helpers::ToV8StringUnsafe(isolate, "WakeEventPage"));
v8::Local<v8::Value> wake_event_page;
if (!v8_context->Global()
->GetPrivate(v8_context, kWakeEventPageKey)
.ToLocal(&wake_event_page) ||
wake_event_page->IsUndefined()) {
WakeEventPageNativeHandler* native_handler = new WakeEventPageNativeHandler(
context, base::BindRepeating(&WakeEventPage::MakeRequest,
base::Unretained(this)));
native_handler->Initialize();
wake_event_page = v8_helpers::GetPropertyUnsafe(
v8_context, native_handler->NewInstance(), kWakeEventPageFunctionName);
v8_context->Global()
->SetPrivate(v8_context, kWakeEventPageKey, wake_event_page)
.FromJust();
}
CHECK(wake_event_page->IsFunction());
return handle_scope.Escape(wake_event_page.As<v8::Function>());
}
WakeEventPage::RequestData::RequestData(int thread_id,
OnResponseCallback on_response)
: thread_id(thread_id), on_response(std::move(on_response)) {}
WakeEventPage::RequestData::~RequestData() = default;
WakeEventPage::WakeEventPage() = default;
WakeEventPage::~WakeEventPage() = default;
void WakeEventPage::MakeRequest(const std::string& extension_id,
OnResponseCallback on_response) {
static base::AtomicSequenceNumber sequence_number;
int request_id = sequence_number.GetNext();
{
base::AutoLock lock(requests_lock_);
requests_[request_id] = std::make_unique<RequestData>(
content::WorkerThread::GetCurrentId(), std::move(on_response));
}
message_filter_->Send(
new ExtensionHostMsg_WakeEventPage(request_id, extension_id));
}
bool WakeEventPage::OnControlMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(WakeEventPage, message)
IPC_MESSAGE_HANDLER(ExtensionMsg_WakeEventPageResponse,
OnWakeEventPageResponse)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void WakeEventPage::OnWakeEventPageResponse(int request_id, bool success) {
std::unique_ptr<RequestData> request_data;
{
base::AutoLock lock(requests_lock_);
auto it = requests_.find(request_id);
CHECK(it != requests_.end()) << "No request with ID " << request_id;
request_data = std::move(it->second);
requests_.erase(it);
}
if (request_data->thread_id == 0) {
std::move(request_data->on_response).Run(success);
} else {
content::WorkerThread::PostTask(
request_data->thread_id,
base::BindOnce(std::move(request_data->on_response), success));
}
}
}