Move all sources under atom/.

This commit is contained in:
Cheng Zhao 2014-03-16 08:30:26 +08:00
parent 26ddbbb0ee
commit 516d46444d
217 changed files with 519 additions and 519 deletions

View file

@ -0,0 +1,33 @@
// 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.
// Get basic type definitions.
#define IPC_MESSAGE_IMPL
#include "atom/common/api/api_messages.h"
// Generate constructors.
#include "ipc/struct_constructor_macros.h"
#include "atom/common/api/api_messages.h"
// Generate destructors.
#include "ipc/struct_destructor_macros.h"
#include "atom/common/api/api_messages.h"
// Generate param traits write methods.
#include "ipc/param_traits_write_macros.h"
namespace IPC {
#include "atom/common/api/api_messages.h"
} // namespace IPC
// Generate param traits read methods.
#include "ipc/param_traits_read_macros.h"
namespace IPC {
#include "atom/common/api/api_messages.h"
} // namespace IPC
// Generate param traits log methods.
#include "ipc/param_traits_log_macros.h"
namespace IPC {
#include "atom/common/api/api_messages.h"
} // namespace IPC

View file

@ -0,0 +1,38 @@
// 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.
// Multiply-included file, no traditional include guard.
#include "base/strings/string16.h"
#include "base/values.h"
#include "atom/common/draggable_region.h"
#include "content/public/common/common_param_traits.h"
#include "ipc/ipc_message_macros.h"
// The message starter should be declared in ipc/ipc_message_start.h. Since
// we don't wan't to patch Chromium, we just pretend to be Content Shell.
#define IPC_MESSAGE_START ShellMsgStart
IPC_STRUCT_TRAITS_BEGIN(atom::DraggableRegion)
IPC_STRUCT_TRAITS_MEMBER(draggable)
IPC_STRUCT_TRAITS_MEMBER(bounds)
IPC_STRUCT_TRAITS_END()
IPC_MESSAGE_ROUTED2(AtomViewHostMsg_Message,
string16 /* channel */,
ListValue /* arguments */)
IPC_SYNC_MESSAGE_ROUTED2_1(AtomViewHostMsg_Message_Sync,
string16 /* channel */,
ListValue /* arguments */,
string16 /* result (in JSON) */)
IPC_MESSAGE_ROUTED2(AtomViewMsg_Message,
string16 /* channel */,
ListValue /* arguments */)
// Sent by the renderer when the draggable regions are updated.
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
std::vector<atom::DraggableRegion> /* regions */)

View file

@ -0,0 +1,88 @@
// 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_clipboard.h"
#include <string>
#include "atom/common/v8/native_type_conversions.h"
#include "ui/base/clipboard/clipboard.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace api {
// static
void Clipboard::Has(const v8::FunctionCallbackInfo<v8::Value>& args) {
std::string format_string;
if (!FromV8Arguments(args, &format_string))
return node::ThrowTypeError("Bad argument");
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::Clipboard::FormatType format(ui::Clipboard::GetFormatType(format_string));
args.GetReturnValue().Set(
clipboard->IsFormatAvailable(format, ui::Clipboard::BUFFER_STANDARD));
}
// static
void Clipboard::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
std::string format_string;
if (!FromV8Arguments(args, &format_string))
return node::ThrowTypeError("Bad argument");
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::Clipboard::FormatType format(ui::Clipboard::GetFormatType(format_string));
std::string data;
clipboard->ReadData(format, &data);
args.GetReturnValue().Set(ToV8Value(data));
}
// static
void Clipboard::ReadText(const v8::FunctionCallbackInfo<v8::Value>& args) {
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
std::string data;
clipboard->ReadAsciiText(ui::Clipboard::BUFFER_STANDARD, &data);
args.GetReturnValue().Set(ToV8Value(data));
}
// static
void Clipboard::WriteText(const v8::FunctionCallbackInfo<v8::Value>& args) {
std::string text;
if (!FromV8Arguments(args, &text))
return node::ThrowTypeError("Bad argument");
ui::Clipboard::ObjectMap object_map;
object_map[ui::Clipboard::CBF_TEXT].push_back(
std::vector<char>(text.begin(), text.end()));
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->WriteObjects(ui::Clipboard::BUFFER_STANDARD, object_map);
}
// static
void Clipboard::Clear(const v8::FunctionCallbackInfo<v8::Value>& args) {
ui::Clipboard::GetForCurrentThread()->Clear(ui::Clipboard::BUFFER_STANDARD);
}
// static
void Clipboard::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "has", Has);
NODE_SET_METHOD(target, "read", Read);
NODE_SET_METHOD(target, "readText", ReadText);
NODE_SET_METHOD(target, "writeText", WriteText);
NODE_SET_METHOD(target, "clear", Clear);
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_common_clipboard, atom::api::Clipboard::Initialize)

View file

