Discard all our custom V8 helpers, use native-mate instead.

This commit is contained in:
Cheng Zhao 2014-04-22 23:07:21 +08:00
parent ef5342b86e
commit a040a96652
28 changed files with 484 additions and 1552 deletions

View file

@ -50,8 +50,6 @@
'atom/browser/api/atom_api_auto_updater.h',
'atom/browser/api/atom_api_browser_ipc.cc',
'atom/browser/api/atom_api_dialog.cc',
'atom/browser/api/atom_api_event.cc',
'atom/browser/api/atom_api_event.h',
'atom/browser/api/atom_api_menu.cc',
'atom/browser/api/atom_api_menu.h',
'atom/browser/api/atom_api_menu_gtk.cc',
@ -146,8 +144,6 @@
'atom/common/api/api_messages.h',
'atom/common/api/atom_api_clipboard.cc',
'atom/common/api/atom_api_crash_reporter.cc',
'atom/common/api/atom_api_event_emitter.cc',
'atom/common/api/atom_api_event_emitter.h',
'atom/common/api/atom_api_id_weak_map.cc',
'atom/common/api/atom_api_id_weak_map.h',
'atom/common/api/atom_api_screen.cc',
@ -181,6 +177,7 @@
'atom/common/linux/application_info.cc',
'atom/common/native_mate_converters/file_path_converter.h',
'atom/common/native_mate_converters/function_converter.h',
'atom/common/native_mate_converters/gurl_converter.h',
'atom/common/native_mate_converters/string16_converter.h',
'atom/common/native_mate_converters/v8_value_converter.cc',
'atom/common/native_mate_converters/v8_value_converter.h',
@ -201,9 +198,6 @@
'atom/common/platform_util_linux.cc',
'atom/common/platform_util_mac.mm',
'atom/common/platform_util_win.cc',
'atom/common/swap_or_assign.h',
'atom/common/v8/scoped_persistent.h',
'atom/common/v8/native_type_conversions.h',
'atom/renderer/api/atom_api_renderer_ipc.cc',
'atom/renderer/api/atom_renderer_bindings.cc',
'atom/renderer/api/atom_renderer_bindings.h',

View file

