diff --git a/atom.gyp b/atom.gyp index c9aff86aa4a..47896d76bb9 100644 --- a/atom.gyp +++ b/atom.gyp @@ -157,6 +157,7 @@ 'common/platform_util.h', 'common/platform_util_mac.mm', 'common/platform_util_win.cc', + 'common/v8_conversions.h', 'common/v8_value_converter_impl.cc', 'common/v8_value_converter_impl.h', 'renderer/api/atom_api_renderer_ipc.cc', diff --git a/browser/api/atom_api_app.cc b/browser/api/atom_api_app.cc index 51a1362b4b6..909bff3950c 100644 --- a/browser/api/atom_api_app.cc +++ b/browser/api/atom_api_app.cc @@ -7,6 +7,7 @@ #include "base/values.h" #include "base/command_line.h" #include "browser/browser.h" +#include "common/v8_conversions.h" #include "vendor/node/src/node.h" namespace atom { @@ -111,14 +112,14 @@ v8::Handle App::GetVersion(const v8::Arguments &args) { v8::Handle App::AppendSwitch(const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsString()) + std::string switch_string; + if (!FromV8Arguments(args, &switch_string)) return node::ThrowError("Bad argument"); - std::string switch_string(*v8::String::Utf8Value(args[0])); if (args.Length() == 1) { CommandLine::ForCurrentProcess()->AppendSwitch(switch_string); } else { - std::string value(*v8::String::Utf8Value(args[1])); + std::string value = FromV8Value(args[1]); CommandLine::ForCurrentProcess()->AppendSwitchASCII( switch_string, value); } @@ -130,10 +131,10 @@ v8::Handle App::AppendSwitch(const v8::Arguments &args) { v8::Handle App::AppendArgument(const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsString()) + std::string value; + if (!FromV8Arguments(args, &value)) return node::ThrowError("Bad argument"); - std::string value(*v8::String::Utf8Value(args[0])); CommandLine::ForCurrentProcess()->AppendArg(value); return v8::Undefined(); @@ -143,7 +144,7 @@ v8::Handle App::AppendArgument(const v8::Arguments &args) { // static v8::Handle App::DockBounce(const v8::Arguments& args) { - std::string type(*v8::String::Utf8Value(args[0])); + std::string type = FromV8Value(args[0]); int request_id = -1; if (type == "critical") @@ -158,21 +159,20 @@ v8::Handle App::DockBounce(const v8::Arguments& args) { // static v8::Handle App::DockCancelBounce(const v8::Arguments& args) { - Browser::Get()->DockCancelBounce(args[0]->IntegerValue()); + Browser::Get()->DockCancelBounce(FromV8Value(args[0])); return v8::Undefined(); } // static v8::Handle App::DockSetBadgeText(const v8::Arguments& args) { - std::string label(*v8::String::Utf8Value(args[0])); - Browser::Get()->DockSetBadgeText(label); + Browser::Get()->DockSetBadgeText(FromV8Value(args[0])); return v8::Undefined(); } // static v8::Handle App::DockGetBadgeText(const v8::Arguments& args) { std::string text(Browser::Get()->DockGetBadgeText()); - return v8::String::New(text.data(), text.size()); + return ToV8Value(text); } #endif // defined(OS_MACOSX) diff --git a/browser/api/atom_api_auto_updater.cc b/browser/api/atom_api_auto_updater.cc index e5e2013b6ad..29bdacc056d 100644 --- a/browser/api/atom_api_auto_updater.cc +++ b/browser/api/atom_api_auto_updater.cc @@ -6,6 +6,7 @@ #include "base/values.h" #include "browser/auto_updater.h" +#include "common/v8_conversions.h" namespace atom { @@ -56,7 +57,7 @@ v8::Handle AutoUpdater::New(const v8::Arguments &args) { // static v8::Handle AutoUpdater::SetFeedURL(const v8::Arguments &args) { - auto_updater::AutoUpdater::SetFeedURL(*v8::String::Utf8Value(args[0])); + auto_updater::AutoUpdater::SetFeedURL(FromV8Value(args[0])); return v8::Undefined(); } diff --git a/browser/api/atom_api_browser_ipc.cc b/browser/api/atom_api_browser_ipc.cc index ac9d6c277c4..7e43e1edb66 100644 --- a/browser/api/atom_api_browser_ipc.cc +++ b/browser/api/atom_api_browser_ipc.cc @@ -6,6 +6,7 @@ #include "base/values.h" #include "common/api/api_messages.h" +#include "common/v8_conversions.h" #include "common/v8_value_converter_impl.h" #include "content/public/browser/render_view_host.h" #include "vendor/node/src/node.h" @@ -22,13 +23,11 @@ namespace api { v8::Handle BrowserIPC::Send(const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsString() || !args[1]->IsNumber() || !args[2]->IsNumber()) + string16 channel; + int process_id, routing_id; + if (!FromV8Arguments(args, &channel, &process_id, &routing_id)) return node::ThrowTypeError("Bad argument"); - std::string channel(*v8::String::Utf8Value(args[0])); - int process_id = args[1]->IntegerValue(); - int routing_id = args[2]->IntegerValue(); - RenderViewHost* render_view_host(RenderViewHost::FromID( process_id, routing_id)); if (!render_view_host) diff --git a/browser/api/atom_api_crash_reporter.cc b/browser/api/atom_api_crash_reporter.cc index 2b23db1d1a6..1c5d85de28e 100644 --- a/browser/api/atom_api_crash_reporter.cc +++ b/browser/api/atom_api_crash_reporter.cc @@ -5,6 +5,7 @@ #include "browser/api/atom_api_crash_reporter.h" #include "browser/crash_reporter.h" +#include "common/v8_conversions.h" #include "vendor/node/src/node.h" #include "vendor/node/src/node_internals.h" @@ -14,22 +15,20 @@ namespace api { // static v8::Handle CrashReporter::SetCompanyName(const v8::Arguments &args) { - std::string name(*v8::String::Utf8Value(args[0])); - crash_reporter::CrashReporter::SetCompanyName(name); + crash_reporter::CrashReporter::SetCompanyName(FromV8Value(args[0])); return v8::Undefined(); } // static v8::Handle CrashReporter::SetSubmissionURL( const v8::Arguments &args) { - std::string url(*v8::String::Utf8Value(args[0])); - crash_reporter::CrashReporter::SetSubmissionURL(url); + crash_reporter::CrashReporter::SetSubmissionURL(FromV8Value(args[0])); return v8::Undefined(); } // static v8::Handle CrashReporter::SetAutoSubmit(const v8::Arguments &args) { - crash_reporter::CrashReporter::SetAutoSubmit(args[0]->BooleanValue()); + crash_reporter::CrashReporter::SetAutoSubmit(FromV8Value(args[0])); return v8::Undefined(); } diff --git a/browser/api/atom_api_dialog.cc b/browser/api/atom_api_dialog.cc index 69b0135b294..e5d52c43472 100644 --- a/browser/api/atom_api_dialog.cc +++ b/browser/api/atom_api_dialog.cc @@ -4,14 +4,14 @@ #include "browser/api/atom_api_dialog.h" -#include - -#include "base/utf_string_conversions.h" -#include "base/values.h" -#include "browser/api/atom_api_window.h" +#include "base/bind.h" #include "browser/native_window.h" #include "browser/ui/file_dialog.h" #include "browser/ui/message_box.h" +#include "common/v8_conversions.h" +#include "vendor/node/src/node_internals.h" + +using node::node_isolate; namespace atom { @@ -19,14 +19,24 @@ namespace api { namespace { -base::FilePath V8ValueToFilePath(v8::Handle path) { - std::string path_string(*v8::String::Utf8Value(path)); - return base::FilePath::FromUTF8Unsafe(path_string); +template +void CallV8Function(v8::Persistent callback, T arg) { + DCHECK(!callback.IsEmpty()); + + v8::HandleScope scope; + v8::Handle value = ToV8Value(arg); + callback->Call(callback, 1, &value); + callback.Dispose(node_isolate); } -v8::Handle FilePathToV8Value(const base::FilePath path) { - std::string path_string(path.AsUTF8Unsafe()); - return v8::String::New(path_string.data(), path_string.size()); +template +void CallV8Function2(v8::Persistent callback, + bool result, + T arg) { + if (result) + return CallV8Function(callback, arg); + else + return CallV8Function(callback, NULL); } void Initialize(v8::Handle target) { @@ -42,84 +52,103 @@ void Initialize(v8::Handle target) { v8::Handle ShowMessageBox(const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsNumber() || // type - !args[1]->IsArray() || // buttons - !args[2]->IsString() || // title - !args[3]->IsString() || // message - !args[4]->IsString()) // detail + int type; + std::vector buttons; + std::string title, message, detail; + if (!FromV8Arguments(args, &type, &buttons, &title, &message, &detail)) return node::ThrowTypeError("Bad argument"); - NativeWindow* native_window = NULL; - if (args[5]->IsObject()) { - Window* window = Window::Unwrap(args[5]->ToObject()); - if (!window || !window->window()) - return node::ThrowError("Invalid window"); + NativeWindow* native_window = FromV8Value(args[5]); + v8::Persistent callback = FromV8Value(args[6]); - native_window = window->window(); + if (callback.IsEmpty()) { + int chosen = atom::ShowMessageBox( + native_window, + (MessageBoxType)type, + buttons, + title, + message, + detail); + return scope.Close(v8::Integer::New(chosen)); + } else { + atom::ShowMessageBox( + native_window, + (MessageBoxType)type, + buttons, + title, + message, + detail, + base::Bind(&CallV8Function, callback)); + return v8::Undefined(); } - - MessageBoxType type = (MessageBoxType)(args[0]->IntegerValue()); - - std::vector buttons; - v8::Handle v8_buttons = v8::Handle::Cast(args[1]); - for (uint32_t i = 0; i < v8_buttons->Length(); ++i) - buttons.push_back(*v8::String::Utf8Value(v8_buttons->Get(i))); - - std::string title(*v8::String::Utf8Value(args[2])); - std::string message(*v8::String::Utf8Value(args[3])); - std::string detail(*v8::String::Utf8Value(args[4])); - - int chosen = atom::ShowMessageBox( - native_window, type, buttons, title, message, detail); - return scope.Close(v8::Integer::New(chosen)); } v8::Handle ShowOpenDialog(const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsString() || // title - !args[1]->IsString() || // default_path - !args[2]->IsNumber()) // properties + std::string title; + base::FilePath default_path; + int properties; + if (!FromV8Arguments(args, &title, &default_path, &properties)) return node::ThrowTypeError("Bad argument"); - std::string title(*v8::String::Utf8Value(args[0])); - base::FilePath default_path(V8ValueToFilePath(args[1])); - int properties = args[2]->IntegerValue(); + NativeWindow* native_window = FromV8Value(args[3]); + v8::Persistent callback = FromV8Value(args[4]); - std::vector paths; - if (!file_dialog::ShowOpenDialog(title, default_path, properties, &paths)) + if (callback.IsEmpty()) { + std::vector paths; + if (!file_dialog::ShowOpenDialog(native_window, + title, + default_path, + properties, + &paths)) + return v8::Undefined(); + + v8::Handle result = v8::Array::New(paths.size()); + for (size_t i = 0; i < paths.size(); ++i) + result->Set(i, ToV8Value(paths[i])); + + return scope.Close(result); + } else { + file_dialog::ShowOpenDialog( + native_window, + title, + default_path, + properties, + base::Bind(&CallV8Function2&>, + callback)); return v8::Undefined(); - - v8::Handle result = v8::Array::New(paths.size()); - for (size_t i = 0; i < paths.size(); ++i) - result->Set(i, FilePathToV8Value(paths[i])); - - return scope.Close(result); + } } v8::Handle ShowSaveDialog(const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsObject() || // window - !args[1]->IsString() || // title - !args[2]->IsString()) // default_path + std::string title; + base::FilePath default_path; + if (!FromV8Arguments(args, &title, &default_path)) return node::ThrowTypeError("Bad argument"); - Window* window = Window::Unwrap(args[0]->ToObject()); - if (!window || !window->window()) - return node::ThrowError("Invalid window"); + NativeWindow* native_window = FromV8Value(args[2]); + v8::Persistent callback = FromV8Value(args[3]); - std::string title(*v8::String::Utf8Value(args[1])); - base::FilePath default_path(V8ValueToFilePath(args[2])); + if (callback.IsEmpty()) { + base::FilePath path; + if (!file_dialog::ShowSaveDialog(native_window, + title, + default_path, + &path)) + return v8::Undefined(); - base::FilePath path; - if (!file_dialog::ShowSaveDialog(window->window(), - title, - default_path, - &path)) + return scope.Close(ToV8Value(path)); + } else { + file_dialog::ShowSaveDialog( + native_window, + title, + default_path, + base::Bind(&CallV8Function2, callback)); return v8::Undefined(); - - return scope.Close(FilePathToV8Value(path)); + } } } // namespace api diff --git a/browser/api/atom_api_event.cc b/browser/api/atom_api_event.cc index 9854401760b..26f8981b1fc 100644 --- a/browser/api/atom_api_event.cc +++ b/browser/api/atom_api_event.cc @@ -4,6 +4,10 @@ #include "browser/api/atom_api_event.h" +#include "browser/native_window.h" +#include "common/api/api_messages.h" +#include "common/v8_conversions.h" + using node::node_isolate; namespace atom { @@ -13,10 +17,14 @@ namespace api { v8::Persistent Event::constructor_template_; Event::Event() - : prevent_default_(false) { + : sender_(NULL), + message_(NULL), + prevent_default_(false) { } Event::~Event() { + if (sender_) + sender_->RemoveObserver(this); } // static @@ -31,6 +39,7 @@ v8::Handle Event::CreateV8Object() { constructor_template_->SetClassName(v8::String::NewSymbol("Event")); NODE_SET_PROTOTYPE_METHOD(t, "preventDefault", PreventDefault); + NODE_SET_PROTOTYPE_METHOD(t, "sendReply", SendReply); } v8::Handle v8_event = @@ -39,14 +48,30 @@ v8::Handle Event::CreateV8Object() { return scope.Close(v8_event); } -v8::Handle Event::New(const v8::Arguments &args) { +void Event::SetSenderAndMessage(NativeWindow* sender, IPC::Message* message) { + DCHECK(!sender_); + DCHECK(!message_); + sender_ = sender; + message_ = message; + + sender_->AddObserver(this); +} + +void Event::OnWindowClosed() { + sender_ = NULL; + message_ = NULL; +} + +// static +v8::Handle Event::New(const v8::Arguments& args) { Event* event = new Event; event->Wrap(args.This()); return args.This(); } -v8::Handle Event::PreventDefault(const v8::Arguments &args) { +// static +v8::Handle Event::PreventDefault(const v8::Arguments& args) { Event* event = Unwrap(args.This()); if (event == NULL) return node::ThrowError("Event is already destroyed"); @@ -56,6 +81,24 @@ v8::Handle Event::PreventDefault(const v8::Arguments &args) { return v8::Undefined(); } +// static +v8::Handle Event::SendReply(const v8::Arguments& args) { + Event* event = Unwrap(args.This()); + if (event == NULL) + return node::ThrowError("Event is already destroyed"); + + if (event->message_ == NULL) + return node::ThrowError("Can only send reply to synchronous events once"); + + string16 json = FromV8Value(args[0]); + + AtomViewHostMsg_Message_Sync::WriteReplyParams(event->message_, json); + event->sender_->Send(event->message_); + + event->message_ = NULL; + return v8::Undefined(); +} + } // namespace api } // namespace atom diff --git a/browser/api/atom_api_event.h b/browser/api/atom_api_event.h index c0ab00078bc..7007892b349 100644 --- a/browser/api/atom_api_event.h +++ b/browser/api/atom_api_event.h @@ -6,19 +6,32 @@ #define ATOM_BROWSER_ATOM_API_EVENT_H_ #include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/string16.h" +#include "browser/native_window_observer.h" #include "vendor/node/src/node_object_wrap.h" +namespace IPC { +class Message; +} + namespace atom { +class NativeWindow; + namespace api { -class Event : public node::ObjectWrap { +class Event : public node::ObjectWrap, + public NativeWindowObserver { public: virtual ~Event(); // Create a V8 Event object. static v8::Handle CreateV8Object(); + // Pass the sender and message to be replied. + void SetSenderAndMessage(NativeWindow* sender, IPC::Message* message); + // Accessor to return handle_, this follows Google C++ Style. v8::Persistent& handle() { return handle_; } @@ -28,12 +41,21 @@ class Event : public node::ObjectWrap { protected: Event(); + // NativeWindowObserver implementations: + virtual void OnWindowClosed() OVERRIDE; + private: - static v8::Handle New(const v8::Arguments &args); - static v8::Handle PreventDefault(const v8::Arguments &args); + static v8::Handle New(const v8::Arguments& args); + + static v8::Handle PreventDefault(const v8::Arguments& args); + static v8::Handle SendReply(const v8::Arguments& args); static v8::Persistent constructor_template_; + // Replyer for the synchronous messages. + NativeWindow* sender_; + IPC::Message* message_; + bool prevent_default_; DISALLOW_COPY_AND_ASSIGN(Event); diff --git a/browser/api/atom_api_menu.cc b/browser/api/atom_api_menu.cc index 27879e7993d..a057f307eb9 100644 --- a/browser/api/atom_api_menu.cc +++ b/browser/api/atom_api_menu.cc @@ -4,8 +4,8 @@ #include "browser/api/atom_api_menu.h" -#include "browser/api/atom_api_window.h" #include "browser/ui/accelerator_util.h" +#include "common/v8_conversions.h" #define UNWRAP_MEMNU_AND_CHECK \ Menu* self = ObjectWrap::Unwrap(args.This()); \ @@ -18,17 +18,6 @@ namespace api { namespace { -// Converts a V8 value to a string16. -string16 V8ValueToUTF16(v8::Handle value) { - v8::String::Value s(value); - return string16(reinterpret_cast(*s), s.length()); -} - -// Converts string16 to V8 String. -v8::Handle UTF16ToV8Value(const string16& s) { - return v8::String::New(reinterpret_cast(s.data()), s.size()); -} - // Call method of delegate object. v8::Handle CallDelegate(v8::Handle default_value, v8::Handle menu, @@ -93,7 +82,7 @@ bool Menu::GetAcceleratorForCommandId(int command_id, "getAcceleratorForCommandId", command_id); if (shortcut->IsString()) { - std::string shortcut_str(*v8::String::Utf8Value(shortcut)); + std::string shortcut_str = FromV8Value(shortcut); return accelerator_util::StringToAccelerator(shortcut_str, accelerator); } @@ -110,18 +99,18 @@ bool Menu::IsItemForCommandIdDynamic(int command_id) const { string16 Menu::GetLabelForCommandId(int command_id) const { v8::HandleScope scope; - return V8ValueToUTF16(CallDelegate(v8::False(), - handle(), - "getLabelForCommandId", - command_id)); + return FromV8Value(CallDelegate(v8::False(), + handle(), + "getLabelForCommandId", + command_id)); } string16 Menu::GetSublabelForCommandId(int command_id) const { v8::HandleScope scope; - return V8ValueToUTF16(CallDelegate(v8::False(), - handle(), - "getSubLabelForCommandId", - command_id)); + return FromV8Value(CallDelegate(v8::False(), + handle(), + "getSubLabelForCommandId", + command_id)); } void Menu::ExecuteCommand(int command_id, int event_flags) { @@ -145,16 +134,15 @@ v8::Handle Menu::New(const v8::Arguments &args) { v8::Handle Menu::InsertItem(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsString()) + int index, command_id; + string16 label; + if (!FromV8Arguments(args, &index, &command_id, &label)) return node::ThrowTypeError("Bad argument"); - int index = args[0]->IntegerValue(); - if (index < 0) - self->model_->AddItem(args[1]->IntegerValue(), V8ValueToUTF16(args[2])); + self->model_->AddItem(command_id, label); else - self->model_->InsertItemAt( - index, args[1]->IntegerValue(), V8ValueToUTF16(args[2])); + self->model_->InsertItemAt(index, command_id, label); return v8::Undefined(); } @@ -163,16 +151,15 @@ v8::Handle Menu::InsertItem(const v8::Arguments &args) { v8::Handle Menu::InsertCheckItem(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsString()) + int index, command_id; + string16 label; + if (!FromV8Arguments(args, &index, &command_id, &label)) return node::ThrowTypeError("Bad argument"); - int index = args[0]->IntegerValue(); - int command_id = args[1]->IntegerValue(); - if (index < 0) - self->model_->AddCheckItem(command_id, V8ValueToUTF16(args[2])); + self->model_->AddCheckItem(command_id, label); else - self->model_->InsertCheckItemAt(index, command_id, V8ValueToUTF16(args[2])); + self->model_->InsertCheckItemAt(index, command_id, label); return v8::Undefined(); } @@ -181,21 +168,15 @@ v8::Handle Menu::InsertCheckItem(const v8::Arguments &args) { v8::Handle Menu::InsertRadioItem(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber() || - !args[1]->IsNumber() || - !args[2]->IsString() || - !args[3]->IsNumber()) + int index, command_id, group_id; + string16 label; + if (!FromV8Arguments(args, &index, &command_id, &label, &group_id)) return node::ThrowTypeError("Bad argument"); - int index = args[0]->IntegerValue(); - int command_id = args[1]->IntegerValue(); - int group_id = args[3]->IntegerValue(); - if (index < 0) - self->model_->AddRadioItem(command_id, V8ValueToUTF16(args[2]), group_id); + self->model_->AddRadioItem(command_id, label, group_id); else - self->model_->InsertRadioItemAt( - index, command_id, V8ValueToUTF16(args[2]), group_id); + self->model_->InsertRadioItemAt(index, command_id, label, group_id); return v8::Undefined(); } @@ -204,11 +185,10 @@ v8::Handle Menu::InsertRadioItem(const v8::Arguments &args) { v8::Handle Menu::InsertSeparator(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber()) + int index; + if (!FromV8Arguments(args, &index)) return node::ThrowTypeError("Bad argument"); - int index = args[0]->IntegerValue(); - if (index < 0) self->model_->AddSeparator(ui::NORMAL_SEPARATOR); else @@ -221,25 +201,20 @@ v8::Handle Menu::InsertSeparator(const v8::Arguments &args) { v8::Handle Menu::InsertSubMenu(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber() || - !args[1]->IsNumber() || - !args[2]->IsString() || - !args[3]->IsObject()) + int index, command_id; + string16 label; + if (!FromV8Arguments(args, &index, &command_id, &label)) return node::ThrowTypeError("Bad argument"); Menu* submenu = ObjectWrap::Unwrap(args[3]->ToObject()); if (!submenu) return node::ThrowTypeError("The submenu is already destroyed"); - int index = args[0]->IntegerValue(); - int command_id = args[1]->IntegerValue(); - if (index < 0) - self->model_->AddSubMenu( - command_id, V8ValueToUTF16(args[2]), submenu->model_.get()); + self->model_->AddSubMenu(command_id, label, submenu->model_.get()); else self->model_->InsertSubMenuAt( - index, command_id, V8ValueToUTF16(args[2]), submenu->model_.get()); + index, command_id, label, submenu->model_.get()); return v8::Undefined(); } @@ -248,7 +223,9 @@ v8::Handle Menu::InsertSubMenu(const v8::Arguments &args) { v8::Handle Menu::SetIcon(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber() || !args[1]->IsString()) + int index; + base::FilePath path; + if (!FromV8Arguments(args, &index, &path)) return node::ThrowTypeError("Bad argument"); // FIXME use webkit_glue's image decoder here. @@ -260,10 +237,12 @@ v8::Handle Menu::SetIcon(const v8::Arguments &args) { v8::Handle Menu::SetSublabel(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - if (!args[0]->IsNumber() || !args[1]->IsString()) + int index; + string16 label; + if (!FromV8Arguments(args, &index, &label)) return node::ThrowTypeError("Bad argument"); - self->model_->SetSublabel(args[0]->IntegerValue(), V8ValueToUTF16(args[1])); + self->model_->SetSublabel(index, label); return v8::Undefined(); } @@ -301,14 +280,14 @@ v8::Handle Menu::GetCommandIdAt(const v8::Arguments &args) { v8::Handle Menu::GetLabelAt(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; int index = args[0]->IntegerValue(); - return UTF16ToV8Value(self->model_->GetLabelAt(index)); + return ToV8Value(self->model_->GetLabelAt(index)); } // static v8::Handle Menu::GetSublabelAt(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; int index = args[0]->IntegerValue(); - return UTF16ToV8Value(self->model_->GetSublabelAt(index)); + return ToV8Value(self->model_->GetSublabelAt(index)); } // static @@ -334,11 +313,11 @@ v8::Handle Menu::IsVisibleAt(const v8::Arguments &args) { v8::Handle Menu::Popup(const v8::Arguments &args) { UNWRAP_MEMNU_AND_CHECK; - Window* window = Window::Unwrap(args[0]->ToObject()); - if (!window) - return node::ThrowTypeError("Invalid window"); + atom::NativeWindow* window; + if (!FromV8Arguments(args, &window)) + return node::ThrowTypeError("Bad argument"); - self->Popup(window->window()); + self->Popup(window); return v8::Undefined(); } diff --git a/browser/api/atom_api_menu_mac.mm b/browser/api/atom_api_menu_mac.mm index acf4849435e..89270190baa 100644 --- a/browser/api/atom_api_menu_mac.mm +++ b/browser/api/atom_api_menu_mac.mm @@ -8,6 +8,7 @@ #include "base/mac/scoped_sending_event.h" #include "base/strings/sys_string_conversions.h" #include "browser/native_window.h" +#include "common/v8_conversions.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_view.h" @@ -95,10 +96,10 @@ v8::Handle Menu::SendActionToFirstResponder( const v8::Arguments &args) { v8::HandleScope scope; - if (!args[0]->IsString()) + std::string action; + if (!FromV8Arguments(args, &action)) return node::ThrowTypeError("Bad argument"); - std::string action(*v8::String::Utf8Value(args[0])); MenuMac::SendActionToFirstResponder(action); return v8::Undefined(); diff --git a/browser/api/atom_api_protocol.cc b/browser/api/atom_api_protocol.cc index 6f0af3dfeb7..a1ae29223e4 100644 --- a/browser/api/atom_api_protocol.cc +++ b/browser/api/atom_api_protocol.cc @@ -9,6 +9,7 @@ #include "browser/net/adapter_request_job.h" #include "browser/net/atom_url_request_context_getter.h" #include "browser/net/atom_url_request_job_factory.h" +#include "common/v8_conversions.h" #include "content/public/browser/browser_thread.h" #include "net/url_request/url_request_context.h" #include "vendor/node/src/node.h" @@ -36,9 +37,9 @@ static const char* kEarlyUseProtocolError = "This method can only be used" void EmitEventInUI(const std::string& event, const std::string& parameter) { v8::HandleScope scope; - v8::Local argv[] = { - v8::String::New(event.data(), event.size()), - v8::String::New(parameter.data(), parameter.size()), + v8::Handle argv[] = { + ToV8Value(event), + ToV8Value(parameter), }; node::MakeCallback(g_protocol_object, "emit", arraysize(argv), argv); } @@ -83,7 +84,7 @@ class CustomProtocolRequestJob : public AdapterRequestJob { // Determine the type of the job we are going to create. if (result->IsString()) { - std::string data = *v8::String::Utf8Value(result); + std::string data = FromV8Value(result); content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, @@ -95,14 +96,12 @@ class CustomProtocolRequestJob : public AdapterRequestJob { return; } else if (result->IsObject()) { v8::Handle obj = result->ToObject(); - std::string name = *v8::String::Utf8Value(obj->GetConstructorName()); + std::string name = FromV8Value(obj->GetConstructorName()); if (name == "RequestStringJob") { - std::string mime_type = *v8::String::Utf8Value(obj->Get( + std::string mime_type = FromV8Value(obj->Get( v8::String::New("mimeType"))); - std::string charset = *v8::String::Utf8Value(obj->Get( - v8::String::New("charset"))); - std::string data = *v8::String::Utf8Value(obj->Get( - v8::String::New("data"))); + std::string charset = FromV8Value(obj->Get(v8::String::New("charset"))); + std::string data = FromV8Value(obj->Get(v8::String::New("data"))); content::BrowserThread::PostTask( content::BrowserThread::IO, @@ -114,8 +113,7 @@ class CustomProtocolRequestJob : public AdapterRequestJob { data)); return; } else if (name == "RequestFileJob") { - base::FilePath path = base::FilePath::FromUTF8Unsafe( - *v8::String::Utf8Value(obj->Get(v8::String::New("path")))); + base::FilePath path = FromV8Value(obj->Get(v8::String::New("path"))); content::BrowserThread::PostTask( content::BrowserThread::IO, @@ -183,7 +181,11 @@ class CustomProtocolHandler : public ProtocolHandler { // static v8::Handle Protocol::RegisterProtocol(const v8::Arguments& args) { - std::string scheme(*v8::String::Utf8Value(args[0])); + std::string scheme; + v8::Persistent callback; + if (!FromV8Arguments(args, &scheme, &callback)) + return node::ThrowTypeError("Bad argument"); + if (g_handlers.find(scheme) != g_handlers.end() || net::URLRequest::IsHandledProtocol(scheme)) return node::ThrowError("The scheme is already registered"); @@ -192,10 +194,7 @@ v8::Handle Protocol::RegisterProtocol(const v8::Arguments& args) { return node::ThrowError(kEarlyUseProtocolError); // Store the handler in a map. - if (!args[1]->IsFunction()) - return node::ThrowError("Handler must be a function"); - g_handlers[scheme] = v8::Persistent::New( - node::node_isolate, v8::Handle::Cast(args[1])); + g_handlers[scheme] = callback; content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, @@ -206,7 +205,9 @@ v8::Handle Protocol::RegisterProtocol(const v8::Arguments& args) { // static v8::Handle Protocol::UnregisterProtocol(const v8::Arguments& args) { - std::string scheme(*v8::String::Utf8Value(args[0])); + std::string scheme; + if (!FromV8Arguments(args, &scheme)) + return node::ThrowTypeError("Bad argument"); if (AtomBrowserContext::Get()->url_request_context_getter() == NULL) return node::ThrowError(kEarlyUseProtocolError); @@ -226,13 +227,16 @@ v8::Handle Protocol::UnregisterProtocol(const v8::Arguments& args) { // static v8::Handle Protocol::IsHandledProtocol(const v8::Arguments& args) { - return v8::Boolean::New(net::URLRequest::IsHandledProtocol( - *v8::String::Utf8Value(args[0]))); + return ToV8Value(net::URLRequest::IsHandledProtocol(FromV8Value(args[0]))); } // static v8::Handle Protocol::InterceptProtocol(const v8::Arguments& args) { - std::string scheme(*v8::String::Utf8Value(args[0])); + std::string scheme; + v8::Persistent callback; + if (!FromV8Arguments(args, &scheme, &callback)) + return node::ThrowTypeError("Bad argument"); + if (!GetRequestJobFactory()->HasProtocolHandler(scheme)) return node::ThrowError("Cannot intercept procotol"); @@ -243,10 +247,7 @@ v8::Handle Protocol::InterceptProtocol(const v8::Arguments& args) { return node::ThrowError(kEarlyUseProtocolError); // Store the handler in a map. - if (!args[1]->IsFunction()) - return node::ThrowError("Handler must be a function"); - g_handlers[scheme] = v8::Persistent::New( - node::node_isolate, v8::Handle::Cast(args[1])); + g_handlers[scheme] = callback; content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, @@ -256,7 +257,9 @@ v8::Handle Protocol::InterceptProtocol(const v8::Arguments& args) { // static v8::Handle Protocol::UninterceptProtocol(const v8::Arguments& args) { - std::string scheme(*v8::String::Utf8Value(args[0])); + std::string scheme; + if (!FromV8Arguments(args, &scheme)) + return node::ThrowTypeError("Bad argument"); if (AtomBrowserContext::Get()->url_request_context_getter() == NULL) return node::ThrowError(kEarlyUseProtocolError); diff --git a/browser/api/atom_api_window.cc b/browser/api/atom_api_window.cc index 4749f7ec04b..64ee74b9f9d 100644 --- a/browser/api/atom_api_window.cc +++ b/browser/api/atom_api_window.cc @@ -6,6 +6,7 @@ #include "base/values.h" #include "browser/native_window.h" +#include "common/v8_conversions.h" #include "common/v8_value_converter_impl.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" @@ -27,15 +28,6 @@ namespace atom { namespace api { -namespace { - -// Converts string16 to V8 String. -v8::Handle UTF16ToV8String(const string16& s) { - return v8::String::New(reinterpret_cast(s.data()), s.size()); -} - -} // namespace - Window::Window(v8::Handle wrapper, base::DictionaryValue* options) : EventEmitter(wrapper), window_(NativeWindow::Create(options)) { @@ -141,7 +133,7 @@ v8::Handle Window::Focus(const v8::Arguments &args) { // static v8::Handle Window::IsFocused(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->IsFocused()); + return ToV8Value(self->window_->IsFocused()); } // static @@ -202,10 +194,11 @@ v8::Handle Window::Restore(const v8::Arguments &args) { v8::Handle Window::SetFullscreen(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1 || !args[0]->IsBoolean()) + bool fs; + if (!FromV8Arguments(args, &fs)) return node::ThrowTypeError("Bad argument"); - self->window_->SetFullscreen(args[0]->BooleanValue()); + self->window_->SetFullscreen(fs); return v8::Undefined(); } @@ -213,18 +206,18 @@ v8::Handle Window::SetFullscreen(const v8::Arguments &args) { v8::Handle Window::IsFullscreen(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->IsFullscreen()); + return ToV8Value(self->window_->IsFullscreen()); } // static v8::Handle Window::SetSize(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 2) + int width, height; + if (!FromV8Arguments(args, &width, &height)) return node::ThrowTypeError("Bad argument"); - self->window_->SetSize( - gfx::Size(args[0]->IntegerValue(), args[1]->IntegerValue())); + self->window_->SetSize(gfx::Size(width, height)); return v8::Undefined(); } @@ -234,8 +227,8 @@ v8::Handle Window::GetSize(const v8::Arguments &args) { gfx::Size size = self->window_->GetSize(); v8::Handle ret = v8::Array::New(2); - ret->Set(0, v8::Integer::New(size.width())); - ret->Set(1, v8::Integer::New(size.height())); + ret->Set(0, ToV8Value(size.width())); + ret->Set(1, ToV8Value(size.height())); return ret; } @@ -244,11 +237,11 @@ v8::Handle Window::GetSize(const v8::Arguments &args) { v8::Handle Window::SetMinimumSize(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 2) + int width, height; + if (!FromV8Arguments(args, &width, &height)) return node::ThrowTypeError("Bad argument"); - self->window_->SetMinimumSize( - gfx::Size(args[0]->IntegerValue(), args[1]->IntegerValue())); + self->window_->SetMinimumSize(gfx::Size(width, height)); return v8::Undefined(); } @@ -258,8 +251,8 @@ v8::Handle Window::GetMinimumSize(const v8::Arguments &args) { gfx::Size size = self->window_->GetMinimumSize(); v8::Handle ret = v8::Array::New(2); - ret->Set(0, v8::Integer::New(size.width())); - ret->Set(1, v8::Integer::New(size.height())); + ret->Set(0, ToV8Value(size.width())); + ret->Set(1, ToV8Value(size.height())); return ret; } @@ -268,11 +261,12 @@ v8::Handle Window::GetMinimumSize(const v8::Arguments &args) { v8::Handle Window::SetMaximumSize(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 2) - return node::ThrowTypeError("Bad argument"); - self->window_->SetMaximumSize( - gfx::Size(args[0]->IntegerValue(), args[1]->IntegerValue())); + int width, height; + if (!FromV8Arguments(args, &width, &height)) + return node::ThrowTypeError("Bad argument"); + + self->window_->SetMaximumSize(gfx::Size(width, height)); return v8::Undefined(); } @@ -282,8 +276,8 @@ v8::Handle Window::GetMaximumSize(const v8::Arguments &args) { gfx::Size size = self->window_->GetMaximumSize(); v8::Handle ret = v8::Array::New(2); - ret->Set(0, v8::Integer::New(size.width())); - ret->Set(1, v8::Integer::New(size.height())); + ret->Set(0, ToV8Value(size.width())); + ret->Set(1, ToV8Value(size.height())); return ret; } @@ -292,10 +286,11 @@ v8::Handle Window::GetMaximumSize(const v8::Arguments &args) { v8::Handle Window::SetResizable(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1 || !args[0]->IsBoolean()) + bool resizable; + if (!FromV8Arguments(args, &resizable)) return node::ThrowTypeError("Bad argument"); - self->window_->SetResizable(args[0]->BooleanValue()); + self->window_->SetResizable(resizable); return v8::Undefined(); } @@ -303,17 +298,18 @@ v8::Handle Window::SetResizable(const v8::Arguments &args) { v8::Handle Window::IsResizable(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->IsResizable()); + return ToV8Value(self->window_->IsResizable()); } // static v8::Handle Window::SetAlwaysOnTop(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1 || !args[0]->IsBoolean()) + bool top; + if (!FromV8Arguments(args, &top)) return node::ThrowTypeError("Bad argument"); - self->window_->SetAlwaysOnTop(args[0]->BooleanValue()); + self->window_->SetAlwaysOnTop(top); return v8::Undefined(); } @@ -321,7 +317,7 @@ v8::Handle Window::SetAlwaysOnTop(const v8::Arguments &args) { v8::Handle Window::IsAlwaysOnTop(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->IsAlwaysOnTop()); + return ToV8Value(self->window_->IsAlwaysOnTop()); } // static @@ -337,11 +333,11 @@ v8::Handle Window::Center(const v8::Arguments &args) { v8::Handle Window::SetPosition(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 2) + int x, y; + if (!FromV8Arguments(args, &x, &y)) return node::ThrowTypeError("Bad argument"); - self->window_->SetPosition( - gfx::Point(args[0]->IntegerValue(), args[1]->IntegerValue())); + self->window_->SetPosition(gfx::Point(x, y)); return v8::Undefined(); } @@ -351,8 +347,8 @@ v8::Handle Window::GetPosition(const v8::Arguments &args) { gfx::Point pos = self->window_->GetPosition(); v8::Handle ret = v8::Array::New(2); - ret->Set(0, v8::Integer::New(pos.x())); - ret->Set(1, v8::Integer::New(pos.y())); + ret->Set(0, ToV8Value(pos.x())); + ret->Set(1, ToV8Value(pos.y())); return ret; } @@ -361,20 +357,18 @@ v8::Handle Window::GetPosition(const v8::Arguments &args) { v8::Handle Window::SetTitle(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1 || !args[0]->IsString()) + std::string title; + if (!FromV8Arguments(args, &title)) return node::ThrowTypeError("Bad argument"); - self->window_->SetTitle(*v8::String::Utf8Value(args[0])); + self->window_->SetTitle(title); return v8::Undefined(); } // static v8::Handle Window::GetTitle(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - - std::string title = self->window_->GetTitle(); - - return v8::String::New(title.c_str(), title.size()); + return ToV8Value(self->window_->GetTitle()); } // static @@ -391,10 +385,11 @@ v8::Handle Window::FlashFrame(const v8::Arguments &args) { v8::Handle Window::SetKiosk(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1 || !args[0]->IsBoolean()) + bool kiosk; + if (!FromV8Arguments(args, &kiosk)) return node::ThrowTypeError("Bad argument"); - self->window_->SetKiosk(args[0]->BooleanValue()); + self->window_->SetKiosk(kiosk); return v8::Undefined(); } @@ -402,7 +397,7 @@ v8::Handle Window::SetKiosk(const v8::Arguments &args) { v8::Handle Window::IsKiosk(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->IsKiosk()); + return ToV8Value(self->window_->IsKiosk()); } // static @@ -427,9 +422,11 @@ v8::Handle Window::CloseDevTools(const v8::Arguments &args) { v8::Handle Window::InspectElement(const v8::Arguments& args) { UNWRAP_WINDOW_AND_CHECK; - self->window_->InspectElement(args[0]->IntegerValue(), - args[1]->IntegerValue()); + int x, y; + if (!FromV8Arguments(args, &x, &y)) + return node::ThrowTypeError("Bad argument"); + self->window_->InspectElement(x, y); return v8::Undefined(); } @@ -454,7 +451,7 @@ v8::Handle Window::BlurWebView(const v8::Arguments &args) { // static v8::Handle Window::IsWebViewFocused(const v8::Arguments& args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->IsWebViewFocused()); + return ToV8Value(self->window_->IsWebViewFocused()); } // static @@ -471,24 +468,21 @@ v8::Handle Window::RestartHangMonitorTimeout( v8::Handle Window::GetPageTitle(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - string16 title = self->window_->GetWebContents()->GetTitle(); - - return UTF16ToV8String(title); + return ToV8Value(self->window_->GetWebContents()->GetTitle()); } // static v8::Handle Window::IsLoading(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->GetWebContents()->IsLoading()); + return ToV8Value(self->window_->GetWebContents()->IsLoading()); } // static v8::Handle Window::IsWaitingForResponse(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New( - self->window_->GetWebContents()->IsWaitingForResponse()); + return ToV8Value(self->window_->GetWebContents()->IsWaitingForResponse()); } // static @@ -504,14 +498,14 @@ v8::Handle Window::Stop(const v8::Arguments &args) { v8::Handle Window::GetRoutingID(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Integer::New(self->window_->GetWebContents()->GetRoutingID()); + return ToV8Value(self->window_->GetWebContents()->GetRoutingID()); } // static v8::Handle Window::GetProcessID(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Integer::New( + return ToV8Value( self->window_->GetWebContents()->GetRenderProcessHost()->GetID()); } @@ -519,19 +513,20 @@ v8::Handle Window::GetProcessID(const v8::Arguments &args) { v8::Handle Window::IsCrashed(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - return v8::Boolean::New(self->window_->GetWebContents()->IsCrashed()); + return ToV8Value(self->window_->GetWebContents()->IsCrashed()); } // static v8::Handle Window::LoadURL(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1 || !args[0]->IsString()) + std::string url; + if (!FromV8Arguments(args, &url)) return node::ThrowTypeError("Bad argument"); NavigationController& controller = self->window_->GetWebContents()->GetController(); - controller.LoadURL(GURL(*v8::String::Utf8Value(args[0])), + controller.LoadURL(GURL(url), content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); @@ -549,7 +544,7 @@ v8::Handle Window::GetURL(const v8::Arguments &args) { if (controller.GetActiveEntry()) url = controller.GetActiveEntry()->GetVirtualURL().spec(); - return v8::String::New(url.c_str(), url.size()); + return ToV8Value(url); } // static @@ -559,7 +554,7 @@ v8::Handle Window::CanGoBack(const v8::Arguments &args) { NavigationController& controller = self->window_->GetWebContents()->GetController(); - return v8::Boolean::New(controller.CanGoBack()); + return ToV8Value(controller.CanGoBack()); } // static @@ -569,21 +564,21 @@ v8::Handle Window::CanGoForward(const v8::Arguments &args) { NavigationController& controller = self->window_->GetWebContents()->GetController(); - return v8::Boolean::New(controller.CanGoForward()); + return ToV8Value(controller.CanGoForward()); } // static v8::Handle Window::CanGoToOffset(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1) + int offset; + if (!FromV8Arguments(args, &offset)) return node::ThrowTypeError("Bad argument"); NavigationController& controller = self->window_->GetWebContents()->GetController(); - int offset = args[0]->IntegerValue(); - return v8::Boolean::New(controller.CanGoToOffset(offset)); + return ToV8Value(controller.CanGoToOffset(offset)); } // static @@ -612,12 +607,13 @@ v8::Handle Window::GoForward(const v8::Arguments &args) { v8::Handle Window::GoToIndex(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1) + int index; + if (!FromV8Arguments(args, &index)) return node::ThrowTypeError("Bad argument"); NavigationController& controller = self->window_->GetWebContents()->GetController(); - controller.GoToIndex(args[0]->IntegerValue()); + controller.GoToIndex(index); return v8::Undefined(); } @@ -626,12 +622,13 @@ v8::Handle Window::GoToIndex(const v8::Arguments &args) { v8::Handle Window::GoToOffset(const v8::Arguments &args) { UNWRAP_WINDOW_AND_CHECK; - if (args.Length() < 1) + int offset; + if (!FromV8Arguments(args, &offset)) return node::ThrowTypeError("Bad argument"); NavigationController& controller = self->window_->GetWebContents()->GetController(); - controller.GoToOffset(args[0]->IntegerValue()); + controller.GoToOffset(offset); return v8::Undefined(); } diff --git a/browser/api/atom_browser_bindings.cc b/browser/api/atom_browser_bindings.cc index fb102ff1c75..c2dc9e78cc0 100644 --- a/browser/api/atom_browser_bindings.cc +++ b/browser/api/atom_browser_bindings.cc @@ -8,6 +8,8 @@ #include "base/logging.h" #include "base/values.h" +#include "browser/api/atom_api_event.h" +#include "common/v8_conversions.h" #include "common/v8_value_converter_impl.h" #include "content/public/browser/browser_thread.h" #include "vendor/node/src/node.h" @@ -42,7 +44,7 @@ void AtomBrowserBindings::AfterLoad() { void AtomBrowserBindings::OnRendererMessage(int process_id, int routing_id, - const std::string& channel, + const string16& channel, const base::ListValue& args) { v8::HandleScope scope; @@ -53,7 +55,7 @@ void AtomBrowserBindings::OnRendererMessage(int process_id, // process.emit(channel, 'message', process_id, routing_id); std::vector> arguments; arguments.reserve(3 + args.GetSize()); - arguments.push_back(v8::String::New(channel.c_str(), channel.size())); + arguments.push_back(ToV8Value(channel)); const base::Value* value; if (args.Get(0, &value)) arguments.push_back(converter->ToV8Value(value, context)); @@ -72,21 +74,24 @@ void AtomBrowserBindings::OnRendererMessage(int process_id, void AtomBrowserBindings::OnRendererMessageSync( int process_id, int routing_id, - const std::string& channel, + const string16& channel, const base::ListValue& args, - base::DictionaryValue* result) { + NativeWindow* sender, + IPC::Message* message) { v8::HandleScope scope; v8::Handle context = v8::Context::GetCurrent(); scoped_ptr converter(new V8ValueConverterImpl()); - v8::Handle event = v8::Object::New(); + // Create the event object. + v8::Handle event = api::Event::CreateV8Object(); + api::Event::Unwrap(event)->SetSenderAndMessage(sender, message); // process.emit(channel, 'sync-message', event, process_id, routing_id); std::vector> arguments; arguments.reserve(3 + args.GetSize()); - arguments.push_back(v8::String::New(channel.c_str(), channel.size())); + arguments.push_back(ToV8Value(channel)); const base::Value* value; if (args.Get(0, &value)) arguments.push_back(converter->ToV8Value(value, context)); @@ -101,11 +106,6 @@ void AtomBrowserBindings::OnRendererMessageSync( } node::MakeCallback(node::process, "emit", arguments.size(), &arguments[0]); - - scoped_ptr base_event(converter->FromV8Value(event, context)); - DCHECK(base_event && base_event->IsType(base::Value::TYPE_DICTIONARY)); - - result->Swap(static_cast(base_event.get())); } } // namespace atom diff --git a/browser/api/atom_browser_bindings.h b/browser/api/atom_browser_bindings.h index 32a6fae6289..dde6997a8c7 100644 --- a/browser/api/atom_browser_bindings.h +++ b/browser/api/atom_browser_bindings.h @@ -5,17 +5,21 @@ #ifndef ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_ #define ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_ -#include - +#include "base/string16.h" #include "common/api/atom_bindings.h" namespace base { -class DictionaryValue; class ListValue; } +namespace IPC { +class Message; +} + namespace atom { +class NativeWindow; + class AtomBrowserBindings : public AtomBindings { public: AtomBrowserBindings(); @@ -27,15 +31,16 @@ class AtomBrowserBindings : public AtomBindings { // Called when received a message from renderer. void OnRendererMessage(int process_id, int routing_id, - const std::string& channel, + const string16& channel, const base::ListValue& args); // Called when received a synchronous message from renderer. void OnRendererMessageSync(int process_id, int routing_id, - const std::string& channel, + const string16& channel, const base::ListValue& args, - base::DictionaryValue* result); + NativeWindow* sender, + IPC::Message* message); // The require('atom').browserMainParts object. v8::Handle browser_main_parts() { diff --git a/browser/api/lib/dialog.coffee b/browser/api/lib/dialog.coffee index 59568bcf5b4..5b333f4a13b 100644 --- a/browser/api/lib/dialog.coffee +++ b/browser/api/lib/dialog.coffee @@ -1,4 +1,5 @@ binding = process.atomBinding 'dialog' +v8Util = process.atomBinding 'v8_util' BrowserWindow = require 'browser-window' fileDialogProperties = @@ -7,48 +8,71 @@ fileDialogProperties = messageBoxTypes = ['none', 'info', 'warning'] module.exports = - showOpenDialog: (options) -> - options = title: 'Open', properties: ['openFile'] unless options? - options.properties = options.properties ? ['openFile'] + showOpenDialog: (window, options, callback) -> + unless window?.constructor is BrowserWindow + # Shift. + callback = options + options = window + window = null + + options ?= title: 'Open', properties: ['openFile'] + options.properties ?= ['openFile'] throw new TypeError('Properties need to be array') unless Array.isArray options.properties properties = 0 for prop, value of fileDialogProperties properties |= value if prop in options.properties - options.title = options.title ? '' - options.defaultPath = options.defaultPath ? '' + options.title ?= '' + options.defaultPath ?= '' - binding.showOpenDialog options.title, options.defaultPath, properties + binding.showOpenDialog String(options.title), + String(options.defaultPath), + properties, + window, + callback - showSaveDialog: (window, options) -> - throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow - options = title: 'Save' unless options? - - options.title = options.title ? '' - options.defaultPath = options.defaultPath ? '' - - binding.showSaveDialog window, options.title, options.defaultPath - - showMessageBox: (window, options) -> - if window? and window.constructor isnt BrowserWindow + showSaveDialog: (window, options, callback) -> + unless window?.constructor is BrowserWindow + # Shift. + callback = options options = window window = null - options = type: 'none' unless options? - options.type = options.type ? 'none' + options ?= title: 'Save' + options.title ?= '' + options.defaultPath ?= '' + + binding.showSaveDialog String(options.title), + String(options.defaultPath), + window, + callback + + showMessageBox: (window, options, callback) -> + unless window?.constructor is BrowserWindow + # Shift. + callback = options + options = window + window = null + + options ?= type: 'none' + options.type ?= 'none' options.type = messageBoxTypes.indexOf options.type throw new TypeError('Invalid message box type') unless options.type > -1 throw new TypeError('Buttons need to be array') unless Array.isArray options.buttons - options.title = options.title ? '' - options.message = options.message ? '' - options.detail = options.detail ? '' + options.title ?= '' + options.message ?= '' + options.detail ?= '' binding.showMessageBox options.type, options.buttons, String(options.title), String(options.message), String(options.detail), - window + window, + callback + +# Mark standard asynchronous functions. +v8Util.setHiddenValue f, 'asynchronous', true for k, f of module.exports diff --git a/browser/api/lib/ipc.coffee b/browser/api/lib/ipc.coffee index 2aa4d06be59..970709ebc90 100644 --- a/browser/api/lib/ipc.coffee +++ b/browser/api/lib/ipc.coffee @@ -14,8 +14,13 @@ class Ipc extends EventEmitter constructor: -> process.on 'ATOM_INTERNAL_MESSAGE', (args...) => @emit(args...) - process.on 'ATOM_INTERNAL_MESSAGE_SYNC', (args...) => - @emit(args...) + process.on 'ATOM_INTERNAL_MESSAGE_SYNC', (channel, event, args...) => + set = (value) -> event.sendReply JSON.stringify(value) + + Object.defineProperty event, 'returnValue', {set} + Object.defineProperty event, 'result', {set} + + @emit(channel, event, args...) send: (processId, routingId, args...) -> @sendChannel(processId, routingId, 'message', args...) diff --git a/browser/atom/rpc-server.coffee b/browser/atom/rpc-server.coffee index f62cd098837..04d5fe2c948 100644 --- a/browser/atom/rpc-server.coffee +++ b/browser/atom/rpc-server.coffee @@ -61,28 +61,40 @@ unwrapArgs = (processId, routingId, args) -> args.map metaToValue +# Call a function and send reply asynchronously if it's a an asynchronous +# style function and the caller didn't pass a callback. +callFunction = (event, processId, routingId, func, caller, args) -> + if v8Util.getHiddenValue(func, 'asynchronous') and typeof args[args.length - 1] isnt 'function' + args.push (ret) -> + event.returnValue = valueToMeta processId, routingId, ret + func.apply caller, args + else + ret = func.apply caller, args + event.returnValue = valueToMeta processId, routingId, ret + ipc.on 'ATOM_BROWSER_REQUIRE', (event, processId, routingId, module) -> try - event.result = valueToMeta processId, routingId, require(module) + event.returnValue = valueToMeta processId, routingId, require(module) catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_GLOBAL', (event, processId, routingId, name) -> try - event.result = valueToMeta processId, routingId, global[name] + event.returnValue = valueToMeta processId, routingId, global[name] catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (event, processId, routingId) -> objectsRegistry.clear processId, routingId + event.returnValue = null ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) -> try BrowserWindow = require 'browser-window' window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId - event.result = valueToMeta processId, routingId, window + event.returnValue = valueToMeta processId, routingId, window catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) -> try @@ -91,18 +103,17 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) -> # Call new with array of arguments. # http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible obj = new (Function::bind.apply(constructor, [null].concat(args))) - event.result = valueToMeta processId, routingId, obj + event.returnValue = valueToMeta processId, routingId, obj catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) -> try args = unwrapArgs processId, routingId, args func = objectsRegistry.get id - ret = func.apply global, args - event.result = valueToMeta processId, routingId, ret + callFunction event, processId, routingId, func, global, args catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, processId, routingId, id, method, args) -> try @@ -110,32 +121,32 @@ ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, processId, routingId, id, meth constructor = objectsRegistry.get(id)[method] # Call new with array of arguments. obj = new (Function::bind.apply(constructor, [null].concat(args))) - event.result = valueToMeta processId, routingId, obj + event.returnValue = valueToMeta processId, routingId, obj catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) -> try args = unwrapArgs processId, routingId, args obj = objectsRegistry.get id - ret = obj[method].apply(obj, args) - event.result = valueToMeta processId, routingId, ret + callFunction event, processId, routingId, obj[method], obj, args catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, processId, routingId, id, name, value) -> try obj = objectsRegistry.get id obj[name] = value + event.returnValue = null catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, processId, routingId, id, name) -> try obj = objectsRegistry.get id - event.result = valueToMeta processId, routingId, obj[name] + event.returnValue = valueToMeta processId, routingId, obj[name] catch e - event.result = errorToMeta e + event.returnValue = errorToMeta e ipc.on 'ATOM_BROWSER_DEREFERENCE', (processId, routingId, storeId) -> objectsRegistry.remove processId, routingId, storeId diff --git a/browser/native_window.cc b/browser/native_window.cc index 154a1a60738..f6028195d30 100644 --- a/browser/native_window.cc +++ b/browser/native_window.cc @@ -300,7 +300,8 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(NativeWindow, message) IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage) - IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message_Sync, OnRendererMessageSync) + IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync, + OnRendererMessageSync) IPC_MESSAGE_HANDLER(AtomViewHostMsg_UpdateDraggableRegions, UpdateDraggableRegions) IPC_MESSAGE_UNHANDLED(handled = false) @@ -340,7 +341,7 @@ void NativeWindow::Observe(int type, } } -void NativeWindow::OnRendererMessage(const std::string& channel, +void NativeWindow::OnRendererMessage(const string16& channel, const base::ListValue& args) { AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessage( GetWebContents()->GetRenderProcessHost()->GetID(), @@ -349,15 +350,16 @@ void NativeWindow::OnRendererMessage(const std::string& channel, args); } -void NativeWindow::OnRendererMessageSync(const std::string& channel, +void NativeWindow::OnRendererMessageSync(const string16& channel, const base::ListValue& args, - base::DictionaryValue* result) { + IPC::Message* reply_msg) { AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessageSync( GetWebContents()->GetRenderProcessHost()->GetID(), GetWebContents()->GetRoutingID(), channel, args, - result); + this, + reply_msg); } } // namespace atom diff --git a/browser/native_window.h b/browser/native_window.h index 3316b07053c..b170c57af86 100644 --- a/browser/native_window.h +++ b/browser/native_window.h @@ -35,6 +35,10 @@ class Rect; class Size; } +namespace IPC { +class Message; +} + namespace atom { class AtomJavaScriptDialogManager; @@ -173,12 +177,12 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, private: void RendererUnresponsiveDelayed(); - void OnRendererMessage(const std::string& channel, + void OnRendererMessage(const string16& channel, const base::ListValue& args); - void OnRendererMessageSync(const std::string& channel, + void OnRendererMessageSync(const string16& channel, const base::ListValue& args, - base::DictionaryValue* result); + IPC::Message* reply_msg); // Notification manager. content::NotificationRegistrar registrar_; diff --git a/browser/ui/file_dialog.h b/browser/ui/file_dialog.h index 67b526f785b..96c0061cdb9 100644 --- a/browser/ui/file_dialog.h +++ b/browser/ui/file_dialog.h @@ -8,6 +8,7 @@ #include #include +#include "base/callback_forward.h" #include "base/files/file_path.h" namespace atom { @@ -23,16 +24,34 @@ enum FileDialogProperty { FILE_DIALOG_CREATE_DIRECTORY = 8, }; -bool ShowOpenDialog(const std::string& title, +typedef base::Callback& paths)> OpenDialogCallback; + +typedef base::Callback SaveDialogCallback; + +bool ShowOpenDialog(atom::NativeWindow* parent_window, + const std::string& title, const base::FilePath& default_path, int properties, std::vector* paths); -bool ShowSaveDialog(atom::NativeWindow* window, +void ShowOpenDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + int properties, + const OpenDialogCallback& callback); + +bool ShowSaveDialog(atom::NativeWindow* parent_window, const std::string& title, const base::FilePath& default_path, base::FilePath* path); +void ShowSaveDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + const SaveDialogCallback& callback); + } // namespace file_dialog #endif // BROWSER_UI_FILE_DIALOG_H_ diff --git a/browser/ui/file_dialog_mac.mm b/browser/ui/file_dialog_mac.mm index d51be89a23a..fd28cf55d6f 100644 --- a/browser/ui/file_dialog_mac.mm +++ b/browser/ui/file_dialog_mac.mm @@ -38,20 +38,11 @@ void SetupDialog(NSSavePanel* dialog, if (default_filename) [dialog setNameFieldStringValue:default_filename]; + [dialog setCanSelectHiddenExtension:YES]; [dialog setAllowsOtherFileTypes:YES]; } -} // namespace - -bool ShowOpenDialog(const std::string& title, - const base::FilePath& default_path, - int properties, - std::vector* paths) { - DCHECK(paths); - NSOpenPanel* dialog = [NSOpenPanel openPanel]; - - SetupDialog(dialog, title, default_path); - +void SetupDialogForProperties(NSOpenPanel* dialog, int properties) { [dialog setCanChooseFiles:(properties & FILE_DIALOG_OPEN_FILE)]; if (properties & FILE_DIALOG_OPEN_DIRECTORY) [dialog setCanChooseDirectories:YES]; @@ -59,49 +50,119 @@ bool ShowOpenDialog(const std::string& title, [dialog setCanCreateDirectories:YES]; if (properties & FILE_DIALOG_MULTI_SELECTIONS) [dialog setAllowsMultipleSelection:YES]; +} - if ([dialog runModal] == NSFileHandlingPanelCancelButton) - return false; +// Run modal dialog with parent window and return user's choice. +int RunModalDialog(NSSavePanel* dialog, atom::NativeWindow* parent_window) { + __block int chosen = NSFileHandlingPanelCancelButton; + if (parent_window == NULL) { + chosen = [dialog runModal]; + } else { + NSWindow* window = parent_window->GetNativeWindow(); + [dialog beginSheetModalForWindow:window + completionHandler:^(NSInteger c) { + chosen = c; + [NSApp stopModal]; + }]; + [NSApp runModalForWindow:window]; + } + + return chosen; +} + +void ReadDialogPaths(NSOpenPanel* dialog, std::vector* paths) { NSArray* urls = [dialog URLs]; for (NSURL* url in urls) if ([url isFileURL]) paths->push_back(base::FilePath(base::SysNSStringToUTF8([url path]))); +} +} // namespace + +bool ShowOpenDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + int properties, + std::vector* paths) { + DCHECK(paths); + NSOpenPanel* dialog = [NSOpenPanel openPanel]; + + SetupDialog(dialog, title, default_path); + SetupDialogForProperties(dialog, properties); + + int chosen = RunModalDialog(dialog, parent_window); + if (chosen == NSFileHandlingPanelCancelButton) + return false; + + ReadDialogPaths(dialog, paths); return true; } -bool ShowSaveDialog(atom::NativeWindow* window, +void ShowOpenDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + int properties, + const OpenDialogCallback& c) { + NSOpenPanel* dialog = [NSOpenPanel openPanel]; + + SetupDialog(dialog, title, default_path); + SetupDialogForProperties(dialog, properties); + + // Duplicate the callback object here since c is a reference and gcd would + // only store the pointer, by duplication we can force gcd to store a copy. + __block OpenDialogCallback callback = c; + + NSWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; + [dialog beginSheetModalForWindow:window + completionHandler:^(NSInteger chosen) { + if (chosen == NSFileHandlingPanelCancelButton) { + callback.Run(false, std::vector()); + } else { + std::vector paths; + ReadDialogPaths(dialog, &paths); + callback.Run(true, paths); + } + }]; +} + +bool ShowSaveDialog(atom::NativeWindow* parent_window, const std::string& title, const base::FilePath& default_path, base::FilePath* path) { - DCHECK(window); DCHECK(path); NSSavePanel* dialog = [NSSavePanel savePanel]; SetupDialog(dialog, title, default_path); - [dialog setCanSelectHiddenExtension:YES]; + int chosen = RunModalDialog(dialog, parent_window); + if (chosen == NSFileHandlingPanelCancelButton || ![[dialog URL] isFileURL]) + return false; - __block bool result = false; - __block base::FilePath ret_path; - [dialog beginSheetModalForWindow:window->GetNativeWindow() + *path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path])); + return true; +} + +void ShowSaveDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + const SaveDialogCallback& c) { + NSSavePanel* dialog = [NSSavePanel savePanel]; + + SetupDialog(dialog, title, default_path); + + __block SaveDialogCallback callback = c; + + NSWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; + [dialog beginSheetModalForWindow:window completionHandler:^(NSInteger chosen) { - if (chosen == NSFileHandlingPanelCancelButton || - ![[dialog URL] isFileURL]) { - result = false; + if (chosen == NSFileHandlingPanelCancelButton) { + callback.Run(false, base::FilePath()); } else { - result = true; - ret_path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path])); + std::string path = base::SysNSStringToUTF8([[dialog URL] path]); + callback.Run(true, base::FilePath(path)); } - - [NSApp stopModal]; }]; - - [NSApp runModalForWindow:window->GetNativeWindow()]; - - *path = ret_path; - return result; } } // namespace file_dialog diff --git a/browser/ui/file_dialog_win.cc b/browser/ui/file_dialog_win.cc index b5c6d1aad4c..fa5f6912d02 100644 --- a/browser/ui/file_dialog_win.cc +++ b/browser/ui/file_dialog_win.cc @@ -162,7 +162,8 @@ class FileDialog { SetDefaultFolder(default_path); } - bool Show(HWND window) { + bool Show(atom::NativeWindow* parent_window) { + HWND window = parent_window ? parent_window->GetNativeWindow() : NULL; return dialog_->DoModal(window) == IDOK; } @@ -198,7 +199,8 @@ class FileDialog { } // namespace -bool ShowOpenDialog(const std::string& title, +bool ShowOpenDialog(atom::NativeWindow* parent_window, + const std::string& title, const base::FilePath& default_path, int properties, std::vector* paths) { @@ -214,7 +216,7 @@ bool ShowOpenDialog(const std::string& title, options, std::vector(), std::vector()); - if (!open_dialog.Show(::GetActiveWindow())) + if (!open_dialog.Show(parent_window)) return false; ATL::CComPtr items; @@ -247,7 +249,21 @@ bool ShowOpenDialog(const std::string& title, return true; } -bool ShowSaveDialog(atom::NativeWindow* window, +void ShowOpenDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + int properties, + const OpenDialogCallback& callback) { + std::vector paths; + bool result = ShowOpenDialog(parent_window, + title, + default_path, + properties, + &paths); + callback.Run(result, paths); +} + +bool ShowSaveDialog(atom::NativeWindow* parent_window, const std::string& title, const base::FilePath& default_path, base::FilePath* path) { @@ -263,7 +279,7 @@ bool ShowSaveDialog(atom::NativeWindow* window, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT, file_ext, std::vector()); - if (!save_dialog.Show(window->GetNativeWindow())) + if (!save_dialog.Show(parent_window)) return false; wchar_t file_name[MAX_PATH]; @@ -290,4 +306,13 @@ bool ShowSaveDialog(atom::NativeWindow* window, return true; } +void ShowSaveDialog(atom::NativeWindow* parent_window, + const std::string& title, + const base::FilePath& default_path, + const SaveDialogCallback& callback) { + base::FilePath path; + bool result = ShowSaveDialog(parent_window, title, default_path, &path); + callback.Run(result, path); +} + } // namespace file_dialog diff --git a/browser/ui/message_box.h b/browser/ui/message_box.h index dfe7198ba67..529e46595cc 100644 --- a/browser/ui/message_box.h +++ b/browser/ui/message_box.h @@ -8,6 +8,8 @@ #include #include +#include "base/callback_forward.h" + namespace atom { class NativeWindow; @@ -18,6 +20,8 @@ enum MessageBoxType { MESSAGE_BOX_TYPE_WARNING }; +typedef base::Callback MessageBoxCallback; + int ShowMessageBox(NativeWindow* parent_window, MessageBoxType type, const std::vector& buttons, @@ -25,6 +29,14 @@ int ShowMessageBox(NativeWindow* parent_window, const std::string& message, const std::string& detail); +void ShowMessageBox(NativeWindow* parent_window, + MessageBoxType type, + const std::vector& buttons, + const std::string& title, + const std::string& message, + const std::string& detail, + const MessageBoxCallback& callback); + } // namespace atom #endif // BROWSER_UI_MESSAGE_BOX_H_ diff --git a/browser/ui/message_box_mac.mm b/browser/ui/message_box_mac.mm index c6fb14555b0..b5d0cc4bb58 100644 --- a/browser/ui/message_box_mac.mm +++ b/browser/ui/message_box_mac.mm @@ -6,20 +6,53 @@ #import +#include "base/callback.h" #include "base/strings/sys_string_conversions.h" #include "browser/native_window.h" #include "browser/ui/nsalert_synchronous_sheet_mac.h" +@interface ModalDelegate : NSObject { + @private + atom::MessageBoxCallback callback_; + NSAlert* alert_; +} +- (id)initWithCallback:(const atom::MessageBoxCallback&)callback + andAlert:(NSAlert*)alert; +@end + +@implementation ModalDelegate + +- (id)initWithCallback:(const atom::MessageBoxCallback&)callback + andAlert:(NSAlert*)alert { + if ((self = [super init])) { + callback_ = callback; + alert_ = alert; + } + return self; +} + +- (void)alertDidEnd:(NSAlert*)alert + returnCode:(NSInteger)returnCode + contextInfo:(void*)contextInfo { + callback_.Run(returnCode); + [alert_ release]; + [self release]; +} + +@end + namespace atom { -int ShowMessageBox(NativeWindow* parent_window, - MessageBoxType type, - const std::vector& buttons, - const std::string& title, - const std::string& message, - const std::string& detail) { +namespace { + +NSAlert* CreateNSAlert(NativeWindow* parent_window, + MessageBoxType type, + const std::vector& buttons, + const std::string& title, + const std::string& message, + const std::string& detail) { // Ignore the title; it's the window title on other platforms and ignorable. - NSAlert* alert = [[[NSAlert alloc] init] autorelease]; + NSAlert* alert = [[NSAlert alloc] init]; [alert setMessageText:base::SysUTF8ToNSString(message)]; [alert setInformativeText:base::SysUTF8ToNSString(detail)]; @@ -40,10 +73,44 @@ int ShowMessageBox(NativeWindow* parent_window, [button setTag:i]; } + return alert; +} + +} // namespace + +int ShowMessageBox(NativeWindow* parent_window, + MessageBoxType type, + const std::vector& buttons, + const std::string& title, + const std::string& message, + const std::string& detail) { + NSAlert* alert = CreateNSAlert( + parent_window, type, buttons, title, message, detail); + [alert autorelease]; + if (parent_window) return [alert runModalSheetForWindow:parent_window->GetNativeWindow()]; else return [alert runModal]; } +void ShowMessageBox(NativeWindow* parent_window, + MessageBoxType type, + const std::vector& buttons, + const std::string& title, + const std::string& message, + const std::string& detail, + const MessageBoxCallback& callback) { + NSAlert* alert = CreateNSAlert( + parent_window, type, buttons, title, message, detail); + ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback + andAlert:alert]; + + NSWindow* window = parent_window ? parent_window->GetNativeWindow() : nil; + [alert beginSheetModalForWindow:window + modalDelegate:delegate + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:nil]; +} + } // namespace atom diff --git a/browser/ui/message_box_win.cc b/browser/ui/message_box_win.cc index 52181e40780..68267c6ddb9 100644 --- a/browser/ui/message_box_win.cc +++ b/browser/ui/message_box_win.cc @@ -4,6 +4,7 @@ #include "browser/ui/message_box.h" +#include "base/callback.h" #include "base/message_loop.h" #include "base/run_loop.h" #include "base/string_util.h" @@ -38,7 +39,14 @@ class MessageDialog : public base::MessageLoop::Dispatcher, const std::string& detail); virtual ~MessageDialog(); - int result() const { return result_; } + void Show(); + + int GetResult() const; + + void set_callback(const MessageBoxCallback& callback) { + delete_on_close_ = true; + callback_ = callback; + } private: // Overridden from MessageLoop::Dispatcher: @@ -63,11 +71,13 @@ class MessageDialog : public base::MessageLoop::Dispatcher, const ui::Event& event) OVERRIDE; bool should_close_; + bool delete_on_close_; int result_; string16 title_; views::Widget* widget_; views::MessageBoxView* message_box_view_; std::vector buttons_; + MessageBoxCallback callback_; DISALLOW_COPY_AND_ASSIGN(MessageDialog); }; @@ -82,6 +92,7 @@ MessageDialog::MessageDialog(NativeWindow* parent_window, const std::string& message, const std::string& detail) : should_close_(false), + delete_on_close_(false), result_(-1), title_(UTF8ToUTF16(title)), widget_(NULL), @@ -124,12 +135,30 @@ MessageDialog::MessageDialog(NativeWindow* parent_window, set_background(views::Background::CreateSolidBackground( skia::COLORREFToSkColor(GetSysColor(COLOR_WINDOW)))); - widget_->Show(); } MessageDialog::~MessageDialog() { } +void MessageDialog::Show() { + widget_->Show(); +} + +int MessageDialog::GetResult() const { + // When the dialog is closed without choosing anything, we think the user + // chose 'Cancel', otherwise we think the default behavior is chosen. + if (result_ == -1) { + for (size_t i = 0; i < buttons_.size(); ++i) + if (LowerCaseEqualsASCII(buttons_[i]->GetText(), "cancel")) { + return i; + } + + return 0; + } else { + return result_; + } +} + //////////////////////////////////////////////////////////////////////////////// // MessageDialog, private: @@ -145,6 +174,11 @@ string16 MessageDialog::GetWindowTitle() const { void MessageDialog::WindowClosing() { should_close_ = true; + + if (delete_on_close_) { + callback_.Run(GetResult()); + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); + } } views::Widget* MessageDialog::GetWidget() { @@ -232,6 +266,7 @@ int ShowMessageBox(NativeWindow* parent_window, const std::string& message, const std::string& detail) { MessageDialog dialog(parent_window, type, buttons, title, message, detail); + dialog.Show(); { base::MessageLoop::ScopedNestableTaskAllower allow( base::MessageLoopForUI::current()); @@ -239,18 +274,21 @@ int ShowMessageBox(NativeWindow* parent_window, run_loop.Run(); } - // When the dialog is closed without choosing anything, we think the user - // chose 'Cancel', otherwise we think the default behavior is chosen. - if (dialog.result() == -1) { - for (size_t i = 0; i < buttons.size(); ++i) - if (LowerCaseEqualsASCII(buttons[i], "cancel")) { - return i; - } + return dialog.GetResult(); +} - return 0; - } else { - return dialog.result(); - } +void ShowMessageBox(NativeWindow* parent_window, + MessageBoxType type, + const std::vector& buttons, + const std::string& title, + const std::string& message, + const std::string& detail, + const MessageBoxCallback& callback) { + // The dialog would be deleted when the dialog is closed. + MessageDialog* dialog = new MessageDialog( + parent_window, type, buttons, title, message, detail); + dialog->set_callback(callback); + dialog->Show(); } } // namespace atom diff --git a/common/api/api_messages.h b/common/api/api_messages.h index ee031e6487e..8d21a7d93c0 100644 --- a/common/api/api_messages.h +++ b/common/api/api_messages.h @@ -4,8 +4,7 @@ // Multiply-included file, no traditional include guard. -#include - +#include "base/string16.h" #include "base/values.h" #include "common/draggable_region.h" #include "content/public/common/common_param_traits.h" @@ -22,16 +21,16 @@ IPC_STRUCT_TRAITS_BEGIN(atom::DraggableRegion) IPC_STRUCT_TRAITS_END() IPC_MESSAGE_ROUTED2(AtomViewHostMsg_Message, - std::string /* channel */, + string16 /* channel */, ListValue /* arguments */) IPC_SYNC_MESSAGE_ROUTED2_1(AtomViewHostMsg_Message_Sync, - std::string /* channel */, + string16 /* channel */, ListValue /* arguments */, - DictionaryValue /* result */) + string16 /* result (in JSON) */) IPC_MESSAGE_ROUTED2(AtomViewMsg_Message, - std::string /* channel */, + string16 /* channel */, ListValue /* arguments */) // Sent by the renderer when the draggable regions are updated. diff --git a/common/v8_conversions.h b/common/v8_conversions.h new file mode 100644 index 00000000000..c59a88a850f --- /dev/null +++ b/common/v8_conversions.h @@ -0,0 +1,228 @@ +// 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 COMMON_V8_CONVERSIONS_H_ +#define COMMON_V8_CONVERSIONS_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "base/string16.h" +#include "browser/api/atom_api_window.h" +#include "v8/include/v8.h" + +// Convert V8 value to arbitrary supported types. +struct FromV8Value { + explicit FromV8Value(v8::Handle 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(*s), s.length()); + } + + operator base::FilePath() { + return base::FilePath::FromUTF8Unsafe(FromV8Value(value_)); + } + + operator std::vector() { + std::vector array; + v8::Handle v8_array = v8::Handle::Cast(value_); + for (uint32_t i = 0; i < v8_array->Length(); ++i) + array.push_back(FromV8Value(v8_array->Get(i))); + + return array; + } + + operator atom::NativeWindow*() { + using atom::api::Window; + if (value_->IsObject()) { + Window* window = Window::Unwrap(value_->ToObject()); + if (window && window->window()) + return window->window(); + } + return NULL; + } + + operator v8::Persistent() { + return value_->IsFunction() ? + v8::Persistent::New( + node::node_isolate, + v8::Handle::Cast(value_)) : + v8::Persistent(); + } + + v8::Handle value_; +}; + +// Convert arbitrary supported native type to V8 value. +inline v8::Handle ToV8Value(int i) { + return v8::Integer::New(i); +} + +inline v8::Handle ToV8Value(bool b) { + return v8::Boolean::New(b); +} + +inline v8::Handle ToV8Value(const std::string& s) { + return v8::String::New(s.data(), s.size()); +} + +inline v8::Handle ToV8Value(const string16& s) { + return v8::String::New(reinterpret_cast(s.data()), s.size()); +} + +inline v8::Handle ToV8Value(const base::FilePath& path) { + std::string path_string(path.AsUTF8Unsafe()); + return v8::String::New(path_string.data(), path_string.size()); +} + +inline v8::Handle ToV8Value(void* whatever) { + return v8::Undefined(); +} + +inline +v8::Handle ToV8Value(const std::vector& paths) { + v8::Handle result = v8::Array::New(paths.size()); + for (size_t i = 0; i < paths.size(); ++i) + result->Set(i, ToV8Value(paths[i])); + return result; +} + +// Check if a V8 Value is of specified type. +template inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + return false; +} + +template<> inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + return value->IsNumber(); +} + +template<> inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + return value->IsBoolean(); +} + +template<> inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + return value->IsString(); +} + +template<> inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + return V8ValueCanBeConvertedTo(value); +} + +template<> inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + return V8ValueCanBeConvertedTo(value); +} + +template<> inline +bool V8ValueCanBeConvertedTo>( + v8::Handle value) { + return value->IsArray(); +} + +template<> inline +bool V8ValueCanBeConvertedTo(v8::Handle value) { + using atom::api::Window; + if (value->IsObject()) { + Window* window = Window::Unwrap(value->ToObject()); + if (window && window->window()) + return true; + } + return false; +} + +template<> inline +bool V8ValueCanBeConvertedTo>( + v8::Handle value) { + return value->IsFunction(); +} + +// Check and convert V8's Arguments to native types. +template inline +bool FromV8Arguments(const v8::Arguments& args, T1* value, int index = 0) { + if (!V8ValueCanBeConvertedTo(args[index])) + return false; + *value = static_cast(FromV8Value(args[index])); + return true; +} + +template inline +bool FromV8Arguments(const v8::Arguments& args, T1* a1, T2* a2) { + return FromV8Arguments(args, a1) && FromV8Arguments(args, a2, 1); +} + +template inline +bool FromV8Arguments(const v8::Arguments& args, T1* a1, T2* a2, T3* a3) { + return FromV8Arguments(args, a1, a2) && + FromV8Arguments(args, a3, 2); +} + +template inline +bool FromV8Arguments(const v8::Arguments& args, + T1* a1, + T2* a2, + T3* a3, + T4* a4) { + return FromV8Arguments(args, a1, a2, a3) && + FromV8Arguments(args, a4, 3); +} + +template inline +bool FromV8Arguments(const v8::Arguments& args, + T1* a1, + T2* a2, + T3* a3, + T4* a4, + T5* a5) { + return FromV8Arguments(args, a1, a2, a3, a4) && + FromV8Arguments(args, a5, 4); +} + +template inline +bool FromV8Arguments(const v8::Arguments& args, + T1* a1, + T2* a2, + T3* a3, + T4* a4, + T5* a5, + T6* a6) { + return FromV8Arguments(args, a1, a2, a3, a4, a5) && + FromV8Arguments(args, a6, 5); +} + +template inline +bool FromV8Arguments(const v8::Arguments& args, + T1* a1, + T2* a2, + T3* a3, + T4* a4, + T5* a5, + T6* a6, + T7* a7) { + return + FromV8Arguments(args, a1, a2, a3, a4, a5, a6) && + FromV8Arguments(args, a7, 6); +} + +#endif // COMMON_V8_CONVERSIONS_H_ diff --git a/docs/api/browser/ipc-browser.md b/docs/api/browser/ipc-browser.md index 34ebda8a4dc..2edaa29463a 100644 --- a/docs/api/browser/ipc-browser.md +++ b/docs/api/browser/ipc-browser.md @@ -18,7 +18,7 @@ Emitted when renderer sent a message to the browser. * `routingId` Integer Emitted when renderer sent a synchronous message to the browser. The receiver -should store the result in `event.result`. +should store the result in `event.returnValue`. **Note:** Due to the limitation of `EventEmitter`, returning value in the event handler has no effect, so we have to store the result by using the diff --git a/docs/api/renderer/ipc-renderer.md b/docs/api/renderer/ipc-renderer.md index cdeadf39f68..4076b93b395 100644 --- a/docs/api/renderer/ipc-renderer.md +++ b/docs/api/renderer/ipc-renderer.md @@ -30,7 +30,7 @@ An example of sending synchronous message from renderer to browser: // In browser: var ipc = require('ipc'); ipc.on('browser-data-request', function(event, processId, routingId, message) { - event.result = 'THIS SOME DATA FROM THE BROWSER'; + event.returnValue = 'THIS SOME DATA FROM THE BROWSER'; }); ``` diff --git a/renderer/api/atom_api_renderer_ipc.cc b/renderer/api/atom_api_renderer_ipc.cc index 022ad28741d..d1263479722 100644 --- a/renderer/api/atom_api_renderer_ipc.cc +++ b/renderer/api/atom_api_renderer_ipc.cc @@ -6,6 +6,7 @@ #include "base/values.h" #include "common/api/api_messages.h" +#include "common/v8_conversions.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/v8_value_converter.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" @@ -47,8 +48,7 @@ v8::Handle RendererIPC::Send(const v8::Arguments &args) { if (!args[0]->IsString()) return node::ThrowTypeError("Bad argument"); - std::string channel(*v8::String::Utf8Value(args[0])); - + string16 channel = FromV8Value(args[0]); RenderView* render_view = GetCurrentRenderView(); // Convert Arguments to Array, so we can use V8ValueConverter to convert it @@ -82,7 +82,7 @@ v8::Handle RendererIPC::SendSync(const v8::Arguments &args) { return node::ThrowTypeError("Bad argument"); v8::Handle context = v8::Context::GetCurrent(); - std::string channel(*v8::String::Utf8Value(args[0])); + string16 channel = FromV8Value(args[0]); // Convert Arguments to Array, so we can use V8ValueConverter to convert it // to ListValue. @@ -97,12 +97,12 @@ v8::Handle RendererIPC::SendSync(const v8::Arguments &args) { RenderView* render_view = GetCurrentRenderView(); - base::DictionaryValue result; + string16 json; IPC::SyncMessage* message = new AtomViewHostMsg_Message_Sync( render_view->GetRoutingID(), channel, *static_cast(arguments.get()), - &result); + &json); // Enable the UI thread in browser to receive messages. message->EnableMessagePumping(); bool success = render_view->Send(message); @@ -110,7 +110,7 @@ v8::Handle RendererIPC::SendSync(const v8::Arguments &args) { if (!success) return node::ThrowError("Unable to send AtomViewHostMsg_Message_Sync"); - return scope.Close(converter->ToV8Value(&result, context)); + return scope.Close(ToV8Value(json)); } // static diff --git a/renderer/api/atom_renderer_bindings.cc b/renderer/api/atom_renderer_bindings.cc index 9471277d000..6609d04eb66 100644 --- a/renderer/api/atom_renderer_bindings.cc +++ b/renderer/api/atom_renderer_bindings.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/values.h" +#include "common/v8_conversions.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/v8_value_converter.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" @@ -51,7 +52,7 @@ void AtomRendererBindings::BindToFrame(WebFrame* frame) { AtomBindings::BindTo(GetProcessObject(context)); } -void AtomRendererBindings::OnBrowserMessage(const std::string& channel, +void AtomRendererBindings::OnBrowserMessage(const string16& channel, const base::ListValue& args) { if (!render_view_->GetWebView()) return; @@ -70,7 +71,7 @@ void AtomRendererBindings::OnBrowserMessage(const std::string& channel, std::vector> arguments; arguments.reserve(1 + args.GetSize()); - arguments.push_back(v8::String::New(channel.c_str(), channel.size())); + arguments.push_back(ToV8Value(channel)); for (size_t i = 0; i < args.GetSize(); i++) { const base::Value* value; diff --git a/renderer/api/atom_renderer_bindings.h b/renderer/api/atom_renderer_bindings.h index 6132ed457a8..394cdea41ec 100644 --- a/renderer/api/atom_renderer_bindings.h +++ b/renderer/api/atom_renderer_bindings.h @@ -5,10 +5,10 @@ #ifndef ATOM_RENDERER_API_ATOM_RENDERER_BINDINGS_H_ #define ATOM_RENDERER_API_ATOM_RENDERER_BINDINGS_H_ -#include - #include "common/api/atom_bindings.h" +#include "base/string16.h" + namespace base { class ListValue; } @@ -32,7 +32,7 @@ class AtomRendererBindings : public AtomBindings { void BindToFrame(WebKit::WebFrame* frame); // Dispatch messages from browser. - void OnBrowserMessage(const std::string& channel, + void OnBrowserMessage(const string16& channel, const base::ListValue& args); private: diff --git a/renderer/api/lib/ipc.coffee b/renderer/api/lib/ipc.coffee index 28b9a9692d0..c54545d8a2a 100644 --- a/renderer/api/lib/ipc.coffee +++ b/renderer/api/lib/ipc.coffee @@ -16,9 +16,11 @@ class Ipc extends EventEmitter ipc.send('ATOM_INTERNAL_MESSAGE', args...) sendSync: (args...) -> - ipc.sendSync('ATOM_INTERNAL_MESSAGE_SYNC', 'sync-message', args...).result + msg = ipc.sendSync('ATOM_INTERNAL_MESSAGE_SYNC', 'sync-message', args...) + JSON.parse(msg) sendChannelSync: (args...) -> - ipc.sendSync('ATOM_INTERNAL_MESSAGE_SYNC', args...).result + msg = ipc.sendSync('ATOM_INTERNAL_MESSAGE_SYNC', args...) + JSON.parse(msg) module.exports = new Ipc diff --git a/renderer/api/lib/remote.coffee b/renderer/api/lib/remote.coffee index d839a9899a9..7eaafc20aa5 100644 --- a/renderer/api/lib/remote.coffee +++ b/renderer/api/lib/remote.coffee @@ -72,6 +72,7 @@ metaToValue = (meta) -> ret.__defineSetter__ member.name, (value) -> # Set member data. ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_SET', meta.id, member.name, value + value ret.__defineGetter__ member.name, -> # Get member data. diff --git a/renderer/atom_render_view_observer.cc b/renderer/atom_render_view_observer.cc index 6a394e44737..4405d531afb 100644 --- a/renderer/atom_render_view_observer.cc +++ b/renderer/atom_render_view_observer.cc @@ -101,7 +101,7 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) { return handled; } -void AtomRenderViewObserver::OnBrowserMessage(const std::string& channel, +void AtomRenderViewObserver::OnBrowserMessage(const string16& channel, const base::ListValue& args) { atom_bindings()->OnBrowserMessage(channel, args); } diff --git a/renderer/atom_render_view_observer.h b/renderer/atom_render_view_observer.h index fd984d01246..d8d90e0cb4b 100644 --- a/renderer/atom_render_view_observer.h +++ b/renderer/atom_render_view_observer.h @@ -35,7 +35,7 @@ class AtomRenderViewObserver : content::RenderViewObserver { virtual void DraggableRegionsChanged(WebKit::WebFrame* frame) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - void OnBrowserMessage(const std::string& channel, + void OnBrowserMessage(const string16& channel, const base::ListValue& args); scoped_ptr atom_bindings_; diff --git a/spec/api/ipc.coffee b/spec/api/ipc.coffee index 4a50b07407c..c50635f9888 100644 --- a/spec/api/ipc.coffee +++ b/spec/api/ipc.coffee @@ -49,7 +49,12 @@ describe 'ipc', -> describe 'ipc.send', -> it 'should work when sending an object containing id property', (done) -> obj = id: 1, name: 'ly' - ipc.on 'message', (message) -> + ipc.once 'message', (message) -> assert.deepEqual message, obj done() ipc.send obj + + describe 'ipc.sendSync', -> + it 'can be replied by setting event.returnValue', -> + msg = ipc.sendChannelSync 'echo', 'test' + assert.equal msg, 'test' diff --git a/spec/main.js b/spec/main.js index f2e52627946..cf2453c5a11 100644 --- a/spec/main.js +++ b/spec/main.js @@ -23,7 +23,11 @@ ipc.on('process.exit', function(pid, rid, code) { }); ipc.on('eval', function(ev, pid, rid, script) { - ev.result = eval(script); + ev.returnValue = eval(script); +}); + +ipc.on('echo', function(ev, pid, rid, msg) { + ev.returnValue = msg; }); process.on('uncaughtException', function() {