@ -0,0 +1,33 @@
// 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_CLIPBOARD_H_
#define ATOM_COMMON_API_ATOM_API_CLIPBOARD_H_
#include "base/basictypes.h"
#include "v8/include/v8.h"
namespace atom {
namespace api {
class Clipboard {
public:
static void Initialize(v8::Handle<v8::Object> target);
private:
static void Has(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Read(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ReadText(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WriteText(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Clear(const v8::FunctionCallbackInfo<v8::Value>& args);
DISALLOW_IMPLICIT_CONSTRUCTORS(Clipboard);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_CLIPBOARD_H_

View file

@ -0,0 +1,38 @@
// 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_crash_reporter.h"
#include "atom/common/crash_reporter/crash_reporter.h"
#include "atom/common/v8/native_type_conversions.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace api {
// static
void CrashReporter::Start(const v8::FunctionCallbackInfo<v8::Value>& args) {
std::string product_name, company_name, submit_url;
bool auto_submit, skip_system;
std::map<std::string, std::string> dict;
if (!FromV8Arguments(args, &product_name, &company_name, &submit_url,
&auto_submit, &skip_system, &dict))
return node::ThrowTypeError("Bad argument");
crash_reporter::CrashReporter::GetInstance()->Start(
product_name, company_name, submit_url, auto_submit, skip_system, dict);
}
// static
void CrashReporter::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "start", Start);
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_common_crash_reporter, atom::api::CrashReporter::Initialize)

View file

@ -0,0 +1,29 @@
// 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_CRASH_REPORTER_H_
#define ATOM_COMMON_API_ATOM_API_CRASH_REPORTER_H_
#include "base/basictypes.h"
#include "v8/include/v8.h"
namespace atom {
namespace api {
class CrashReporter {
public:
static void Initialize(v8::Handle<v8::Object> target);
private:
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
DISALLOW_IMPLICIT_CONSTRUCTORS(CrashReporter);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_CRASH_REPORTER_H_

View file

@ -0,0 +1,81 @@
// 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 "base/logging.h"
#include "atom/browser/api/atom_api_event.h"
#include "atom/common/v8/native_type_conversions.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace api {
EventEmitter::EventEmitter(v8::Handle<v8::Object> wrapper) {
Wrap(wrapper);
}
EventEmitter::~EventEmitter() {
// Use Locker in browser process.
scoped_ptr<v8::Locker> locker;
if (node::g_standalone_mode)
locker.reset(new v8::Locker(node_isolate));
// Clear the aligned pointer, it should have been done by ObjectWrap but
// somehow node v0.11.x changed this behaviour.
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) {
// Use Locker in browser process.
scoped_ptr<v8::Locker> locker;
if (node::g_standalone_mode)
locker.reset(new v8::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

@ -0,0 +1,43 @@
// 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

@ -0,0 +1,142 @@
// 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.
#include "atom/common/api/atom_api_id_weak_map.h"
#include <algorithm>
#include "base/logging.h"
#include "atom/common/v8/native_type_conversions.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace api {
IDWeakMap::IDWeakMap()
: next_id_(0) {
}
IDWeakMap::~IDWeakMap() {
}
bool IDWeakMap::Has(int key) const {
return map_.find(key) != map_.end();
}
void IDWeakMap::Erase(int key) {
if (Has(key))
map_.erase(key);
else
LOG(WARNING) << "Object with key " << key << " is being GCed for twice.";
}
int IDWeakMap::GetNextID() {
return ++next_id_;
}
// static
void IDWeakMap::WeakCallback(v8::Isolate* isolate,
v8::Persistent<v8::Object>* value,
IDWeakMap* self) {
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> local = v8::Local<v8::Object>::New(isolate, *value);
self->Erase(
FromV8Value(local->GetHiddenValue(v8::String::New("IDWeakMapKey"))));
}
// static
void IDWeakMap::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
(new IDWeakMap)->Wrap(args.This());
}
// static
void IDWeakMap::Add(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (!args[0]->IsObject())
return node::ThrowTypeError("Bad argument");
IDWeakMap* self = Unwrap<IDWeakMap>(args.This());
int key = self->GetNextID();
v8::Local<v8::Object> v8_value = args[0]->ToObject();
v8_value->SetHiddenValue(v8::String::New("IDWeakMapKey"), ToV8Value(key));
self->map_[key] = new RefCountedPersistent<v8::Object>(v8_value);
self->map_[key]->MakeWeak(self, WeakCallback);
args.GetReturnValue().Set(key);
}
// static
void IDWeakMap::Get(const v8::FunctionCallbackInfo<v8::Value>& args) {
int key;
if (!FromV8Arguments(args, &key))
return node::ThrowTypeError("Bad argument");
IDWeakMap* self = Unwrap<IDWeakMap>(args.This());
if (!self->Has(key))
return node::ThrowError("Invalid key");
args.GetReturnValue().Set(self->map_[key]->NewHandle());
}
// static
void IDWeakMap::Has(const v8::FunctionCallbackInfo<v8::Value>& args) {
int key;
if (!FromV8Arguments(args, &key))
return node::ThrowTypeError("Bad argument");
IDWeakMap* self = Unwrap<IDWeakMap>(args.This());
args.GetReturnValue().Set(self->Has(key));
}
// static
void IDWeakMap::Keys(const v8::FunctionCallbackInfo<v8::Value>& args) {
IDWeakMap* self = Unwrap<IDWeakMap>(args.This());
v8::Local<v8::Array> keys = v8::Array::New(self->map_.size());
int i = 0;
for (auto el = self->map_.begin(); el != self->map_.end(); ++el) {
keys->Set(i, ToV8Value(el->first));
++i;
}
args.GetReturnValue().Set(keys);
}
// static
void IDWeakMap::Remove(const v8::FunctionCallbackInfo<v8::Value>& args) {
int key;
if (!FromV8Arguments(args, &key))
return node::ThrowTypeError("Bad argument");
IDWeakMap* self = Unwrap<IDWeakMap>(args.This());
if (!self->Has(key))
return node::ThrowError("Invalid key");
self->Erase(key);
}
// static
void IDWeakMap::Initialize(v8::Handle<v8::Object> target) {
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(v8::String::NewSymbol("IDWeakMap"));
NODE_SET_PROTOTYPE_METHOD(t, "add", Add);
NODE_SET_PROTOTYPE_METHOD(t, "get", Get);
NODE_SET_PROTOTYPE_METHOD(t, "has", Has);
NODE_SET_PROTOTYPE_METHOD(t, "keys", Keys);
NODE_SET_PROTOTYPE_METHOD(t, "remove", Remove);
target->Set(v8::String::NewSymbol("IDWeakMap"), t->GetFunction());
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_common_id_weak_map, atom::api::IDWeakMap::Initialize)

View file

@ -0,0 +1,52 @@
// 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_ATOM_API_ID_WEAK_MAP_H_
#define ATOM_COMMON_API_ATOM_API_ID_WEAK_MAP_H_
#include <map>
#include "base/basictypes.h"
#include "atom/common/v8/scoped_persistent.h"
#include "vendor/node/src/node_object_wrap.h"
namespace atom {
namespace api {
class IDWeakMap : public node::ObjectWrap {
public:
static void Initialize(v8::Handle<v8::Object> target);
private:
IDWeakMap();
virtual ~IDWeakMap();
bool Has(int key) const;
void Erase(int key);
int GetNextID();
static void WeakCallback(v8::Isolate* isolate,
v8::Persistent<v8::Object>* value,
IDWeakMap* self);
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Add(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Get(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Has(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Keys(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Remove(const v8::FunctionCallbackInfo<v8::Value>& args);
int next_id_;
std::map<int, RefCountedV8Object> map_;
DISALLOW_COPY_AND_ASSIGN(IDWeakMap);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_ID_WEAK_MAP_H_

View file

@ -0,0 +1,93 @@
// 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_screen.h"
#include "atom/common/v8/native_type_conversions.h"
#include "ui/gfx/screen.h"
#include "atom/common/v8/node_common.h"
#if defined(TOOLKIT_GTK)
#include "base/command_line.h"
#include "ui/gfx/gtk_util.h"
#endif
#define UNWRAP_SCREEN_AND_CHECK \
Screen* self = ObjectWrap::Unwrap<Screen>(args.This()); \
if (self == NULL) \
return node::ThrowError("Screen is already destroyed")
namespace atom {
namespace api {
namespace {
v8::Handle<v8::Object> DisplayToV8Value(const gfx::Display& display) {
v8::Handle<v8::Object> obj = v8::Object::New();
obj->Set(ToV8Value("bounds"), ToV8Value(display.bounds()));
obj->Set(ToV8Value("workArea"), ToV8Value(display.work_area()));
obj->Set(ToV8Value("size"), ToV8Value(display.size()));
obj->Set(ToV8Value("workAreaSize"), ToV8Value(display.work_area_size()));
obj->Set(ToV8Value("scaleFactor"), ToV8Value(display.device_scale_factor()));
return obj;
}
} // namespace
Screen::Screen(v8::Handle<v8::Object> wrapper)
: EventEmitter(wrapper),
screen_(gfx::Screen::GetNativeScreen()) {
}
Screen::~Screen() {
}
// static
void Screen::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
if (!args.IsConstructCall())
return node::ThrowError("Require constructor call");
new Screen(args.This());
}
// static
void Screen::GetCursorScreenPoint(
const v8::FunctionCallbackInfo<v8::Value>& args) {
UNWRAP_SCREEN_AND_CHECK;
args.GetReturnValue().Set(ToV8Value(self->screen_->GetCursorScreenPoint()));
}
// static
void Screen::GetPrimaryDisplay(
const v8::FunctionCallbackInfo<v8::Value>& args) {
UNWRAP_SCREEN_AND_CHECK;
gfx::Display display = self->screen_->GetPrimaryDisplay();
args.GetReturnValue().Set(DisplayToV8Value(display));
}
// static
void Screen::Initialize(v8::Handle<v8::Object> target) {
#if defined(TOOLKIT_GTK)
gfx::GdkInitFromCommandLine(*CommandLine::ForCurrentProcess());
#endif
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(v8::String::NewSymbol("Screen"));
NODE_SET_PROTOTYPE_METHOD(t, "getCursorScreenPoint", GetCursorScreenPoint);
NODE_SET_PROTOTYPE_METHOD(t, "getPrimaryDisplay", GetPrimaryDisplay);
target->Set(v8::String::NewSymbol("Screen"), t->GetFunction());
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_common_screen, atom::api::Screen::Initialize)

View file

@ -0,0 +1,44 @@
// 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_SCREEN_H_
#define ATOM_COMMON_API_ATOM_API_SCREEN_H_
#include "atom/common/api/atom_api_event_emitter.h"
namespace gfx {
class Screen;
}
namespace atom {
namespace api {
class Screen : public EventEmitter {
public:
virtual ~Screen();
static void Initialize(v8::Handle<v8::Object> target);
protected:
explicit Screen(v8::Handle<v8::Object> wrapper);
private:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetCursorScreenPoint(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPrimaryDisplay(
const v8::FunctionCallbackInfo<v8::Value>& args);
gfx::Screen* screen_;
DISALLOW_COPY_AND_ASSIGN(Screen);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_SCREEN_H_

View file

@ -0,0 +1,74 @@
// 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_shell.h"
#include <string>
#include "base/files/file_path.h"
#include "atom/common/platform_util.h"
#include "atom/common/v8/native_type_conversions.h"
#include "url/gurl.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace api {
// static
void Shell::ShowItemInFolder(const v8::FunctionCallbackInfo<v8::Value>& args) {
base::FilePath file_path;
if (!FromV8Arguments(args, &file_path))
return node::ThrowTypeError("Bad argument");
platform_util::ShowItemInFolder(file_path);
}
// static
void Shell::OpenItem(const v8::FunctionCallbackInfo<v8::Value>& args) {
base::FilePath file_path;
if (!FromV8Arguments(args, &file_path))
return node::ThrowTypeError("Bad argument");
platform_util::OpenItem(file_path);
}
// static
void Shell::OpenExternal(const v8::FunctionCallbackInfo<v8::Value>& args) {
GURL url;
if (!FromV8Arguments(args, &url))
return node::ThrowTypeError("Bad argument");
platform_util::OpenExternal(url);
}
// static
void Shell::MoveItemToTrash(const v8::FunctionCallbackInfo<v8::Value>& args) {
base::FilePath file_path;
if (!FromV8Arguments(args, &file_path))
return node::ThrowTypeError("Bad argument");
platform_util::MoveItemToTrash(file_path);
}
// static
void Shell::Beep(const v8::FunctionCallbackInfo<v8::Value>& args) {
platform_util::Beep();
}
// static
void Shell::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "showItemInFolder", ShowItemInFolder);
NODE_SET_METHOD(target, "openItem", OpenItem);
NODE_SET_METHOD(target, "openExternal", OpenExternal);
NODE_SET_METHOD(target, "moveItemToTrash", MoveItemToTrash);
NODE_SET_METHOD(target, "beep", Beep);
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_common_shell, atom::api::Shell::Initialize)

View file

@ -0,0 +1,33 @@
// 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_SHELL_H_
#define ATOM_COMMON_API_ATOM_API_SHELL_H_
#include "base/basictypes.h"
#include "v8/include/v8.h"
namespace atom {
namespace api {
class Shell {
public:
static void Initialize(v8::Handle<v8::Object> target);
private:
static void ShowItemInFolder(const v8::FunctionCallbackInfo<v8::Value>& args);
static void OpenItem(const v8::FunctionCallbackInfo<v8::Value>& args);
static void OpenExternal(const v8::FunctionCallbackInfo<v8::Value>& args);
static void MoveItemToTrash(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Beep(const v8::FunctionCallbackInfo<v8::Value>& args);
DISALLOW_IMPLICIT_CONSTRUCTORS(Shell);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_SHELL_H_

View file

@ -0,0 +1,61 @@
// 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/object_life_monitor.h"
#include "atom/common/v8/native_type_conversions.h"
#include "v8/include/v8-profiler.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace api {
namespace {
void CreateObjectWithName(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
t->SetClassName(args[0]->ToString());
args.GetReturnValue().Set(t->GetFunction()->NewInstance());
}
void GetHiddenValue(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(
args[0]->ToObject()->GetHiddenValue(args[1]->ToString()));
}
void SetHiddenValue(const v8::FunctionCallbackInfo<v8::Value>& args) {
args[0]->ToObject()->SetHiddenValue(args[1]->ToString(), args[2]);
}
void GetObjectHash(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args[0]->ToObject()->GetIdentityHash());
}
void SetDestructor(const v8::FunctionCallbackInfo<v8::Value>& args) {
ObjectLifeMonitor::BindTo(args[0]->ToObject(), args[1]);
}
void TakeHeapSnapshot(const v8::FunctionCallbackInfo<v8::Value>& args) {
node::node_isolate->GetHeapProfiler()->TakeHeapSnapshot(
v8::String::New("test"));
}
} // namespace
void InitializeV8Util(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "createObjectWithName", CreateObjectWithName);
NODE_SET_METHOD(target, "getHiddenValue", GetHiddenValue);
NODE_SET_METHOD(target, "setHiddenValue", SetHiddenValue);
NODE_SET_METHOD(target, "getObjectHash", GetObjectHash);
NODE_SET_METHOD(target, "setDestructor", SetDestructor);
NODE_SET_METHOD(target, "takeHeapSnapshot", TakeHeapSnapshot);
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_common_v8_util, atom::api::InitializeV8Util)

View file

@ -0,0 +1,190 @@
// 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_bindings.h"
#include "base/logging.h"
#include "atom/common/atom_version.h"
#include "atom/common/v8/native_type_conversions.h"
#include "atom/common/v8/node_common.h"
namespace atom {
namespace {
static int kMaxCallStackSize = 200; // Same with WebKit.
// Async handle to wake up uv loop.
static uv_async_t g_next_tick_uv_handle;
// Async handle to execute the stored v8 callback.
static uv_async_t g_callback_uv_handle;
// Stored v8 callback, to be called by the async handler.
RefCountedV8Function g_v8_callback;
// Dummy class type that used for crashing the program.
struct DummyClass { bool crash; };
// Async handler to call next process.nextTick callbacks.
void UvCallNextTick(uv_async_t* handle, int status) {
node::Environment* env = node::Environment::GetCurrent(node_isolate);
node::Environment::TickInfo* tick_info = env->tick_info();
if (tick_info->in_tick())
return;
if (tick_info->length() == 0) {
tick_info->set_index(0);
return;
}
tick_info->set_in_tick(true);
env->tick_callback_function()->Call(env->process_object(), 0, NULL);
tick_info->set_in_tick(false);
}
// Async handler to execute the stored v8 callback.
void UvOnCallback(uv_async_t* handle, int status) {
v8::HandleScope handle_scope(node_isolate);
v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global();
g_v8_callback->NewHandle()->Call(global, 0, NULL);
}
// Called when there is a fatal error in V8, we just crash the process here so
// we can get the stack trace.
void FatalErrorCallback(const char* location, const char* message) {
LOG(ERROR) << "Fatal error in V8: " << location << " " << message;
static_cast<DummyClass*>(NULL)->crash = true;
}
v8::Handle<v8::Object> DumpStackFrame(v8::Handle<v8::StackFrame> stack_frame) {
v8::Local<v8::Object> result = v8::Object::New();
result->Set(ToV8Value("line"), ToV8Value(stack_frame->GetLineNumber()));
result->Set(ToV8Value("column"), ToV8Value(stack_frame->GetColumn()));
v8::Handle<v8::String> script = stack_frame->GetScriptName();
if (!script.IsEmpty())
result->Set(ToV8Value("script"), script);
v8::Handle<v8::String> function = stack_frame->GetScriptNameOrSourceURL();
if (!function.IsEmpty())
result->Set(ToV8Value("function"), function);
return result;
}
} // namespace
// Defined in atom_extensions.cc.
node::node_module_struct* GetBuiltinModule(const char *name, bool is_browser);
AtomBindings::AtomBindings() {
uv_async_init(uv_default_loop(), &g_next_tick_uv_handle, UvCallNextTick);
uv_async_init(uv_default_loop(), &g_callback_uv_handle, UvOnCallback);
v8::V8::SetFatalErrorHandler(FatalErrorCallback);
}
AtomBindings::~AtomBindings() {
}
void AtomBindings::BindTo(v8::Handle<v8::Object> process) {
NODE_SET_METHOD(process, "atomBinding", Binding);
NODE_SET_METHOD(process, "crash", Crash);
NODE_SET_METHOD(process, "activateUvLoop", ActivateUVLoop);
NODE_SET_METHOD(process, "log", Log);
NODE_SET_METHOD(process, "getCurrentStackTrace", GetCurrentStackTrace);
NODE_SET_METHOD(process, "scheduleCallback", ScheduleCallback);
process->Get(v8::String::New("versions"))->ToObject()->
Set(v8::String::New("atom-shell"), v8::String::New(ATOM_VERSION_STRING));
}
// static
void AtomBindings::Binding(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Local<v8::String> module = args[0]->ToString();
v8::String::Utf8Value module_v(module);
node::node_module_struct* modp;
v8::Local<v8::Object> process = v8::Context::GetCurrent()->Global()->
Get(v8::String::New("process"))->ToObject();
DCHECK(!process.IsEmpty());
// is_browser = process.__atom_type == 'browser'.
bool is_browser = std::string("browser") == *v8::String::Utf8Value(
process->Get(v8::String::New("__atom_type")));
// Cached in process.__atom_binding_cache.
v8::Local<v8::Object> binding_cache;
v8::Local<v8::String> bc_name = v8::String::New("__atomBindingCache");
if (process->Has(bc_name)) {
binding_cache = process->Get(bc_name)->ToObject();
DCHECK(!binding_cache.IsEmpty());
} else {
binding_cache = v8::Object::New();
process->Set(bc_name, binding_cache);
}
v8::Local<v8::Object> exports;
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
return args.GetReturnValue().Set(exports);
}
if ((modp = GetBuiltinModule(*module_v, is_browser)) != NULL) {
exports = v8::Object::New();
// Internal bindings don't have a "module" object,
// only exports.
modp->register_func(exports, v8::Undefined());
binding_cache->Set(module, exports);
return args.GetReturnValue().Set(exports);
}
return node::ThrowError("No such module");
}
// static
void AtomBindings::Crash(const v8::FunctionCallbackInfo<v8::Value>& args) {
static_cast<DummyClass*>(NULL)->crash = true;
}
// static
void AtomBindings::ActivateUVLoop(
const v8::FunctionCallbackInfo<v8::Value>& args) {
uv_async_send(&g_next_tick_uv_handle);
}
// static
void AtomBindings::Log(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::Utf8Value str(args[0]);
logging::LogMessage("CONSOLE", 0, 0).stream() << *str;
}
// static
void AtomBindings::GetCurrentStackTrace(
const v8::FunctionCallbackInfo<v8::Value>& args) {
int stack_limit = kMaxCallStackSize;
FromV8Arguments(args, &stack_limit);
v8::Local<v8::StackTrace> stack_trace = v8::StackTrace::CurrentStackTrace(
stack_limit, v8::StackTrace::kDetailed);
int frame_count = stack_trace->GetFrameCount();
v8::Local<v8::Array> result = v8::Array::New(frame_count);
for (int i = 0; i < frame_count; ++i)
result->Set(i, DumpStackFrame(stack_trace->GetFrame(i)));
args.GetReturnValue().Set(result);
}
// static
void AtomBindings::ScheduleCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (!FromV8Arguments(args, &g_v8_callback))
return node::ThrowTypeError("Bad arguments");
uv_async_send(&g_callback_uv_handle);
}
} // namespace atom

View file

@ -0,0 +1,36 @@
// 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_BINDINGS_
#define ATOM_COMMON_API_ATOM_BINDINGS_
#include "base/basictypes.h"
#include "v8/include/v8.h"
namespace atom {
class AtomBindings {
public:
AtomBindings();
virtual ~AtomBindings();
// Add process.atom_binding function, which behaves like process.binding but
// load native code from atom-shell instead.
virtual void BindTo(v8::Handle<v8::Object> process);
private:
static void Binding(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Crash(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ActivateUVLoop(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Log(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetCurrentStackTrace(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void ScheduleCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
DISALLOW_COPY_AND_ASSIGN(AtomBindings);
};
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_BINDINGS_

View file

@ -0,0 +1,56 @@
// 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 <string.h>
#include <stdio.h>
#include "base/strings/string_util.h"
#include "vendor/node/src/node_version.h"
#include "atom/common/v8/node_common.h"
namespace atom {
#undef NODE_EXT_LIST_START
#undef NODE_EXT_LIST_ITEM
#undef NODE_EXT_LIST_END
#define NODE_EXT_LIST_START
#define NODE_EXT_LIST_ITEM NODE_MODULE_DECL
#define NODE_EXT_LIST_END
#include "atom/common/api/atom_extensions.h"
#undef NODE_EXT_LIST_START
#undef NODE_EXT_LIST_ITEM
#undef NODE_EXT_LIST_END
#define NODE_EXT_STRING(x) &x ## _module,
#define NODE_EXT_LIST_START node::node_module_struct *node_module_list[] = {
#define NODE_EXT_LIST_ITEM NODE_EXT_STRING
#define NODE_EXT_LIST_END NULL};
#include "atom/common/api/atom_extensions.h" // NOLINT
node::node_module_struct* GetBuiltinModule(const char *name, bool is_browser) {
char common[128];
char spec[128];
node::node_module_struct *cur = NULL;
base::snprintf(common, sizeof(common), "atom_common_%s", name);
base::snprintf(spec, sizeof(spec),
(is_browser ? "atom_browser_%s": "atom_renderer_%s"),
name);
/* TODO: you could look these up in a hash, but there are only
* a few, and once loaded they are cached. */
for (int i = 0; node_module_list[i] != NULL; i++) {
cur = node_module_list[i];
if (strcmp(cur->modname, common) == 0 || strcmp(cur->modname, spec) == 0) {
return cur;
}
}
return NULL;
}
} // namespace atom

View file

@ -0,0 +1,34 @@
// 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.
// Multiply-included file, no traditional include guard.
// Used by atom_extensions.cc to declare a list of built-in modules of Atom.
NODE_EXT_LIST_START
// Module names start with `atom_browser_` can only be used by browser process.
NODE_EXT_LIST_ITEM(atom_browser_app)
NODE_EXT_LIST_ITEM(atom_browser_auto_updater)
NODE_EXT_LIST_ITEM(atom_browser_dialog)
NODE_EXT_LIST_ITEM(atom_browser_ipc)
NODE_EXT_LIST_ITEM(atom_browser_menu)
NODE_EXT_LIST_ITEM(atom_browser_power_monitor)
NODE_EXT_LIST_ITEM(atom_browser_protocol)
NODE_EXT_LIST_ITEM(atom_browser_window)
// Module names start with `atom_renderer_` can only be used by renderer
// process.
NODE_EXT_LIST_ITEM(atom_renderer_ipc)
// Module names start with `atom_common_` can be used by both browser and
// renderer processes.
NODE_EXT_LIST_ITEM(atom_common_clipboard)
NODE_EXT_LIST_ITEM(atom_common_crash_reporter)
NODE_EXT_LIST_ITEM(atom_common_id_weak_map)
NODE_EXT_LIST_ITEM(atom_common_screen)
NODE_EXT_LIST_ITEM(atom_common_shell)
NODE_EXT_LIST_ITEM(atom_common_v8_util)
NODE_EXT_LIST_END

View file

@ -0,0 +1,25 @@
module.exports =
class CallbacksRegistry
constructor: ->
@emptyFunc = -> throw new Error "Browser trying to call a non-exist callback
in renderer, this usually happens when renderer code forgot to release
a callback installed on objects in browser when renderer was going to be
unloaded or released."
@callbacks = {}
add: (callback) ->
id = Math.random().toString()
@callbacks[id] = callback
id
get: (id) ->
@callbacks[id] ? ->
call: (id, args...) ->
@get(id).call global, args...
apply: (id, args...) ->
@get(id).apply global, args...
remove: (id) ->
delete @callbacks[id]

View file

@ -0,0 +1 @@
module.exports = process.atomBinding 'clipboard'

View file

@ -0,0 +1,38 @@
{spawn} = require 'child_process'
binding = process.atomBinding 'crash_reporter'
class CrashReporter
start: (options={}) ->
{productName, companyName, submitUrl, autoSubmit, ignoreSystemCrashHandler, extra} = options
productName ?= 'Atom-Shell'
companyName ?= 'GitHub, Inc'
submitUrl ?= 'http://54.249.141.255:1127/post'
autoSubmit ?= true
ignoreSystemCrashHandler ?= false
extra ?= {}
extra._productName ?= productName
extra._companyName ?= companyName
extra._version ?=
if process.__atom_type is 'browser'
require('app').getVersion()
else
require('remote').require('app').getVersion()
start = -> binding.start productName, companyName, submitUrl, autoSubmit, ignoreSystemCrashHandler, extra
if process.platform is 'win32'
args = [
"--reporter-url=#{submitUrl}"
"--application-name=#{productName}"
"--v=1"
]
env = ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1
spawn process.execPath, args, {env, detached: true}
start()
else
start()
module.exports = new CrashReporter

View file

@ -0,0 +1,3 @@
IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap
module.exports = IDWeakMap

View file

@ -0,0 +1,3 @@
{Screen} = process.atomBinding 'screen'
module.exports = new Screen

View file

@ -0,0 +1 @@
module.exports = process.atomBinding 'shell'

View file

@ -0,0 +1,38 @@
// 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.
#include "atom/common/api/object_life_monitor.h"
namespace atom {
// static
void ObjectLifeMonitor::BindTo(v8::Handle<v8::Object> target,
v8::Handle<v8::Value> destructor) {
target->SetHiddenValue(v8::String::New("destructor"), destructor);
ObjectLifeMonitor* olm = new ObjectLifeMonitor();
olm->handle_.reset(target);
olm->handle_.MakeWeak(olm, WeakCallback);
}
ObjectLifeMonitor::ObjectLifeMonitor() {
}
// static
void ObjectLifeMonitor::WeakCallback(v8::Isolate* isolate,
v8::Persistent<v8::Object>* value,
ObjectLifeMonitor* self) {
// destructor.call(object, object);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> obj = self->handle_.NewHandle();
v8::Local<v8::Function>::Cast(obj->GetHiddenValue(
v8::String::New("destructor")))->Call(obj, 0, NULL);
}
delete self;
}
} // namespace atom

View file

@ -0,0 +1,33 @@
// 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 "base/basictypes.h"
#include "atom/common/v8/scoped_persistent.h"
namespace atom {
class ObjectLifeMonitor {
public:
static void BindTo(v8::Handle<v8::Object> target,
v8::Handle<v8::Value> destructor);
private:
ObjectLifeMonitor();
static void WeakCallback(v8::Isolate* isolate,
v8::Persistent<v8::Object>* value,
ObjectLifeMonitor* self);
ScopedPersistent<v8::Object> handle_;
DISALLOW_COPY_AND_ASSIGN(ObjectLifeMonitor);
};
} // namespace atom
#endif // ATOM_COMMON_API_OBJECT_LIFE_MONITOR_H_

View file

@ -0,0 +1,43 @@
// 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_VERSION_H
#define ATOM_VERSION_H
#define ATOM_MAJOR_VERSION 0
#define ATOM_MINOR_VERSION 10
#define ATOM_PATCH_VERSION 7
#define ATOM_VERSION_IS_RELEASE 1
#ifndef ATOM_TAG
# define ATOM_TAG ""
#endif
#ifndef ATOM_STRINGIFY
#define ATOM_STRINGIFY(n) ATOM_STRINGIFY_HELPER(n)
#define ATOM_STRINGIFY_HELPER(n) #n
#endif
#if ATOM_VERSION_IS_RELEASE
# define ATOM_VERSION_STRING ATOM_STRINGIFY(ATOM_MAJOR_VERSION) "." \
ATOM_STRINGIFY(ATOM_MINOR_VERSION) "." \
ATOM_STRINGIFY(ATOM_PATCH_VERSION) \
ATOM_TAG
#else
# define ATOM_VERSION_STRING ATOM_STRINGIFY(ATOM_MAJOR_VERSION) "." \
ATOM_STRINGIFY(ATOM_MINOR_VERSION) "." \
ATOM_STRINGIFY(ATOM_PATCH_VERSION) \
ATOM_TAG "-pre"
#endif
#define ATOM_VERSION "v" ATOM_VERSION_STRING
#define ATOM_VERSION_AT_LEAST(major, minor, patch) \
(( (major) < ATOM_MAJOR_VERSION) \
|| ((major) == ATOM_MAJOR_VERSION && (minor) < ATOM_MINOR_VERSION) \
|| ((major) == ATOM_MAJOR_VERSION && (minor) == ATOM_MINOR_VERSION && (patch) <= ATOM_PATCH_VERSION))
#endif /* ATOM_VERSION_H */

View file

@ -0,0 +1,42 @@
// 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/crash_reporter/crash_reporter.h"
#include "base/command_line.h"
#include "atom/browser/browser.h"
#include "atom/common/atom_version.h"
#include "content/public/common/content_switches.h"
namespace crash_reporter {
CrashReporter::CrashReporter() {
const CommandLine& command = *CommandLine::ForCurrentProcess();
is_browser_ = command.GetSwitchValueASCII(switches::kProcessType).empty();
}
CrashReporter::~CrashReporter() {
}
void CrashReporter::Start(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler,
const StringMap& extra_parameters) {
SetUploadParameters(extra_parameters);
InitBreakpad(product_name, ATOM_VERSION_STRING, company_name, submit_url,
auto_submit, skip_system_crash_handler);
}
void CrashReporter::SetUploadParameters(const StringMap& parameters) {
upload_parameters_ = parameters;
upload_parameters_["process_type"] = is_browser_ ? "browser" : "renderer";
// Setting platform dependent parameters.
SetUploadParameters();
}
} // namespace crash_reporter

View file

@ -0,0 +1,51 @@
// 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_CRASH_REPORTER_CRASH_REPORTER_H_
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
#include <map>
#include <string>
#include "base/basictypes.h"
namespace crash_reporter {
class CrashReporter {
public:
typedef std::map<std::string, std::string> StringMap;
static CrashReporter* GetInstance();
void Start(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler,
const StringMap& extra_parameters);
protected:
CrashReporter();
virtual ~CrashReporter();
virtual void InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) = 0;
virtual void SetUploadParameters() = 0;
StringMap upload_parameters_;
bool is_browser_;
private:
void SetUploadParameters(const StringMap& parameters);
DISALLOW_COPY_AND_ASSIGN(CrashReporter);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_

View file

@ -0,0 +1,134 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Copyright (c) 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.
#include "atom/common/crash_reporter/crash_reporter_linux.h"
#include <sys/time.h>
#include <unistd.h>
#include "base/debug/crash_logging.h"
#include "base/files/file_path.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process/memory.h"
#include "base/memory/singleton.h"
#include "vendor/breakpad/src/client/linux/handler/exception_handler.h"
#include "vendor/breakpad/src/common/linux/linux_libc_support.h"
using google_breakpad::ExceptionHandler;
using google_breakpad::MinidumpDescriptor;
namespace crash_reporter {
namespace {
static const size_t kDistroSize = 128;
// Define a preferred limit on minidump sizes, because Crash Server currently
// throws away any larger than 1.2MB (1.2 * 1024 * 1024). A value of -1 means
// no limit.
static const off_t kMaxMinidumpFileSize = 1258291;
} // namespace
CrashReporterLinux::CrashReporterLinux()
: process_start_time_(0),
pid_(getpid()) {
// Set the base process start time value.
struct timeval tv;
if (!gettimeofday(&tv, NULL)) {
uint64_t ret = tv.tv_sec;
ret *= 1000;
ret += tv.tv_usec / 1000;
process_start_time_ = ret;
}
// Make base::g_linux_distro work.
base::SetLinuxDistro(base::GetLinuxDistro());
}
CrashReporterLinux::~CrashReporterLinux() {
}
void CrashReporterLinux::InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) {
EnableCrashDumping();
crash_keys_.SetKeyValue("prod", "Atom-Shell");
crash_keys_.SetKeyValue("ver", version.c_str());
upload_url_ = submit_url;
for (StringMap::const_iterator iter = upload_parameters_.begin();
iter != upload_parameters_.end(); ++iter)
crash_keys_.SetKeyValue(iter->first.c_str(), iter->second.c_str());
}
void CrashReporterLinux::SetUploadParameters() {
upload_parameters_["platform"] = "linux";
}
void CrashReporterLinux::EnableCrashDumping() {
base::FilePath tmp_path("/tmp");
PathService::Get(base::DIR_TEMP, &tmp_path);
base::FilePath dumps_path(tmp_path);
MinidumpDescriptor minidump_descriptor(dumps_path.value());
minidump_descriptor.set_size_limit(kMaxMinidumpFileSize);
breakpad_.reset(new ExceptionHandler(
minidump_descriptor,
NULL,
CrashDone,
this,
true, // Install handlers.
-1));
}
bool CrashReporterLinux::CrashDone(const MinidumpDescriptor& minidump,
void* context,
const bool succeeded) {
CrashReporterLinux* self = static_cast<CrashReporterLinux*>(context);
// WARNING: this code runs in a compromised context. It may not call into
// libc nor allocate memory normally.
if (!succeeded) {
const char msg[] = "Failed to generate minidump.";
WriteLog(msg, sizeof(msg) - 1);
return false;
}
DCHECK(!minidump.IsFD());
BreakpadInfo info = {0};
info.filename = minidump.path();
info.fd = minidump.fd();
info.distro = base::g_linux_distro;
info.distro_length = my_strlen(base::g_linux_distro);
info.upload = true;
info.process_start_time = self->process_start_time_;
info.oom_size = base::g_oom_size;
info.pid = self->pid_;
info.upload_url = self->upload_url_.c_str();
info.crash_keys = &self->crash_keys_;
HandleCrashDump(info);
return true;
}
// static
CrashReporterLinux* CrashReporterLinux::GetInstance() {
return Singleton<CrashReporterLinux>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterLinux::GetInstance();
}
} // namespace crash_reporter

View file

@ -0,0 +1,57 @@
// 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_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "atom/common/crash_reporter/crash_reporter.h"
#include "atom/common/crash_reporter/linux/crash_dump_handler.h"
template <typename T> struct DefaultSingletonTraits;
namespace google_breakpad {
class ExceptionHandler;
class MinidumpDescriptor;
}
namespace crash_reporter {
class CrashReporterLinux : public CrashReporter {
public:
static CrashReporterLinux* GetInstance();
virtual void InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) OVERRIDE;
virtual void SetUploadParameters() OVERRIDE;
private:
friend struct DefaultSingletonTraits<CrashReporterLinux>;
CrashReporterLinux();
virtual ~CrashReporterLinux();
void EnableCrashDumping();
static bool CrashDone(const google_breakpad::MinidumpDescriptor& minidump,
void* context,
const bool succeeded);
scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
CrashKeyStorage crash_keys_;
uint64_t process_start_time_;
pid_t pid_;
std::string upload_url_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterLinux);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_

View file

@ -0,0 +1,41 @@
// 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_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
#include "base/compiler_specific.h"
#include "atom/common/crash_reporter/crash_reporter.h"
#import "vendor/breakpad/src/client/mac/Framework/Breakpad.h"
template <typename T> struct DefaultSingletonTraits;
namespace crash_reporter {
class CrashReporterMac : public CrashReporter {
public:
static CrashReporterMac* GetInstance();
virtual void InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) OVERRIDE;
virtual void SetUploadParameters() OVERRIDE;
private:
friend struct DefaultSingletonTraits<CrashReporterMac>;
CrashReporterMac();
virtual ~CrashReporterMac();
BreakpadRef breakpad_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterMac);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_

View file

@ -0,0 +1,81 @@
// 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/crash_reporter/crash_reporter_mac.h"
#include "base/memory/singleton.h"
#include "base/strings/sys_string_conversions.h"
#import "vendor/breakpad/src/client/apple/Framework/BreakpadDefines.h"
namespace crash_reporter {
CrashReporterMac::CrashReporterMac()
: breakpad_(NULL) {
}
CrashReporterMac::~CrashReporterMac() {
if (breakpad_ != NULL)
BreakpadRelease(breakpad_);
}
void CrashReporterMac::InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) {
if (breakpad_ != NULL)
BreakpadRelease(breakpad_);
NSMutableDictionary* parameters =
[NSMutableDictionary dictionaryWithCapacity:4];
[parameters setValue:@"Atom-Shell"
forKey:@BREAKPAD_PRODUCT];
[parameters setValue:base::SysUTF8ToNSString(product_name)
forKey:@BREAKPAD_PRODUCT_DISPLAY];
[parameters setValue:base::SysUTF8ToNSString(version)
forKey:@BREAKPAD_VERSION];
[parameters setValue:base::SysUTF8ToNSString(company_name)
forKey:@BREAKPAD_VENDOR];
[parameters setValue:base::SysUTF8ToNSString(submit_url)
forKey:@BREAKPAD_URL];
[parameters setValue:(auto_submit ? @"YES" : @"NO")
forKey:@BREAKPAD_SKIP_CONFIRM];
[parameters setValue:(skip_system_crash_handler ? @"YES" : @"NO")
forKey:@BREAKPAD_SEND_AND_EXIT];
// Report all crashes (important for testing the crash reporter).
[parameters setValue:@"0" forKey:@BREAKPAD_REPORT_INTERVAL];
// Put dump files under "/tmp/ProductName Crashes".
std::string dump_dir = "/tmp/" + product_name + " Crashes";
[parameters setValue:base::SysUTF8ToNSString(dump_dir)
forKey:@BREAKPAD_DUMP_DIRECTORY];
breakpad_ = BreakpadCreate(parameters);
for (StringMap::const_iterator iter = upload_parameters_.begin();
iter != upload_parameters_.end(); ++iter) {
BreakpadAddUploadParameter(breakpad_,
base::SysUTF8ToNSString(iter->first),
base::SysUTF8ToNSString(iter->second));
}
}
void CrashReporterMac::SetUploadParameters() {
upload_parameters_["platform"] = "darwin";
}
// static
CrashReporterMac* CrashReporterMac::GetInstance() {
return Singleton<CrashReporterMac>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterMac::GetInstance();
}
} // namespace crash_reporter

View file

@ -0,0 +1,129 @@
// 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/crash_reporter/crash_reporter_win.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
namespace crash_reporter {
namespace {
// Minidump with stacks, PEB, TEB, and unloaded module list.
const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
MiniDumpWithProcessThreadData | // Get PEB and TEB.
MiniDumpWithUnloadedModules); // Get unloaded modules when available.
const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\$1 Crash Service";
} // namespace
CrashReporterWin::CrashReporterWin() {
}
CrashReporterWin::~CrashReporterWin() {
}
void CrashReporterWin::InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) {
skip_system_crash_handler_ = skip_system_crash_handler;
base::FilePath temp_dir;
if (!file_util::GetTempDir(&temp_dir)) {
LOG(ERROR) << "Cannot get temp directory";
return;
}
string16 pipe_name = ReplaceStringPlaceholders(kPipeNameFormat,
UTF8ToUTF16(product_name),
NULL);
// Wait until the crash service is started.
HANDLE waiting_event =
::CreateEventW(NULL, TRUE, FALSE, L"g_atom_shell_crash_service");
if (waiting_event != INVALID_HANDLE_VALUE)
WaitForSingleObject(waiting_event, 1000);
breakpad_.reset(new google_breakpad::ExceptionHandler(
temp_dir.value(),
FilterCallback,
MinidumpCallback,
this,
google_breakpad::ExceptionHandler::HANDLER_ALL,
kSmallDumpType,
pipe_name.c_str(),
GetCustomInfo(product_name, version, company_name)));
if (!breakpad_->IsOutOfProcess())
LOG(ERROR) << "Cannot initialize out-of-process crash handler";
}
void CrashReporterWin::SetUploadParameters() {
upload_parameters_["platform"] = "win32";
}
// static
bool CrashReporterWin::FilterCallback(void* context,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion) {
return true;
}
// static
bool CrashReporterWin::MinidumpCallback(const wchar_t* dump_path,
const wchar_t* minidump_id,
void* context,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
bool succeeded) {
CrashReporterWin* self = static_cast<CrashReporterWin*>(context);
if (succeeded && !self->skip_system_crash_handler_)
return true;
else
return false;
}
google_breakpad::CustomClientInfo* CrashReporterWin::GetCustomInfo(
const std::string& product_name,
const std::string& version,
const std::string& company_name) {
custom_info_entries_.clear();
custom_info_entries_.reserve(2 + upload_parameters_.size());
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
L"prod", L"Atom-Shell"));
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
L"ver", UTF8ToWide(version).c_str()));
for (StringMap::const_iterator iter = upload_parameters_.begin();
iter != upload_parameters_.end(); ++iter) {
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
UTF8ToWide(iter->first).c_str(),
UTF8ToWide(iter->second).c_str()));
}
custom_info_.entries = &custom_info_entries_.front();
custom_info_.count = custom_info_entries_.size();
return &custom_info_;
}
// static
CrashReporterWin* CrashReporterWin::GetInstance() {
return Singleton<CrashReporterWin>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterWin::GetInstance();
}
} // namespace crash_reporter

View file

@ -0,0 +1,64 @@
// 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_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "atom/common/crash_reporter/crash_reporter.h"
#include "vendor/breakpad/src/client/windows/handler/exception_handler.h"
template <typename T> struct DefaultSingletonTraits;
namespace crash_reporter {
class CrashReporterWin : public CrashReporter {
public:
static CrashReporterWin* GetInstance();
virtual void InitBreakpad(const std::string& product_name,
const std::string& version,
const std::string& company_name,
const std::string& submit_url,
bool auto_submit,
bool skip_system_crash_handler) OVERRIDE;
virtual void SetUploadParameters() OVERRIDE;
private:
friend struct DefaultSingletonTraits<CrashReporterWin>;
CrashReporterWin();
virtual ~CrashReporterWin();
static bool FilterCallback(void* context,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion);
static bool MinidumpCallback(const wchar_t* dump_path,
const wchar_t* minidump_id,
void* context,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
bool succeeded);
// Returns the custom info structure based on parameters.
google_breakpad::CustomClientInfo* GetCustomInfo(
const std::string& product_name,
const std::string& version,
const std::string& company_name);
// Custom information to be passed to crash handler.
std::vector<google_breakpad::CustomInfoEntry> custom_info_entries_;
google_breakpad::CustomClientInfo custom_info_;
bool skip_system_crash_handler_;
scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterWin);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_

View file

@ -0,0 +1,668 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Copyright (c) 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.
// For linux_syscall_support.h. This makes it safe to call embedded system
// calls when in seccomp mode.
#include "atom/common/crash_reporter/linux/crash_dump_handler.h"
#include <poll.h>
#include <algorithm>
#include "base/posix/eintr_wrapper.h"
#include "vendor/breakpad/src/client/linux/minidump_writer/directory_reader.h"
#include "vendor/breakpad/src/common/linux/linux_libc_support.h"
#include "vendor/breakpad/src/common/memory.h"
#include "third_party/lss/linux_syscall_support.h"
// Some versions of gcc are prone to warn about unused return values. In cases
// where we either a) know the call cannot fail, or b) there is nothing we
// can do when a call fails, we mark the return code as ignored. This avoids
// spurious compiler warnings.
#define IGNORE_RET(x) do { if (x); } while (0)
namespace crash_reporter {
namespace {
// String buffer size to use to convert a uint64_t to string.
const size_t kUint64StringSize = 21;
// Writes the value |v| as 16 hex characters to the memory pointed at by
// |output|.
void write_uint64_hex(char* output, uint64_t v) {
static const char hextable[] = "0123456789abcdef";
for (int i = 15; i >= 0; --i) {
output[i] = hextable[v & 15];
v >>= 4;
}
}
// uint64_t version of my_int_len() from
// breakpad/src/common/linux/linux_libc_support.h. Return the length of the
// given, non-negative integer when expressed in base 10.
unsigned my_uint64_len(uint64_t i) {
if (!i)
return 1;
unsigned len = 0;
while (i) {
len++;
i /= 10;
}
return len;
}
// uint64_t version of my_uitos() from
// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
// integer to a string (not null-terminated).
void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
for (unsigned index = i_len; index; --index, i /= 10)
output[index - 1] = '0' + (i % 10);
}
// Converts a struct timeval to milliseconds.
uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) {
uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
ret *= 1000;
ret += tv->tv_usec / 1000;
return ret;
}
size_t LengthWithoutTrailingSpaces(const char* str, size_t len) {
while (len > 0 && str[len - 1] == ' ') {
len--;
}
return len;
}
// MIME substrings.
const char g_rn[] = "\r\n";
const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
const char g_quote_msg[] = "\"";
const char g_dashdash_msg[] = "--";
const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
const char g_content_type_msg[] = "Content-Type: application/octet-stream";
// MimeWriter manages an iovec for writing MIMEs to a file.
class MimeWriter {
public:
static const int kIovCapacity = 30;
static const size_t kMaxCrashChunkSize = 64;
MimeWriter(int fd, const char* const mime_boundary);
~MimeWriter();
// Append boundary.
virtual void AddBoundary();
// Append end of file boundary.
virtual void AddEnd();
// Append key/value pair with specified sizes.
virtual void AddPairData(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size);
// Append key/value pair.
void AddPairString(const char* msg_type,
const char* msg_data) {
AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
}
// Append key/value pair, splitting value into chunks no larger than
// |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
// The msg_type string will have a counter suffix to distinguish each chunk.
virtual void AddPairDataInChunks(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size,
size_t chunk_size,
bool strip_trailing_spaces);
// Add binary file contents to be uploaded with the specified filename.
virtual void AddFileContents(const char* filename_msg,
uint8_t* file_data,
size_t file_size);
// Flush any pending iovecs to the output file.
void Flush() {
IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
iov_index_ = 0;
}
protected:
void AddItem(const void* base, size_t size);
// Minor performance trade-off for easier-to-maintain code.
void AddString(const char* str) {
AddItem(str, my_strlen(str));
}
void AddItemWithoutTrailingSpaces(const void* base, size_t size);
struct kernel_iovec iov_[kIovCapacity];
int iov_index_;
// Output file descriptor.
int fd_;
const char* const mime_boundary_;
private:
DISALLOW_COPY_AND_ASSIGN(MimeWriter);
};
MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
: iov_index_(0),
fd_(fd),
mime_boundary_(mime_boundary) {
}
MimeWriter::~MimeWriter() {
}
void MimeWriter::AddBoundary() {
AddString(mime_boundary_);
AddString(g_rn);
}
void MimeWriter::AddEnd() {
AddString(mime_boundary_);
AddString(g_dashdash_msg);
AddString(g_rn);
}
void MimeWriter::AddPairData(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size) {
AddString(g_form_data_msg);
AddItem(msg_type, msg_type_size);
AddString(g_quote_msg);
AddString(g_rn);
AddString(g_rn);
AddItem(msg_data, msg_data_size);
AddString(g_rn);
}
void MimeWriter::AddPairDataInChunks(const char* msg_type,
size_t msg_type_size,
const char* msg_data,
size_t msg_data_size,
size_t chunk_size,
bool strip_trailing_spaces) {
if (chunk_size > kMaxCrashChunkSize)
return;
unsigned i = 0;
size_t done = 0, msg_length = msg_data_size;
while (msg_length) {
char num[kUint64StringSize];
const unsigned num_len = my_uint_len(++i);
my_uitos(num, i, num_len);
size_t chunk_len = std::min(chunk_size, msg_length);
AddString(g_form_data_msg);
AddItem(msg_type, msg_type_size);
AddItem(num, num_len);
AddString(g_quote_msg);
AddString(g_rn);
AddString(g_rn);
if (strip_trailing_spaces) {
AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
} else {
AddItem(msg_data + done, chunk_len);
}
AddString(g_rn);
AddBoundary();
Flush();
done += chunk_len;
msg_length -= chunk_len;
}
}
void MimeWriter::AddFileContents(const char* filename_msg, uint8_t* file_data,
size_t file_size) {
AddString(g_form_data_msg);
AddString(filename_msg);
AddString(g_rn);
AddString(g_content_type_msg);
AddString(g_rn);
AddString(g_rn);
AddItem(file_data, file_size);
AddString(g_rn);
}
void MimeWriter::AddItem(const void* base, size_t size) {
// Check if the iovec is full and needs to be flushed to output file.
if (iov_index_ == kIovCapacity) {
Flush();
}
iov_[iov_index_].iov_base = const_cast<void*>(base);
iov_[iov_index_].iov_len = size;
++iov_index_;
}
void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
AddItem(base, LengthWithoutTrailingSpaces(static_cast<const char*>(base),
size));
}
void LoadDataFromFD(google_breakpad::PageAllocator* allocator,
int fd, bool close_fd, uint8_t** file_data, size_t* size) {
struct kernel_stat st;
if (sys_fstat(fd, &st) != 0) {
static const char msg[] = "Cannot upload crash dump: stat failed\n";
WriteLog(msg, sizeof(msg) - 1);
if (close_fd)
IGNORE_RET(sys_close(fd));
return;
}
*file_data = reinterpret_cast<uint8_t*>(allocator->Alloc(st.st_size));
if (!(*file_data)) {
static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
WriteLog(msg, sizeof(msg) - 1);
if (close_fd)
IGNORE_RET(sys_close(fd));
return;
}
my_memset(*file_data, 0xf, st.st_size);
*size = st.st_size;
int byte_read = sys_read(fd, *file_data, *size);
if (byte_read == -1) {
static const char msg[] = "Cannot upload crash dump: read failed\n";
WriteLog(msg, sizeof(msg) - 1);
if (close_fd)
IGNORE_RET(sys_close(fd));
return;
}
if (close_fd)
IGNORE_RET(sys_close(fd));
}
void LoadDataFromFile(google_breakpad::PageAllocator* allocator,
const char* filename,
int* fd, uint8_t** file_data, size_t* size) {
// WARNING: this code runs in a compromised context. It may not call into
// libc nor allocate memory normally.
*fd = sys_open(filename, O_RDONLY, 0);
*size = 0;
if (*fd < 0) {
static const char msg[] = "Cannot upload crash dump: failed to open\n";
WriteLog(msg, sizeof(msg) - 1);
return;
}
LoadDataFromFD(allocator, *fd, true, file_data, size);
}
// Spawn the appropriate upload process for the current OS:
// - generic Linux invokes wget.
// - ChromeOS invokes crash_reporter.
// |dumpfile| is the path to the dump data file.
// |mime_boundary| is only used on Linux.
// |exe_buf| is only used on CrOS and is the crashing process' name.
void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
const char* dumpfile,
const char* mime_boundary,
const char* exe_buf,
google_breakpad::PageAllocator* allocator) {
// The --header argument to wget looks like:
// --header=Content-Type: multipart/form-data; boundary=XYZ
// where the boundary has two fewer leading '-' chars
static const char header_msg[] =
"--header=Content-Type: multipart/form-data; boundary=";
char* const header = reinterpret_cast<char*>(allocator->Alloc(
sizeof(header_msg) - 1 + strlen(mime_boundary) - 2 + 1));
memcpy(header, header_msg, sizeof(header_msg) - 1);
memcpy(header + sizeof(header_msg) - 1, mime_boundary + 2,
strlen(mime_boundary) - 2);
// We grab the NUL byte from the end of |mime_boundary|.
// The --post-file argument to wget looks like:
// --post-file=/tmp/...
static const char post_file_msg[] = "--post-file=";
char* const post_file = reinterpret_cast<char*>(allocator->Alloc(
sizeof(post_file_msg) - 1 + strlen(dumpfile) + 1));
memcpy(post_file, post_file_msg, sizeof(post_file_msg) - 1);
memcpy(post_file + sizeof(post_file_msg) - 1, dumpfile, strlen(dumpfile));
static const char kWgetBinary[] = "/usr/bin/wget";
const char* args[] = {
kWgetBinary,
header,
post_file,
// TODO(zcbenz): Enabling custom upload url.
info.upload_url,
"--timeout=10", // Set a timeout so we don't hang forever.
"--tries=1", // Don't retry if the upload fails.
"-O", // output reply to fd 3
"/dev/fd/3",
NULL,
};
static const char msg[] = "Cannot upload crash dump: cannot exec "
"/usr/bin/wget\n";
execve(args[0], const_cast<char**>(args), environ);
WriteLog(msg, sizeof(msg) - 1);
sys__exit(1);
}
} // namespace
void HandleCrashDump(const BreakpadInfo& info) {
int dumpfd;
bool keep_fd = false;
size_t dump_size;
uint8_t* dump_data;
google_breakpad::PageAllocator allocator;
const char* exe_buf = NULL;
if (info.fd != -1) {
// Dump is provided with an open FD.
keep_fd = true;
dumpfd = info.fd;
// The FD is pointing to the end of the file.
// Rewind, we'll read the data next.
if (lseek(dumpfd, 0, SEEK_SET) == -1) {
static const char msg[] = "Cannot upload crash dump: failed to "
"reposition minidump FD\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(dumpfd));
return;
}
LoadDataFromFD(&allocator, info.fd, false, &dump_data, &dump_size);
} else {
// Dump is provided with a path.
keep_fd = false;
LoadDataFromFile(
&allocator, info.filename, &dumpfd, &dump_data, &dump_size);
}
// We need to build a MIME block for uploading to the server. Since we are
// going to fork and run wget, it needs to be written to a temp file.
const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
if (ufd < 0) {
static const char msg[] = "Cannot upload crash dump because /dev/urandom"
" is missing\n";
WriteLog(msg, sizeof(msg) - 1);
return;
}
static const char temp_file_template[] =
"/tmp/chromium-upload-XXXXXXXXXXXXXXXX";
char temp_file[sizeof(temp_file_template)];
int temp_file_fd = -1;
if (keep_fd) {
temp_file_fd = dumpfd;
// Rewind the destination, we are going to overwrite it.
if (lseek(dumpfd, 0, SEEK_SET) == -1) {
static const char msg[] = "Cannot upload crash dump: failed to "
"reposition minidump FD (2)\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(dumpfd));
return;
}
} else {
if (info.upload) {
memcpy(temp_file, temp_file_template, sizeof(temp_file_template));
for (unsigned i = 0; i < 10; ++i) {
uint64_t t;
sys_read(ufd, &t, sizeof(t));
write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t);
temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (temp_file_fd >= 0)
break;
}
if (temp_file_fd < 0) {
static const char msg[] = "Failed to create temporary file in /tmp: "
"cannot upload crash dump\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(ufd));
return;
}
} else {
temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
if (temp_file_fd < 0) {
static const char msg[] = "Failed to save crash dump: failed to open\n";
WriteLog(msg, sizeof(msg) - 1);
IGNORE_RET(sys_close(ufd));
return;
}
}
}
// The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
char mime_boundary[28 + 16 + 1];
my_memset(mime_boundary, '-', 28);
uint64_t boundary_rand;
sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
write_uint64_hex(mime_boundary + 28, boundary_rand);
mime_boundary[28 + 16] = 0;
IGNORE_RET(sys_close(ufd));
// The MIME block looks like this:
// BOUNDARY \r\n
// Content-Disposition: form-data; name="prod" \r\n \r\n
// Chrome_Linux \r\n
// BOUNDARY \r\n
// Content-Disposition: form-data; name="ver" \r\n \r\n
// 1.2.3.4 \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="ptime" \r\n \r\n
// abcdef \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="ptype" \r\n \r\n
// abcdef \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="lsb-release" \r\n \r\n
// abcdef \r\n
// BOUNDARY \r\n
//
// zero or one:
// Content-Disposition: form-data; name="oom-size" \r\n \r\n
// 1234567890 \r\n
// BOUNDARY \r\n
//
// zero or more (up to CrashKeyStorage::num_entries = 64):
// Content-Disposition: form-data; name=crash-key-name \r\n
// crash-key-value \r\n
// BOUNDARY \r\n
//
// Content-Disposition: form-data; name="dump"; filename="dump" \r\n
// Content-Type: application/octet-stream \r\n \r\n
// <dump contents>
// \r\n BOUNDARY -- \r\n
MimeWriter writer(temp_file_fd, mime_boundary);
{
writer.AddBoundary();
if (info.pid > 0) {
char pid_value_buf[kUint64StringSize];
uint64_t pid_value_len = my_uint64_len(info.pid);
my_uint64tos(pid_value_buf, info.pid, pid_value_len);
static const char pid_key_name[] = "pid";
writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1,
pid_value_buf, pid_value_len);
writer.AddBoundary();
}
writer.Flush();
}
if (info.process_start_time > 0) {
struct kernel_timeval tv;
if (!sys_gettimeofday(&tv, NULL)) {
uint64_t time = kernel_timeval_to_ms(&tv);
if (time > info.process_start_time) {
time -= info.process_start_time;
char time_str[kUint64StringSize];
const unsigned time_len = my_uint64_len(time);
my_uint64tos(time_str, time, time_len);
static const char process_time_msg[] = "ptime";
writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
time_str, time_len);
writer.AddBoundary();
writer.Flush();
}
}
}
if (info.distro_length) {
static const char distro_msg[] = "lsb-release";
writer.AddPairString(distro_msg, info.distro);
writer.AddBoundary();
writer.Flush();
}
if (info.oom_size) {
char oom_size_str[kUint64StringSize];
const unsigned oom_size_len = my_uint64_len(info.oom_size);
my_uint64tos(oom_size_str, info.oom_size, oom_size_len);
static const char oom_size_msg[] = "oom-size";
writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1,
oom_size_str, oom_size_len);
writer.AddBoundary();
writer.Flush();
}
if (info.crash_keys) {
CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
const CrashKeyStorage::Entry* entry;
while ((entry = crash_key_iterator.Next())) {
writer.AddPairString(entry->key, entry->value);
writer.AddBoundary();
writer.Flush();
}
}
writer.AddFileContents(g_dump_msg, dump_data, dump_size);
writer.AddEnd();
writer.Flush();
IGNORE_RET(sys_close(temp_file_fd));
if (!info.upload)
return;
const pid_t child = sys_fork();
if (!child) {
// Spawned helper process.
//
// This code is called both when a browser is crashing (in which case,
// nothing really matters any more) and when a renderer/plugin crashes, in
// which case we need to continue.
//
// Since we are a multithreaded app, if we were just to fork(), we might
// grab file descriptors which have just been created in another thread and
// hold them open for too long.
//
// Thus, we have to loop and try and close everything.
const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0);
if (fd < 0) {
for (unsigned i = 3; i < 8192; ++i)
IGNORE_RET(sys_close(i));
} else {
google_breakpad::DirectoryReader reader(fd);
const char* name;
while (reader.GetNextEntry(&name)) {
int i;
if (my_strtoui(&i, name) && i > 2 && i != fd)
IGNORE_RET(sys_close(i));
reader.PopEntry();
}
IGNORE_RET(sys_close(fd));
}
IGNORE_RET(sys_setsid());
// Leave one end of a pipe in the upload process and watch for it getting
// closed by the upload process exiting.
int fds[2];
if (sys_pipe(fds) >= 0) {
const pid_t upload_child = sys_fork();
if (!upload_child) {
// Upload process.
IGNORE_RET(sys_close(fds[0]));
IGNORE_RET(sys_dup2(fds[1], 3));
ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf,
&allocator);
}
// Helper process.
if (upload_child > 0) {
IGNORE_RET(sys_close(fds[1]));
char id_buf[17]; // Crash report IDs are expected to be 16 chars.
ssize_t len = -1;
// Upload should finish in about 10 seconds. Add a few more 500 ms
// internals to account for process startup time.
for (size_t wait_count = 0; wait_count < 24; ++wait_count) {
struct kernel_pollfd poll_fd;
poll_fd.fd = fds[0];
poll_fd.events = POLLIN | POLLPRI | POLLERR;
int ret = sys_poll(&poll_fd, 1, 500);
if (ret < 0) {
// Error
break;
} else if (ret > 0) {
// There is data to read.
len = HANDLE_EINTR(sys_read(fds[0], id_buf, sizeof(id_buf) - 1));
break;
}
// ret == 0 -> timed out, continue waiting.
}
if (len > 0) {
// Write crash dump id to stderr.
id_buf[len] = 0;
static const char msg[] = "\nCrash dump id: ";
WriteLog(msg, sizeof(msg) - 1);
WriteLog(id_buf, my_strlen(id_buf));
WriteLog("\n", 1);
}
if (sys_waitpid(upload_child, NULL, WNOHANG) == 0) {
// Upload process is still around, kill it.
sys_kill(upload_child, SIGKILL);
}
}
}
// Helper process.
IGNORE_RET(sys_unlink(info.filename));
IGNORE_RET(sys_unlink(temp_file));
sys__exit(0);
}
// Main browser process.
if (child <= 0)
return;
(void) HANDLE_EINTR(sys_waitpid(child, NULL, 0));
}
size_t WriteLog(const char* buf, size_t nbytes) {
return sys_write(2, buf, nbytes);
}
} // namespace crash_reporter

View file

@ -0,0 +1,38 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Copyright (c) 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_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
#define ATOM_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
#include "base/basictypes.h"
#include "vendor/breakpad/src/common/simple_string_dictionary.h"
namespace crash_reporter {
typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage;
// BreakpadInfo describes a crash report.
// The minidump information can either be contained in a file descriptor (fd) or
// in a file (whose path is in filename).
struct BreakpadInfo {
int fd; // File descriptor to the Breakpad dump data.
const char* filename; // Path to the Breakpad dump data.
const char* distro; // Linux distro string.
unsigned distro_length; // Length of |distro|.
bool upload; // Whether to upload or save crash dump.
uint64_t process_start_time; // Uptime of the crashing process.
size_t oom_size; // Amount of memory requested if OOM.
uint64_t pid; // PID where applicable.
const char* upload_url; // URL to upload the minidump.
CrashKeyStorage* crash_keys;
};
void HandleCrashDump(const BreakpadInfo& info);
size_t WriteLog(const char* buf, size_t nbytes);
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_

View file

@ -0,0 +1,490 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/common/crash_reporter/win/crash_service.h"
#include <windows.h>
#include <sddl.h>
#include <fstream> // NOLINT
#include <map>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/win/windows_version.h"
#include "vendor/breakpad/src/client/windows/crash_generation/client_info.h"
#include "vendor/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
#include "vendor/breakpad/src/client/windows/sender/crash_report_sender.h"
namespace breakpad {
namespace {
const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
const wchar_t kGoogleReportURL[] = L"https://clients2.google.com/cr/report";
const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt";
typedef std::map<std::wstring, std::wstring> CrashMap;
bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info,
const std::wstring& reporter_tag, CrashMap* map) {
google_breakpad::CustomClientInfo info = client_info->GetCustomInfo();
for (uintptr_t i = 0; i < info.count; ++i) {
(*map)[info.entries[i].name] = info.entries[i].value;
}
(*map)[L"rept"] = reporter_tag;
return !map->empty();
}
bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) {
std::wstring file_path(dump_path);
size_t last_dot = file_path.rfind(L'.');
if (last_dot == std::wstring::npos)
return false;
file_path.resize(last_dot);
file_path += L".txt";
std::wofstream file(file_path.c_str(),
std::ios_base::out | std::ios_base::app | std::ios::binary);
if (!file.is_open())
return false;
CrashMap::const_iterator pos;
for (pos = map.begin(); pos != map.end(); ++pos) {
std::wstring line = pos->first;
line += L':';
line += pos->second;
line += L'\n';
file.write(line.c_str(), static_cast<std::streamsize>(line.length()));
}
return true;
}
// The window procedure task is to handle when a) the user logs off.
// b) the system shuts down or c) when the user closes the window.
LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam) {
switch (message) {
case WM_CLOSE:
case WM_ENDSESSION:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wparam, lparam);
}
return 0;
}
// This is the main and only application window.
HWND g_top_window = NULL;
bool CreateTopWindow(HINSTANCE instance, bool visible) {
WNDCLASSEXW wcx = {0};
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = CrashSvcWndProc;
wcx.hInstance = instance;
wcx.lpszClassName = L"crash_svc_class";
ATOM atom = ::RegisterClassExW(&wcx);
DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED;
// The window size is zero but being a popup window still shows in the
// task bar and can be closed using the system menu or using task manager.
HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
NULL, NULL, instance, NULL);
if (!window)
return false;
::UpdateWindow(window);
VLOG(1) << "window handle is " << window;
g_top_window = window;
return true;
}
// Simple helper class to keep the process alive until the current request
// finishes.
class ProcessingLock {
public:
ProcessingLock() {
::InterlockedIncrement(&op_count_);
}
~ProcessingLock() {
::InterlockedDecrement(&op_count_);
}
static bool IsWorking() {
return (op_count_ != 0);
}
private:
static volatile LONG op_count_;
};
volatile LONG ProcessingLock::op_count_ = 0;
// This structure contains the information that the worker thread needs to
// send a crash dump to the server.
struct DumpJobInfo {
DWORD pid;
CrashService* self;
CrashMap map;
std::wstring dump_path;
DumpJobInfo(DWORD process_id, CrashService* service,
const CrashMap& crash_map, const std::wstring& path)
: pid(process_id), self(service), map(crash_map), dump_path(path) {
}
};
} // namespace
// Command line switches:
const char CrashService::kMaxReports[] = "max-reports";
const char CrashService::kNoWindow[] = "no-window";
const char CrashService::kReporterTag[] = "reporter";
const char CrashService::kDumpsDir[] = "dumps-dir";
const char CrashService::kPipeName[] = "pipe-name";
const char CrashService::kReporterURL[] = "reporter-url";
CrashService::CrashService()
: sender_(NULL),
dumper_(NULL),
requests_handled_(0),
requests_sent_(0),
clients_connected_(0),
clients_terminated_(0) {
}
CrashService::~CrashService() {
base::AutoLock lock(sending_);
delete dumper_;
delete sender_;
}
bool CrashService::Initialize(const base::FilePath& operating_dir,
const base::FilePath& dumps_path) {
using google_breakpad::CrashReportSender;
using google_breakpad::CrashGenerationServer;
std::wstring pipe_name = kTestPipeName;
int max_reports = -1;
// The checkpoint file allows CrashReportSender to enforce the the maximum
// reports per day quota. Does not seem to serve any other purpose.
base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile);
CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
base::FilePath dumps_path_to_use = dumps_path;
if (cmd_line.HasSwitch(kDumpsDir)) {
dumps_path_to_use =
base::FilePath(cmd_line.GetSwitchValueNative(kDumpsDir));
}
// We can override the send reports quota with a command line switch.
if (cmd_line.HasSwitch(kMaxReports))
max_reports = _wtoi(cmd_line.GetSwitchValueNative(kMaxReports).c_str());
// Allow the global pipe name to be overridden for better testability.
if (cmd_line.HasSwitch(kPipeName))
pipe_name = cmd_line.GetSwitchValueNative(kPipeName);
if (max_reports > 0) {
// Create the http sender object.
sender_ = new CrashReportSender(checkpoint_path.value());
sender_->set_max_reports_per_day(max_reports);
}
SECURITY_ATTRIBUTES security_attributes = {0};
SECURITY_ATTRIBUTES* security_attributes_actual = NULL;
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
SECURITY_DESCRIPTOR* security_descriptor =
reinterpret_cast<SECURITY_DESCRIPTOR*>(
GetSecurityDescriptorForLowIntegrity());
DCHECK(security_descriptor != NULL);
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = security_descriptor;
security_attributes.bInheritHandle = FALSE;
security_attributes_actual = &security_attributes;
}
// Create the OOP crash generator object.
dumper_ = new CrashGenerationServer(pipe_name, security_attributes_actual,
&CrashService::OnClientConnected, this,
&CrashService::OnClientDumpRequest, this,
&CrashService::OnClientExited, this,
NULL, NULL,
true, &dumps_path_to_use.value());
if (!dumper_) {
LOG(ERROR) << "could not create dumper";
if (security_attributes.lpSecurityDescriptor)
LocalFree(security_attributes.lpSecurityDescriptor);
return false;
}
if (!CreateTopWindow(::GetModuleHandleW(NULL),
!cmd_line.HasSwitch(kNoWindow))) {
LOG(ERROR) << "could not create window";
if (security_attributes.lpSecurityDescriptor)
LocalFree(security_attributes.lpSecurityDescriptor);
return false;
}
reporter_tag_ = L"crash svc";
if (cmd_line.HasSwitch(kReporterTag))
reporter_tag_ = cmd_line.GetSwitchValueNative(kReporterTag);
reporter_url_ = kGoogleReportURL;
if (cmd_line.HasSwitch(kReporterURL))
reporter_url_ = cmd_line.GetSwitchValueNative(kReporterURL);
// Log basic information.
VLOG(1) << "pipe name is " << pipe_name
<< "\ndumps at " << dumps_path_to_use.value();
if (sender_) {
VLOG(1) << "checkpoint is " << checkpoint_path.value()
<< "\nserver is " << reporter_url_
<< "\nmaximum " << sender_->max_reports_per_day() << " reports/day"
<< "\nreporter is " << reporter_tag_;
}
// Start servicing clients.
if (!dumper_->Start()) {
LOG(ERROR) << "could not start dumper";
if (security_attributes.lpSecurityDescriptor)
LocalFree(security_attributes.lpSecurityDescriptor);
return false;
}
if (security_attributes.lpSecurityDescriptor)
LocalFree(security_attributes.lpSecurityDescriptor);
// Create or open an event to signal the browser process that the crash
// service is initialized.
HANDLE running_event =
::CreateEventW(NULL, TRUE, TRUE, L"g_atom_shell_crash_service");
// If the browser already had the event open, the CreateEvent call did not
// signal it. We need to do it manually.
::SetEvent(running_event);
return true;
}
void CrashService::OnClientConnected(void* context,
const google_breakpad::ClientInfo* client_info) {
ProcessingLock lock;
VLOG(1) << "client start. pid = " << client_info->pid();
CrashService* self = static_cast<CrashService*>(context);
::InterlockedIncrement(&self->clients_connected_);
}
void CrashService::OnClientExited(void* context,
const google_breakpad::ClientInfo* client_info) {
ProcessingLock lock;
VLOG(1) << "client end. pid = " << client_info->pid();
CrashService* self = static_cast<CrashService*>(context);
::InterlockedIncrement(&self->clients_terminated_);
if (!self->sender_)
return;
// When we are instructed to send reports we need to exit if there are
// no more clients to service. The next client that runs will start us.
// Only chrome.exe starts crash_service with a non-zero max_reports.
if (self->clients_connected_ > self->clients_terminated_)
return;
if (self->sender_->max_reports_per_day() > 0) {
// Wait for the other thread to send crashes, if applicable. The sender
// thread takes the sending_ lock, so the sleep is just to give it a
// chance to start.
::Sleep(1000);
base::AutoLock lock(self->sending_);
// Some people can restart chrome very fast, check again if we have
// a new client before exiting for real.
if (self->clients_connected_ == self->clients_terminated_) {
VLOG(1) << "zero clients. exiting";
::PostMessage(g_top_window, WM_CLOSE, 0, 0);
}
}
}
void CrashService::OnClientDumpRequest(void* context,
const google_breakpad::ClientInfo* client_info,
const std::wstring* file_path) {
ProcessingLock lock;
if (!file_path) {
LOG(ERROR) << "dump with no file path";
return;
}
if (!client_info) {
LOG(ERROR) << "dump with no client info";
return;
}
CrashService* self = static_cast<CrashService*>(context);
if (!self) {
LOG(ERROR) << "dump with no context";
return;
}
CrashMap map;
CustomInfoToMap(client_info, self->reporter_tag_, &map);
// Move dump file to the directory under client breakpad dump location.
base::FilePath dump_location = base::FilePath(*file_path);
CrashMap::const_iterator it = map.find(L"breakpad-dump-location");
if (it != map.end()) {
base::FilePath alternate_dump_location = base::FilePath(it->second);
file_util::CreateDirectoryW(alternate_dump_location);
alternate_dump_location = alternate_dump_location.Append(
dump_location.BaseName());
base::Move(dump_location, alternate_dump_location);
dump_location = alternate_dump_location;
}
DWORD pid = client_info->pid();
VLOG(1) << "dump for pid = " << pid << " is " << dump_location.value();
if (!WriteCustomInfoToFile(dump_location.value(), map)) {
LOG(ERROR) << "could not write custom info file";
}
if (!self->sender_)
return;
// Send the crash dump using a worker thread. This operation has retry
// logic in case there is no internet connection at the time.
DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map,
dump_location.value());
if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
dump_job, WT_EXECUTELONGFUNCTION)) {
LOG(ERROR) << "could not queue job";
}
}
// We are going to try sending the report several times. If we can't send,
// we sleep from one minute to several hours depending on the retry round.
DWORD CrashService::AsyncSendDump(void* context) {
if (!context)
return 0;
DumpJobInfo* info = static_cast<DumpJobInfo*>(context);
std::wstring report_id = L"<unsent>";
const DWORD kOneMinute = 60*1000;
const DWORD kOneHour = 60*kOneMinute;
const DWORD kSleepSchedule[] = {
24*kOneHour,
8*kOneHour,
4*kOneHour,
kOneHour,
15*kOneMinute,
0};
int retry_round = arraysize(kSleepSchedule) - 1;
do {
::Sleep(kSleepSchedule[retry_round]);
{
// Take the server lock while sending. This also prevent early
// termination of the service object.
base::AutoLock lock(info->self->sending_);
VLOG(1) << "trying to send report for pid = " << info->pid;
google_breakpad::ReportResult send_result
= info->self->sender_->SendCrashReport(info->self->reporter_url_,
info->map,
info->dump_path,
&report_id);
switch (send_result) {
case google_breakpad::RESULT_FAILED:
report_id = L"<network issue>";
break;
case google_breakpad::RESULT_REJECTED:
report_id = L"<rejected>";
++info->self->requests_handled_;
retry_round = 0;
break;
case google_breakpad::RESULT_SUCCEEDED:
++info->self->requests_sent_;
++info->self->requests_handled_;
retry_round = 0;
break;
case google_breakpad::RESULT_THROTTLED:
report_id = L"<throttled>";
break;
default:
report_id = L"<unknown>";
break;
};
}
VLOG(1) << "dump for pid =" << info->pid << " crash2 id =" << report_id;
--retry_round;
} while (retry_round >= 0);
if (!::DeleteFileW(info->dump_path.c_str()))
LOG(WARNING) << "could not delete " << info->dump_path;
delete info;
return 0;
}
int CrashService::ProcessingLoop() {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
VLOG(1) << "session ending..";
while (ProcessingLock::IsWorking()) {
::Sleep(50);
}
VLOG(1) << "clients connected :" << clients_connected_
<< "\nclients terminated :" << clients_terminated_
<< "\ndumps serviced :" << requests_handled_
<< "\ndumps reported :" << requests_sent_;
return static_cast<int>(msg.wParam);
}
PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() {
// Build the SDDL string for the label.
std::wstring sddl = L"S:(ML;;NW;;;S-1-16-4096)";
DWORD error = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR sec_desc = NULL;
PACL sacl = NULL;
BOOL sacl_present = FALSE;
BOOL sacl_defaulted = FALSE;
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
SDDL_REVISION,
&sec_desc, NULL)) {
if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
&sacl_defaulted)) {
return sec_desc;
}
}
return NULL;
}
} // namespace breakpad

