Move all sources under atom/.
This commit is contained in:
parent
26ddbbb0ee
commit
516d46444d
217 changed files with 519 additions and 519 deletions
33
atom/common/api/api_messages.cc
Normal file
33
atom/common/api/api_messages.cc
Normal 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
|
38
atom/common/api/api_messages.h
Normal file
38
atom/common/api/api_messages.h
Normal 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 */)
|
88
atom/common/api/atom_api_clipboard.cc
Normal file
88
atom/common/api/atom_api_clipboard.cc
Normal 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)
|
33
atom/common/api/atom_api_clipboard.h
Normal file
33
atom/common/api/atom_api_clipboard.h
Normal 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_
|
38
atom/common/api/atom_api_crash_reporter.cc
Normal file
38
atom/common/api/atom_api_crash_reporter.cc
Normal 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)
|
29
atom/common/api/atom_api_crash_reporter.h
Normal file
29
atom/common/api/atom_api_crash_reporter.h
Normal 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_
|
81
atom/common/api/atom_api_event_emitter.cc
Normal file
81
atom/common/api/atom_api_event_emitter.cc
Normal 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
|
43
atom/common/api/atom_api_event_emitter.h
Normal file
43
atom/common/api/atom_api_event_emitter.h
Normal 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_
|
142
atom/common/api/atom_api_id_weak_map.cc
Normal file
142
atom/common/api/atom_api_id_weak_map.cc
Normal 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)
|
52
atom/common/api/atom_api_id_weak_map.h
Normal file
52
atom/common/api/atom_api_id_weak_map.h
Normal 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_
|
93
atom/common/api/atom_api_screen.cc
Normal file
93
atom/common/api/atom_api_screen.cc
Normal 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)
|
44
atom/common/api/atom_api_screen.h
Normal file
44
atom/common/api/atom_api_screen.h
Normal 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_
|
74
atom/common/api/atom_api_shell.cc
Normal file
74
atom/common/api/atom_api_shell.cc
Normal 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)
|
33
atom/common/api/atom_api_shell.h
Normal file
33
atom/common/api/atom_api_shell.h
Normal 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_
|
61
atom/common/api/atom_api_v8_util.cc
Normal file
61
atom/common/api/atom_api_v8_util.cc
Normal 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)
|
190
atom/common/api/atom_bindings.cc
Normal file
190
atom/common/api/atom_bindings.cc
Normal 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
|
36
atom/common/api/atom_bindings.h
Normal file
36
atom/common/api/atom_bindings.h
Normal 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_
|
56
atom/common/api/atom_extensions.cc
Normal file
56
atom/common/api/atom_extensions.cc
Normal 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
|
34
atom/common/api/atom_extensions.h
Normal file
34
atom/common/api/atom_extensions.h
Normal 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
|
25
atom/common/api/lib/callbacks-registry.coffee
Normal file
25
atom/common/api/lib/callbacks-registry.coffee
Normal 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]
|
1
atom/common/api/lib/clipboard.coffee
Normal file
1
atom/common/api/lib/clipboard.coffee
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = process.atomBinding 'clipboard'
|
38
atom/common/api/lib/crash-reporter.coffee
Normal file
38
atom/common/api/lib/crash-reporter.coffee
Normal 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
|
3
atom/common/api/lib/id-weak-map.coffee
Normal file
3
atom/common/api/lib/id-weak-map.coffee
Normal file
|
@ -0,0 +1,3 @@
|
|||
IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap
|
||||
|
||||
module.exports = IDWeakMap
|
3
atom/common/api/lib/screen.coffee
Normal file
3
atom/common/api/lib/screen.coffee
Normal file
|
@ -0,0 +1,3 @@
|
|||
{Screen} = process.atomBinding 'screen'
|
||||
|
||||
module.exports = new Screen
|
1
atom/common/api/lib/shell.coffee
Normal file
1
atom/common/api/lib/shell.coffee
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = process.atomBinding 'shell'
|
38
atom/common/api/object_life_monitor.cc
Normal file
38
atom/common/api/object_life_monitor.cc
Normal 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
|
33
atom/common/api/object_life_monitor.h
Normal file
33
atom/common/api/object_life_monitor.h
Normal 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_
|
43
atom/common/atom_version.h
Normal file
43
atom/common/atom_version.h
Normal 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 */
|
42
atom/common/crash_reporter/crash_reporter.cc
Normal file
42
atom/common/crash_reporter/crash_reporter.cc
Normal 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
|
51
atom/common/crash_reporter/crash_reporter.h
Normal file
51
atom/common/crash_reporter/crash_reporter.h
Normal 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_
|
134
atom/common/crash_reporter/crash_reporter_linux.cc
Normal file
134
atom/common/crash_reporter/crash_reporter_linux.cc
Normal 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
|
57
atom/common/crash_reporter/crash_reporter_linux.h
Normal file
57
atom/common/crash_reporter/crash_reporter_linux.h
Normal 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_
|
41
atom/common/crash_reporter/crash_reporter_mac.h
Normal file
41
atom/common/crash_reporter/crash_reporter_mac.h
Normal 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_
|
81
atom/common/crash_reporter/crash_reporter_mac.mm
Normal file
81
atom/common/crash_reporter/crash_reporter_mac.mm
Normal 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
|
129
atom/common/crash_reporter/crash_reporter_win.cc
Normal file
129
atom/common/crash_reporter/crash_reporter_win.cc
Normal 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
|
64
atom/common/crash_reporter/crash_reporter_win.h
Normal file
64
atom/common/crash_reporter/crash_reporter_win.h
Normal 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_
|
668
atom/common/crash_reporter/linux/crash_dump_handler.cc
Normal file
668
atom/common/crash_reporter/linux/crash_dump_handler.cc
Normal 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
|
38
atom/common/crash_reporter/linux/crash_dump_handler.h
Normal file
38
atom/common/crash_reporter/linux/crash_dump_handler.h
Normal 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_
|
490
atom/common/crash_reporter/win/crash_service.cc
Normal file
490
atom/common/crash_reporter/win/crash_service.cc
Normal 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
|
||||
|
131
atom/common/crash_reporter/win/crash_service.h
Normal file
131
atom/common/crash_reporter/win/crash_service.h
Normal 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_
|
92
atom/common/crash_reporter/win/crash_service_main.cc
Normal file
92
atom/common/crash_reporter/win/crash_service_main.cc
Normal 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
|
15
atom/common/crash_reporter/win/crash_service_main.h
Normal file
15
atom/common/crash_reporter/win/crash_service_main.h
Normal 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_
|
13
atom/common/draggable_region.cc
Normal file
13
atom/common/draggable_region.cc
Normal 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
|
21
atom/common/draggable_region.h
Normal file
21
atom/common/draggable_region.h
Normal 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_
|
21
atom/common/lib/init.coffee
Normal file
21
atom/common/lib/init.coffee
Normal 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
|
19
atom/common/linux/application_info.cc
Normal file
19
atom/common/linux/application_info.cc
Normal 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
|
258
atom/common/node_bindings.cc
Normal file
258
atom/common/node_bindings.cc
Normal 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
101
atom/common/node_bindings.h
Normal 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_
|
57
atom/common/node_bindings_linux.cc
Normal file
57
atom/common/node_bindings_linux.cc
Normal 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
|
34
atom/common/node_bindings_linux.h
Normal file
34
atom/common/node_bindings_linux.h
Normal 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_
|
68
atom/common/node_bindings_mac.cc
Normal file
68
atom/common/node_bindings_mac.cc
Normal 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
|
34
atom/common/node_bindings_mac.h
Normal file
34
atom/common/node_bindings_mac.h
Normal 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_
|
63
atom/common/node_bindings_win.cc
Normal file
63
atom/common/node_bindings_win.cc
Normal 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
|
26
atom/common/node_bindings_win.h
Normal file
26
atom/common/node_bindings_win.h
Normal 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_
|
41
atom/common/options_switches.cc
Normal file
41
atom/common/options_switches.cc
Normal 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
|
36
atom/common/options_switches.h
Normal file
36
atom/common/options_switches.h
Normal 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_
|
35
atom/common/platform_util.h
Normal file
35
atom/common/platform_util.h
Normal 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_
|
80
atom/common/platform_util_linux.cc
Normal file
80
atom/common/platform_util_linux.cc
Normal 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
|
148
atom/common/platform_util_mac.mm
Normal file
148
atom/common/platform_util_mac.mm
Normal 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
|
186
atom/common/platform_util_win.cc
Normal file
186
atom/common/platform_util_win.cc
Normal 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
|
14
atom/common/resources/mac/Info.plist
Normal file
14
atom/common/resources/mac/Info.plist
Normal 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>
|
3206
atom/common/resources/mac/MainMenu.xib
Normal file
3206
atom/common/resources/mac/MainMenu.xib
Normal file
File diff suppressed because it is too large
Load diff
71
atom/common/swap_or_assign.h
Normal file
71
atom/common/swap_or_assign.h
Normal 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_
|
344
atom/common/v8/native_type_conversions.h
Normal file
344
atom/common/v8/native_type_conversions.h
Normal 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_
|
27
atom/common/v8/node_common.h
Normal file
27
atom/common/v8/node_common.h
Normal 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_
|
115
atom/common/v8/scoped_persistent.h
Normal file
115
atom/common/v8/scoped_persistent.h
Normal 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_
|
332
atom/common/v8/v8_value_converter.cc
Normal file
332
atom/common/v8/v8_value_converter.cc
Normal 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
|
80
atom/common/v8/v8_value_converter.h
Normal file
80
atom/common/v8/v8_value_converter.h
Normal 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_
|
Loading…
Add table
Add a link
Reference in a new issue