@ -16,30 +16,6 @@
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<atom::NativeWindow*> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
atom::NativeWindow** out) {
using atom::api::Window;
if (val->IsNull()) {
*out = NULL;
return true; // NULL is a valid value for NativeWindow*.
} else if (val->IsObject()) {
Window* window = Window::Unwrap<Window>(val->ToObject());
*out = window->window();
return true;
} else {
return false;
}
}
};
} // namespace mate
namespace {
void ShowMessageBox(int type,
@ -47,18 +23,18 @@ void ShowMessageBox(int type,
const std::string& title,
const std::string& message,
const std::string& detail,
atom::NativeWindow* window,
atom::api::Window* window,
mate::Arguments* args) {
v8::Handle<v8::Value> peek = args->PeekNext();
atom::MessageBoxCallback callback;
if (mate::Converter<atom::MessageBoxCallback>::FromV8(node_isolate,
peek,
&callback)) {
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, title,
message, detail, callback);
atom::ShowMessageBox(window->window(), (atom::MessageBoxType)type, buttons,
title, message, detail, callback);
} else {
int chosen = atom::ShowMessageBox(
window,
window->window(),
(atom::MessageBoxType)type,
buttons,
title,
@ -71,18 +47,18 @@ void ShowMessageBox(int type,
void ShowOpenDialog(const std::string& title,
const base::FilePath& default_path,
int properties,
atom::NativeWindow* window,
atom::api::Window* window,
mate::Arguments* args) {
v8::Handle<v8::Value> peek = args->PeekNext();
file_dialog::OpenDialogCallback callback;
if (mate::Converter<file_dialog::OpenDialogCallback>::FromV8(node_isolate,
peek,
&callback)) {
file_dialog::ShowOpenDialog(window, title, default_path, properties,
callback);
file_dialog::ShowOpenDialog(window->window(), title, default_path,
properties, callback);
} else {
std::vector<base::FilePath> paths;
if (file_dialog::ShowOpenDialog(window,
if (file_dialog::ShowOpenDialog(window->window(),
title,
default_path,
properties,
@ -93,19 +69,18 @@ void ShowOpenDialog(const std::string& title,
void ShowSaveDialog(const std::string& title,
const base::FilePath& default_path,
atom::NativeWindow* window,
atom::api::Window* window,
mate::Arguments* args) {
v8::Handle<v8::Value> peek = args->PeekNext();
file_dialog::SaveDialogCallback callback;
if (mate::Converter<file_dialog::SaveDialogCallback>::FromV8(node_isolate,
peek,
&callback)) {
file_dialog::ShowSaveDialog(window, title, default_path, callback);
file_dialog::ShowSaveDialog(window->window(), title, default_path,
callback);
} else {
base::FilePath path;
if (file_dialog::ShowSaveDialog(window,
title,
default_path,
if (file_dialog::ShowSaveDialog(window->window(), title, default_path,
&path))
args->Return(path);
}

View file

@ -1,99 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/browser/api/atom_api_event.h"
#include "atom/common/api/api_messages.h"
#include "atom/common/node_includes.h"
#include "atom/common/v8/native_type_conversions.h"
#include "content/public/browser/web_contents.h"
namespace atom {
namespace api {
ScopedPersistent<v8::Function> Event::constructor_template_;
Event::Event()
: sender_(NULL),
message_(NULL),
prevent_default_(false) {
}
Event::~Event() {
}
// static
v8::Handle<v8::Object> Event::CreateV8Object() {
if (constructor_template_.IsEmpty()) {
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(v8::String::NewSymbol("Event"));
NODE_SET_PROTOTYPE_METHOD(t, "preventDefault", PreventDefault);
NODE_SET_PROTOTYPE_METHOD(t, "sendReply", SendReply);
NODE_SET_PROTOTYPE_METHOD(t, "destroy", Destroy);
constructor_template_.reset(t->GetFunction());
}
v8::Handle<v8::Function> t = constructor_template_.NewHandle(node_isolate);
return t->NewInstance(0, NULL);
}
void Event::SetSenderAndMessage(content::WebContents* sender,
IPC::Message* message) {
DCHECK(!sender_);
DCHECK(!message_);
sender_ = sender;
message_ = message;
Observe(sender);
}
void Event::WebContentsDestroyed(content::WebContents* web_contents) {
sender_ = NULL;
message_ = NULL;
}
// static
void Event::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
Event* event = new Event;
event->Wrap(args.This());
}
// static
void Event::PreventDefault(const v8::FunctionCallbackInfo<v8::Value>& args) {
Event* event = Unwrap<Event>(args.This());
if (event == NULL)
return node::ThrowError("Event is already destroyed");
event->prevent_default_ = true;
}
// static
void Event::SendReply(const v8::FunctionCallbackInfo<v8::Value>& args) {
Event* event = Unwrap<Event>(args.This());
if (event == NULL)
return node::ThrowError("Event is already destroyed");
if (event->message_ == NULL || event->sender_ == NULL)
return node::ThrowError("Can only send reply to synchronous events");
string16 json = FromV8Value(args[0]);
AtomViewHostMsg_Message_Sync::WriteReplyParams(event->message_, json);
event->sender_->Send(event->message_);
delete event;
}
// static
void Event::Destroy(const v8::FunctionCallbackInfo<v8::Value>& args) {
delete Unwrap<Event>(args.This());
}
} // namespace api
} // namespace atom

View file

@ -1,65 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_EVENT_H_
#define ATOM_BROWSER_API_ATOM_API_EVENT_H_
#include "atom/common/v8/scoped_persistent.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/strings/string16.h"
#include "content/public/browser/web_contents_observer.h"
#include "vendor/node/src/node_object_wrap.h"
namespace IPC {
class Message;
}
namespace atom {
namespace api {
class Event : public node::ObjectWrap,
public content::WebContentsObserver {
public:
virtual ~Event();
// Create a V8 Event object.
static v8::Handle<v8::Object> CreateV8Object();
// Pass the sender and message to be replied.
void SetSenderAndMessage(content::WebContents* sender, IPC::Message* message);
// Whether event.preventDefault() is called.
bool prevent_default() const { return prevent_default_; }
protected:
Event();
// content::WebContentsObserver implementations:
virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE;
private:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void PreventDefault(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SendReply(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
static ScopedPersistent<v8::Function> constructor_template_;
// Replyer for the synchronous messages.
content::WebContents* sender_;
IPC::Message* message_;
bool prevent_default_;
DISALLOW_COPY_AND_ASSIGN(Event);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_EVENT_H_

View file

@ -13,30 +13,6 @@
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<atom::NativeWindow*> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
atom::NativeWindow** out) {
using atom::api::Window;
if (val->IsNull()) {
*out = NULL;
return true; // NULL is a valid value for NativeWindow*.
} else if (val->IsObject()) {
Window* window = Window::Unwrap<Window>(val->ToObject());
*out = window->window();
return true;
} else {
return false;
}
}
};
} // namespace mate
namespace atom {
namespace api {

View file

@ -7,14 +7,13 @@
#include <string>
#include "atom/browser/api/atom_api_window.h"
#include "base/memory/scoped_ptr.h"
#include "ui/base/models/simple_menu_model.h"
#include "native_mate/wrappable.h"
namespace atom {
class NativeWindow;
namespace api {
class MenuMac;
@ -51,7 +50,7 @@ class Menu : public mate::Wrappable,
virtual string16 GetSublabelForCommandId(int command_id) const OVERRIDE;
virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
virtual void Popup(NativeWindow* window) = 0;
virtual void Popup(Window* window) = 0;
scoped_ptr<ui::SimpleMenuModel> model_;
@ -81,7 +80,7 @@ class Menu : public mate::Wrappable,
bool IsVisibleAt(int index) const;
#if defined(OS_WIN) || defined(TOOLKIT_GTK)
void AttachToWindow(NativeWindow* window);
void AttachToWindow(Window* window);
#endif
DISALLOW_COPY_AND_ASSIGN(Menu);

View file

@ -18,10 +18,11 @@ namespace api {
MenuGtk::MenuGtk() {
}
void MenuGtk::Popup(NativeWindow* native_window) {
void MenuGtk::Popup(Window* window) {
uint32_t triggering_event_time;
gfx::Point point;
BrowserWindow* native_window = window->window();
GdkEventButton* event = native_window->GetWebContents()->
GetRenderWidgetHostView()->GetLastMouseDown();
if (event) {
@ -36,11 +37,8 @@ void MenuGtk::Popup(NativeWindow* native_window) {
menu_gtk_->PopupAsContext(point, triggering_event_time);
}
void Menu::AttachToWindow(NativeWindow* window) {
if (window == NULL)
return node::ThrowTypeError("Window is dead");
static_cast<NativeWindowGtk*>(native_window)->SetMenu(model_.get());
void Menu::AttachToWindow(Window* window) {
static_cast<NativeWindowGtk*>(window->window())->SetMenu(model_.get());
}
// static

View file

@ -15,7 +15,7 @@ namespace api {
class MenuGtk : public Menu,
public ::MenuGtk::Delegate {
protected:
virtual void Popup(NativeWindow* window) OVERRIDE;
virtual void Popup(Window* window) OVERRIDE;
private:
MenuGtk();

View file

@ -19,7 +19,7 @@ class MenuMac : public Menu {
protected:
MenuMac();
virtual void Popup(NativeWindow* window) OVERRIDE;
virtual void Popup(Window* window) OVERRIDE;
base::scoped_nsobject<AtomMenuController> menu_controller_;

View file

@ -19,22 +19,23 @@ namespace api {
MenuMac::MenuMac() {
}
void MenuMac::Popup(NativeWindow* native_window) {
void MenuMac::Popup(Window* window) {
base::scoped_nsobject<AtomMenuController> menu_controller(
[[AtomMenuController alloc] initWithModel:model_.get()]);
NSWindow* window = native_window->GetNativeWindow();
NativeWindow* native_window = window->window();
NSWindow* nswindow = native_window->GetNativeWindow();
content::WebContents* web_contents = native_window->GetWebContents();
// Fake out a context menu event.
NSEvent* currentEvent = [NSApp currentEvent];
NSPoint position = [window mouseLocationOutsideOfEventStream];
NSPoint position = [nswindow mouseLocationOutsideOfEventStream];
NSTimeInterval eventTime = [currentEvent timestamp];
NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown
location:position
modifierFlags:NSRightMouseDownMask
timestamp:eventTime
windowNumber:[window windowNumber]
windowNumber:[nswindow windowNumber]
context:nil
eventNumber:0
clickCount:1

View file

@ -18,17 +18,14 @@ namespace api {
MenuWin::MenuWin() {
}
void MenuWin::Popup(NativeWindow* native_window) {
void MenuWin::Popup(Window* window) {
gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
menu_.reset(new atom::Menu2(model_.get()));
menu_->RunContextMenuAt(cursor);
}
void Menu::AttachToWindow(NativeWindow* window) {
if (window == NULL)
return node::ThrowTypeError("Window is dead");
static_cast<NativeWindowWin*>(native_window)->SetMenu(model_.get());
void Menu::AttachToWindow(Window* window) {
static_cast<NativeWindowWin*>(window->window())->SetMenu(model_.get());
}
// static

View file

@ -15,7 +15,7 @@ namespace api {
class MenuWin : public Menu {
protected:
virtual void Popup(NativeWindow* window) OVERRIDE;
virtual void Popup(Window* window) OVERRIDE;
private:
MenuWin();

File diff suppressed because it is too large Load diff

View file

@ -10,33 +10,41 @@
#include "base/memory/scoped_ptr.h"
#include "atom/browser/native_window_observer.h"
#include "atom/common/api/atom_api_event_emitter.h"
#include "atom/common/v8/scoped_persistent.h"
#include "atom/browser/api/event_emitter.h"
class GURL;
namespace base {
class DictionaryValue;
}
namespace mate {
class Arguments;
class Dictionary;
}
namespace atom {
class NativeWindow;
namespace api {
class Window : public EventEmitter,
class Window : public mate::EventEmitter,
public NativeWindowObserver {
public:
virtual ~Window();
static mate::Wrappable* New(mate::Arguments* args,
const base::DictionaryValue& options);
static void Initialize(v8::Handle<v8::Object> target);
static void BuildPrototype(v8::Isolate* isolate,
v8::Handle<v8::ObjectTemplate> prototype);
NativeWindow* window() { return window_.get(); }
NativeWindow* window() const { return window_.get(); }
protected:
explicit Window(v8::Handle<v8::Object> wrapper,
base::DictionaryValue* options);
explicit Window(base::DictionaryValue* options);
virtual ~Window();
// Implementations of NativeWindowObserver.
// Implementations of NativeWindowObserver:
virtual void OnPageTitleUpdated(bool* prevent_default,
const std::string& title) OVERRIDE;
virtual void OnLoadingStateChanged(bool is_loading) OVERRIDE;
@ -49,82 +57,73 @@ class Window : public EventEmitter,
virtual void OnRendererCrashed() OVERRIDE;
private:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
// APIs for NativeWindow.
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Focus(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsFocused(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Show(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Hide(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsVisible(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Maximize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Unmaximize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Minimize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Restore(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetFullscreen(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsFullscreen(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetSize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetSize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetMinimumSize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMinimumSize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetMaximumSize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMaximumSize(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetResizable(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsResizable(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetAlwaysOnTop(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsAlwaysOnTop(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Center(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetPosition(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPosition(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTitle(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetTitle(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FlashFrame(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetKiosk(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsKiosk(const v8::FunctionCallbackInfo<v8::Value>& args);
static void OpenDevTools(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CloseDevTools(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsDevToolsOpened(const v8::FunctionCallbackInfo<v8::Value>& args);
static void InspectElement(const v8::FunctionCallbackInfo<v8::Value>& args);
static void DebugDevTools(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FocusOnWebView(const v8::FunctionCallbackInfo<v8::Value>& args);
static void BlurWebView(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsWebViewFocused(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CapturePage(const v8::FunctionCallbackInfo<v8::Value>& args);
void Destroy();
void Close();
void Focus();
bool IsFocused();
void Show();
void Hide();
bool IsVisible();
void Maximize();
void Unmaximize();
void Minimize();
void Restore();
void SetFullscreen(bool fullscreen);
bool IsFullscreen();
void SetSize(int width, int height);
std::vector<int> GetSize();
void SetMinimumSize(int width, int height);
std::vector<int> GetMinimumSize();
void SetMaximumSize(int width, int height);
std::vector<int> GetMaximumSize();
void SetResizable(bool resizable);
bool IsResizable();
void SetAlwaysOnTop(bool top);
bool IsAlwaysOnTop();
void Center();
void SetPosition(int x, int y);
std::vector<int> GetPosition();
void SetTitle(const std::string& title);
std::string GetTitle();
void FlashFrame(bool flash);
void SetKiosk(bool kiosk);
bool IsKiosk();
void OpenDevTools();
void CloseDevTools();
bool IsDevToolsOpened();
void InspectElement(int x, int y);
void DebugDevTools();
void FocusOnWebView();
void BlurWebView();
bool IsWebViewFocused();
void CapturePage(mate::Arguments* args);
// APIs for WebContents.
static void GetPageTitle(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsLoading(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsWaitingForResponse(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetRoutingID(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetProcessID(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsCrashed(const v8::FunctionCallbackInfo<v8::Value>& args);
string16 GetPageTitle();
bool IsLoading();
bool IsWaitingForResponse();
void Stop();
int GetRoutingID();
int GetProcessID();
bool IsCrashed();
// APIs for devtools.
static void GetDevTools(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ExecuteJavaScriptInDevTools(
const v8::FunctionCallbackInfo<v8::Value>& args);
mate::Dictionary GetDevTools(v8::Isolate* isolate);
void ExecuteJavaScriptInDevTools(const std::string& code);
// APIs for NavigationController.
static void LoadURL(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetURL(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CanGoBack(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CanGoForward(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CanGoToOffset(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GoBack(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GoForward(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GoToIndex(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GoToOffset(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Reload(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ReloadIgnoringCache(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Called when capturePage is done.
void OnCapturePageDone(const RefCountedV8Function& callback,
const std::vector<unsigned char>& data);
void LoadURL(const GURL& url);
GURL GetURL();
bool CanGoBack();
bool CanGoForward();
bool CanGoToOffset(int offset);
void GoBack();
void GoForward();
void GoToIndex(int index);
void GoToOffset(int offset);
void Reload();
void ReloadIgnoringCache();
scoped_ptr<NativeWindow> window_;

View file

@ -297,6 +297,10 @@ void NativeWindow::CapturePage(const gfx::Rect& rect,
callback));
}
void NativeWindow::DestroyWebContents() {
inspectable_web_contents_.reset();
}
void NativeWindow::CloseWebContents() {
bool prevent_default = false;
FOR_EACH_OBSERVER(NativeWindowObserver,
@ -365,10 +369,6 @@ void NativeWindow::NotifyWindowBlur() {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowBlur());
}
void NativeWindow::DestroyWebContents() {
inspectable_web_contents_.reset();
}
// In atom-shell all reloads and navigations started by renderer process would
// be redirected to this method, so we can have precise control of how we
// would open the url (in our case, is to restart the renderer process). See

View file

@ -158,6 +158,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
// Should be called by platform code when user want to close the window.
virtual void CloseWebContents();
// Destroy the WebContents immediately.
virtual void DestroyWebContents();
base::WeakPtr<NativeWindow> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
@ -196,9 +199,6 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
void NotifyWindowClosed();
void NotifyWindowBlur();
// Destroy the inspectable_web_contents.
void DestroyWebContents();
// Called when the window needs to update its draggable region.
virtual void UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) = 0;

View file

@ -1,74 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/common/api/atom_api_event_emitter.h"
#include <vector>
#include "atom/browser/api/atom_api_event.h"
#include "atom/common/browser_v8_locker.h"
#include "atom/common/v8/native_type_conversions.h"
#include "base/logging.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace api {
EventEmitter::EventEmitter(v8::Handle<v8::Object> wrapper) {
Wrap(wrapper);
}
EventEmitter::~EventEmitter() {
// Clear the aligned pointer, it should have been done by ObjectWrap but
// somehow node v0.11.x changed this behaviour.
BrowserV8Locker locker(node_isolate);
v8::HandleScope handle_scope(node_isolate);
handle()->SetAlignedPointerInInternalField(0, NULL);
}
bool EventEmitter::Emit(const std::string& name) {
base::ListValue args;
return Emit(name, &args);
}
bool EventEmitter::Emit(const std::string& name, base::ListValue* args) {
BrowserV8Locker locker(node_isolate);
v8::HandleScope handle_scope(node_isolate);
v8::Handle<v8::Context> context = v8::Context::GetCurrent();
scoped_ptr<V8ValueConverter> converter(new V8ValueConverter);
v8::Handle<v8::Object> v8_event = Event::CreateV8Object();
Event* event = Event::Unwrap<Event>(v8_event);
// Generate arguments for calling handle.emit.
std::vector<v8::Handle<v8::Value>> v8_args;
v8_args.reserve(args->GetSize() + 2);
v8_args.push_back(ToV8Value(name));
v8_args.push_back(v8_event);
for (size_t i = 0; i < args->GetSize(); i++) {
base::Value* value = NULL;
if (args->Get(i, &value)) {
DCHECK(value);
v8_args.push_back(converter->ToV8Value(value, context));
} else {
NOTREACHED() << "Wrong offset " << i << " for " << *args;
}
}
node::MakeCallback(handle(), "emit", v8_args.size(), &v8_args[0]);
bool prevent_default = event->prevent_default();
// Don't wait for V8 GC, delete it immediately.
delete event;
return prevent_default;
}
} // namespace api
} // namespace atom

View file

@ -1,43 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_
#define ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_
#include <string>
#include "base/basictypes.h"
#include "vendor/node/src/node_object_wrap.h"
namespace base {
class ListValue;
}
namespace atom {
namespace api {
// Class interiting EventEmitter should assume it's a javascript object which
// interits require('events').EventEmitter, this class provides many helper
// methods to do event processing in C++.
class EventEmitter : public node::ObjectWrap {
public:
virtual ~EventEmitter();
// Emit an event and returns whether the handler has called preventDefault().
bool Emit(const std::string& name);
bool Emit(const std::string& name, base::ListValue* args);
protected:
explicit EventEmitter(v8::Handle<v8::Object> wrapper);
private:
DISALLOW_COPY_AND_ASSIGN(EventEmitter);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_

View file

@ -6,30 +6,11 @@
#include "atom/common/platform_util.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "native_mate/dictionary.h"
#include "url/gurl.h"
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<GURL> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
GURL* out) {
std::string url;
if (Converter<std::string>::FromV8(isolate, val, &url)) {
*out = GURL(url);
return true;
} else {
return false;
}
}
};
} // namespace mate
namespace {
void Initialize(v8::Handle<v8::Object> exports) {

View file

@ -1,13 +1,12 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Copyright (c) 2012 Intel Corp. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_OBJECT_LIFE_MONITOR_H_
#define ATOM_COMMON_API_OBJECT_LIFE_MONITOR_H_
#include "atom/common/v8/scoped_persistent.h"
#include "base/basictypes.h"
#include "native_mate/scoped_persistent.h"
namespace atom {
@ -23,7 +22,7 @@ class ObjectLifeMonitor {
v8::Persistent<v8::Object>* value,
ObjectLifeMonitor* self);
ScopedPersistent<v8::Object> handle_;
mate::ScopedPersistent<v8::Object> handle_;
DISALLOW_COPY_AND_ASSIGN(ObjectLifeMonitor);
};

View file

@ -0,0 +1,34 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_GURL_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_GURL_CONVERTER_H_
#include "native_mate/converter.h"
#include "url/gurl.h"
namespace mate {
template<>
struct Converter<GURL> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const GURL& val) {
return Converter<std::string>::ToV8(isolate, val.spec());
}
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
GURL* out) {
std::string url;
if (Converter<std::string>::FromV8(isolate, val, &url)) {
*out = GURL(url);
return true;
} else {
return false;
}
}
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_GURL_CONVERTER_H_

View file

@ -9,6 +9,20 @@
namespace mate {
bool Converter<base::DictionaryValue>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
base::DictionaryValue* out) {
scoped_ptr<atom::V8ValueConverter> converter(new atom::V8ValueConverter);
scoped_ptr<base::Value> value(converter->FromV8Value(
val, v8::Context::GetCurrent()));
if (value->IsType(base::Value::TYPE_DICTIONARY)) {
out->Swap(static_cast<DictionaryValue*>(value.get()));
return true;
} else {
return false;
}
}
bool Converter<base::ListValue>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
base::ListValue* out) {

View file

@ -8,11 +8,19 @@
#include "native_mate/converter.h"
namespace base {
class DictionaryValue;
class ListValue;
}
namespace mate {
template<>
struct Converter<base::DictionaryValue> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
base::DictionaryValue* out);
};
template<>
struct Converter<base::ListValue> {
static bool FromV8(v8::Isolate* isolate,

View file

@ -14,6 +14,7 @@
#include "vendor/node/src/env.h"
#include "vendor/node/src/env-inl.h"
#include "vendor/node/src/node.h"
#include "vendor/node/src/node_buffer.h"
#include "vendor/node/src/node_internals.h"
using node::node_isolate;

View file

@ -1,71 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_SWAP_OR_ASSIGN_H_
#define ATOM_COMMON_SWAP_OR_ASSIGN_H_
#include "base/compiler_specific.h"
namespace internal {
#if defined(OS_WIN)
template<typename T> inline
void SwapOrAssign(T& v1, const T& v2) {
__if_exists(T::swap) {
v1.swap(const_cast<T&>(v2));
}
__if_not_exists(T::swap) {
v1 = v2;
}
}
template<typename T> inline
void SwapOrAssign(T*& v1, T* v2) {
v1 = v2;
}
inline
void SwapOrAssign(int& v1, int v2) {
v1 = v2;
}
inline
void SwapOrAssign(bool& v1, bool v2) {
v1 = v2;
}
#else // defined(OS_WIN)
// Helper to detect whether value has specified method.
template <typename T>
class HasSwapMethod {
typedef char one;
typedef long two;
template <typename C> static one test(char[sizeof(&C::swap)]) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
template<typename T> inline
typename enable_if<HasSwapMethod<T>::value>::type SwapOrAssign(
T& v1, const T& v2) {
v1.swap(const_cast<T&>(v2));
}
template<typename T> inline
typename enable_if<!HasSwapMethod<T>::value>::type SwapOrAssign(
T& v1, const T& v2) {
v1 = v2;
}
#endif // !defined(OS_WIN)
} // namespace internal
#endif // ATOM_COMMON_SWAP_OR_ASSIGN_H_

View file

@ -1,344 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_V8_NATIVE_TYPE_CONVERSIONS_H_
#define ATOM_COMMON_V8_NATIVE_TYPE_CONVERSIONS_H_
#include <map>
#include <string>
#include <vector>
#include "atom/browser/api/atom_api_window.h"
#include "atom/common/swap_or_assign.h"
#include "atom/common/v8/scoped_persistent.h"
#include "atom/common/native_mate_converters/v8_value_converter.h"
#include "base/files/file_path.h"
#include "base/strings/string16.h"
#include "base/template_util.h"
#include "base/values.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "url/gurl.h"
// Convert V8 value to arbitrary supported types.
struct FromV8Value {
explicit FromV8Value(v8::Handle<v8::Value> value) : value_(value) {}
operator int() {
return value_->IntegerValue();
}
operator bool() {
return value_->BooleanValue();
}
operator std::string() {
return *v8::String::Utf8Value(value_);
}
operator string16() {
v8::String::Value s(value_);
return string16(reinterpret_cast<const char16*>(*s), s.length());
}
operator GURL() {
std::string str = FromV8Value(value_);
return GURL(str);
}
operator base::FilePath() {
return base::FilePath::FromUTF8Unsafe(FromV8Value(value_));
}
operator gfx::Rect() {
v8::Handle<v8::Object> rect = value_->ToObject();
v8::Handle<v8::Value> x = rect->Get(v8::String::New("x"));
v8::Handle<v8::Value> y = rect->Get(v8::String::New("y"));
v8::Handle<v8::Value> width = rect->Get(v8::String::New("width"));
v8::Handle<v8::Value> height = rect->Get(v8::String::New("height"));
if (!x->IsNumber() || !y->IsNumber() ||
!width->IsNumber() || !height->IsNumber())
return gfx::Rect();
else
return gfx::Rect(x->IntegerValue(), y->IntegerValue(),
width->IntegerValue(), height->IntegerValue());
}
operator scoped_ptr<base::Value>() {
scoped_ptr<atom::V8ValueConverter> converter(new atom::V8ValueConverter);
return scoped_ptr<base::Value>(
converter->FromV8Value(value_, v8::Context::GetCurrent()));
}
template<class T>
operator std::vector<T>() {
std::vector<T> array;
v8::Handle<v8::Array> v8_array = v8::Handle<v8::Array>::Cast(value_);
for (uint32_t i = 0; i < v8_array->Length(); ++i)
array.push_back(FromV8Value(v8_array->Get(i)));
return array;
}
template<class K, class V>
operator std::map<K, V>() {
std::map<K, V> dict;
v8::Handle<v8::Object> v8_dict = value_->ToObject();
v8::Handle<v8::Array> v8_keys = v8_dict->GetOwnPropertyNames();
for (uint32_t i = 0; i < v8_keys->Length(); ++i) {
v8::Handle<v8::Value> v8_key = v8_keys->Get(i);
K key = FromV8Value(v8_key);
dict[key] = V(FromV8Value(v8_dict->Get(v8_key)));
}
return dict;
}
operator atom::NativeWindow*() {
using atom::api::Window;
if (value_->IsObject()) {
Window* window = Window::Unwrap<Window>(value_->ToObject());
if (window && window->window())
return window->window();
}
return NULL;
}
operator atom::RefCountedV8Function() {
v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value_);
return atom::RefCountedV8Function(
new atom::RefCountedPersistent<v8::Function>(func));
}
v8::Handle<v8::Value> value_;
};
// Convert arbitrary supported native type to V8 value.
inline v8::Handle<v8::Value> ToV8Value(int i) {
return v8::Integer::New(i);
}
inline v8::Handle<v8::Value> ToV8Value(bool b) {
return v8::Boolean::New(b);
}
inline v8::Handle<v8::Value> ToV8Value(float f) {
return v8::Number::New(f);
}
inline v8::Handle<v8::Value> ToV8Value(double f) {
return v8::Number::New(f);
}
inline v8::Handle<v8::Value> ToV8Value(const char* s) {
return v8::String::New(s);
}
inline v8::Handle<v8::Value> ToV8Value(const std::string& s) {
return v8::String::New(s.data(), s.size());
}
inline v8::Handle<v8::Value> ToV8Value(const string16& s) {
return v8::String::New(reinterpret_cast<const uint16_t*>(s.data()), s.size());
}
inline v8::Handle<v8::Value> ToV8Value(const GURL& url) {
return ToV8Value(url.spec());
}
inline v8::Handle<v8::Value> ToV8Value(const base::FilePath& path) {
std::string path_string(path.AsUTF8Unsafe());
return v8::String::New(path_string.data(), path_string.size());
}
inline v8::Handle<v8::Value> ToV8Value(void* whatever) {
return v8::Undefined();
}
template<class T> inline
v8::Handle<v8::Value> ToV8Value(const std::vector<T>& arr) {
v8::Handle<v8::Array> result = v8::Array::New(arr.size());
for (size_t i = 0; i < arr.size(); ++i)
result->Set(i, ToV8Value(arr[i]));
return result;
}
inline v8::Handle<v8::Value> ToV8Value(const gfx::Point& point) {
v8::Handle<v8::Object> obj = v8::Object::New();
obj->Set(ToV8Value("x"), ToV8Value(point.x()));
obj->Set(ToV8Value("y"), ToV8Value(point.y()));
return obj;
}
inline v8::Handle<v8::Value> ToV8Value(const gfx::Rect& rect) {
v8::Handle<v8::Object> obj = v8::Object::New();
obj->Set(ToV8Value("x"), ToV8Value(rect.x()));
obj->Set(ToV8Value("y"), ToV8Value(rect.y()));
obj->Set(ToV8Value("width"), ToV8Value(rect.width()));
obj->Set(ToV8Value("height"), ToV8Value(rect.height()));
return obj;
}
inline v8::Handle<v8::Value> ToV8Value(const gfx::Size& size) {
v8::Handle<v8::Object> obj = v8::Object::New();
obj->Set(ToV8Value("width"), ToV8Value(size.width()));
obj->Set(ToV8Value("height"), ToV8Value(size.height()));
return obj;
}
// Check if a V8 Value is of specified type.
template<class T> inline
bool V8ValueCanBeConvertedTo(v8::Handle<v8::Value> value) {
return false;
}
template<> inline
bool V8ValueCanBeConvertedTo<int>(v8::Handle<v8::Value> value) {
return value->IsNumber();
}
template<> inline
bool V8ValueCanBeConvertedTo<bool>(v8::Handle<v8::Value> value) {
return value->IsBoolean();
}
template<> inline
bool V8ValueCanBeConvertedTo<std::string>(v8::Handle<v8::Value> value) {
return value->IsString();
}
template<> inline
bool V8ValueCanBeConvertedTo<string16>(v8::Handle<v8::Value> value) {
return V8ValueCanBeConvertedTo<std::string>(value);
}
template<> inline
bool V8ValueCanBeConvertedTo<GURL>(v8::Handle<v8::Value> value) {
return V8ValueCanBeConvertedTo<std::string>(value);
}
template<> inline
bool V8ValueCanBeConvertedTo<base::FilePath>(v8::Handle<v8::Value> value) {
return V8ValueCanBeConvertedTo<std::string>(value);
}
template<> inline
bool V8ValueCanBeConvertedTo<gfx::Rect>(v8::Handle<v8::Value> value) {
return value->IsObject();
}
template<> inline
bool V8ValueCanBeConvertedTo<scoped_ptr<base::Value>>(
v8::Handle<v8::Value> value) {
return value->IsObject();
}
template<> inline
bool V8ValueCanBeConvertedTo<std::vector<std::string>>(
v8::Handle<v8::Value> value) {
return value->IsArray();
}
template<> inline
bool V8ValueCanBeConvertedTo<std::map<std::string, std::string>>(
v8::Handle<v8::Value> value) {
return value->IsObject();
}
template<> inline
bool V8ValueCanBeConvertedTo<atom::NativeWindow*>(v8::Handle<v8::Value> value) {
using atom::api::Window;
if (value->IsObject()) {
Window* window = Window::Unwrap<Window>(value->ToObject());
if (window && window->window())
return true;
}
return false;
}
template<> inline
bool V8ValueCanBeConvertedTo<atom::RefCountedV8Function>(
v8::Handle<v8::Value> value) {
return value->IsFunction();
}
// Check and convert V8's Arguments to native types.
template<typename T1> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* value,
int index = 0) {
if (!V8ValueCanBeConvertedTo<T1>(args[index]))
return false;
internal::SwapOrAssign(*value,
FromV8Value(args[index]).operator T1());
return true;
}
template<typename T1, typename T2> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* a1,
T2* a2) {
return FromV8Arguments<T1>(args, a1) && FromV8Arguments<T2>(args, a2, 1);
}
template<typename T1, typename T2, typename T3> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* a1,
T2* a2,
T3* a3) {
return FromV8Arguments<T1, T2>(args, a1, a2) &&
FromV8Arguments<T3>(args, a3, 2);
}
template<typename T1, typename T2, typename T3, typename T4> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* a1,
T2* a2,
T3* a3,
T4* a4) {
return FromV8Arguments<T1, T2, T3>(args, a1, a2, a3) &&
FromV8Arguments<T4>(args, a4, 3);
}
template<typename T1, typename T2, typename T3, typename T4, typename T5> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* a1,
T2* a2,
T3* a3,
T4* a4,
T5* a5) {
return FromV8Arguments<T1, T2, T3, T4>(args, a1, a2, a3, a4) &&
FromV8Arguments<T5>(args, a5, 4);
}
template<typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* a1,
T2* a2,
T3* a3,
T4* a4,
T5* a5,
T6* a6) {
return FromV8Arguments<T1, T2, T3, T4, T5>(args, a1, a2, a3, a4, a5) &&
FromV8Arguments<T6>(args, a6, 5);
}
template<typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7> inline
bool FromV8Arguments(const v8::FunctionCallbackInfo<v8::Value>& args,
T1* a1,
T2* a2,
T3* a3,
T4* a4,
T5* a5,
T6* a6,
T7* a7) {
return
FromV8Arguments<T1, T2, T3, T4, T5, T6>(args, a1, a2, a3, a4, a5, a6) &&
FromV8Arguments<T7>(args, a7, 6);
}
#endif // ATOM_COMMON_V8_NATIVE_TYPE_CONVERSIONS_H_

View file

@ -1,115 +0,0 @@
// Copyright 2013 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.
#ifndef ATOM_COMMON_V8_SCOPED_PERSISTENT_H_
#define ATOM_COMMON_V8_SCOPED_PERSISTENT_H_
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "v8/include/v8.h"
namespace atom {
// A v8::Persistent handle to a V8 value which destroys and clears the
// underlying handle on destruction.
template <typename T>
class ScopedPersistent {
public:
ScopedPersistent() {
}
explicit ScopedPersistent(v8::Handle<T> handle) {
reset(handle);
}
~ScopedPersistent() {
reset();
}
void reset(v8::Handle<T> handle) {
if (!handle.IsEmpty())
handle_.Reset(GetIsolate(handle), handle);
else
reset();
}
void reset() {
handle_.Reset();
}
bool IsEmpty() const {
return handle_.IsEmpty();
}
v8::Handle<T> NewHandle() const {
if (handle_.IsEmpty())
return v8::Local<T>();
return v8::Local<T>::New(GetIsolate(handle_), handle_);
}
v8::Handle<T> NewHandle(v8::Isolate* isolate) const {
if (handle_.IsEmpty())
return v8::Local<T>();
return v8::Local<T>::New(isolate, handle_);
}
template <typename P>
void MakeWeak(P* parameters,
typename v8::WeakReferenceCallbacks<T, P>::Revivable callback) {
handle_.MakeWeak(parameters, callback);
}
private:
template <typename U>
static v8::Isolate* GetIsolate(v8::Handle<U> object_handle) {
// Only works for v8::Object and its subclasses. Add specialisations for
// anything else.
if (!object_handle.IsEmpty())
return GetIsolate(object_handle->CreationContext());
return v8::Isolate::GetCurrent();
}
static v8::Isolate* GetIsolate(v8::Handle<v8::Context> context_handle) {
if (!context_handle.IsEmpty())
return context_handle->GetIsolate();
return v8::Isolate::GetCurrent();
}
static v8::Isolate* GetIsolate(
v8::Handle<v8::ObjectTemplate> template_handle) {
return v8::Isolate::GetCurrent();
}
template <typename U>
static v8::Isolate* GetIsolate(const U& any_handle) {
return v8::Isolate::GetCurrent();
}
v8::Persistent<T> handle_;
DISALLOW_COPY_AND_ASSIGN(ScopedPersistent);
};
template <typename T>
class RefCountedPersistent : public ScopedPersistent<T>,
public base::RefCounted<RefCountedPersistent<T>> {
public:
RefCountedPersistent() {}
explicit RefCountedPersistent(v8::Handle<T> handle)
: ScopedPersistent<T>(handle) {
}
protected:
friend class base::RefCounted<RefCountedPersistent<T>>;
~RefCountedPersistent() {}
private:
DISALLOW_COPY_AND_ASSIGN(RefCountedPersistent);
};
typedef scoped_refptr<RefCountedPersistent<v8::Function>> RefCountedV8Function;
typedef scoped_refptr<RefCountedPersistent<v8::Object>> RefCountedV8Object;
} // namespace atom
#endif // ATOM_COMMON_V8_SCOPED_PERSISTENT_H_

2
vendor/native_mate vendored

@ -1 +1 @@
Subproject commit 9cc90ac7d52e04d909ffdceda130e12d8f79e0e8
Subproject commit 3d219b4be6f37fe99ca2e738eb0ea9d1b152af34