View file

@ -0,0 +1,131 @@
// Copyright (c) 2011 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 COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
#define COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
#include <string>
#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/synchronization/lock.h"
namespace google_breakpad {
class CrashReportSender;
class CrashGenerationServer;
class ClientInfo;
}
namespace breakpad {
// This class implements an out-of-process crash server. It uses breakpad's
// CrashGenerationServer and CrashReportSender to generate and then send the
// crash dumps. Internally, it uses OS specific pipe to allow applications to
// register for crash dumps and later on when a registered application crashes
// it will signal an event that causes this code to wake up and perform a
// crash dump on the signaling process. The dump is then stored on disk and
// possibly sent to the crash2 servers.
class CrashService {
public:
CrashService();
~CrashService();
// Starts servicing crash dumps. Returns false if it failed. Do not use
// other members in that case. |operating_dir| is where the CrashService
// should store breakpad's checkpoint file. |dumps_path| is the directory
// where the crash dumps should be stored.
bool Initialize(const base::FilePath& operating_dir,
const base::FilePath& dumps_path);
// Command line switches:
//
// --max-reports=<number>
// Allows to override the maximum number for reports per day. Normally
// the crash dumps are never sent so if you want to send any you must
// specify a positive number here.
static const char kMaxReports[];
// --no-window
// Does not create a visible window on the desktop. The window does not have
// any other functionality other than allowing the crash service to be
// gracefully closed.
static const char kNoWindow[];
// --reporter=<string>
// Allows to specify a custom string that appears on the detail crash report
// page in the crash server. This should be a 25 chars or less string.
// The default tag if not specified is 'crash svc'.
static const char kReporterTag[];
// --dumps-dir=<directory-path>
// Override the directory to which crash dump files will be written.
static const char kDumpsDir[];
// --pipe-name=<string>
// Override the name of the Windows named pipe on which we will
// listen for crash dump request messages.
static const char kPipeName[];
// --reporter-url=<string>
// Override the URL to which crash reports will be sent to.
static const char kReporterURL[];
// Returns number of crash dumps handled.
int requests_handled() const {
return requests_handled_;
}
// Returns number of crash clients registered.
int clients_connected() const {
return clients_connected_;
}
// Returns number of crash clients terminated.
int clients_terminated() const {
return clients_terminated_;
}
// Starts the processing loop. This function does not return unless the
// user is logging off or the user closes the crash service window. The
// return value is a good number to pass in ExitProcess().
int ProcessingLoop();
private:
static void OnClientConnected(void* context,
const google_breakpad::ClientInfo* client_info);
static void OnClientDumpRequest(
void* context,
const google_breakpad::ClientInfo* client_info,
const std::wstring* file_path);
static void OnClientExited(void* context,
const google_breakpad::ClientInfo* client_info);
// This routine sends the crash dump to the server. It takes the sending_
// lock when it is performing the send.
static DWORD __stdcall AsyncSendDump(void* context);
// Returns the security descriptor which access to low integrity processes
// The caller is supposed to free the security descriptor by calling
// LocalFree.
PSECURITY_DESCRIPTOR GetSecurityDescriptorForLowIntegrity();
google_breakpad::CrashGenerationServer* dumper_;
google_breakpad::CrashReportSender* sender_;
// the extra tag sent to the server with each dump.
std::wstring reporter_tag_;
// receiver URL of crash reports.
std::wstring reporter_url_;
// clients serviced statistics:
int requests_handled_;
int requests_sent_;
volatile LONG clients_connected_;
volatile LONG clients_terminated_;
base::Lock sending_;
DISALLOW_COPY_AND_ASSIGN(CrashService);
};
} // namespace breakpad
#endif // COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_

