// Copyright (c) 2019 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #ifndef SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_H_ #define SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_H_ #include #include #include "content/public/browser/browser_thread.h" #include "electron/shell/common/api/api.mojom.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/gin_helper/wrappable.h" namespace content { class RenderFrameHost; } namespace gin_helper { namespace internal { v8::Local CreateEvent( v8::Isolate* isolate, v8::Local sender = v8::Local(), v8::Local custom_event = v8::Local()); v8::Local CreateEventFromFlags(v8::Isolate* isolate, int flags); v8::Local CreateNativeEvent( v8::Isolate* isolate, v8::Local sender, content::RenderFrameHost* frame, electron::mojom::ElectronBrowser::MessageSyncCallback callback); } // namespace internal // Provide helperers to emit event in JavaScript. template class EventEmitter : public gin_helper::Wrappable { public: using Base = gin_helper::Wrappable; using ValueArray = std::vector>; // Make the convinient methods visible: // https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members v8::Isolate* isolate() const { return Base::isolate(); } v8::Local GetWrapper() const { return Base::GetWrapper(); } v8::MaybeLocal GetWrapper(v8::Isolate* isolate) const { return Base::GetWrapper(isolate); } // this.emit(name, event, args...); template bool EmitCustomEvent(base::StringPiece name, v8::Local event, Args&&... args) { return EmitWithEvent(name, internal::CreateEvent(isolate(), GetWrapper(), event), std::forward(args)...); } // this.emit(name, new Event(flags), args...); template bool EmitWithFlags(base::StringPiece name, int flags, Args&&... args) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); return EmitCustomEvent(name, internal::CreateEventFromFlags(isolate(), flags), std::forward(args)...); } // this.emit(name, new Event(), args...); template bool Emit(base::StringPiece name, Args&&... args) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); v8::Local wrapper = GetWrapper(); if (wrapper.IsEmpty()) return false; v8::Local event = internal::CreateEvent(isolate(), wrapper); return EmitWithEvent(name, event, std::forward(args)...); } // this.emit(name, new Event(sender, message), args...); template bool EmitWithSender(base::StringPiece name, content::RenderFrameHost* sender, electron::mojom::ElectronBrowser::InvokeCallback callback, Args&&... args) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); v8::Local wrapper = GetWrapper(); if (wrapper.IsEmpty()) return false; v8::Local event = internal::CreateNativeEvent( isolate(), wrapper, sender, std::move(callback)); return EmitWithEvent(name, event, std::forward(args)...); } protected: EventEmitter() {} private: // this.emit(name, event, args...); template bool EmitWithEvent(base::StringPiece name, v8::Local event, Args&&... args) { // It's possible that |this| will be deleted by EmitEvent, so save anything // we need from |this| before calling EmitEvent. auto* isolate = this->isolate(); auto context = isolate->GetCurrentContext(); gin_helper::EmitEvent(isolate, GetWrapper(), name, event, std::forward(args)...); v8::Local defaultPrevented; if (event->Get(context, gin::StringToV8(isolate, "defaultPrevented")) .ToLocal(&defaultPrevented)) { return defaultPrevented->BooleanValue(isolate); } return false; } DISALLOW_COPY_AND_ASSIGN(EventEmitter); }; } // namespace gin_helper #endif // SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_H_