View file

@ -0,0 +1,92 @@
// 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/crash_reporter/win/crash_service_main.h"
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "atom/common/crash_reporter/win/crash_service.h"
namespace crash_service {
namespace {
const char kApplicationName[] = "application-name";
const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\$1 Crash Service";
const wchar_t kStandardLogFile[] = L"operation_log.txt";
bool GetCrashServiceDirectory(const std::wstring& application_name,
base::FilePath* dir) {
base::FilePath temp_dir;
if (!file_util::GetTempDir(&temp_dir))
return false;
temp_dir = temp_dir.Append(application_name + L" Crashes");
if (!base::PathExists(temp_dir)) {
if (!file_util::CreateDirectory(temp_dir))
return false;
}
*dir = temp_dir;
return true;
}
} // namespace.
int Main(const wchar_t* cmd) {
// Initialize all Chromium things.
base::AtExitManager exit_manager;
CommandLine::Init(0, NULL);
CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
// Use the application's name as pipe name and output directory.
if (!cmd_line.HasSwitch(kApplicationName)) {
LOG(ERROR) << "Application's name must be specified with --"
<< kApplicationName;
return 1;
}
std::wstring application_name = cmd_line.GetSwitchValueNative(
kApplicationName);
// We use/create a directory under the user's temp folder, for logging.
base::FilePath operating_dir;
GetCrashServiceDirectory(application_name, &operating_dir);
base::FilePath log_file = operating_dir.Append(kStandardLogFile);
// Logging to stderr (to help with debugging failures on the
// buildbots) and to a file.
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_ALL;
settings.log_file = log_file.value().c_str();
logging::InitLogging(settings);
// Logging with pid, tid and timestamp.
logging::SetLogItems(true, true, true, false);
VLOG(1) << "Session start. cmdline is [" << cmd << "]";
// Setting the crash reporter.
string16 pipe_name = ReplaceStringPlaceholders(kPipeNameFormat,
application_name,
NULL);
cmd_line.AppendSwitch("no-window");
cmd_line.AppendSwitchASCII("max-reports", "128");
cmd_line.AppendSwitchASCII("reporter", "atom-shell-crash-service");
cmd_line.AppendSwitchNative("pipe-name", pipe_name);
breakpad::CrashService crash_service;
if (!crash_service.Initialize(operating_dir, operating_dir))
return 2;
VLOG(1) << "Ready to process crash requests";
// Enter the message loop.
int retv = crash_service.ProcessingLoop();
// Time to exit.
VLOG(1) << "Session end. return code is " << retv;
return retv;
}
} // namespace crash_service

View file

@ -0,0 +1,15 @@
// 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_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
#define COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
namespace crash_service {
// Program entry, should be called by main();
int Main(const wchar_t* cmd_line);
} // namespace crash_service
#endif // COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_

View file

@ -0,0 +1,13 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/common/draggable_region.h"
namespace atom {
DraggableRegion::DraggableRegion()
: draggable(false) {
}
} // namespace atom

View file

@ -0,0 +1,21 @@
// Copyright (c) 2012 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_DRAGGABLE_REGION_H_
#define ATOM_COMMON_DRAGGABLE_REGION_H_
#include "ui/gfx/rect.h"
namespace atom {
struct DraggableRegion {
bool draggable;
gfx::Rect bounds;
DraggableRegion();
};
} // namespace atom
#endif // ATOM_COMMON_DRAGGABLE_REGION_H_

View file

@ -0,0 +1,21 @@
path = require 'path'
timers = require 'timers'
Module = require 'module'
# Add common/api/lib to module search paths.
globalPaths = Module.globalPaths
globalPaths.push path.join(process.resourcesPath, 'common', 'api', 'lib')
# setImmediate and process.nextTick makes use of uv_check and uv_prepare to
# run the callbacks, however since we only run uv loop on requests, the
# callbacks wouldn't be called until something else activated the uv loop,
# which would delay the callbacks for arbitrary long time. So we should
# initiatively activate the uv loop once setImmediate and process.nextTick is
# called.
wrapWithActivateUvLoop = (func) ->
->
process.activateUvLoop()
func.apply this, arguments
process.nextTick = wrapWithActivateUvLoop process.nextTick
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
global.clearImmediate = timers.clearImmediate

View file

@ -0,0 +1,19 @@
// 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 <string>
#include "atom/common/atom_version.h"
namespace brightray {
std::string GetApplicationName() {
return "Atom-Shell";
}
std::string GetApplicationVersion() {
return ATOM_VERSION_STRING;
}
} // namespace brightray

View file

@ -0,0 +1,258 @@
// 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/node_bindings.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/base_paths.h"
#include "base/path_service.h"
#include "atom/common/v8/native_type_conversions.h"
#include "content/public/browser/browser_thread.h"
#if defined(OS_WIN)
#include "base/strings/utf_string_conversions.h"
#endif
#include "atom/common/v8/node_common.h"
using content::BrowserThread;
// Forward declaration of internal node functions.
namespace node {
void Init(int*, const char**, int*, const char***);
void Load(Environment* env);
void SetupProcessObject(Environment*, int, const char* const*, int,
const char* const*);
}
namespace atom {
namespace {
// Empty callback for async handle.
void UvNoOp(uv_async_t* handle, int status) {
}
// Convert the given vector to an array of C-strings. The strings in the
// returned vector are only guaranteed valid so long as the vector of strings
// is not modified.
scoped_ptr<const char*[]> StringVectorToArgArray(
const std::vector<std::string>& vector) {
scoped_ptr<const char*[]> array(new const char*[vector.size()]);
for (size_t i = 0; i < vector.size(); ++i)
array[i] = vector[i].c_str();
return array.Pass();
}
#if defined(OS_WIN)
std::vector<std::string> String16VectorToStringVector(
const std::vector<string16>& vector) {
std::vector<std::string> utf8_vector;
utf8_vector.reserve(vector.size());
for (size_t i = 0; i < vector.size(); ++i)
utf8_vector.push_back(UTF16ToUTF8(vector[i]));
return utf8_vector;
}
#endif
} // namespace
node::Environment* global_env = NULL;
NodeBindings::NodeBindings(bool is_browser)
: is_browser_(is_browser),
message_loop_(NULL),
uv_loop_(uv_default_loop()),
embed_closed_(false),
uv_env_(NULL),
weak_factory_(this) {
}
NodeBindings::~NodeBindings() {
// Quit the embed thread.
embed_closed_ = true;
uv_sem_post(&embed_sem_);
WakeupEmbedThread();
// Wait for everything to be done.
uv_thread_join(&embed_thread_);
// Clear uv.
uv_sem_destroy(&embed_sem_);
uv_timer_stop(&idle_timer_);
}
void NodeBindings::Initialize() {
// Init idle GC for browser.
if (is_browser_) {
uv_timer_init(uv_default_loop(), &idle_timer_);
uv_timer_start(&idle_timer_, IdleCallback, 5000, 5000);
}
// Open node's error reporting system for browser process.
node::g_standalone_mode = is_browser_;
node::g_upstream_node_mode = false;
// Init node.
// (we assume it would not node::Init would not modify the parameters under
// embedded mode).
node::Init(NULL, NULL, NULL, NULL);
}
node::Environment* NodeBindings::CreateEnvironment(
v8::Handle<v8::Context> context) {
std::vector<std::string> args =
#if defined(OS_WIN)
String16VectorToStringVector(CommandLine::ForCurrentProcess()->argv());
#else
CommandLine::ForCurrentProcess()->argv();
#endif
// Feed node the path to initialization script.
base::FilePath exec_path(CommandLine::ForCurrentProcess()->argv()[0]);
PathService::Get(base::FILE_EXE, &exec_path);
base::FilePath resources_path =
#if defined(OS_MACOSX)
is_browser_ ? exec_path.DirName().DirName().Append("Resources") :
exec_path.DirName().DirName().DirName().DirName().DirName()
.Append("Resources");
#else
exec_path.DirName().AppendASCII("resources");
#endif
base::FilePath script_path =
resources_path.AppendASCII(is_browser_ ? "browser" : "renderer")
.AppendASCII("lib")
.AppendASCII("init.js");
std::string script_path_str = script_path.AsUTF8Unsafe();
args.insert(args.begin() + 1, script_path_str.c_str());
// Convert string vector to const char* array.
scoped_ptr<const char*[]> c_argv = StringVectorToArgArray(args);
// Construct the parameters that passed to node::CreateEnvironment:
v8::Isolate* isolate = context->GetIsolate();
int argc = args.size();
const char** argv = c_argv.get();
int exec_argc = 0;
const char** exec_argv = NULL;
using namespace v8; // NOLINT
using namespace node; // NOLINT
// Following code are stripped from node::CreateEnvironment in node.cc:
HandleScope handle_scope(isolate);
Context::Scope context_scope(context);
Environment* env = Environment::New(context);
uv_check_init(env->event_loop(), env->immediate_check_handle());
uv_unref(
reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()));
uv_idle_init(env->event_loop(), env->immediate_idle_handle());
uv_prepare_init(env->event_loop(), env->idle_prepare_handle());
uv_check_init(env->event_loop(), env->idle_check_handle());
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()));
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle()));
Local<FunctionTemplate> process_template = FunctionTemplate::New();
process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process"));
Local<Object> process_object = process_template->GetFunction()->NewInstance();
env->set_process_object(process_object);
SetupProcessObject(env, argc, argv, exec_argc, exec_argv);
Load(env);
return env;
}
void NodeBindings::PrepareMessageLoop() {
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
// Add dummy handle for libuv, otherwise libuv would quit when there is
// nothing to do.
uv_async_init(uv_loop_, &dummy_uv_handle_, UvNoOp);
// Start worker that will interrupt main loop when having uv events.
uv_sem_init(&embed_sem_, 0);
uv_thread_create(&embed_thread_, EmbedThreadRunner, this);
}
void NodeBindings::RunMessageLoop() {
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
// The MessageLoop should have been created, remember the one in main thread.
message_loop_ = base::MessageLoop::current();
// Run uv loop for once to give the uv__io_poll a chance to add all events.
UvRunOnce();
}
void NodeBindings::UvRunOnce() {
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
// Use Locker in browser process.
scoped_ptr<v8::Locker> locker;
if (is_browser_)
locker.reset(new v8::Locker(node_isolate));
v8::HandleScope handle_scope(node_isolate);
// Enter node context while dealing with uv events, by default the global
// env would be used unless user specified another one (this happens for
// renderer process, which wraps the uv loop with web page context).
node::Environment* env = uv_env() ? uv_env() : global_env;
v8::Context::Scope context_scope(env->context());
// Deal with uv events.
int r = uv_run(uv_loop_, (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT));
if (r == 0 || uv_loop_->stop_flag != 0)
message_loop_->QuitWhenIdle(); // Quit from uv.
// Tell the worker thread to continue polling.
uv_sem_post(&embed_sem_);
}
void NodeBindings::WakeupMainThread() {
DCHECK(message_loop_);
message_loop_->PostTask(FROM_HERE, base::Bind(&NodeBindings::UvRunOnce,
weak_factory_.GetWeakPtr()));
}
void NodeBindings::WakeupEmbedThread() {
uv_async_send(&dummy_uv_handle_);
}
// static
void NodeBindings::EmbedThreadRunner(void *arg) {
NodeBindings* self = static_cast<NodeBindings*>(arg);
while (true) {
// Wait for the main loop to deal with events.
uv_sem_wait(&self->embed_sem_);
if (self->embed_closed_)
break;
// Wait for something to happen in uv loop.
// Note that the PollEvents() is implemented by derived classes, so when
// this class is being destructed the PollEvents() would not be available
// anymore. Because of it we must make sure we only invoke PollEvents()
// when this class is alive.
self->PollEvents();
if (self->embed_closed_)
break;
// Deal with event in main thread.
self->WakeupMainThread();
}
}
// static
void NodeBindings::IdleCallback(uv_timer_t*, int) {
v8::V8::IdleNotification();
}
} // namespace atom

101
atom/common/node_bindings.h Normal file
View file

@ -0,0 +1,101 @@
// 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_NODE_BINDINGS_H_
#define ATOM_COMMON_NODE_BINDINGS_H_
#include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
#include "v8/include/v8.h"
#include "vendor/node/deps/uv/include/uv.h"
namespace base {
class MessageLoop;
}
namespace node {
class Environment;
}
namespace atom {
class NodeBindings {
public:
static NodeBindings* Create(bool is_browser);
virtual ~NodeBindings();
// Setup V8, libuv.
virtual void Initialize();
// Create the environment and load node.js.
virtual node::Environment* CreateEnvironment(v8::Handle<v8::Context> context);
// Prepare for message loop integration.
virtual void PrepareMessageLoop();
// Do message loop integration.
virtual void RunMessageLoop();
// Gets/sets the environment to wrap uv loop.
void set_uv_env(node::Environment* env) { uv_env_ = env; }
node::Environment* uv_env() const { return uv_env_; }
protected:
explicit NodeBindings(bool is_browser);
// Called to poll events in new thread.
virtual void PollEvents() = 0;
// Run the libuv loop for once.
void UvRunOnce();
// Make the main thread run libuv loop.
void WakeupMainThread();
// Interrupt the PollEvents.
void WakeupEmbedThread();
// Are we running in browser.
bool is_browser_;
// Main thread's MessageLoop.
base::MessageLoop* message_loop_;
// Main thread's libuv loop.
uv_loop_t* uv_loop_;
private:
// Thread to poll uv events.
static void EmbedThreadRunner(void *arg);
// Do idle GC.
static void IdleCallback(uv_timer_t*, int);
// Whether the libuv loop has ended.
bool embed_closed_;
// Dummy handle to make uv's loop not quit.
uv_async_t dummy_uv_handle_;
// Timer to do idle GC.
uv_timer_t idle_timer_;
// Thread for polling events.
uv_thread_t embed_thread_;
// Semaphore to wait for main loop in the embed thread.
uv_sem_t embed_sem_;
// Environment that to wrap the uv loop.
node::Environment* uv_env_;
base::WeakPtrFactory<NodeBindings> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(NodeBindings);
};
} // namespace atom
#endif // ATOM_COMMON_NODE_BINDINGS_H_

View file

@ -0,0 +1,57 @@
// 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.
#include "atom/common/node_bindings_linux.h"
#include <sys/epoll.h>
namespace atom {
NodeBindingsLinux::NodeBindingsLinux(bool is_browser)
: NodeBindings(is_browser),
epoll_(epoll_create(1)) {
int backend_fd = uv_backend_fd(uv_loop_);
struct epoll_event ev = { 0 };
ev.events = EPOLLIN;
ev.data.fd = backend_fd;
epoll_ctl(epoll_, EPOLL_CTL_ADD, backend_fd, &ev);
}
NodeBindingsLinux::~NodeBindingsLinux() {
}
void NodeBindingsLinux::RunMessageLoop() {
// Get notified when libuv's watcher queue changes.
uv_loop_->data = this;
uv_loop_->on_watcher_queue_updated = OnWatcherQueueChanged;
NodeBindings::RunMessageLoop();
}
// static
void NodeBindingsLinux::OnWatcherQueueChanged(uv_loop_t* loop) {
NodeBindingsLinux* self = static_cast<NodeBindingsLinux*>(loop->data);
// We need to break the io polling in the epoll thread when loop's watcher
// queue changes, otherwise new events cannot be notified.
self->WakeupEmbedThread();
}
void NodeBindingsLinux::PollEvents() {
int timeout = uv_backend_timeout(uv_loop_);
// Wait for new libuv events.
int r;
do {
struct epoll_event ev;
r = epoll_wait(epoll_, &ev, 1, timeout);
} while (r == -1 && errno == EINTR);
}
// static
NodeBindings* NodeBindings::Create(bool is_browser) {
return new NodeBindingsLinux(is_browser);
}
} // namespace atom

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_NODE_BINDINGS_LINUX_H_
#define ATOM_COMMON_NODE_BINDINGS_LINUX_H_
#include "base/compiler_specific.h"
#include "atom/common/node_bindings.h"
namespace atom {
class NodeBindingsLinux : public NodeBindings {
public:
explicit NodeBindingsLinux(bool is_browser);
virtual ~NodeBindingsLinux();
virtual void RunMessageLoop() OVERRIDE;
private:
// Called when uv's watcher queue changes.
static void OnWatcherQueueChanged(uv_loop_t* loop);
virtual void PollEvents() OVERRIDE;
// Epoll to poll for uv's backend fd.
int epoll_;
DISALLOW_COPY_AND_ASSIGN(NodeBindingsLinux);
};
} // namespace atom
#endif // ATOM_COMMON_NODE_BINDINGS_LINUX_H_

View file

@ -0,0 +1,68 @@
// 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/node_bindings_mac.h"
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include "atom/common/v8/node_common.h"
namespace atom {
NodeBindingsMac::NodeBindingsMac(bool is_browser)
: NodeBindings(is_browser),
kqueue_(kqueue()) {
// Add uv's backend fd to kqueue.
struct kevent ev;
EV_SET(&ev, uv_backend_fd(uv_loop_), EVFILT_READ, EV_ADD | EV_ENABLE,
0, 0, 0);
kevent(kqueue_, &ev, 1, NULL, 0, NULL);
}
NodeBindingsMac::~NodeBindingsMac() {
}
void NodeBindingsMac::RunMessageLoop() {
// Get notified when libuv's watcher queue changes.
uv_loop_->data = this;
uv_loop_->on_watcher_queue_updated = OnWatcherQueueChanged;
NodeBindings::RunMessageLoop();
}
// static
void NodeBindingsMac::OnWatcherQueueChanged(uv_loop_t* loop) {
NodeBindingsMac* self = static_cast<NodeBindingsMac*>(loop->data);
// We need to break the io polling in the kqueue thread when loop's watcher
// queue changes, otherwise new events cannot be notified.
self->WakeupEmbedThread();
}
void NodeBindingsMac::PollEvents() {
struct timespec spec;
int timeout = uv_backend_timeout(uv_loop_);
if (timeout != -1) {
spec.tv_sec = timeout / 1000;
spec.tv_nsec = (timeout % 1000) * 1000000;
}
// Wait for new libuv events.
int r;
do {
struct kevent ev;
r = ::kevent(kqueue_, NULL, 0, &ev, 1,
timeout == -1 ? NULL : &spec);
} while (r == -1 && errno == EINTR);
}
// static
NodeBindings* NodeBindings::Create(bool is_browser) {
return new NodeBindingsMac(is_browser);
}
} // namespace atom

View file

@ -0,0 +1,34 @@
// 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_NODE_BINDINGS_MAC_
#define ATOM_COMMON_NODE_BINDINGS_MAC_
#include "base/compiler_specific.h"
#include "atom/common/node_bindings.h"
namespace atom {
class NodeBindingsMac : public NodeBindings {
public:
explicit NodeBindingsMac(bool is_browser);
virtual ~NodeBindingsMac();
virtual void RunMessageLoop() OVERRIDE;
private:
// Called when uv's watcher queue changes.
static void OnWatcherQueueChanged(uv_loop_t* loop);
virtual void PollEvents() OVERRIDE;
// Kqueue to poll for uv's backend fd.
int kqueue_;
DISALLOW_COPY_AND_ASSIGN(NodeBindingsMac);
};
} // namespace atom
#endif // ATOM_COMMON_NODE_BINDINGS_MAC_

View file

@ -0,0 +1,63 @@
// 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/node_bindings_win.h"
#include <windows.h>
#include "base/logging.h"
extern "C" {
#include "vendor/node/deps/uv/src/win/internal.h"
}
namespace atom {
NodeBindingsWin::NodeBindingsWin(bool is_browser)
: NodeBindings(is_browser) {
}
NodeBindingsWin::~NodeBindingsWin() {
}
void NodeBindingsWin::PollEvents() {
// Unlike Unix, in which we can just rely on one backend fd to determine
// whether we should iterate libuv loop, on Window, IOCP is just one part
// of the libuv loop, we should also check whether we have other types of
// events.
bool block = uv_loop_->idle_handles == NULL &&
uv_loop_->pending_reqs_tail == NULL &&
uv_loop_->endgame_handles == NULL &&
!uv_loop_->stop_flag &&
(uv_loop_->active_handles > 0 ||
!QUEUE_EMPTY(&uv_loop_->active_reqs));
// When there is no other types of events, we block on the IOCP.
if (block) {
DWORD bytes, timeout;
ULONG_PTR key;
OVERLAPPED* overlapped;
timeout = uv_get_poll_timeout(uv_loop_);
GetQueuedCompletionStatus(uv_loop_->iocp,
&bytes,
&key,
&overlapped,
timeout);
// Give the event back so libuv can deal with it.
if (overlapped != NULL)
PostQueuedCompletionStatus(uv_loop_->iocp,
bytes,
key,
overlapped);
}
}
// static
NodeBindings* NodeBindings::Create(bool is_browser) {
return new NodeBindingsWin(is_browser);
}
} // namespace atom

View file

@ -0,0 +1,26 @@
// 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_NODE_BINDINGS_WIN_
#define ATOM_COMMON_NODE_BINDINGS_WIN_
#include "base/compiler_specific.h"
#include "atom/common/node_bindings.h"
namespace atom {
class NodeBindingsWin : public NodeBindings {
public:
explicit NodeBindingsWin(bool is_browser);
virtual ~NodeBindingsWin();
private:
virtual void PollEvents() OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(NodeBindingsWin);
};
} // namespace atom
#endif // ATOM_COMMON_NODE_BINDINGS_WIN_

View file

@ -0,0 +1,41 @@
// 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/options_switches.h"
namespace atom {
namespace switches {
const char kTitle[] = "title";
const char kIcon[] = "icon";
const char kFrame[] = "frame";
const char kShow[] = "show";
const char kCenter[] = "center";
const char kX[] = "x";
const char kY[] = "y";
const char kWidth[] = "width";
const char kHeight[] = "height";
const char kMinWidth[] = "min-width";
const char kMinHeight[] = "min-height";
const char kMaxWidth[] = "max-width";
const char kMaxHeight[] = "max-height";
const char kResizable[] = "resizable";
const char kFullscreen[] = "fullscreen";
// Start with the kiosk mode, see Opera's page for description:
// http://www.opera.com/support/mastering/kiosk/
const char kKiosk[] = "kiosk";
// Make windows stays on the top of all other windows.
const char kAlwaysOnTop[] = "always-on-top";
const char kNodeIntegration[] = "node-integration";
// Enable the NSView to accept first mouse event.
const char kAcceptFirstMouse[] = "accept-first-mouse";
} // namespace switches
} // namespace atom

View file

@ -0,0 +1,36 @@
// 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_OPTIONS_SWITCHES_
#define ATOM_COMMON_OPTIONS_SWITCHES_
namespace atom {
namespace switches {
extern const char kTitle[];
extern const char kIcon[];
extern const char kFrame[];
extern const char kShow[];
extern const char kCenter[];
extern const char kX[];
extern const char kY[];
extern const char kWidth[];
extern const char kHeight[];
extern const char kMinWidth[];
extern const char kMinHeight[];
extern const char kMaxWidth[];
extern const char kMaxHeight[];
extern const char kResizable[];
extern const char kFullscreen[];
extern const char kKiosk[];
extern const char kAlwaysOnTop[];
extern const char kNodeIntegration[];
extern const char kAcceptFirstMouse[];
} // namespace switches
} // namespace atom
#endif // ATOM_COMMON_OPTIONS_SWITCHES_

View file

@ -0,0 +1,35 @@
// 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_PLATFORM_UTIL_H_
#define ATOM_COMMON_PLATFORM_UTIL_H_
class GURL;
namespace base {
class FilePath;
}
namespace platform_util {
// Show the given file in a file manager. If possible, select the file.
// Must be called from the UI thread.
void ShowItemInFolder(const base::FilePath& full_path);
// Open the given file in the desktop's default manner.
// Must be called from the UI thread.
void OpenItem(const base::FilePath& full_path);
// Open the given external protocol URL in the desktop's default manner.
// (For example, mailto: URLs in the default mail user agent.)
void OpenExternal(const GURL& url);
// Move a file to trash.
void MoveItemToTrash(const base::FilePath& full_path);
void Beep();
} // namespace platform_util
#endif // ATOM_COMMON_PLATFORM_UTIL_H_

View file

@ -0,0 +1,80 @@
// 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/platform_util.h"
#include <stdio.h>
#include "base/file_util.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "url/gurl.h"
namespace {
void XDGUtil(const std::string& util, const std::string& arg) {
std::vector<std::string> argv;
argv.push_back(util);
argv.push_back(arg);
base::LaunchOptions options;
// xdg-open can fall back on mailcap which eventually might plumb through
// to a command that needs a terminal. Set the environment variable telling
// it that we definitely don't have a terminal available and that it should
// bring up a new terminal if necessary. See "man mailcap".
options.environ["MM_NOTTTY"] = "1";
base::ProcessHandle handle;
if (base::LaunchProcess(argv, options, &handle))
base::EnsureProcessGetsReaped(handle);
}
void XDGOpen(const std::string& path) {
XDGUtil("xdg-open", path);
}
void XDGEmail(const std::string& email) {
XDGUtil("xdg-email", email);
}
} // namespace
namespace platform_util {
// TODO(estade): It would be nice to be able to select the file in the file
// manager, but that probably requires extending xdg-open. For now just
// show the folder.
void ShowItemInFolder(const base::FilePath& full_path) {
base::FilePath dir = full_path.DirName();
if (!base::DirectoryExists(dir))
return;
XDGOpen(dir.value());
}
void OpenItem(const base::FilePath& full_path) {
XDGOpen(full_path.value());
}
void OpenExternal(const GURL& url) {
if (url.SchemeIs("mailto"))
XDGEmail(url.spec());
else
XDGOpen(url.spec());
}
void MoveItemToTrash(const base::FilePath& full_path) {
XDGUtil("gvfs-trash", full_path.value());
}
void Beep() {
// echo '\a' > /dev/console
FILE* console = fopen("/dev/console", "r");
if (console == NULL)
return;
fprintf(console, "\a");
fclose(console);
}
} // namespace platform_util

View file

@ -0,0 +1,148 @@
// 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/platform_util.h"
#include <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "base/mac/scoped_aedesc.h"
#include "base/strings/sys_string_conversions.h"
#include "url/gurl.h"
namespace platform_util {
void ShowItemInFolder(const base::FilePath& full_path) {
DCHECK([NSThread isMainThread]);
NSString* path_string = base::SysUTF8ToNSString(full_path.value());
if (!path_string || ![[NSWorkspace sharedWorkspace] selectFile:path_string
inFileViewerRootedAtPath:nil])
LOG(WARNING) << "NSWorkspace failed to select file " << full_path.value();
}
// This function opens a file. This doesn't use LaunchServices or NSWorkspace
// because of two bugs:
// 1. Incorrect app activation with com.apple.quarantine:
// http://crbug.com/32921
// 2. Silent no-op for unassociated file types: http://crbug.com/50263
// Instead, an AppleEvent is constructed to tell the Finder to open the
// document.
void OpenItem(const base::FilePath& full_path) {
DCHECK([NSThread isMainThread]);
NSString* path_string = base::SysUTF8ToNSString(full_path.value());
if (!path_string)
return;
// Create the target of this AppleEvent, the Finder.
base::mac::ScopedAEDesc<AEAddressDesc> address;
const OSType finderCreatorCode = 'MACS';
OSErr status = AECreateDesc(typeApplSignature, // type
&finderCreatorCode, // data
sizeof(finderCreatorCode), // dataSize
address.OutPointer()); // result
if (status != noErr) {
OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE target";
return;
}
// Build the AppleEvent data structure that instructs Finder to open files.
base::mac::ScopedAEDesc<AppleEvent> theEvent;
status = AECreateAppleEvent(kCoreEventClass, // theAEEventClass
kAEOpenDocuments, // theAEEventID
address, // target
kAutoGenerateReturnID, // returnID
kAnyTransactionID, // transactionID
theEvent.OutPointer()); // result
if (status != noErr) {
OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE event";
return;
}
// Create the list of files (only ever one) to open.
base::mac::ScopedAEDesc<AEDescList> fileList;
status = AECreateList(NULL, // factoringPtr
0, // factoredSize
false, // isRecord
fileList.OutPointer()); // resultList
if (status != noErr) {
OSSTATUS_LOG(WARNING, status) << "Could not create OpenItem() AE file list";
return;
}
// Add the single path to the file list. C-style cast to avoid both a
// static_cast and a const_cast to get across the toll-free bridge.
CFURLRef pathURLRef = (CFURLRef)[NSURL fileURLWithPath:path_string];
FSRef pathRef;
if (CFURLGetFSRef(pathURLRef, &pathRef)) {
status = AEPutPtr(fileList.OutPointer(), // theAEDescList
0, // index
typeFSRef, // typeCode
&pathRef, // dataPtr
sizeof(pathRef)); // dataSize
if (status != noErr) {
OSSTATUS_LOG(WARNING, status)
<< "Could not add file path to AE list in OpenItem()";
return;
}
} else {
LOG(WARNING) << "Could not get FSRef for path URL in OpenItem()";
return;
}
// Attach the file list to the AppleEvent.
status = AEPutParamDesc(theEvent.OutPointer(), // theAppleEvent
keyDirectObject, // theAEKeyword
fileList); // theAEDesc
if (status != noErr) {
OSSTATUS_LOG(WARNING, status)
<< "Could not put the AE file list the path in OpenItem()";
return;
}
// Send the actual event. Do not care about the reply.
base::mac::ScopedAEDesc<AppleEvent> reply;
status = AESend(theEvent, // theAppleEvent
reply.OutPointer(), // reply
kAENoReply + kAEAlwaysInteract, // sendMode
kAENormalPriority, // sendPriority
kAEDefaultTimeout, // timeOutInTicks
NULL, // idleProc
NULL); // filterProc
if (status != noErr) {
OSSTATUS_LOG(WARNING, status)
<< "Could not send AE to Finder in OpenItem()";
}
}
void OpenExternal(const GURL& url) {
DCHECK([NSThread isMainThread]);
NSString* url_string = base::SysUTF8ToNSString(url.spec());
NSURL* ns_url = [NSURL URLWithString:url_string];
if (!ns_url || ![[NSWorkspace sharedWorkspace] openURL:ns_url])
LOG(WARNING) << "NSWorkspace failed to open URL " << url;
}
void MoveItemToTrash(const base::FilePath& full_path) {
DCHECK([NSThread isMainThread]);
NSString* path_string = base::SysUTF8ToNSString(full_path.value());
NSArray* file_array =
[NSArray arrayWithObject:[path_string lastPathComponent]];
if (!path_string || !file_array || ![[NSWorkspace sharedWorkspace]
performFileOperation:NSWorkspaceRecycleOperation
source:[path_string stringByDeletingLastPathComponent]
destination:@""
files:file_array
tag:nil])
LOG(WARNING) << "NSWorkspace failed to move file " << full_path.value()
<< " to trash";
}
void Beep() {
NSBeep();
}
} // namespace platform_util

View file

@ -0,0 +1,186 @@
// 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/platform_util.h"
#include <windows.h>
#include <commdlg.h>
#include <dwmapi.h>
#include <shellapi.h>
#include <shlobj.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "url/gurl.h"
#include "ui/base/win/shell.h"
namespace {
// Old ShellExecute crashes the process when the command for a given scheme
// is empty. This function tells if it is.
bool ValidateShellCommandForScheme(const std::string& scheme) {
base::win::RegKey key;
std::wstring registry_path = ASCIIToWide(scheme) +
L"\\shell\\open\\command";
key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ);
if (!key.Valid())
return false;
DWORD size = 0;
key.ReadValue(NULL, NULL, &size, NULL);
if (size <= 2)
return false;
return true;
}
} // namespace
namespace platform_util {
void ShowItemInFolder(const base::FilePath& full_path) {
base::FilePath dir = full_path.DirName().AsEndingWithSeparator();
// ParseDisplayName will fail if the directory is "C:", it must be "C:\\".
if (dir.empty())
return;
typedef HRESULT (WINAPI *SHOpenFolderAndSelectItemsFuncPtr)(
PCIDLIST_ABSOLUTE pidl_Folder,
UINT cidl,
PCUITEMID_CHILD_ARRAY pidls,
DWORD flags);
static SHOpenFolderAndSelectItemsFuncPtr open_folder_and_select_itemsPtr =
NULL;
static bool initialize_open_folder_proc = true;
if (initialize_open_folder_proc) {
initialize_open_folder_proc = false;
// The SHOpenFolderAndSelectItems API is exposed by shell32 version 6
// and does not exist in Win2K. We attempt to retrieve this function export
// from shell32 and if it does not exist, we just invoke ShellExecute to
// open the folder thus losing the functionality to select the item in
// the process.
HMODULE shell32_base = GetModuleHandle(L"shell32.dll");
if (!shell32_base) {
NOTREACHED() << " " << __FUNCTION__ << "(): Can't open shell32.dll";
return;
}
open_folder_and_select_itemsPtr =
reinterpret_cast<SHOpenFolderAndSelectItemsFuncPtr>
(GetProcAddress(shell32_base, "SHOpenFolderAndSelectItems"));
}
if (!open_folder_and_select_itemsPtr) {
ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW);
return;
}
base::win::ScopedComPtr<IShellFolder> desktop;
HRESULT hr = SHGetDesktopFolder(desktop.Receive());
if (FAILED(hr))
return;
base::win::ScopedCoMem<ITEMIDLIST> dir_item;
hr = desktop->ParseDisplayName(NULL, NULL,
const_cast<wchar_t *>(dir.value().c_str()),
NULL, &dir_item, NULL);
if (FAILED(hr))
return;
base::win::ScopedCoMem<ITEMIDLIST> file_item;
hr = desktop->ParseDisplayName(NULL, NULL,
const_cast<wchar_t *>(full_path.value().c_str()),
NULL, &file_item, NULL);
if (FAILED(hr))
return;
const ITEMIDLIST* highlight[] = { file_item };
hr = (*open_folder_and_select_itemsPtr)(dir_item, arraysize(highlight),
highlight, NULL);
if (FAILED(hr)) {
// On some systems, the above call mysteriously fails with "file not
// found" even though the file is there. In these cases, ShellExecute()
// seems to work as a fallback (although it won't select the file).
if (hr == ERROR_FILE_NOT_FOUND) {
ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW);
} else {
LPTSTR message = NULL;
DWORD message_length = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
0, hr, 0, reinterpret_cast<LPTSTR>(&message), 0, NULL);
LOG(WARNING) << " " << __FUNCTION__
<< "(): Can't open full_path = \""
<< full_path.value() << "\""
<< " hr = " << hr
<< " " << reinterpret_cast<LPTSTR>(&message);
if (message)
LocalFree(message);
}
}
}
void OpenItem(const base::FilePath& full_path) {
ui::win::OpenItemViaShell(full_path);
}
void OpenExternal(const GURL& url) {
// Quote the input scheme to be sure that the command does not have
// parameters unexpected by the external program. This url should already
// have been escaped.
std::string escaped_url = url.spec();
escaped_url.insert(0, "\"");
escaped_url += "\"";
// According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp:
// "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in
// ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6
// support URLS of 2083 chars in length, 2K is safe."
const size_t kMaxUrlLength = 2048;
if (escaped_url.length() > kMaxUrlLength) {
NOTREACHED();
return;
}
if (base::win::GetVersion() < base::win::VERSION_WIN7) {
if (!ValidateShellCommandForScheme(url.scheme()))
return;
}
if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(NULL, "open",
escaped_url.c_str(), NULL, NULL,
SW_SHOWNORMAL)) <= 32) {
// We fail to execute the call. We could display a message to the user.
// TODO(nsylvain): we should also add a dialog to warn on errors. See
// bug 1136923.
return;
}
}
void MoveItemToTrash(const base::FilePath& path) {
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
// so we have to use wcscpy because wcscpy_s writes non-NULLs
// into the rest of the buffer.
wchar_t double_terminated_path[MAX_PATH + 1] = {0};
#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path, path.value().c_str());
SHFILEOPSTRUCT file_operation = {0};
file_operation.wFunc = FO_DELETE;
file_operation.pFrom = double_terminated_path;
file_operation.fFlags = FOF_ALLOWUNDO;
SHFileOperation(&file_operation);
}
void Beep() {
MessageBeep(MB_OK);
}
} // namespace platform_util

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Atom Framework</string>
<key>CFBundleIdentifier</key>
<string>com.github.AtomFramework</string>
<key>CFBundleName</key>
<string>Atom Framework</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
</dict>
</plist>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
// 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

@ -0,0 +1,344 @@
// 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 "base/files/file_path.h"
#include "base/strings/string16.h"
#include "base/template_util.h"
#include "base/values.h"
#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/v8/v8_value_converter.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

@ -0,0 +1,27 @@
// 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_NODE_COMMON_H_
#define ATOM_COMMON_V8_NODE_COMMON_H_
// Include common headers for using node APIs.
#undef CHECK
#undef CHECK_EQ
#undef CHECK_NE
#undef DISALLOW_COPY_AND_ASSIGN
#include "vendor/node/src/env.h"
#include "vendor/node/src/env-inl.h"
#include "vendor/node/src/node.h"
#include "vendor/node/src/node_internals.h"
using node::node_isolate;
namespace atom {
// Defined in node_bindings.cc.
// For renderer it's created in atom_renderer_client.cc.
// For browser it's created in atom_browser_main_parts.cc.
extern node::Environment* global_env;
}
#endif // ATOM_COMMON_V8_NODE_COMMON_H_

View file

@ -0,0 +1,115 @@
// 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_SCOPED_PERSISTENT_H_
#define ATOM_COMMON_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_SCOPED_PERSISTENT_H_

View file

@ -0,0 +1,332 @@
// 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/v8/v8_value_converter.h"
#include <string>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
namespace atom {
V8ValueConverter::V8ValueConverter()
: date_allowed_(false),
reg_exp_allowed_(false),
function_allowed_(false),
strip_null_from_objects_(false),
avoid_identity_hash_for_testing_(false) {}
void V8ValueConverter::SetDateAllowed(bool val) {
date_allowed_ = val;
}
void V8ValueConverter::SetRegExpAllowed(bool val) {
reg_exp_allowed_ = val;
}
void V8ValueConverter::SetFunctionAllowed(bool val) {
function_allowed_ = val;
}
void V8ValueConverter::SetStripNullFromObjects(bool val) {
strip_null_from_objects_ = val;
}
v8::Handle<v8::Value> V8ValueConverter::ToV8Value(
const base::Value* value, v8::Handle<v8::Context> context) const {
v8::Context::Scope context_scope(context);
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
return handle_scope.Close(ToV8ValueImpl(value));
}
Value* V8ValueConverter::FromV8Value(
v8::Handle<v8::Value> val,
v8::Handle<v8::Context> context) const {
v8::Context::Scope context_scope(context);
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
HashToHandleMap unique_map;
return FromV8ValueImpl(val, &unique_map);
}
v8::Handle<v8::Value> V8ValueConverter::ToV8ValueImpl(
const base::Value* value) const {
CHECK(value);
switch (value->GetType()) {
case base::Value::TYPE_NULL:
return v8::Null();
case base::Value::TYPE_BOOLEAN: {
bool val = false;
CHECK(value->GetAsBoolean(&val));
return v8::Boolean::New(val);
}
case base::Value::TYPE_INTEGER: {
int val = 0;
CHECK(value->GetAsInteger(&val));
return v8::Integer::New(val);
}
case base::Value::TYPE_DOUBLE: {
double val = 0.0;
CHECK(value->GetAsDouble(&val));
return v8::Number::New(val);
}
case base::Value::TYPE_STRING: {
std::string val;
CHECK(value->GetAsString(&val));
return v8::String::New(val.c_str(), val.length());
}
case base::Value::TYPE_LIST:
return ToV8Array(static_cast<const base::ListValue*>(value));
case base::Value::TYPE_DICTIONARY:
return ToV8Object(static_cast<const base::DictionaryValue*>(value));
default:
LOG(ERROR) << "Unexpected value type: " << value->GetType();
return v8::Null();
}
}
v8::Handle<v8::Value> V8ValueConverter::ToV8Array(
const base::ListValue* val) const {
v8::Handle<v8::Array> result(v8::Array::New(val->GetSize()));
for (size_t i = 0; i < val->GetSize(); ++i) {
const base::Value* child = NULL;
CHECK(val->Get(i, &child));
v8::Handle<v8::Value> child_v8 = ToV8ValueImpl(child);
CHECK(!child_v8.IsEmpty());
v8::TryCatch try_catch;
result->Set(static_cast<uint32>(i), child_v8);
if (try_catch.HasCaught())
LOG(ERROR) << "Setter for index " << i << " threw an exception.";
}
return result;
}
v8::Handle<v8::Value> V8ValueConverter::ToV8Object(
const base::DictionaryValue* val) const {
v8::Handle<v8::Object> result(v8::Object::New());
for (base::DictionaryValue::Iterator iter(*val);
!iter.IsAtEnd(); iter.Advance()) {
const std::string& key = iter.key();
v8::Handle<v8::Value> child_v8 = ToV8ValueImpl(&iter.value());
CHECK(!child_v8.IsEmpty());
v8::TryCatch try_catch;
result->Set(v8::String::New(key.c_str(), key.length()), child_v8);
if (try_catch.HasCaught()) {
LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
<< "exception.";
}
}
return result;
}
Value* V8ValueConverter::FromV8ValueImpl(v8::Handle<v8::Value> val,
HashToHandleMap* unique_map) const {
CHECK(!val.IsEmpty());
if (val->IsNull())
return base::Value::CreateNullValue();
if (val->IsBoolean())
return new base::FundamentalValue(val->ToBoolean()->Value());
if (val->IsInt32())
return new base::FundamentalValue(val->ToInt32()->Value());
if (val->IsNumber())
return new base::FundamentalValue(val->ToNumber()->Value());
if (val->IsString()) {
v8::String::Utf8Value utf8(val->ToString());
return new base::StringValue(std::string(*utf8, utf8.length()));
}
if (val->IsUndefined())
// JSON.stringify ignores undefined.
return NULL;
if (val->IsDate()) {
if (!date_allowed_)
// JSON.stringify would convert this to a string, but an object is more
// consistent within this class.
return FromV8Object(val->ToObject(), unique_map);
v8::Date* date = v8::Date::Cast(*val);
return new base::FundamentalValue(date->NumberValue() / 1000.0);
}
if (val->IsRegExp()) {
if (!reg_exp_allowed_)
// JSON.stringify converts to an object.
return FromV8Object(val->ToObject(), unique_map);
return new base::StringValue(*v8::String::Utf8Value(val->ToString()));
}
// v8::Value doesn't have a ToArray() method for some reason.
if (val->IsArray())
return FromV8Array(val.As<v8::Array>(), unique_map);
if (val->IsFunction()) {
if (!function_allowed_)
// JSON.stringify refuses to convert function(){}.
return NULL;
return FromV8Object(val->ToObject(), unique_map);
}
if (val->IsObject()) {
return FromV8Object(val->ToObject(), unique_map);
}
LOG(ERROR) << "Unexpected v8 value type encountered.";
return NULL;
}
Value* V8ValueConverter::FromV8Array(v8::Handle<v8::Array> val,
HashToHandleMap* unique_map) const {
if (!UpdateAndCheckUniqueness(unique_map, val))
return base::Value::CreateNullValue();
scoped_ptr<v8::Context::Scope> scope;
// If val was created in a different context than our current one, change to
// that context, but change back after val is converted.
if (!val->CreationContext().IsEmpty() &&
val->CreationContext() != v8::Context::GetCurrent())
scope.reset(new v8::Context::Scope(val->CreationContext()));
base::ListValue* result = new base::ListValue();
// Only fields with integer keys are carried over to the ListValue.
for (uint32 i = 0; i < val->Length(); ++i) {
v8::TryCatch try_catch;
v8::Handle<v8::Value> child_v8 = val->Get(i);
if (try_catch.HasCaught()) {
LOG(ERROR) << "Getter for index " << i << " threw an exception.";
child_v8 = v8::Null();
}
if (!val->HasRealIndexedProperty(i))
continue;
base::Value* child = FromV8ValueImpl(child_v8, unique_map);
if (child)
result->Append(child);
else
// JSON.stringify puts null in places where values don't serialize, for
// example undefined and functions. Emulate that behavior.
result->Append(base::Value::CreateNullValue());
}
return result;
}
Value* V8ValueConverter::FromV8Object(
v8::Handle<v8::Object> val,
HashToHandleMap* unique_map) const {
if (!UpdateAndCheckUniqueness(unique_map, val))
return base::Value::CreateNullValue();
scoped_ptr<v8::Context::Scope> scope;
// If val was created in a different context than our current one, change to
// that context, but change back after val is converted.
if (!val->CreationContext().IsEmpty() &&
val->CreationContext() != v8::Context::GetCurrent())
scope.reset(new v8::Context::Scope(val->CreationContext()));
scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames());
for (uint32 i = 0; i < property_names->Length(); ++i) {
v8::Handle<v8::Value> key(property_names->Get(i));
// Extend this test to cover more types as necessary and if sensible.
if (!key->IsString() &&
!key->IsNumber()) {
NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" "
"is neither a string nor a number";
continue;
}
// Skip all callbacks: crbug.com/139933
if (val->HasRealNamedCallbackProperty(key->ToString()))
continue;
v8::String::Utf8Value name_utf8(key->ToString());
v8::TryCatch try_catch;
v8::Handle<v8::Value> child_v8 = val->Get(key);
if (try_catch.HasCaught()) {
LOG(ERROR) << "Getter for property " << *name_utf8
<< " threw an exception.";
child_v8 = v8::Null();
}
scoped_ptr<base::Value> child(FromV8ValueImpl(child_v8, unique_map));
if (!child.get())
// JSON.stringify skips properties whose values don't serialize, for
// example undefined and functions. Emulate that behavior.
continue;
// Strip null if asked (and since undefined is turned into null, undefined
// too). The use case for supporting this is JSON-schema support,
// specifically for extensions, where "optional" JSON properties may be
// represented as null, yet due to buggy legacy code elsewhere isn't
// treated as such (potentially causing crashes). For example, the
// "tabs.create" function takes an object as its first argument with an
// optional "windowId" property.
//
// Given just
//
// tabs.create({})
//
// this will work as expected on code that only checks for the existence of
// a "windowId" property (such as that legacy code). However given
//
// tabs.create({windowId: null})
//
// there *is* a "windowId" property, but since it should be an int, code
// on the browser which doesn't additionally check for null will fail.
// We can avoid all bugs related to this by stripping null.
if (strip_null_from_objects_ && child->IsType(Value::TYPE_NULL))
continue;
result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()),
child.release());
}
return result.release();
}
bool V8ValueConverter::UpdateAndCheckUniqueness(
HashToHandleMap* map,
v8::Handle<v8::Object> handle) const {
typedef HashToHandleMap::const_iterator Iterator;
int hash = avoid_identity_hash_for_testing_ ? 0 : handle->GetIdentityHash();
// We only compare using == with handles to objects with the same identity
// hash. Different hash obviously means different objects, but two objects in
// a couple of thousands could have the same identity hash.
std::pair<Iterator, Iterator> range = map->equal_range(hash);
for (Iterator it = range.first; it != range.second; ++it) {
// Operator == for handles actually compares the underlying objects.
if (it->second == handle)
return false;
}
map->insert(std::pair<int, v8::Handle<v8::Object> >(hash, handle));
return true;
}
} // namespace atom

View file

@ -0,0 +1,80 @@
// 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_V8_VALUE_CONVERTER_H_
#define ATOM_COMMON_V8_V8_VALUE_CONVERTER_H_
#include <map>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "v8/include/v8.h"
namespace base {
class BinaryValue;
class DictionaryValue;
class ListValue;
class Value;
}
namespace atom {
class V8ValueConverter {
public:
V8ValueConverter();
void SetDateAllowed(bool val);
void SetRegExpAllowed(bool val);
void SetFunctionAllowed(bool val);
void SetStripNullFromObjects(bool val);
v8::Handle<v8::Value> ToV8Value(const base::Value* value,
v8::Handle<v8::Context> context) const;
base::Value* FromV8Value(v8::Handle<v8::Value> value,
v8::Handle<v8::Context> context) const;
private:
typedef std::multimap<int, v8::Handle<v8::Object> > HashToHandleMap;
v8::Handle<v8::Value> ToV8ValueImpl(const base::Value* value) const;
v8::Handle<v8::Value> ToV8Array(const base::ListValue* list) const;
v8::Handle<v8::Value> ToV8Object(
const base::DictionaryValue* dictionary) const;
base::Value* FromV8ValueImpl(v8::Handle<v8::Value> value,
HashToHandleMap* unique_map) const;
base::Value* FromV8Array(v8::Handle<v8::Array> array,
HashToHandleMap* unique_map) const;
base::Value* FromV8Object(v8::Handle<v8::Object> object,
HashToHandleMap* unique_map) const;
// If |handle| is not in |map|, then add it to |map| and return true.
// Otherwise do nothing and return false. Here "A is unique" means that no
// other handle B in the map points to the same object as A. Note that A can
// be unique even if there already is another handle with the same identity
// hash (key) in the map, because two objects can have the same hash.
bool UpdateAndCheckUniqueness(HashToHandleMap* map,
v8::Handle<v8::Object> handle) const;
// If true, we will convert Date JavaScript objects to doubles.
bool date_allowed_;
// If true, we will convert RegExp JavaScript objects to string.
bool reg_exp_allowed_;
// If true, we will convert Function JavaScript objects to dictionaries.
bool function_allowed_;
// If true, undefined and null values are ignored when converting v8 objects
// into Values.
bool strip_null_from_objects_;
bool avoid_identity_hash_for_testing_;
DISALLOW_COPY_AND_ASSIGN(V8ValueConverter);
};
} // namespace atom
#endif // ATOM_COMMON_V8_V8_VALUE_CONVERTER_H_