refactor: rename the atom directory to shell

This commit is contained in:
Samuel Attard 2019-06-19 13:43:10 -07:00 committed by Samuel Attard
parent 4575a4aae3
commit d7f07e8a80
631 changed files with 0 additions and 0 deletions

12
shell/common/api/BUILD.gn Normal file
View file

@ -0,0 +1,12 @@
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo") {
sources = [
"api.mojom",
]
public_deps = [
"//mojo/public/mojom/base",
"//ui/gfx/geometry/mojo",
]
}

View file

@ -0,0 +1,78 @@
module atom.mojom;
import "mojo/public/mojom/base/values.mojom";
import "mojo/public/mojom/base/string16.mojom";
import "ui/gfx/geometry/mojo/geometry.mojom";
interface ElectronRenderer {
Message(
bool internal,
bool send_to_all,
string channel,
mojo_base.mojom.ListValue arguments,
int32 sender_id);
UpdateCrashpadPipeName(string pipe_name);
TakeHeapSnapshot(handle file) => (bool success);
};
interface ElectronAutofillAgent {
AcceptDataListSuggestion(mojo_base.mojom.String16 value);
};
struct DraggableRegion {
bool draggable;
gfx.mojom.Rect bounds;
};
interface ElectronBrowser {
// Emits an event on |channel| from the ipcMain JavaScript object in the main
// process.
Message(
bool internal,
string channel,
mojo_base.mojom.ListValue arguments);
// Emits an event on |channel| from the ipcMain JavaScript object in the main
// process, and returns the response.
Invoke(
string channel,
mojo_base.mojom.ListValue arguments) => (mojo_base.mojom.Value result);
// Emits an event on |channel| from the ipcMain JavaScript object in the main
// process, and waits synchronously for a response.
//
// NB. this is not marked [Sync] because mojo synchronous methods can be
// reordered with respect to asynchronous methods on the same channel.
// Instead, callers can manually block on the response to this method.
MessageSync(
bool internal,
string channel,
mojo_base.mojom.ListValue arguments) => (mojo_base.mojom.Value result);
// Emits an event from the |ipcRenderer| JavaScript object in the target
// WebContents's main frame, specified by |web_contents_id|.
MessageTo(
bool internal,
bool send_to_all,
int32 web_contents_id,
string channel,
mojo_base.mojom.ListValue arguments);
MessageHost(
string channel,
mojo_base.mojom.ListValue arguments);
UpdateDraggableRegions(
array<DraggableRegion> regions);
SetTemporaryZoomLevel(double zoom_level);
[Sync]
DoGetZoomLevel() => (double result);
// TODO: move these into a separate interface
ShowAutofillPopup(gfx.mojom.RectF bounds, array<mojo_base.mojom.String16> values, array<mojo_base.mojom.String16> labels);
HideAutofillPopup();
};

View file

@ -0,0 +1,141 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <vector>
#include "atom/common/asar/archive.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/node_includes.h"
#include "native_mate/arguments.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "native_mate/wrappable.h"
#include "third_party/electron_node/src/node_native_module.h"
namespace {
class Archive : public mate::Wrappable<Archive> {
public:
static v8::Local<v8::Value> Create(v8::Isolate* isolate,
const base::FilePath& path) {
auto archive = std::make_unique<asar::Archive>(path);
if (!archive->Init())
return v8::False(isolate);
return (new Archive(isolate, std::move(archive)))->GetWrapper();
}
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "Archive"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetProperty("path", &Archive::GetPath)
.SetMethod("getFileInfo", &Archive::GetFileInfo)
.SetMethod("stat", &Archive::Stat)
.SetMethod("readdir", &Archive::Readdir)
.SetMethod("realpath", &Archive::Realpath)
.SetMethod("copyFileOut", &Archive::CopyFileOut)
.SetMethod("getFd", &Archive::GetFD);
}
protected:
Archive(v8::Isolate* isolate, std::unique_ptr<asar::Archive> archive)
: archive_(std::move(archive)) {
Init(isolate);
}
// Returns the path of the file.
base::FilePath GetPath() { return archive_->path(); }
// Reads the offset and size of file.
v8::Local<v8::Value> GetFileInfo(v8::Isolate* isolate,
const base::FilePath& path) {
asar::Archive::FileInfo info;
if (!archive_ || !archive_->GetFileInfo(path, &info))
return v8::False(isolate);
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("size", info.size);
dict.Set("unpacked", info.unpacked);
dict.Set("offset", info.offset);
return dict.GetHandle();
}
// Returns a fake result of fs.stat(path).
v8::Local<v8::Value> Stat(v8::Isolate* isolate, const base::FilePath& path) {
asar::Archive::Stats stats;
if (!archive_ || !archive_->Stat(path, &stats))
return v8::False(isolate);
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("size", stats.size);
dict.Set("offset", stats.offset);
dict.Set("isFile", stats.is_file);
dict.Set("isDirectory", stats.is_directory);
dict.Set("isLink", stats.is_link);
return dict.GetHandle();
}
// Returns all files under a directory.
v8::Local<v8::Value> Readdir(v8::Isolate* isolate,
const base::FilePath& path) {
std::vector<base::FilePath> files;
if (!archive_ || !archive_->Readdir(path, &files))
return v8::False(isolate);
return mate::ConvertToV8(isolate, files);
}
// Returns the path of file with symbol link resolved.
v8::Local<v8::Value> Realpath(v8::Isolate* isolate,
const base::FilePath& path) {
base::FilePath realpath;
if (!archive_ || !archive_->Realpath(path, &realpath))
return v8::False(isolate);
return mate::ConvertToV8(isolate, realpath);
}
// Copy the file out into a temporary file and returns the new path.
v8::Local<v8::Value> CopyFileOut(v8::Isolate* isolate,
const base::FilePath& path) {
base::FilePath new_path;
if (!archive_ || !archive_->CopyFileOut(path, &new_path))
return v8::False(isolate);
return mate::ConvertToV8(isolate, new_path);
}
// Return the file descriptor.
int GetFD() const {
if (!archive_)
return -1;
return archive_->GetFD();
}
private:
std::unique_ptr<asar::Archive> archive_;
DISALLOW_COPY_AND_ASSIGN(Archive);
};
void InitAsarSupport(v8::Isolate* isolate, v8::Local<v8::Value> require) {
// Evaluate asar_init.js.
std::vector<v8::Local<v8::String>> asar_init_params = {
node::FIXED_ONE_BYTE_STRING(isolate, "require")};
std::vector<v8::Local<v8::Value>> asar_init_args = {require};
node::per_process::native_module_loader.CompileAndCall(
isolate->GetCurrentContext(), "electron/js2c/asar_init",
&asar_init_params, &asar_init_args, nullptr);
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("createArchive", &Archive::Create);
dict.SetMethod("initAsarSupport", &InitAsarSupport);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_asar, Initialize)

View file

@ -0,0 +1,229 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/atom_api_clipboard.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
namespace atom {
namespace api {
ui::ClipboardType Clipboard::GetClipboardType(mate::Arguments* args) {
std::string type;
if (args->GetNext(&type) && type == "selection")
return ui::CLIPBOARD_TYPE_SELECTION;
else
return ui::CLIPBOARD_TYPE_COPY_PASTE;
}
std::vector<base::string16> Clipboard::AvailableFormats(mate::Arguments* args) {
std::vector<base::string16> format_types;
bool ignore;
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->ReadAvailableTypes(GetClipboardType(args), &format_types, &ignore);
return format_types;
}
bool Clipboard::Has(const std::string& format_string, mate::Arguments* args) {
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardFormatType format(
ui::ClipboardFormatType::GetType(format_string));
return clipboard->IsFormatAvailable(format, GetClipboardType(args));
}
std::string Clipboard::Read(const std::string& format_string) {
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardFormatType format(
ui::ClipboardFormatType::GetType(format_string));
std::string data;
clipboard->ReadData(format, &data);
return data;
}
v8::Local<v8::Value> Clipboard::ReadBuffer(const std::string& format_string,
mate::Arguments* args) {
std::string data = Read(format_string);
return node::Buffer::Copy(args->isolate(), data.data(), data.length())
.ToLocalChecked();
}
void Clipboard::WriteBuffer(const std::string& format,
const v8::Local<v8::Value> buffer,
mate::Arguments* args) {
if (!node::Buffer::HasInstance(buffer)) {
args->ThrowError("buffer must be a node Buffer");
return;
}
ui::ScopedClipboardWriter writer(GetClipboardType(args));
writer.WriteData(
ui::ClipboardFormatType::GetType(format).Serialize(),
std::string(node::Buffer::Data(buffer), node::Buffer::Length(buffer)));
}
void Clipboard::Write(const mate::Dictionary& data, mate::Arguments* args) {
ui::ScopedClipboardWriter writer(GetClipboardType(args));
base::string16 text, html, bookmark;
gfx::Image image;
if (data.Get("text", &text)) {
writer.WriteText(text);
if (data.Get("bookmark", &bookmark))
writer.WriteBookmark(bookmark, base::UTF16ToUTF8(text));
}
if (data.Get("rtf", &text)) {
std::string rtf = base::UTF16ToUTF8(text);
writer.WriteRTF(rtf);
}
if (data.Get("html", &html))
writer.WriteHTML(html, std::string());
if (data.Get("image", &image))
writer.WriteImage(image.AsBitmap());
}
base::string16 Clipboard::ReadText(mate::Arguments* args) {
base::string16 data;
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
auto type = GetClipboardType(args);
if (clipboard->IsFormatAvailable(ui::ClipboardFormatType::GetPlainTextWType(),
type)) {
clipboard->ReadText(type, &data);
} else if (clipboard->IsFormatAvailable(
ui::ClipboardFormatType::GetPlainTextType(), type)) {
std::string result;
clipboard->ReadAsciiText(type, &result);
data = base::ASCIIToUTF16(result);
}
return data;
}
void Clipboard::WriteText(const base::string16& text, mate::Arguments* args) {
ui::ScopedClipboardWriter writer(GetClipboardType(args));
writer.WriteText(text);
}
base::string16 Clipboard::ReadRTF(mate::Arguments* args) {
std::string data;
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->ReadRTF(GetClipboardType(args), &data);
return base::UTF8ToUTF16(data);
}
void Clipboard::WriteRTF(const std::string& text, mate::Arguments* args) {
ui::ScopedClipboardWriter writer(GetClipboardType(args));
writer.WriteRTF(text);
}
base::string16 Clipboard::ReadHTML(mate::Arguments* args) {
base::string16 data;
base::string16 html;
std::string url;
uint32_t start;
uint32_t end;
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->ReadHTML(GetClipboardType(args), &html, &url, &start, &end);
data = html.substr(start, end - start);
return data;
}
void Clipboard::WriteHTML(const base::string16& html, mate::Arguments* args) {
ui::ScopedClipboardWriter writer(GetClipboardType(args));
writer.WriteHTML(html, std::string());
}
v8::Local<v8::Value> Clipboard::ReadBookmark(mate::Arguments* args) {
base::string16 title;
std::string url;
mate::Dictionary dict = mate::Dictionary::CreateEmpty(args->isolate());
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->ReadBookmark(&title, &url);
dict.Set("title", title);
dict.Set("url", url);
return dict.GetHandle();
}
void Clipboard::WriteBookmark(const base::string16& title,
const std::string& url,
mate::Arguments* args) {
ui::ScopedClipboardWriter writer(GetClipboardType(args));
writer.WriteBookmark(title, url);
}
gfx::Image Clipboard::ReadImage(mate::Arguments* args) {
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
SkBitmap bitmap = clipboard->ReadImage(GetClipboardType(args));
return gfx::Image::CreateFrom1xBitmap(bitmap);
}
void Clipboard::WriteImage(const gfx::Image& image, mate::Arguments* args) {
ui::ScopedClipboardWriter writer(GetClipboardType(args));
SkBitmap orig = image.AsBitmap();
SkBitmap bmp;
if (bmp.tryAllocPixels(orig.info()) &&
orig.readPixels(bmp.info(), bmp.getPixels(), bmp.rowBytes(), 0, 0)) {
writer.WriteImage(bmp);
}
}
#if !defined(OS_MACOSX)
void Clipboard::WriteFindText(const base::string16& text) {}
base::string16 Clipboard::ReadFindText() {
return base::string16();
}
#endif
void Clipboard::Clear(mate::Arguments* args) {
ui::Clipboard::GetForCurrentThread()->Clear(GetClipboardType(args));
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("availableFormats", &atom::api::Clipboard::AvailableFormats);
dict.SetMethod("has", &atom::api::Clipboard::Has);
dict.SetMethod("read", &atom::api::Clipboard::Read);
dict.SetMethod("write", &atom::api::Clipboard::Write);
dict.SetMethod("readText", &atom::api::Clipboard::ReadText);
dict.SetMethod("writeText", &atom::api::Clipboard::WriteText);
dict.SetMethod("readRTF", &atom::api::Clipboard::ReadRTF);
dict.SetMethod("writeRTF", &atom::api::Clipboard::WriteRTF);
dict.SetMethod("readHTML", &atom::api::Clipboard::ReadHTML);
dict.SetMethod("writeHTML", &atom::api::Clipboard::WriteHTML);
dict.SetMethod("readBookmark", &atom::api::Clipboard::ReadBookmark);
dict.SetMethod("writeBookmark", &atom::api::Clipboard::WriteBookmark);
dict.SetMethod("readImage", &atom::api::Clipboard::ReadImage);
dict.SetMethod("writeImage", &atom::api::Clipboard::WriteImage);
dict.SetMethod("readFindText", &atom::api::Clipboard::ReadFindText);
dict.SetMethod("writeFindText", &atom::api::Clipboard::WriteFindText);
dict.SetMethod("readBuffer", &atom::api::Clipboard::ReadBuffer);
dict.SetMethod("writeBuffer", &atom::api::Clipboard::WriteBuffer);
dict.SetMethod("clear", &atom::api::Clipboard::Clear);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_clipboard, Initialize)

View file

@ -0,0 +1,64 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT 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 <string>
#include <vector>
#include "native_mate/arguments.h"
#include "native_mate/dictionary.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/gfx/image/image.h"
namespace atom {
namespace api {
class Clipboard {
public:
static ui::ClipboardType GetClipboardType(mate::Arguments* args);
static std::vector<base::string16> AvailableFormats(mate::Arguments* args);
static bool Has(const std::string& format_string, mate::Arguments* args);
static void Clear(mate::Arguments* args);
static std::string Read(const std::string& format_string);
static void Write(const mate::Dictionary& data, mate::Arguments* args);
static base::string16 ReadText(mate::Arguments* args);
static void WriteText(const base::string16& text, mate::Arguments* args);
static base::string16 ReadRTF(mate::Arguments* args);
static void WriteRTF(const std::string& text, mate::Arguments* args);
static base::string16 ReadHTML(mate::Arguments* args);
static void WriteHTML(const base::string16& html, mate::Arguments* args);
static v8::Local<v8::Value> ReadBookmark(mate::Arguments* args);
static void WriteBookmark(const base::string16& title,
const std::string& url,
mate::Arguments* args);
static gfx::Image ReadImage(mate::Arguments* args);
static void WriteImage(const gfx::Image& image, mate::Arguments* args);
static base::string16 ReadFindText();
static void WriteFindText(const base::string16& text);
static v8::Local<v8::Value> ReadBuffer(const std::string& format_string,
mate::Arguments* args);
static void WriteBuffer(const std::string& format_string,
const v8::Local<v8::Value> buffer,
mate::Arguments* args);
private:
DISALLOW_COPY_AND_ASSIGN(Clipboard);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_CLIPBOARD_H_

View file

@ -0,0 +1,24 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/atom_api_clipboard.h"
#include "base/strings/sys_string_conversions.h"
#include "ui/base/cocoa/find_pasteboard.h"
namespace atom {
namespace api {
void Clipboard::WriteFindText(const base::string16& text) {
NSString* text_ns = base::SysUTF16ToNSString(text);
[[FindPasteboard sharedInstance] setFindText:text_ns];
}
base::string16 Clipboard::ReadFindText() {
return GetFindPboardText();
}
} // namespace api
} // namespace atom

View file

@ -0,0 +1,61 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/strings/string_util.h"
#include "native_mate/converter.h"
#include "native_mate/dictionary.h"
#include "services/network/public/cpp/network_switches.h"
namespace {
bool HasSwitch(const std::string& name) {
return base::CommandLine::ForCurrentProcess()->HasSwitch(name.c_str());
}
base::CommandLine::StringType GetSwitchValue(const std::string& name) {
return base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
name.c_str());
}
void AppendSwitch(const std::string& switch_string, mate::Arguments* args) {
auto* command_line = base::CommandLine::ForCurrentProcess();
if (base::EndsWith(switch_string, "-path",
base::CompareCase::INSENSITIVE_ASCII) ||
switch_string == network::switches::kLogNetLog) {
base::FilePath path;
args->GetNext(&path);
command_line->AppendSwitchPath(switch_string, path);
return;
}
base::CommandLine::StringType value;
if (args->GetNext(&value))
command_line->AppendSwitchNative(switch_string, value);
else
command_line->AppendSwitch(switch_string);
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
auto* command_line = base::CommandLine::ForCurrentProcess();
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("hasSwitch", &HasSwitch);
dict.SetMethod("getSwitchValue", &GetSwitchValue);
dict.SetMethod("appendSwitch", &AppendSwitch);
dict.SetMethod("appendArgument",
base::BindRepeating(&base::CommandLine::AppendArg,
base::Unretained(command_line)));
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_command_line, Initialize)

View file

@ -0,0 +1,65 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <map>
#include <string>
#include "atom/common/crash_reporter/crash_reporter.h"
#include "atom/common/gin_util.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/map_converter.h"
#include "base/bind.h"
#include "gin/data_object_builder.h"
#include "gin/dictionary.h"
#include "atom/common/node_includes.h"
using crash_reporter::CrashReporter;
namespace gin {
template <>
struct Converter<CrashReporter::UploadReportResult> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const CrashReporter::UploadReportResult& reports) {
return gin::DataObjectBuilder(isolate)
.Set("date",
v8::Date::New(isolate->GetCurrentContext(), reports.first * 1000.0)
.ToLocalChecked())
.Set("id", reports.second)
.Build();
}
};
} // namespace gin
namespace {
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
using gin_util::SetMethod;
auto reporter = base::Unretained(CrashReporter::GetInstance());
SetMethod(exports, "start",
base::BindRepeating(&CrashReporter::Start, reporter));
SetMethod(exports, "addExtraParameter",
base::BindRepeating(&CrashReporter::AddExtraParameter, reporter));
SetMethod(
exports, "removeExtraParameter",
base::BindRepeating(&CrashReporter::RemoveExtraParameter, reporter));
SetMethod(exports, "getParameters",
base::BindRepeating(&CrashReporter::GetParameters, reporter));
SetMethod(exports, "getUploadedReports",
base::BindRepeating(&CrashReporter::GetUploadedReports, reporter));
SetMethod(exports, "setUploadToServer",
base::BindRepeating(&CrashReporter::SetUploadToServer, reporter));
SetMethod(exports, "getUploadToServer",
base::BindRepeating(&CrashReporter::GetUploadToServer, reporter));
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_crash_reporter, Initialize)

View file

@ -0,0 +1,63 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_ATOM_API_KEY_WEAK_MAP_H_
#define ATOM_COMMON_API_ATOM_API_KEY_WEAK_MAP_H_
#include "atom/common/key_weak_map.h"
#include "native_mate/handle.h"
#include "native_mate/object_template_builder.h"
#include "native_mate/wrappable.h"
namespace atom {
namespace api {
template <typename K>
class KeyWeakMap : public mate::Wrappable<KeyWeakMap<K>> {
public:
static mate::Handle<KeyWeakMap<K>> Create(v8::Isolate* isolate) {
return mate::CreateHandle(isolate, new KeyWeakMap<K>(isolate));
}
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "KeyWeakMap"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("set", &KeyWeakMap<K>::Set)
.SetMethod("get", &KeyWeakMap<K>::Get)
.SetMethod("has", &KeyWeakMap<K>::Has)
.SetMethod("remove", &KeyWeakMap<K>::Remove);
}
protected:
explicit KeyWeakMap(v8::Isolate* isolate) {
mate::Wrappable<KeyWeakMap<K>>::Init(isolate);
}
~KeyWeakMap() override {}
private:
// API for KeyWeakMap.
void Set(v8::Isolate* isolate, const K& key, v8::Local<v8::Object> object) {
key_weak_map_.Set(isolate, key, object);
}
v8::Local<v8::Object> Get(v8::Isolate* isolate, const K& key) {
return key_weak_map_.Get(isolate, key).ToLocalChecked();
}
bool Has(const K& key) { return key_weak_map_.Has(key); }
void Remove(const K& key) { key_weak_map_.Remove(key); }
atom::KeyWeakMap<K> key_weak_map_;
DISALLOW_COPY_AND_ASSIGN(KeyWeakMap);
};
} // namespace api
} // namespace atom
#endif // ATOM_COMMON_API_ATOM_API_KEY_WEAK_MAP_H_

View file

@ -0,0 +1,713 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/atom_api_native_image.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "atom/common/asar/asar_util.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/node_includes.h"
#include "base/files/file_util.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "native_mate/object_template_builder.h"
#include "net/base/data_url.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "ui/base/layout.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/image/image_util.h"
#if defined(OS_WIN)
#include "atom/common/asar/archive.h"
#include "base/win/scoped_gdi_object.h"
#include "ui/gfx/icon_util.h"
#endif
namespace atom {
namespace api {
namespace {
struct ScaleFactorPair {
const char* name;
float scale;
};
ScaleFactorPair kScaleFactorPairs[] = {
// The "@2x" is put as first one to make scale matching faster.
{"@2x", 2.0f}, {"@3x", 3.0f}, {"@1x", 1.0f}, {"@4x", 4.0f},
{"@5x", 5.0f}, {"@1.25x", 1.25f}, {"@1.33x", 1.33f}, {"@1.4x", 1.4f},
{"@1.5x", 1.5f}, {"@1.8x", 1.8f}, {"@2.5x", 2.5f},
};
float GetScaleFactorFromPath(const base::FilePath& path) {
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
// We don't try to convert string to float here because it is very very
// expensive.
for (const auto& kScaleFactorPair : kScaleFactorPairs) {
if (base::EndsWith(filename, kScaleFactorPair.name,
base::CompareCase::INSENSITIVE_ASCII))
return kScaleFactorPair.scale;
}
return 1.0f;
}
// Get the scale factor from options object at the first argument
float GetScaleFactorFromOptions(mate::Arguments* args) {
float scale_factor = 1.0f;
mate::Dictionary options;
if (args->GetNext(&options))
options.Get("scaleFactor", &scale_factor);
return scale_factor;
}
bool AddImageSkiaRepFromPNG(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
double scale_factor) {
SkBitmap bitmap;
if (!gfx::PNGCodec::Decode(data, size, &bitmap))
return false;
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
return true;
}
bool AddImageSkiaRepFromJPEG(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
double scale_factor) {
auto bitmap = gfx::JPEGCodec::Decode(data, size);
if (!bitmap)
return false;
// `JPEGCodec::Decode()` doesn't tell `SkBitmap` instance it creates
// that all of its pixels are opaque, that's why the bitmap gets
// an alpha type `kPremul_SkAlphaType` instead of `kOpaque_SkAlphaType`.
// Let's fix it here.
// TODO(alexeykuzmin): This workaround should be removed
// when the `JPEGCodec::Decode()` code is fixed.
// See https://github.com/electron/electron/issues/11294.
bitmap->setAlphaType(SkAlphaType::kOpaque_SkAlphaType);
image->AddRepresentation(gfx::ImageSkiaRep(*bitmap, scale_factor));
return true;
}
bool AddImageSkiaRepFromBuffer(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
int width,
int height,
double scale_factor) {
// Try PNG first.
if (AddImageSkiaRepFromPNG(image, data, size, scale_factor))
return true;
// Try JPEG second.
if (AddImageSkiaRepFromJPEG(image, data, size, scale_factor))
return true;
if (width == 0 || height == 0)
return false;
auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
if (size < info.computeMinByteSize())
return false;
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height, false);
bitmap.writePixels({info, data, bitmap.rowBytes()});
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
return true;
}
bool AddImageSkiaRepFromPath(gfx::ImageSkia* image,
const base::FilePath& path,
double scale_factor) {
std::string file_contents;
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!asar::ReadFileToString(path, &file_contents))
return false;
}
const unsigned char* data =
reinterpret_cast<const unsigned char*>(file_contents.data());
size_t size = file_contents.size();
return AddImageSkiaRepFromBuffer(image, data, size, 0, 0, scale_factor);
}
bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
const base::FilePath& path) {
bool succeed = false;
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
if (base::MatchPattern(filename, "*@*x"))
// Don't search for other representations if the DPI has been specified.
return AddImageSkiaRepFromPath(image, path, GetScaleFactorFromPath(path));
else
succeed |= AddImageSkiaRepFromPath(image, path, 1.0f);
for (const ScaleFactorPair& pair : kScaleFactorPairs)
succeed |= AddImageSkiaRepFromPath(
image, path.InsertBeforeExtensionASCII(pair.name), pair.scale);
return succeed;
}
base::FilePath NormalizePath(const base::FilePath& path) {
if (!path.ReferencesParent()) {
return path;
}
base::FilePath absolute_path = MakeAbsoluteFilePath(path);
// MakeAbsoluteFilePath returns an empty path on failures so use original path
if (absolute_path.empty()) {
return path;
} else {
return absolute_path;
}
}
#if defined(OS_MACOSX)
bool IsTemplateFilename(const base::FilePath& path) {
return (base::MatchPattern(path.value(), "*Template.*") ||
base::MatchPattern(path.value(), "*Template@*x.*"));
}
#endif
#if defined(OS_WIN)
base::win::ScopedHICON ReadICOFromPath(int size, const base::FilePath& path) {
// If file is in asar archive, we extract it to a temp file so LoadImage can
// load it.
base::FilePath asar_path, relative_path;
base::FilePath image_path(path);
if (asar::GetAsarArchivePath(image_path, &asar_path, &relative_path)) {
std::shared_ptr<asar::Archive> archive =
asar::GetOrCreateAsarArchive(asar_path);
if (archive)
archive->CopyFileOut(relative_path, &image_path);
}
// Load the icon from file.
return base::win::ScopedHICON(
static_cast<HICON>(LoadImage(NULL, image_path.value().c_str(), IMAGE_ICON,
size, size, LR_LOADFROMFILE)));
}
bool ReadImageSkiaFromICO(gfx::ImageSkia* image, HICON icon) {
// Convert the icon from the Windows specific HICON to gfx::ImageSkia.
SkBitmap bitmap = IconUtil::CreateSkBitmapFromHICON(icon);
if (bitmap.isNull())
return false;
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, 1.0f));
return true;
}
#endif
void Noop(char*, void*) {}
} // namespace
NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image)
: image_(image) {
Init(isolate);
if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
isolate->AdjustAmountOfExternalAllocatedMemory(
image_.ToImageSkia()->bitmap()->computeByteSize());
}
}
#if defined(OS_WIN)
NativeImage::NativeImage(v8::Isolate* isolate, const base::FilePath& hicon_path)
: hicon_path_(hicon_path) {
// Use the 256x256 icon as fallback icon.
gfx::ImageSkia image_skia;
ReadImageSkiaFromICO(&image_skia, GetHICON(256));
image_ = gfx::Image(image_skia);
Init(isolate);
if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
isolate->AdjustAmountOfExternalAllocatedMemory(
image_.ToImageSkia()->bitmap()->computeByteSize());
}
}
#endif
NativeImage::~NativeImage() {
if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) {
isolate()->AdjustAmountOfExternalAllocatedMemory(-static_cast<int64_t>(
image_.ToImageSkia()->bitmap()->computeByteSize()));
}
}
#if defined(OS_WIN)
HICON NativeImage::GetHICON(int size) {
auto iter = hicons_.find(size);
if (iter != hicons_.end())
return iter->second.get();
// First try loading the icon with specified size.
if (!hicon_path_.empty()) {
hicons_[size] = ReadICOFromPath(size, hicon_path_);
return hicons_[size].get();
}
// Then convert the image to ICO.
if (image_.IsEmpty())
return NULL;
hicons_[size] = IconUtil::CreateHICONFromSkBitmap(image_.AsBitmap());
return hicons_[size].get();
}
#endif
v8::Local<v8::Value> NativeImage::ToPNG(mate::Arguments* args) {
float scale_factor = GetScaleFactorFromOptions(args);
if (scale_factor == 1.0f) {
// Use raw 1x PNG bytes when available
scoped_refptr<base::RefCountedMemory> png = image_.As1xPNGBytes();
if (png->size() > 0) {
const char* data = reinterpret_cast<const char*>(png->front());
size_t size = png->size();
return node::Buffer::Copy(args->isolate(), data, size).ToLocalChecked();
}
}
const SkBitmap bitmap =
image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap();
std::vector<unsigned char> encoded;
gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &encoded);
const char* data = reinterpret_cast<char*>(encoded.data());
size_t size = encoded.size();
return node::Buffer::Copy(args->isolate(), data, size).ToLocalChecked();
}
v8::Local<v8::Value> NativeImage::ToBitmap(mate::Arguments* args) {
float scale_factor = GetScaleFactorFromOptions(args);
const SkBitmap bitmap =
image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap();
SkPixelRef* ref = bitmap.pixelRef();
if (!ref)
return node::Buffer::New(args->isolate(), 0).ToLocalChecked();
return node::Buffer::Copy(args->isolate(),
reinterpret_cast<const char*>(ref->pixels()),
bitmap.computeByteSize())
.ToLocalChecked();
}
v8::Local<v8::Value> NativeImage::ToJPEG(v8::Isolate* isolate, int quality) {
std::vector<unsigned char> output;
gfx::JPEG1xEncodedDataFromImage(image_, quality, &output);
if (output.empty())
return node::Buffer::New(isolate, 0).ToLocalChecked();
return node::Buffer::Copy(isolate,
reinterpret_cast<const char*>(&output.front()),
output.size())
.ToLocalChecked();
}
std::string NativeImage::ToDataURL(mate::Arguments* args) {
float scale_factor = GetScaleFactorFromOptions(args);
if (scale_factor == 1.0f) {
// Use raw 1x PNG bytes when available
scoped_refptr<base::RefCountedMemory> png = image_.As1xPNGBytes();
if (png->size() > 0)
return webui::GetPngDataUrl(png->front(), png->size());
}
return webui::GetBitmapDataUrl(
image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap());
}
v8::Local<v8::Value> NativeImage::GetBitmap(mate::Arguments* args) {
float scale_factor = GetScaleFactorFromOptions(args);
const SkBitmap bitmap =
image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap();
SkPixelRef* ref = bitmap.pixelRef();
if (!ref)
return node::Buffer::New(args->isolate(), 0).ToLocalChecked();
return node::Buffer::New(args->isolate(),
reinterpret_cast<char*>(ref->pixels()),
bitmap.computeByteSize(), &Noop, nullptr)
.ToLocalChecked();
}
v8::Local<v8::Value> NativeImage::GetNativeHandle(v8::Isolate* isolate,
mate::Arguments* args) {
#if defined(OS_MACOSX)
if (IsEmpty())
return node::Buffer::New(isolate, 0).ToLocalChecked();
NSImage* ptr = image_.AsNSImage();
return node::Buffer::Copy(isolate, reinterpret_cast<char*>(ptr),
sizeof(void*))
.ToLocalChecked();
#else
args->ThrowError("Not implemented");
return v8::Undefined(isolate);
#endif
}
bool NativeImage::IsEmpty() {
return image_.IsEmpty();
}
gfx::Size NativeImage::GetSize() {
return image_.Size();
}
float NativeImage::GetAspectRatio() {
gfx::Size size = GetSize();
if (size.IsEmpty())
return 1.f;
else
return static_cast<float>(size.width()) / static_cast<float>(size.height());
}
mate::Handle<NativeImage> NativeImage::Resize(
v8::Isolate* isolate,
const base::DictionaryValue& options) {
gfx::Size size = GetSize();
int width = size.width();
int height = size.height();
bool width_set = options.GetInteger("width", &width);
bool height_set = options.GetInteger("height", &height);
size.SetSize(width, height);
if (width_set && !height_set) {
// Scale height to preserve original aspect ratio
size.set_height(width);
size = gfx::ScaleToRoundedSize(size, 1.f, 1.f / GetAspectRatio());
} else if (height_set && !width_set) {
// Scale width to preserve original aspect ratio
size.set_width(height);
size = gfx::ScaleToRoundedSize(size, GetAspectRatio(), 1.f);
}
skia::ImageOperations::ResizeMethod method =
skia::ImageOperations::ResizeMethod::RESIZE_BEST;
std::string quality;
options.GetString("quality", &quality);
if (quality == "good")
method = skia::ImageOperations::ResizeMethod::RESIZE_GOOD;
else if (quality == "better")
method = skia::ImageOperations::ResizeMethod::RESIZE_BETTER;
gfx::ImageSkia resized = gfx::ImageSkiaOperations::CreateResizedImage(
image_.AsImageSkia(), method, size);
return mate::CreateHandle(isolate,
new NativeImage(isolate, gfx::Image(resized)));
}
mate::Handle<NativeImage> NativeImage::Crop(v8::Isolate* isolate,
const gfx::Rect& rect) {
gfx::ImageSkia cropped =
gfx::ImageSkiaOperations::ExtractSubset(image_.AsImageSkia(), rect);
return mate::CreateHandle(isolate,
new NativeImage(isolate, gfx::Image(cropped)));
}
void NativeImage::AddRepresentation(const mate::Dictionary& options) {
int width = 0;
int height = 0;
float scale_factor = 1.0f;
options.Get("width", &width);
options.Get("height", &height);
options.Get("scaleFactor", &scale_factor);
bool skia_rep_added = false;
gfx::ImageSkia image_skia = image_.AsImageSkia();
v8::Local<v8::Value> buffer;
GURL url;
if (options.Get("buffer", &buffer) && node::Buffer::HasInstance(buffer)) {
auto* data = reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer));
auto size = node::Buffer::Length(buffer);
skia_rep_added = AddImageSkiaRepFromBuffer(&image_skia, data, size, width,
height, scale_factor);
} else if (options.Get("dataURL", &url)) {
std::string mime_type, charset, data;
if (net::DataURL::Parse(url, &mime_type, &charset, &data)) {
auto* data_ptr = reinterpret_cast<const unsigned char*>(data.c_str());
if (mime_type == "image/png") {
skia_rep_added = AddImageSkiaRepFromPNG(&image_skia, data_ptr,
data.size(), scale_factor);
} else if (mime_type == "image/jpeg") {
skia_rep_added = AddImageSkiaRepFromJPEG(&image_skia, data_ptr,
data.size(), scale_factor);
}
}
}
// Re-initialize image when first representation is added to an empty image
if (skia_rep_added && IsEmpty()) {
gfx::Image image(image_skia);
image_ = std::move(image);
}
}
#if !defined(OS_MACOSX)
void NativeImage::SetTemplateImage(bool setAsTemplate) {}
bool NativeImage::IsTemplateImage() {
return false;
}
#endif
// static
mate::Handle<NativeImage> NativeImage::CreateEmpty(v8::Isolate* isolate) {
return mate::CreateHandle(isolate, new NativeImage(isolate, gfx::Image()));
}
// static
mate::Handle<NativeImage> NativeImage::Create(v8::Isolate* isolate,
const gfx::Image& image) {
return mate::CreateHandle(isolate, new NativeImage(isolate, image));
}
// static
mate::Handle<NativeImage> NativeImage::CreateFromPNG(v8::Isolate* isolate,
const char* buffer,
size_t length) {
gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(
reinterpret_cast<const unsigned char*>(buffer), length);
return Create(isolate, image);
}
// static
mate::Handle<NativeImage> NativeImage::CreateFromJPEG(v8::Isolate* isolate,
const char* buffer,
size_t length) {
gfx::Image image = gfx::ImageFrom1xJPEGEncodedData(
reinterpret_cast<const unsigned char*>(buffer), length);
return Create(isolate, image);
}
// static
mate::Handle<NativeImage> NativeImage::CreateFromPath(
v8::Isolate* isolate,
const base::FilePath& path) {
base::FilePath image_path = NormalizePath(path);
#if defined(OS_WIN)
if (image_path.MatchesExtension(FILE_PATH_LITERAL(".ico"))) {
return mate::CreateHandle(isolate, new NativeImage(isolate, image_path));
}
#endif
gfx::ImageSkia image_skia;
PopulateImageSkiaRepsFromPath(&image_skia, image_path);
gfx::Image image(image_skia);
mate::Handle<NativeImage> handle = Create(isolate, image);
#if defined(OS_MACOSX)
if (IsTemplateFilename(image_path))
handle->SetTemplateImage(true);
#endif
return handle;
}
// static
mate::Handle<NativeImage> NativeImage::CreateFromBitmap(
mate::Arguments* args,
v8::Local<v8::Value> buffer,
const mate::Dictionary& options) {
if (!node::Buffer::HasInstance(buffer)) {
args->ThrowError("buffer must be a node Buffer");
return mate::Handle<NativeImage>();
}
unsigned int width = 0;
unsigned int height = 0;
double scale_factor = 1.;
if (!options.Get("width", &width)) {
args->ThrowError("width is required");
return mate::Handle<NativeImage>();
}
if (!options.Get("height", &height)) {
args->ThrowError("height is required");
return mate::Handle<NativeImage>();
}
auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
auto size_bytes = info.computeMinByteSize();
if (size_bytes != node::Buffer::Length(buffer)) {
args->ThrowError("invalid buffer size");
return mate::Handle<NativeImage>();
}
options.Get("scaleFactor", &scale_factor);
if (width == 0 || height == 0) {
return CreateEmpty(args->isolate());
}
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height, false);
bitmap.writePixels({info, node::Buffer::Data(buffer), bitmap.rowBytes()});
gfx::ImageSkia image_skia;
image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
return Create(args->isolate(), gfx::Image(image_skia));
}
// static
mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
mate::Arguments* args,
v8::Local<v8::Value> buffer) {
if (!node::Buffer::HasInstance(buffer)) {
args->ThrowError("buffer must be a node Buffer");
return mate::Handle<NativeImage>();
}
int width = 0;
int height = 0;
double scale_factor = 1.;
mate::Dictionary options;
if (args->GetNext(&options)) {
options.Get("width", &width);
options.Get("height", &height);
options.Get("scaleFactor", &scale_factor);
}
gfx::ImageSkia image_skia;
AddImageSkiaRepFromBuffer(
&image_skia, reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
node::Buffer::Length(buffer), width, height, scale_factor);
return Create(args->isolate(), gfx::Image(image_skia));
}
// static
mate::Handle<NativeImage> NativeImage::CreateFromDataURL(v8::Isolate* isolate,
const GURL& url) {
std::string mime_type, charset, data;
if (net::DataURL::Parse(url, &mime_type, &charset, &data)) {
if (mime_type == "image/png")
return CreateFromPNG(isolate, data.c_str(), data.size());
else if (mime_type == "image/jpeg")
return CreateFromJPEG(isolate, data.c_str(), data.size());
}
return CreateEmpty(isolate);
}
#if !defined(OS_MACOSX)
mate::Handle<NativeImage> NativeImage::CreateFromNamedImage(
mate::Arguments* args,
const std::string& name) {
return CreateEmpty(args->isolate());
}
#endif
// static
void NativeImage::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "NativeImage"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("toPNG", &NativeImage::ToPNG)
.SetMethod("toJPEG", &NativeImage::ToJPEG)
.SetMethod("toBitmap", &NativeImage::ToBitmap)
.SetMethod("getBitmap", &NativeImage::GetBitmap)
.SetMethod("getNativeHandle", &NativeImage::GetNativeHandle)
.SetMethod("toDataURL", &NativeImage::ToDataURL)
.SetMethod("isEmpty", &NativeImage::IsEmpty)
.SetMethod("getSize", &NativeImage::GetSize)
.SetMethod("_setTemplateImage", &NativeImage::SetTemplateImage)
.SetMethod("_isTemplateImage", &NativeImage::IsTemplateImage)
.SetProperty("isMacTemplateImage", &NativeImage::IsTemplateImage,
&NativeImage::SetTemplateImage)
.SetMethod("resize", &NativeImage::Resize)
.SetMethod("crop", &NativeImage::Crop)
.SetMethod("getAspectRatio", &NativeImage::GetAspectRatio)
.SetMethod("addRepresentation", &NativeImage::AddRepresentation);
}
} // namespace api
} // namespace atom
namespace mate {
v8::Local<v8::Value> Converter<mate::Handle<atom::api::NativeImage>>::ToV8(
v8::Isolate* isolate,
const mate::Handle<atom::api::NativeImage>& val) {
return val.ToV8();
}
bool Converter<mate::Handle<atom::api::NativeImage>>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
mate::Handle<atom::api::NativeImage>* out) {
// Try converting from file path.
base::FilePath path;
if (ConvertFromV8(isolate, val, &path)) {
*out = atom::api::NativeImage::CreateFromPath(isolate, path);
// Should throw when failed to initialize from path.
return !(*out)->image().IsEmpty();
}
WrappableBase* wrapper =
static_cast<WrappableBase*>(internal::FromV8Impl(isolate, val));
if (!wrapper)
return false;
*out = CreateHandle(isolate, static_cast<atom::api::NativeImage*>(wrapper));
return true;
}
} // namespace mate
namespace {
using atom::api::NativeImage;
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.Set("NativeImage", NativeImage::GetConstructor(isolate)
->GetFunction(context)
.ToLocalChecked());
mate::Dictionary native_image = mate::Dictionary::CreateEmpty(isolate);
dict.Set("nativeImage", native_image);
native_image.SetMethod("createEmpty", &NativeImage::CreateEmpty);
native_image.SetMethod("createFromPath", &NativeImage::CreateFromPath);
native_image.SetMethod("createFromBitmap", &NativeImage::CreateFromBitmap);
native_image.SetMethod("createFromBuffer", &NativeImage::CreateFromBuffer);
native_image.SetMethod("createFromDataURL", &NativeImage::CreateFromDataURL);
native_image.SetMethod("createFromNamedImage",
&NativeImage::CreateFromNamedImage);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_native_image, Initialize)

View file

@ -0,0 +1,133 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_ATOM_API_NATIVE_IMAGE_H_
#define ATOM_COMMON_API_ATOM_API_NATIVE_IMAGE_H_
#include <map>
#include <string>
#include "base/values.h"
#include "native_mate/dictionary.h"
#include "native_mate/handle.h"
#include "native_mate/wrappable.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image.h"
#if defined(OS_WIN)
#include "base/files/file_path.h"
#include "base/win/scoped_gdi_object.h"
#endif
class GURL;
namespace base {
class FilePath;
}
namespace gfx {
class Size;
}
namespace mate {
class Arguments;
}
namespace atom {
namespace api {
class NativeImage : public mate::Wrappable<NativeImage> {
public:
static mate::Handle<NativeImage> CreateEmpty(v8::Isolate* isolate);
static mate::Handle<NativeImage> Create(v8::Isolate* isolate,
const gfx::Image& image);
static mate::Handle<NativeImage> CreateFromPNG(v8::Isolate* isolate,
const char* buffer,
size_t length);
static mate::Handle<NativeImage> CreateFromJPEG(v8::Isolate* isolate,
const char* buffer,
size_t length);
static mate::Handle<NativeImage> CreateFromPath(v8::Isolate* isolate,
const base::FilePath& path);
static mate::Handle<NativeImage> CreateFromBitmap(
mate::Arguments* args,
v8::Local<v8::Value> buffer,
const mate::Dictionary& options);
static mate::Handle<NativeImage> CreateFromBuffer(
mate::Arguments* args,
v8::Local<v8::Value> buffer);
static mate::Handle<NativeImage> CreateFromDataURL(v8::Isolate* isolate,
const GURL& url);
static mate::Handle<NativeImage> CreateFromNamedImage(
mate::Arguments* args,
const std::string& name);
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
#if defined(OS_WIN)
HICON GetHICON(int size);
#endif
const gfx::Image& image() const { return image_; }
protected:
NativeImage(v8::Isolate* isolate, const gfx::Image& image);
#if defined(OS_WIN)
NativeImage(v8::Isolate* isolate, const base::FilePath& hicon_path);
#endif
~NativeImage() override;
private:
v8::Local<v8::Value> ToPNG(mate::Arguments* args);
v8::Local<v8::Value> ToJPEG(v8::Isolate* isolate, int quality);
v8::Local<v8::Value> ToBitmap(mate::Arguments* args);
v8::Local<v8::Value> GetBitmap(mate::Arguments* args);
v8::Local<v8::Value> GetNativeHandle(v8::Isolate* isolate,
mate::Arguments* args);
mate::Handle<NativeImage> Resize(v8::Isolate* isolate,
const base::DictionaryValue& options);
mate::Handle<NativeImage> Crop(v8::Isolate* isolate, const gfx::Rect& rect);
std::string ToDataURL(mate::Arguments* args);
bool IsEmpty();
gfx::Size GetSize();
float GetAspectRatio();
void AddRepresentation(const mate::Dictionary& options);
// Mark the image as template image.
void SetTemplateImage(bool setAsTemplate);
// Determine if the image is a template image.
bool IsTemplateImage();
#if defined(OS_WIN)
base::FilePath hicon_path_;
std::map<int, base::win::ScopedHICON> hicons_;
#endif
gfx::Image image_;
DISALLOW_COPY_AND_ASSIGN(NativeImage);
};
} // namespace api
} // namespace atom
namespace mate {
// A custom converter that allows converting path to NativeImage.
template <>
struct Converter<mate::Handle<atom::api::NativeImage>> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const mate::Handle<atom::api::NativeImage>& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
mate::Handle<atom::api::NativeImage>* out);
};
} // namespace mate
#endif // ATOM_COMMON_API_ATOM_API_NATIVE_IMAGE_H_

View file

@ -0,0 +1,76 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/atom_api_native_image.h"
#include <string>
#include <vector>
#import <Cocoa/Cocoa.h>
#include "base/strings/sys_string_conversions.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
namespace atom {
namespace api {
NSData* bufferFromNSImage(NSImage* image) {
CGImageRef ref = [image CGImageForProposedRect:nil context:nil hints:nil];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:ref];
[rep setSize:[image size]];
return [rep representationUsingType:NSPNGFileType
properties:[[NSDictionary alloc] init]];
}
double safeShift(double in, double def) {
if (in >= 0 || in <= 1 || in == def)
return in;
return def;
}
mate::Handle<NativeImage> NativeImage::CreateFromNamedImage(
mate::Arguments* args,
const std::string& name) {
@autoreleasepool {
std::vector<double> hsl_shift;
NSImage* image = [NSImage imageNamed:base::SysUTF8ToNSString(name)];
if (!image.valid) {
return CreateEmpty(args->isolate());
}
NSData* png_data = bufferFromNSImage(image);
if (args->GetNext(&hsl_shift) && hsl_shift.size() == 3) {
gfx::Image gfx_image = gfx::Image::CreateFrom1xPNGBytes(
reinterpret_cast<const unsigned char*>((char*)[png_data bytes]),
[png_data length]);
color_utils::HSL shift = {safeShift(hsl_shift[0], -1),
safeShift(hsl_shift[1], 0.5),
safeShift(hsl_shift[2], 0.5)};
png_data = bufferFromNSImage(
gfx::Image(gfx::ImageSkiaOperations::CreateHSLShiftedImage(
gfx_image.AsImageSkia(), shift))
.AsNSImage());
}
return CreateFromPNG(args->isolate(), (char*)[png_data bytes],
[png_data length]);
}
}
void NativeImage::SetTemplateImage(bool setAsTemplate) {
[image_.AsNSImage() setTemplate:setAsTemplate];
}
bool NativeImage::IsTemplateImage() {
return [image_.AsNSImage() isTemplate];
}
} // namespace api
} // namespace atom

View file

@ -0,0 +1,147 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <string>
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/platform_util.h"
#include "atom/common/promise_util.h"
#include "native_mate/dictionary.h"
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#include "base/win/shortcut.h"
namespace mate {
template <>
struct Converter<base::win::ShortcutOperation> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
base::win::ShortcutOperation* out) {
std::string operation;
if (!ConvertFromV8(isolate, val, &operation))
return false;
if (operation.empty() || operation == "create")
*out = base::win::SHORTCUT_CREATE_ALWAYS;
else if (operation == "update")
*out = base::win::SHORTCUT_UPDATE_EXISTING;
else if (operation == "replace")
*out = base::win::SHORTCUT_REPLACE_EXISTING;
else
return false;
return true;
}
};
} // namespace mate
#endif
namespace {
void OnOpenExternalFinished(atom::util::Promise promise,
const std::string& error) {
if (error.empty())
promise.Resolve();
else
promise.RejectWithErrorMessage(error.c_str());
}
v8::Local<v8::Promise> OpenExternal(const GURL& url, mate::Arguments* args) {
atom::util::Promise promise(args->isolate());
v8::Local<v8::Promise> handle = promise.GetHandle();
platform_util::OpenExternalOptions options;
if (args->Length() >= 2) {
mate::Dictionary obj;
if (args->GetNext(&obj)) {
obj.Get("activate", &options.activate);
obj.Get("workingDirectory", &options.working_dir);
}
}
platform_util::OpenExternal(
url, options,
base::BindOnce(&OnOpenExternalFinished, std::move(promise)));
return handle;
}
#if defined(OS_WIN)
bool WriteShortcutLink(const base::FilePath& shortcut_path,
mate::Arguments* args) {
base::win::ShortcutOperation operation = base::win::SHORTCUT_CREATE_ALWAYS;
args->GetNext(&operation);
mate::Dictionary options = mate::Dictionary::CreateEmpty(args->isolate());
if (!args->GetNext(&options)) {
args->ThrowError();
return false;
}
base::win::ShortcutProperties properties;
base::FilePath path;
base::string16 str;
int index;
if (options.Get("target", &path))
properties.set_target(path);
if (options.Get("cwd", &path))
properties.set_working_dir(path);
if (options.Get("args", &str))
properties.set_arguments(str);
if (options.Get("description", &str))
properties.set_description(str);
if (options.Get("icon", &path) && options.Get("iconIndex", &index))
properties.set_icon(path, index);
if (options.Get("appUserModelId", &str))
properties.set_app_id(str);
base::win::ScopedCOMInitializer com_initializer;
return base::win::CreateOrUpdateShortcutLink(shortcut_path, properties,
operation);
}
v8::Local<v8::Value> ReadShortcutLink(mate::Arguments* args,
const base::FilePath& path) {
using base::win::ShortcutProperties;
mate::Dictionary options = mate::Dictionary::CreateEmpty(args->isolate());
base::win::ScopedCOMInitializer com_initializer;
base::win::ShortcutProperties properties;
if (!base::win::ResolveShortcutProperties(
path, ShortcutProperties::PROPERTIES_ALL, &properties)) {
args->ThrowError("Failed to read shortcut link");
return v8::Null(args->isolate());
}
options.Set("target", properties.target);
options.Set("cwd", properties.working_dir);
options.Set("args", properties.arguments);
options.Set("description", properties.description);
options.Set("icon", properties.icon);
options.Set("iconIndex", properties.icon_index);
options.Set("appUserModelId", properties.app_id);
return options.GetHandle();
}
#endif
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("showItemInFolder", &platform_util::ShowItemInFolder);
dict.SetMethod("openItem", &platform_util::OpenItem);
dict.SetMethod("openExternal", &OpenExternal);
dict.SetMethod("moveItemToTrash", &platform_util::MoveItemToTrash);
dict.SetMethod("beep", &platform_util::Beep);
#if defined(OS_WIN)
dict.SetMethod("writeShortcutLink", &WriteShortcutLink);
dict.SetMethod("readShortcutLink", &ReadShortcutLink);
#endif
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_shell, Initialize)

View file

@ -0,0 +1,134 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <string>
#include <utility>
#include "atom/common/api/atom_api_key_weak_map.h"
#include "atom/common/api/remote_callback_freer.h"
#include "atom/common/api/remote_object_freer.h"
#include "atom/common/native_mate_converters/content_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/node_includes.h"
#include "base/hash/hash.h"
#include "native_mate/dictionary.h"
#include "url/origin.h"
#include "v8/include/v8-profiler.h"
namespace std {
// The hash function used by DoubleIDWeakMap.
template <typename Type1, typename Type2>
struct hash<std::pair<Type1, Type2>> {
std::size_t operator()(std::pair<Type1, Type2> value) const {
return base::HashInts(base::Hash(value.first), value.second);
}
};
} // namespace std
namespace mate {
template <typename Type1, typename Type2>
struct Converter<std::pair<Type1, Type2>> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
std::pair<Type1, Type2>* out) {
if (!val->IsArray())
return false;
v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
if (array->Length() != 2)
return false;
auto context = isolate->GetCurrentContext();
return Converter<Type1>::FromV8(
isolate, array->Get(context, 0).ToLocalChecked(), &out->first) &&
Converter<Type2>::FromV8(
isolate, array->Get(context, 1).ToLocalChecked(), &out->second);
}
};
} // namespace mate
namespace {
v8::Local<v8::Value> GetHiddenValue(v8::Isolate* isolate,
v8::Local<v8::Object> object,
v8::Local<v8::String> key) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, key);
v8::Local<v8::Value> value;
v8::Maybe<bool> result = object->HasPrivate(context, privateKey);
if (!(result.IsJust() && result.FromJust()))
return v8::Local<v8::Value>();
if (object->GetPrivate(context, privateKey).ToLocal(&value))
return value;
return v8::Local<v8::Value>();
}
void SetHiddenValue(v8::Isolate* isolate,
v8::Local<v8::Object> object,
v8::Local<v8::String> key,
v8::Local<v8::Value> value) {
if (value.IsEmpty())
return;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, key);
object->SetPrivate(context, privateKey, value);
}
void DeleteHiddenValue(v8::Isolate* isolate,
v8::Local<v8::Object> object,
v8::Local<v8::String> key) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, key);
// Actually deleting the value would make force the object into
// dictionary mode which is unnecessarily slow. Instead, we replace
// the hidden value with "undefined".
object->SetPrivate(context, privateKey, v8::Undefined(isolate));
}
int32_t GetObjectHash(v8::Local<v8::Object> object) {
return object->GetIdentityHash();
}
void TakeHeapSnapshot(v8::Isolate* isolate) {
isolate->GetHeapProfiler()->TakeHeapSnapshot();
}
void RequestGarbageCollectionForTesting(v8::Isolate* isolate) {
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::GarbageCollectionType::kFullGarbageCollection);
}
bool IsSameOrigin(const GURL& l, const GURL& r) {
return url::Origin::Create(l).IsSameOriginWith(url::Origin::Create(r));
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("getHiddenValue", &GetHiddenValue);
dict.SetMethod("setHiddenValue", &SetHiddenValue);
dict.SetMethod("deleteHiddenValue", &DeleteHiddenValue);
dict.SetMethod("getObjectHash", &GetObjectHash);
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
dict.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo);
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
dict.SetMethod("addRemoteObjectRef", &atom::RemoteObjectFreer::AddRef);
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
dict.SetMethod(
"createDoubleIDWeakMap",
&atom::api::KeyWeakMap<std::pair<std::string, int32_t>>::Create);
dict.SetMethod("requestGarbageCollectionForTesting",
&RequestGarbageCollectionForTesting);
dict.SetMethod("isSameOrigin", &IsSameOrigin);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_v8_util, Initialize)

View file

@ -0,0 +1,34 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_CONSTRUCTOR_H_
#define ATOM_COMMON_API_CONSTRUCTOR_H_
#include "native_mate/constructor.h"
namespace mate {
// Create a FunctionTemplate that can be "new"ed in JavaScript.
// It is user's responsibility to ensure this function is called for one type
// only ONCE in the program's whole lifetime, otherwise we would have memory
// leak.
template <typename T, typename Sig>
v8::Local<v8::Function> CreateConstructor(
v8::Isolate* isolate,
const base::RepeatingCallback<Sig>& func) {
#ifndef NDEBUG
static bool called = false;
CHECK(!called) << "CreateConstructor can only be called for one type once";
called = true;
#endif
v8::Local<v8::FunctionTemplate> templ = CreateFunctionTemplate(
isolate, base::BindRepeating(&mate::internal::InvokeNew<Sig>, func));
templ->InstanceTemplate()->SetInternalFieldCount(1);
T::BuildPrototype(isolate, templ);
return templ->GetFunction(isolate->GetCurrentContext()).ToLocalChecked();
}
} // namespace mate
#endif // ATOM_COMMON_API_CONSTRUCTOR_H_

View file

@ -0,0 +1,364 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/electron_bindings.h"
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include "atom/browser/browser.h"
#include "atom/common/api/locker.h"
#include "atom/common/application_info.h"
#include "atom/common/heap_snapshot.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/promise_util.h"
#include "base/logging.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/system/sys_info.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/common/chrome_version.h"
#include "electron/electron_version.h"
#include "native_mate/dictionary.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
#include "third_party/blink/renderer/platform/heap/process_heap.h" // nogncheck
namespace atom {
namespace {
// Dummy class type that used for crashing the program.
struct DummyClass {
bool crash;
};
// 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;
ElectronBindings::Crash();
}
} // namespace
ElectronBindings::ElectronBindings(uv_loop_t* loop) {
uv_async_init(loop, &call_next_tick_async_, OnCallNextTick);
call_next_tick_async_.data = this;
metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
}
ElectronBindings::~ElectronBindings() {
uv_close(reinterpret_cast<uv_handle_t*>(&call_next_tick_async_), nullptr);
}
// static
void ElectronBindings::BindProcess(v8::Isolate* isolate,
mate::Dictionary* process,
base::ProcessMetrics* metrics) {
// These bindings are shared between sandboxed & unsandboxed renderers
process->SetMethod("crash", &Crash);
process->SetMethod("hang", &Hang);
process->SetMethod("log", &Log);
process->SetMethod("getCreationTime", &GetCreationTime);
process->SetMethod("getHeapStatistics", &GetHeapStatistics);
process->SetMethod("getBlinkMemoryInfo", &GetBlinkMemoryInfo);
process->SetMethod("getProcessMemoryInfo", &GetProcessMemoryInfo);
process->SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo);
process->SetMethod("getSystemVersion",
&base::SysInfo::OperatingSystemVersion);
process->SetMethod("getIOCounters", &GetIOCounters);
process->SetMethod("getCPUUsage",
base::BindRepeating(&ElectronBindings::GetCPUUsage,
base::Unretained(metrics)));
#if defined(MAS_BUILD)
process->SetReadOnly("mas", true);
#endif
#if defined(OS_WIN)
if (IsRunningInDesktopBridge())
process->SetReadOnly("windowsStore", true);
#endif
}
void ElectronBindings::BindTo(v8::Isolate* isolate,
v8::Local<v8::Object> process) {
isolate->SetFatalErrorHandler(FatalErrorCallback);
mate::Dictionary dict(isolate, process);
BindProcess(isolate, &dict, metrics_.get());
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
#if defined(OS_POSIX)
dict.SetMethod("setFdLimit", &base::IncreaseFdLimitTo);
#endif
dict.SetMethod("activateUvLoop",
base::BindRepeating(&ElectronBindings::ActivateUVLoop,
base::Unretained(this)));
mate::Dictionary versions;
if (dict.Get("versions", &versions)) {
versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING);
versions.SetReadOnly("chrome", CHROME_VERSION_STRING);
}
}
void ElectronBindings::EnvironmentDestroyed(node::Environment* env) {
auto it =
std::find(pending_next_ticks_.begin(), pending_next_ticks_.end(), env);
if (it != pending_next_ticks_.end())
pending_next_ticks_.erase(it);
}
void ElectronBindings::ActivateUVLoop(v8::Isolate* isolate) {
node::Environment* env = node::Environment::GetCurrent(isolate);
if (std::find(pending_next_ticks_.begin(), pending_next_ticks_.end(), env) !=
pending_next_ticks_.end())
return;
pending_next_ticks_.push_back(env);
uv_async_send(&call_next_tick_async_);
}
// static
void ElectronBindings::OnCallNextTick(uv_async_t* handle) {
ElectronBindings* self = static_cast<ElectronBindings*>(handle->data);
for (std::list<node::Environment*>::const_iterator it =
self->pending_next_ticks_.begin();
it != self->pending_next_ticks_.end(); ++it) {
node::Environment* env = *it;
mate::Locker locker(env->isolate());
v8::Context::Scope context_scope(env->context());
node::InternalCallbackScope scope(
env, v8::Local<v8::Object>(), {0, 0},
node::InternalCallbackScope::kAllowEmptyResource);
}
self->pending_next_ticks_.clear();
}
// static
void ElectronBindings::Log(const base::string16& message) {
std::cout << message << std::flush;
}
// static
void ElectronBindings::Crash() {
static_cast<DummyClass*>(nullptr)->crash = true;
}
// static
void ElectronBindings::Hang() {
for (;;)
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
}
// static
v8::Local<v8::Value> ElectronBindings::GetHeapStatistics(v8::Isolate* isolate) {
v8::HeapStatistics v8_heap_stats;
isolate->GetHeapStatistics(&v8_heap_stats);
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("totalHeapSize",
static_cast<double>(v8_heap_stats.total_heap_size() >> 10));
dict.Set(
"totalHeapSizeExecutable",
static_cast<double>(v8_heap_stats.total_heap_size_executable() >> 10));
dict.Set("totalPhysicalSize",
static_cast<double>(v8_heap_stats.total_physical_size() >> 10));
dict.Set("totalAvailableSize",
static_cast<double>(v8_heap_stats.total_available_size() >> 10));
dict.Set("usedHeapSize",
static_cast<double>(v8_heap_stats.used_heap_size() >> 10));
dict.Set("heapSizeLimit",
static_cast<double>(v8_heap_stats.heap_size_limit() >> 10));
dict.Set("mallocedMemory",
static_cast<double>(v8_heap_stats.malloced_memory() >> 10));
dict.Set("peakMallocedMemory",
static_cast<double>(v8_heap_stats.peak_malloced_memory() >> 10));
dict.Set("doesZapGarbage",
static_cast<bool>(v8_heap_stats.does_zap_garbage()));
return dict.GetHandle();
}
// static
v8::Local<v8::Value> ElectronBindings::GetCreationTime(v8::Isolate* isolate) {
auto timeValue = base::Process::Current().CreationTime();
if (timeValue.is_null()) {
return v8::Null(isolate);
}
double jsTime = timeValue.ToJsTime();
return v8::Number::New(isolate, jsTime);
}
// static
v8::Local<v8::Value> ElectronBindings::GetSystemMemoryInfo(
v8::Isolate* isolate,
mate::Arguments* args) {
base::SystemMemoryInfoKB mem_info;
if (!base::GetSystemMemoryInfo(&mem_info)) {
args->ThrowError("Unable to retrieve system memory information");
return v8::Undefined(isolate);
}
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("total", mem_info.total);
// See Chromium's "base/process/process_metrics.h" for an explanation.
int free =
#if defined(OS_WIN)
mem_info.avail_phys;
#else
mem_info.free;
#endif
dict.Set("free", free);
// NB: These return bogus values on macOS
#if !defined(OS_MACOSX)
dict.Set("swapTotal", mem_info.swap_total);
dict.Set("swapFree", mem_info.swap_free);
#endif
return dict.GetHandle();
}
// static
v8::Local<v8::Promise> ElectronBindings::GetProcessMemoryInfo(
v8::Isolate* isolate) {
util::Promise promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
if (mate::Locker::IsBrowserProcess() && !Browser::Get()->is_ready()) {
promise.RejectWithErrorMessage(
"Memory Info is available only after app ready");
return handle;
}
v8::Global<v8::Context> context(isolate, isolate->GetCurrentContext());
memory_instrumentation::MemoryInstrumentation::GetInstance()
->RequestGlobalDumpForPid(
base::GetCurrentProcId(), std::vector<std::string>(),
base::BindOnce(&ElectronBindings::DidReceiveMemoryDump,
std::move(context), std::move(promise)));
return handle;
}
// static
v8::Local<v8::Value> ElectronBindings::GetBlinkMemoryInfo(
v8::Isolate* isolate) {
auto allocated = blink::ProcessHeap::TotalAllocatedObjectSize();
auto marked = blink::ProcessHeap::TotalMarkedObjectSize();
auto total = blink::ProcessHeap::TotalAllocatedSpace();
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("allocated", static_cast<double>(allocated >> 10));
dict.Set("marked", static_cast<double>(marked >> 10));
dict.Set("total", static_cast<double>(total >> 10));
return dict.GetHandle();
}
// static
void ElectronBindings::DidReceiveMemoryDump(
v8::Global<v8::Context> context,
util::Promise promise,
bool success,
std::unique_ptr<memory_instrumentation::GlobalMemoryDump> global_dump) {
v8::Isolate* isolate = promise.isolate();
mate::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
v8::MicrotasksScope script_scope(isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Context::Scope context_scope(
v8::Local<v8::Context>::New(isolate, context));
if (!success) {
promise.RejectWithErrorMessage("Failed to create memory dump");
return;
}
bool resolved = false;
for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump :
global_dump->process_dumps()) {
if (base::GetCurrentProcId() == dump.pid()) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
const auto& osdump = dump.os_dump();
#if defined(OS_LINUX) || defined(OS_WIN)
dict.Set("residentSet", osdump.resident_set_kb);
#endif
dict.Set("private", osdump.private_footprint_kb);
dict.Set("shared", osdump.shared_footprint_kb);
promise.Resolve(dict.GetHandle());
resolved = true;
break;
}
}
if (!resolved) {
promise.RejectWithErrorMessage(
R"(Failed to find current process memory details in memory dump)");
}
}
// static
v8::Local<v8::Value> ElectronBindings::GetCPUUsage(
base::ProcessMetrics* metrics,
v8::Isolate* isolate) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
int processor_count = base::SysInfo::NumberOfProcessors();
dict.Set("percentCPUUsage",
metrics->GetPlatformIndependentCPUUsage() / processor_count);
// NB: This will throw NOTIMPLEMENTED() on Windows
// For backwards compatibility, we'll return 0
#if !defined(OS_WIN)
dict.Set("idleWakeupsPerSecond", metrics->GetIdleWakeupsPerSecond());
#else
dict.Set("idleWakeupsPerSecond", 0);
#endif
return dict.GetHandle();
}
// static
v8::Local<v8::Value> ElectronBindings::GetIOCounters(v8::Isolate* isolate) {
auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
base::IoCounters io_counters;
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
if (metrics->GetIOCounters(&io_counters)) {
dict.Set("readOperationCount", io_counters.ReadOperationCount);
dict.Set("writeOperationCount", io_counters.WriteOperationCount);
dict.Set("otherOperationCount", io_counters.OtherOperationCount);
dict.Set("readTransferCount", io_counters.ReadTransferCount);
dict.Set("writeTransferCount", io_counters.WriteTransferCount);
dict.Set("otherTransferCount", io_counters.OtherTransferCount);
}
return dict.GetHandle();
}
// static
bool ElectronBindings::TakeHeapSnapshot(v8::Isolate* isolate,
const base::FilePath& file_path) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
base::File file(file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
return atom::TakeHeapSnapshot(isolate, &file);
}
} // namespace atom

View file

@ -0,0 +1,87 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_ELECTRON_BINDINGS_H_
#define ATOM_COMMON_API_ELECTRON_BINDINGS_H_
#include <list>
#include <memory>
#include "atom/common/promise_util.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/process_metrics.h"
#include "base/strings/string16.h"
#include "native_mate/arguments.h"
#include "uv.h" // NOLINT(build/include)
#include "v8/include/v8.h"
namespace mate {
class Dictionary;
}
namespace memory_instrumentation {
class GlobalMemoryDump;
}
namespace node {
class Environment;
}
namespace atom {
class ElectronBindings {
public:
explicit ElectronBindings(uv_loop_t* loop);
virtual ~ElectronBindings();
// Add process.electronBinding function, which behaves like process.binding
// but load native code from Electron instead.
void BindTo(v8::Isolate* isolate, v8::Local<v8::Object> process);
// Should be called when a node::Environment has been destroyed.
void EnvironmentDestroyed(node::Environment* env);
static void BindProcess(v8::Isolate* isolate,
mate::Dictionary* process,
base::ProcessMetrics* metrics);
static void Log(const base::string16& message);
static void Crash();
private:
static void Hang();
static v8::Local<v8::Value> GetHeapStatistics(v8::Isolate* isolate);
static v8::Local<v8::Value> GetCreationTime(v8::Isolate* isolate);
static v8::Local<v8::Value> GetSystemMemoryInfo(v8::Isolate* isolate,
mate::Arguments* args);
static v8::Local<v8::Promise> GetProcessMemoryInfo(v8::Isolate* isolate);
static v8::Local<v8::Value> GetBlinkMemoryInfo(v8::Isolate* isolate);
static v8::Local<v8::Value> GetCPUUsage(base::ProcessMetrics* metrics,
v8::Isolate* isolate);
static v8::Local<v8::Value> GetIOCounters(v8::Isolate* isolate);
static bool TakeHeapSnapshot(v8::Isolate* isolate,
const base::FilePath& file_path);
void ActivateUVLoop(v8::Isolate* isolate);
static void OnCallNextTick(uv_async_t* handle);
static void DidReceiveMemoryDump(
v8::Global<v8::Context> context,
util::Promise promise,
bool success,
std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump);
uv_async_t call_next_tick_async_;
std::list<node::Environment*> pending_next_ticks_;
std::unique_ptr<base::ProcessMetrics> metrics_;
DISALLOW_COPY_AND_ASSIGN(ElectronBindings);
};
} // namespace atom
#endif // ATOM_COMMON_API_ELECTRON_BINDINGS_H_

View file

@ -0,0 +1,38 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/api/locker.h"
#include "atom/common/node_includes.h"
namespace mate {
namespace internal {
v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
v8::Local<v8::Object> obj,
const char* method,
ValueVector* args) {
// Perform microtask checkpoint after running JavaScript.
v8::MicrotasksScope script_scope(isolate,
v8::MicrotasksScope::kRunMicrotasks);
// Use node::MakeCallback to call the callback, and it will also run pending
// tasks in Node.js.
v8::MaybeLocal<v8::Value> ret = node::MakeCallback(
isolate, obj, method, args->size(), &args->front(), {0, 0});
// If the JS function throws an exception (doesn't return a value) the result
// of MakeCallback will be empty and therefore ToLocal will be false, in this
// case we need to return "false" as that indicates that the event emitter did
// not handle the event
v8::Local<v8::Value> localRet;
if (ret.ToLocal(&localRet)) {
return localRet;
}
return v8::Boolean::New(isolate, false);
}
} // namespace internal
} // namespace mate

View file

@ -0,0 +1,69 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_EVENT_EMITTER_CALLER_H_
#define ATOM_COMMON_API_EVENT_EMITTER_CALLER_H_
#include <utility>
#include <vector>
#include "atom/common/native_mate_converters/string16_converter.h"
#include "native_mate/converter.h"
namespace mate {
namespace internal {
using ValueVector = std::vector<v8::Local<v8::Value>>;
v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
v8::Local<v8::Object> obj,
const char* method,
ValueVector* args);
} // namespace internal
// obj.emit.apply(obj, name, args...);
// The caller is responsible of allocating a HandleScope.
template <typename StringType>
v8::Local<v8::Value> EmitEvent(v8::Isolate* isolate,
v8::Local<v8::Object> obj,
const StringType& name,
const internal::ValueVector& args) {
internal::ValueVector concatenated_args = {StringToV8(isolate, name)};
concatenated_args.reserve(1 + args.size());
concatenated_args.insert(concatenated_args.end(), args.begin(), args.end());
return internal::CallMethodWithArgs(isolate, obj, "emit", &concatenated_args);
}
// obj.emit(name, args...);
// The caller is responsible of allocating a HandleScope.
template <typename StringType, typename... Args>
v8::Local<v8::Value> EmitEvent(v8::Isolate* isolate,
v8::Local<v8::Object> obj,
const StringType& name,
Args&&... args) {
internal::ValueVector converted_args = {
StringToV8(isolate, name),
ConvertToV8(isolate, std::forward<Args>(args))...,
};
return internal::CallMethodWithArgs(isolate, obj, "emit", &converted_args);
}
// obj.custom_emit(args...)
template <typename... Args>
v8::Local<v8::Value> CustomEmit(v8::Isolate* isolate,
v8::Local<v8::Object> object,
const char* custom_emit,
Args&&... args) {
internal::ValueVector converted_args = {
ConvertToV8(isolate, std::forward<Args>(args))...,
};
return internal::CallMethodWithArgs(isolate, object, custom_emit,
&converted_args);
}
} // namespace mate
#endif // ATOM_COMMON_API_EVENT_EMITTER_CALLER_H_

View file

@ -0,0 +1,71 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/node_includes.h"
#include "electron/buildflags/buildflags.h"
#include "native_mate/dictionary.h"
#include "printing/buildflags/buildflags.h"
namespace {
bool IsDesktopCapturerEnabled() {
return BUILDFLAG(ENABLE_DESKTOP_CAPTURER);
}
bool IsOffscreenRenderingEnabled() {
return BUILDFLAG(ENABLE_OSR);
}
bool IsPDFViewerEnabled() {
return BUILDFLAG(ENABLE_PDF_VIEWER);
}
bool IsRunAsNodeEnabled() {
return BUILDFLAG(ENABLE_RUN_AS_NODE);
}
bool IsFakeLocationProviderEnabled() {
return BUILDFLAG(OVERRIDE_LOCATION_PROVIDER);
}
bool IsViewApiEnabled() {
return BUILDFLAG(ENABLE_VIEW_API);
}
bool IsTtsEnabled() {
return BUILDFLAG(ENABLE_TTS);
}
bool IsPrintingEnabled() {
return BUILDFLAG(ENABLE_PRINTING);
}
bool IsComponentBuild() {
#if defined(COMPONENT_BUILD)
return true;
#else
return false;
#endif
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("isDesktopCapturerEnabled", &IsDesktopCapturerEnabled);
dict.SetMethod("isOffscreenRenderingEnabled", &IsOffscreenRenderingEnabled);
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
dict.SetMethod("isRunAsNodeEnabled", &IsRunAsNodeEnabled);
dict.SetMethod("isFakeLocationProviderEnabled",
&IsFakeLocationProviderEnabled);
dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled);
dict.SetMethod("isTtsEnabled", &IsTtsEnabled);
dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled);
dict.SetMethod("isComponentBuild", &IsComponentBuild);
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_features, Initialize)

View file

@ -0,0 +1,16 @@
// Copyright 2014 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.chromium file.
#include "atom/common/api/locker.h"
namespace mate {
Locker::Locker(v8::Isolate* isolate) {
if (IsBrowserProcess())
locker_.reset(new v8::Locker(isolate));
}
Locker::~Locker() {}
} // namespace mate

36
shell/common/api/locker.h Normal file
View file

@ -0,0 +1,36 @@
// Copyright 2014 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.chromium file.
#ifndef ATOM_COMMON_API_LOCKER_H_
#define ATOM_COMMON_API_LOCKER_H_
#include <memory>
#include "base/macros.h"
#include "v8/include/v8.h"
namespace mate {
// Only lock when lockers are used in current thread.
class Locker {
public:
explicit Locker(v8::Isolate* isolate);
~Locker();
// Returns whether current process is browser process, currently we detect it
// by checking whether current has used V8 Lock, but it might be a bad idea.
static inline bool IsBrowserProcess() { return v8::Locker::IsActive(); }
private:
void* operator new(size_t size);
void operator delete(void*, size_t);
std::unique_ptr<v8::Locker> locker_;
DISALLOW_COPY_AND_ASSIGN(Locker);
};
} // namespace mate
#endif // ATOM_COMMON_API_LOCKER_H_

View file

@ -0,0 +1,41 @@
// Copyright (c) 2013 GitHub, Inc.
// Copyright (c) 2012 Intel Corp. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/object_life_monitor.h"
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
namespace atom {
ObjectLifeMonitor::ObjectLifeMonitor(v8::Isolate* isolate,
v8::Local<v8::Object> target)
: target_(isolate, target), weak_ptr_factory_(this) {
target_.SetWeak(this, OnObjectGC, v8::WeakCallbackType::kParameter);
}
ObjectLifeMonitor::~ObjectLifeMonitor() {
if (target_.IsEmpty())
return;
target_.ClearWeak();
target_.Reset();
}
// static
void ObjectLifeMonitor::OnObjectGC(
const v8::WeakCallbackInfo<ObjectLifeMonitor>& data) {
ObjectLifeMonitor* self = data.GetParameter();
self->target_.Reset();
self->RunDestructor();
data.SetSecondPassCallback(Free);
}
// static
void ObjectLifeMonitor::Free(
const v8::WeakCallbackInfo<ObjectLifeMonitor>& data) {
delete data.GetParameter();
}
} // namespace atom

View file

@ -0,0 +1,34 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT 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/macros.h"
#include "base/memory/weak_ptr.h"
#include "v8/include/v8.h"
namespace atom {
class ObjectLifeMonitor {
protected:
ObjectLifeMonitor(v8::Isolate* isolate, v8::Local<v8::Object> target);
virtual ~ObjectLifeMonitor();
virtual void RunDestructor() = 0;
private:
static void OnObjectGC(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
static void Free(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
v8::Global<v8::Object> target_;
base::WeakPtrFactory<ObjectLifeMonitor> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ObjectLifeMonitor);
};
} // namespace atom
#endif // ATOM_COMMON_API_OBJECT_LIFE_MONITOR_H_

View file

@ -0,0 +1,59 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/remote_callback_freer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "electron/atom/common/api/api.mojom.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
namespace atom {
// static
void RemoteCallbackFreer::BindTo(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id,
content::WebContents* web_contents) {
new RemoteCallbackFreer(isolate, target, context_id, object_id, web_contents);
}
RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id,
content::WebContents* web_contents)
: ObjectLifeMonitor(isolate, target),
content::WebContentsObserver(web_contents),
context_id_(context_id),
object_id_(object_id) {}
RemoteCallbackFreer::~RemoteCallbackFreer() {}
void RemoteCallbackFreer::RunDestructor() {
auto* channel = "ELECTRON_RENDERER_RELEASE_CALLBACK";
base::ListValue args;
int32_t sender_id = 0;
args.AppendString(context_id_);
args.AppendInteger(object_id_);
auto* frame_host = web_contents()->GetMainFrame();
if (frame_host) {
mojom::ElectronRendererAssociatedPtr electron_ptr;
frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
mojo::MakeRequest(&electron_ptr));
electron_ptr->Message(true /* internal */, false /* send_to_all */, channel,
args.Clone(), sender_id);
}
Observe(nullptr);
}
void RemoteCallbackFreer::RenderViewDeleted(content::RenderViewHost*) {
delete this;
}
} // namespace atom

View file

@ -0,0 +1,46 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
#define ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
#include <string>
#include "atom/common/api/object_life_monitor.h"
#include "content/public/browser/web_contents_observer.h"
namespace atom {
class RemoteCallbackFreer : public ObjectLifeMonitor,
public content::WebContentsObserver {
public:
static void BindTo(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id,
content::WebContents* web_conents);
protected:
RemoteCallbackFreer(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id,
content::WebContents* web_conents);
~RemoteCallbackFreer() override;
void RunDestructor() override;
// content::WebContentsObserver:
void RenderViewDeleted(content::RenderViewHost*) override;
private:
std::string context_id_;
int object_id_;
DISALLOW_COPY_AND_ASSIGN(RemoteCallbackFreer);
};
} // namespace atom
#endif // ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_

View file

@ -0,0 +1,85 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/api/remote_object_freer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/renderer/render_frame.h"
#include "electron/atom/common/api/api.mojom.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/web/web_local_frame.h"
using blink::WebLocalFrame;
namespace atom {
namespace {
content::RenderFrame* GetCurrentRenderFrame() {
WebLocalFrame* frame = WebLocalFrame::FrameForCurrentContext();
if (!frame)
return nullptr;
return content::RenderFrame::FromWebFrame(frame);
}
} // namespace
// static
void RemoteObjectFreer::BindTo(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id) {
new RemoteObjectFreer(isolate, target, context_id, object_id);
}
// static
void RemoteObjectFreer::AddRef(const std::string& context_id, int object_id) {
ref_mapper_[context_id][object_id]++;
}
// static
std::map<std::string, std::map<int, int>> RemoteObjectFreer::ref_mapper_;
RemoteObjectFreer::RemoteObjectFreer(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id)
: ObjectLifeMonitor(isolate, target),
context_id_(context_id),
object_id_(object_id),
routing_id_(MSG_ROUTING_NONE) {
content::RenderFrame* render_frame = GetCurrentRenderFrame();
if (render_frame) {
routing_id_ = render_frame->GetRoutingID();
}
}
RemoteObjectFreer::~RemoteObjectFreer() {}
void RemoteObjectFreer::RunDestructor() {
content::RenderFrame* render_frame =
content::RenderFrame::FromRoutingID(routing_id_);
if (!render_frame)
return;
auto* channel = "ELECTRON_BROWSER_DEREFERENCE";
base::ListValue args;
args.AppendString(context_id_);
args.AppendInteger(object_id_);
args.AppendInteger(ref_mapper_[context_id_][object_id_]);
// Reset our local ref count in case we are in a GC race condition and will
// get more references in an inbound IPC message
ref_mapper_[context_id_].erase(object_id_);
if (ref_mapper_[context_id_].empty())
ref_mapper_.erase(context_id_);
mojom::ElectronBrowserAssociatedPtr electron_ptr;
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
mojo::MakeRequest(&electron_ptr));
electron_ptr->Message(true, channel, args.Clone());
}
} // namespace atom

View file

@ -0,0 +1,45 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
#define ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
#include <map>
#include <string>
#include "atom/common/api/object_life_monitor.h"
namespace atom {
class RemoteObjectFreer : public ObjectLifeMonitor {
public:
static void BindTo(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id);
static void AddRef(const std::string& context_id, int object_id);
protected:
RemoteObjectFreer(v8::Isolate* isolate,
v8::Local<v8::Object> target,
const std::string& context_id,
int object_id);
~RemoteObjectFreer() override;
void RunDestructor() override;
// { context_id => { object_id => ref_count }}
static std::map<std::string, std::map<int, int>> ref_mapper_;
private:
std::string context_id_;
int object_id_;
int routing_id_;
DISALLOW_COPY_AND_ASSIGN(RemoteObjectFreer);
};
} // namespace atom
#endif // ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_

View file

@ -0,0 +1,56 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/application_info.h"
#include "atom/browser/browser.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "chrome/common/chrome_version.h"
#include "content/public/common/user_agent.h"
#include "electron/electron_version.h"
namespace atom {
namespace {
base::NoDestructor<std::string> g_overridden_application_name;
base::NoDestructor<std::string> g_overridden_application_version;
} // namespace
// name
void OverrideApplicationName(const std::string& name) {
*g_overridden_application_name = name;
}
std::string GetOverriddenApplicationName() {
return *g_overridden_application_name;
}
// version
void OverrideApplicationVersion(const std::string& version) {
*g_overridden_application_version = version;
}
std::string GetOverriddenApplicationVersion() {
return *g_overridden_application_version;
}
std::string GetApplicationUserAgent() {
// Construct user agent string.
Browser* browser = Browser::Get();
std::string name, user_agent;
if (!base::RemoveChars(browser->GetName(), " ", &name))
name = browser->GetName();
if (name == ELECTRON_PRODUCT_NAME) {
user_agent = "Chrome/" CHROME_VERSION_STRING " " ELECTRON_PRODUCT_NAME
"/" ELECTRON_VERSION_STRING;
} else {
user_agent = base::StringPrintf(
"%s/%s Chrome/%s " ELECTRON_PRODUCT_NAME "/" ELECTRON_VERSION_STRING,
name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING);
}
return content::BuildUserAgentFromProduct(user_agent);
}
} // namespace atom

View file

@ -0,0 +1,37 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_APPLICATION_INFO_H_
#define ATOM_COMMON_APPLICATION_INFO_H_
#if defined(OS_WIN)
#include "atom/browser/win/scoped_hstring.h"
#include "base/strings/string16.h"
#endif
#include <string>
namespace atom {
void OverrideApplicationName(const std::string& name);
std::string GetOverriddenApplicationName();
void OverrideApplicationVersion(const std::string& version);
std::string GetOverriddenApplicationVersion();
std::string GetApplicationName();
std::string GetApplicationVersion();
// Returns the user agent of Electron.
std::string GetApplicationUserAgent();
#if defined(OS_WIN)
PCWSTR GetRawAppUserModelID();
bool GetAppUserModelID(ScopedHString* app_id);
void SetAppUserModelID(const base::string16& name);
bool IsRunningInDesktopBridge();
#endif
} // namespace atom
#endif // ATOM_COMMON_APPLICATION_INFO_H_

View file

@ -0,0 +1,79 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/application_info.h"
#include <gio/gdesktopappinfo.h>
#include <gio/gio.h>
#include <memory>
#include <string>
#include "atom/common/platform_util.h"
#include "base/environment.h"
#include "base/logging.h"
#include "chrome/browser/ui/libgtkui/gtk_util.h"
#include "electron/electron_version.h"
namespace {
GDesktopAppInfo* get_desktop_app_info() {
GDesktopAppInfo* ret = nullptr;
std::string desktop_id;
if (platform_util::GetDesktopName(&desktop_id))
ret = g_desktop_app_info_new(desktop_id.c_str());
return ret;
}
} // namespace
namespace atom {
std::string GetApplicationName() {
// attempt #1: the string set in app.setName()
std::string ret = GetOverriddenApplicationName();
// attempt #2: the 'Name' entry from .desktop file's [Desktop] section
if (ret.empty()) {
GDesktopAppInfo* info = get_desktop_app_info();
if (info != nullptr) {
char* str = g_desktop_app_info_get_string(info, "Name");
g_clear_object(&info);
if (str != nullptr)
ret = str;
g_clear_pointer(&str, g_free);
}
}
// attempt #3: Electron's name
if (ret.empty()) {
ret = ELECTRON_PRODUCT_NAME;
}
return ret;
}
std::string GetApplicationVersion() {
std::string ret;
// ensure ELECTRON_PRODUCT_NAME and GetApplicationVersion match up
if (GetApplicationName() == ELECTRON_PRODUCT_NAME)
ret = ELECTRON_VERSION_STRING;
// try to use the string set in app.setVersion()
if (ret.empty())
ret = GetOverriddenApplicationVersion();
// no known version number; return some safe fallback
if (ret.empty()) {
LOG(WARNING) << "No version found. Was app.setVersion() called?";
ret = "0.0";
}
return ret;
}
} // namespace atom

View file

@ -0,0 +1,36 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#import "atom/common/application_info.h"
#include <string>
#import "atom/common/mac/main_application_bundle.h"
#import "base/mac/foundation_util.h"
#import "base/strings/sys_string_conversions.h"
namespace atom {
namespace {
std::string ApplicationInfoDictionaryValue(NSString* key) {
return base::SysNSStringToUTF8(
[MainApplicationBundle().infoDictionary objectForKey:key]);
}
std::string ApplicationInfoDictionaryValue(CFStringRef key) {
return ApplicationInfoDictionaryValue(base::mac::CFToNSCast(key));
}
} // namespace
std::string GetApplicationName() {
return ApplicationInfoDictionaryValue(kCFBundleNameKey);
}
std::string GetApplicationVersion() {
return ApplicationInfoDictionaryValue(@"CFBundleShortVersionString");
}
} // namespace atom

View file

@ -0,0 +1,113 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/application_info.h"
#include <windows.h> // windows.h must be included first
#include <VersionHelpers.h>
#include <appmodel.h>
#include <shlobj.h>
#include <memory>
#include "atom/browser/win/scoped_hstring.h"
#include "base/file_version_info.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
namespace atom {
namespace {
base::string16 g_app_user_model_id;
}
const wchar_t kAppUserModelIDFormat[] = L"electron.app.$1";
std::string GetApplicationName() {
auto* module = GetModuleHandle(nullptr);
std::unique_ptr<FileVersionInfo> info(
FileVersionInfo::CreateFileVersionInfoForModule(module));
return base::UTF16ToUTF8(info->product_name());
}
std::string GetApplicationVersion() {
auto* module = GetModuleHandle(nullptr);
std::unique_ptr<FileVersionInfo> info(
FileVersionInfo::CreateFileVersionInfoForModule(module));
return base::UTF16ToUTF8(info->product_version());
}
void SetAppUserModelID(const base::string16& name) {
g_app_user_model_id = name;
SetCurrentProcessExplicitAppUserModelID(g_app_user_model_id.c_str());
}
PCWSTR GetRawAppUserModelID() {
if (g_app_user_model_id.empty()) {
PWSTR current_app_id;
if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&current_app_id))) {
g_app_user_model_id = current_app_id;
} else {
std::string name = GetApplicationName();
base::string16 generated_app_id = base::ReplaceStringPlaceholders(
kAppUserModelIDFormat, base::UTF8ToUTF16(name), nullptr);
SetAppUserModelID(generated_app_id);
}
CoTaskMemFree(current_app_id);
}
return g_app_user_model_id.c_str();
}
bool GetAppUserModelID(ScopedHString* app_id) {
app_id->Reset(GetRawAppUserModelID());
return app_id->success();
}
bool IsRunningInDesktopBridgeImpl() {
if (IsWindows8OrGreater()) {
// GetPackageFamilyName is not available on Windows 7
using GetPackageFamilyNameFuncPtr = decltype(&GetPackageFamilyName);
static bool initialize_get_package_family_name = true;
static GetPackageFamilyNameFuncPtr get_package_family_namePtr = NULL;
if (initialize_get_package_family_name) {
initialize_get_package_family_name = false;
HMODULE kernel32_base = GetModuleHandle(L"Kernel32.dll");
if (!kernel32_base) {
NOTREACHED() << " " << __FUNCTION__ << "(): Can't open Kernel32.dll";
return false;
}
get_package_family_namePtr =
reinterpret_cast<GetPackageFamilyNameFuncPtr>(
GetProcAddress(kernel32_base, "GetPackageFamilyName"));
if (!get_package_family_namePtr) {
return false;
}
}
UINT32 length;
wchar_t packageFamilyName[PACKAGE_FAMILY_NAME_MAX_LENGTH + 1];
HANDLE proc = GetCurrentProcess();
LONG result =
(*get_package_family_namePtr)(proc, &length, packageFamilyName);
return result == ERROR_SUCCESS;
} else {
return false;
}
}
bool IsRunningInDesktopBridge() {
static bool result = IsRunningInDesktopBridgeImpl();
return result;
}
} // namespace atom

View file

@ -0,0 +1,315 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/asar/archive.h"
#include <string>
#include <utility>
#include <vector>
#include "atom/common/asar/scoped_temporary_file.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/pickle.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/post_task.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#if defined(OS_WIN)
#include <io.h>
#endif
namespace asar {
namespace {
#if defined(OS_WIN)
const char kSeparators[] = "\\/";
#else
const char kSeparators[] = "/";
#endif
bool GetNodeFromPath(std::string path,
const base::DictionaryValue* root,
const base::DictionaryValue** out);
// Gets the "files" from "dir".
bool GetFilesNode(const base::DictionaryValue* root,
const base::DictionaryValue* dir,
const base::DictionaryValue** out) {
// Test for symbol linked directory.
std::string link;
if (dir->GetStringWithoutPathExpansion("link", &link)) {
const base::DictionaryValue* linked_node = nullptr;
if (!GetNodeFromPath(link, root, &linked_node))
return false;
dir = linked_node;
}
return dir->GetDictionaryWithoutPathExpansion("files", out);
}
// Gets sub-file "name" from "dir".
bool GetChildNode(const base::DictionaryValue* root,
const std::string& name,
const base::DictionaryValue* dir,
const base::DictionaryValue** out) {
if (name == "") {
*out = root;
return true;
}
const base::DictionaryValue* files = nullptr;
return GetFilesNode(root, dir, &files) &&
files->GetDictionaryWithoutPathExpansion(name, out);
}
// Gets the node of "path" from "root".
bool GetNodeFromPath(std::string path,
const base::DictionaryValue* root,
const base::DictionaryValue** out) {
if (path == "") {
*out = root;
return true;
}
const base::DictionaryValue* dir = root;
for (size_t delimiter_position = path.find_first_of(kSeparators);
delimiter_position != std::string::npos;
delimiter_position = path.find_first_of(kSeparators)) {
const base::DictionaryValue* child = nullptr;
if (!GetChildNode(root, path.substr(0, delimiter_position), dir, &child))
return false;
dir = child;
path.erase(0, delimiter_position + 1);
}
return GetChildNode(root, path, dir, out);
}
bool FillFileInfoWithNode(Archive::FileInfo* info,
uint32_t header_size,
const base::DictionaryValue* node) {
int size;
if (!node->GetInteger("size", &size))
return false;
info->size = static_cast<uint32_t>(size);
if (node->GetBoolean("unpacked", &info->unpacked) && info->unpacked)
return true;
std::string offset;
if (!node->GetString("offset", &offset))
return false;
if (!base::StringToUint64(offset, &info->offset))
return false;
info->offset += header_size;
node->GetBoolean("executable", &info->executable);
return true;
}
} // namespace
Archive::Archive(const base::FilePath& path)
: path_(path), file_(base::File::FILE_OK) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
#if defined(OS_WIN)
fd_ = _open_osfhandle(reinterpret_cast<intptr_t>(file_.GetPlatformFile()), 0);
#elif defined(OS_POSIX)
fd_ = file_.GetPlatformFile();
#endif
}
Archive::~Archive() {
#if defined(OS_WIN)
if (fd_ != -1) {
_close(fd_);
// Don't close the handle since we already closed the fd.
file_.TakePlatformFile();
}
#endif
base::ThreadRestrictions::ScopedAllowIO allow_io;
file_.Close();
}
bool Archive::Init() {
if (!file_.IsValid()) {
if (file_.error_details() != base::File::FILE_ERROR_NOT_FOUND) {
LOG(WARNING) << "Opening " << path_.value() << ": "
<< base::File::ErrorToString(file_.error_details());
}
return false;
}
std::vector<char> buf;
int len;
buf.resize(8);
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
len = file_.ReadAtCurrentPos(buf.data(), buf.size());
}
if (len != static_cast<int>(buf.size())) {
PLOG(ERROR) << "Failed to read header size from " << path_.value();
return false;
}
uint32_t size;
if (!base::PickleIterator(base::Pickle(buf.data(), buf.size()))
.ReadUInt32(&size)) {
LOG(ERROR) << "Failed to parse header size from " << path_.value();
return false;
}
buf.resize(size);
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
len = file_.ReadAtCurrentPos(buf.data(), buf.size());
}
if (len != static_cast<int>(buf.size())) {
PLOG(ERROR) << "Failed to read header from " << path_.value();
return false;
}
std::string header;
if (!base::PickleIterator(base::Pickle(buf.data(), buf.size()))
.ReadString(&header)) {
LOG(ERROR) << "Failed to parse header from " << path_.value();
return false;
}
base::Optional<base::Value> value = base::JSONReader::Read(header);
if (!value || !value->is_dict()) {
LOG(ERROR) << "Failed to parse header";
return false;
}
header_size_ = 8 + size;
header_ = base::DictionaryValue::From(
std::make_unique<base::Value>(value->Clone()));
return true;
}
bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) {
if (!header_)
return false;
const base::DictionaryValue* node;
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
return false;
std::string link;
if (node->GetString("link", &link))
return GetFileInfo(base::FilePath::FromUTF8Unsafe(link), info);
return FillFileInfoWithNode(info, header_size_, node);
}
bool Archive::Stat(const base::FilePath& path, Stats* stats) {
if (!header_)
return false;
const base::DictionaryValue* node;
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
return false;
if (node->FindKey("link")) {
stats->is_file = false;
stats->is_link = true;
return true;
}
if (node->FindKey("files")) {
stats->is_file = false;
stats->is_directory = true;
return true;
}
return FillFileInfoWithNode(stats, header_size_, node);
}
bool Archive::Readdir(const base::FilePath& path,
std::vector<base::FilePath>* list) {
if (!header_)
return false;
const base::DictionaryValue* node;
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
return false;
const base::DictionaryValue* files;
if (!GetFilesNode(header_.get(), node, &files))
return false;
base::DictionaryValue::Iterator iter(*files);
while (!iter.IsAtEnd()) {
list->push_back(base::FilePath::FromUTF8Unsafe(iter.key()));
iter.Advance();
}
return true;
}
bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) {
if (!header_)
return false;
const base::DictionaryValue* node;
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
return false;
std::string link;
if (node->GetString("link", &link)) {
*realpath = base::FilePath::FromUTF8Unsafe(link);
return true;
}
*realpath = path;
return true;
}
bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
auto it = external_files_.find(path.value());
if (it != external_files_.end()) {
*out = it->second->path();
return true;
}
FileInfo info;
if (!GetFileInfo(path, &info))
return false;
if (info.unpacked) {
*out = path_.AddExtension(FILE_PATH_LITERAL("unpacked")).Append(path);
return true;
}
auto temp_file = std::make_unique<ScopedTemporaryFile>();
base::FilePath::StringType ext = path.Extension();
if (!temp_file->InitFromFile(&file_, ext, info.offset, info.size))
return false;
#if defined(OS_POSIX)
if (info.executable) {
// chmod a+x temp_file;
base::SetPosixFilePermissions(temp_file->path(), 0755);
}
#endif
*out = temp_file->path();
external_files_[path.value()] = std::move(temp_file);
return true;
}
int Archive::GetFD() const {
return fd_;
}
} // namespace asar

View file

@ -0,0 +1,87 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_ASAR_ARCHIVE_H_
#define ATOM_COMMON_ASAR_ARCHIVE_H_
#include <memory>
#include <unordered_map>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_path.h"
namespace base {
class DictionaryValue;
}
namespace asar {
class ScopedTemporaryFile;
// This class represents an asar package, and provides methods to read
// information from it.
class Archive {
public:
struct FileInfo {
FileInfo() : unpacked(false), executable(false), size(0), offset(0) {}
bool unpacked;
bool executable;
uint32_t size;
uint64_t offset;
};
struct Stats : public FileInfo {
Stats() : is_file(true), is_directory(false), is_link(false) {}
bool is_file;
bool is_directory;
bool is_link;
};
explicit Archive(const base::FilePath& path);
virtual ~Archive();
// Read and parse the header.
bool Init();
// Get the info of a file.
bool GetFileInfo(const base::FilePath& path, FileInfo* info);
// Fs.stat(path).
bool Stat(const base::FilePath& path, Stats* stats);
// Fs.readdir(path).
bool Readdir(const base::FilePath& path, std::vector<base::FilePath>* files);
// Fs.realpath(path).
bool Realpath(const base::FilePath& path, base::FilePath* realpath);
// Copy the file into a temporary file, and return the new path.
// For unpacked file, this method will return its real path.
bool CopyFileOut(const base::FilePath& path, base::FilePath* out);
// Returns the file's fd.
int GetFD() const;
base::FilePath path() const { return path_; }
base::DictionaryValue* header() const { return header_.get(); }
private:
base::FilePath path_;
base::File file_;
int fd_ = -1;
uint32_t header_size_ = 0;
std::unique_ptr<base::DictionaryValue> header_;
// Cached external temporary files.
std::unordered_map<base::FilePath::StringType,
std::unique_ptr<ScopedTemporaryFile>>
external_files_;
DISALLOW_COPY_AND_ASSIGN(Archive);
};
} // namespace asar
#endif // ATOM_COMMON_ASAR_ARCHIVE_H_

View file

@ -0,0 +1,100 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/asar/asar_util.h"
#include <map>
#include <string>
#include "atom/common/asar/archive.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/stl_util.h"
#include "base/threading/thread_local.h"
namespace asar {
namespace {
// The global instance of ArchiveMap, will be destroyed on exit.
typedef std::map<base::FilePath, std::shared_ptr<Archive>> ArchiveMap;
base::LazyInstance<base::ThreadLocalPointer<ArchiveMap>>::Leaky
g_archive_map_tls = LAZY_INSTANCE_INITIALIZER;
const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar");
} // namespace
std::shared_ptr<Archive> GetOrCreateAsarArchive(const base::FilePath& path) {
if (!g_archive_map_tls.Pointer()->Get())
g_archive_map_tls.Pointer()->Set(new ArchiveMap);
ArchiveMap& archive_map = *g_archive_map_tls.Pointer()->Get();
if (!ContainsKey(archive_map, path)) {
std::shared_ptr<Archive> archive(new Archive(path));
if (!archive->Init())
return nullptr;
archive_map[path] = archive;
}
return archive_map[path];
}
void ClearArchives() {
if (g_archive_map_tls.Pointer()->Get())
delete g_archive_map_tls.Pointer()->Get();
}
bool GetAsarArchivePath(const base::FilePath& full_path,
base::FilePath* asar_path,
base::FilePath* relative_path) {
base::FilePath iter = full_path;
while (true) {
base::FilePath dirname = iter.DirName();
if (iter.MatchesExtension(kAsarExtension))
break;
else if (iter == dirname)
return false;
iter = dirname;
}
base::FilePath tail;
if (!iter.AppendRelativePath(full_path, &tail))
return false;
*asar_path = iter;
*relative_path = tail;
return true;
}
bool ReadFileToString(const base::FilePath& path, std::string* contents) {
base::FilePath asar_path, relative_path;
if (!GetAsarArchivePath(path, &asar_path, &relative_path))
return base::ReadFileToString(path, contents);
std::shared_ptr<Archive> archive = GetOrCreateAsarArchive(asar_path);
if (!archive)
return false;
Archive::FileInfo info;
if (!archive->GetFileInfo(relative_path, &info))
return false;
if (info.unpacked) {
base::FilePath real_path;
// For unpacked file it will return the real path instead of doing the copy.
archive->CopyFileOut(relative_path, &real_path);
return base::ReadFileToString(real_path, contents);
}
base::File src(asar_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!src.IsValid())
return false;
contents->resize(info.size);
return static_cast<int>(info.size) ==
src.Read(info.offset, const_cast<char*>(contents->data()),
contents->size());
}
} // namespace asar

View file

@ -0,0 +1,35 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_ASAR_ASAR_UTIL_H_
#define ATOM_COMMON_ASAR_ASAR_UTIL_H_
#include <memory>
#include <string>
namespace base {
class FilePath;
}
namespace asar {
class Archive;
// Gets or creates a new Archive from the path.
std::shared_ptr<Archive> GetOrCreateAsarArchive(const base::FilePath& path);
// Destroy cached Archive objects.
void ClearArchives();
// Separates the path to Archive out.
bool GetAsarArchivePath(const base::FilePath& full_path,
base::FilePath* asar_path,
base::FilePath* relative_path);
// Same with base::ReadFileToString but supports asar Archive.
bool ReadFileToString(const base::FilePath& path, std::string* contents);
} // namespace asar
#endif // ATOM_COMMON_ASAR_ASAR_UTIL_H_

View file

@ -0,0 +1,74 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/asar/scoped_temporary_file.h"
#include <vector>
#include "base/files/file_util.h"
#include "base/threading/thread_restrictions.h"
namespace asar {
ScopedTemporaryFile::ScopedTemporaryFile() {}
ScopedTemporaryFile::~ScopedTemporaryFile() {
if (!path_.empty()) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
// On Windows it is very likely the file is already in use (because it is
// mostly used for Node native modules), so deleting it now will halt the
// program.
#if defined(OS_WIN)
base::DeleteFileAfterReboot(path_);
#else
base::DeleteFile(path_, false);
#endif
}
}
bool ScopedTemporaryFile::Init(const base::FilePath::StringType& ext) {
if (!path_.empty())
return true;
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!base::CreateTemporaryFile(&path_))
return false;
#if defined(OS_WIN)
// Keep the original extension.
if (!ext.empty()) {
base::FilePath new_path = path_.AddExtension(ext);
if (!base::Move(path_, new_path))
return false;
path_ = new_path;
}
#endif
return true;
}
bool ScopedTemporaryFile::InitFromFile(base::File* src,
const base::FilePath::StringType& ext,
uint64_t offset,
uint64_t size) {
if (!src->IsValid())
return false;
if (!Init(ext))
return false;
std::vector<char> buf(size);
int len = src->Read(offset, buf.data(), buf.size());
if (len != static_cast<int>(size))
return false;
base::File dest(path_, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
if (!dest.IsValid())
return false;
return dest.WriteAtCurrentPos(buf.data(), buf.size()) ==
static_cast<int>(size);
}
} // namespace asar

View file

@ -0,0 +1,44 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
#define ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
#include "base/files/file_path.h"
namespace base {
class File;
}
namespace asar {
// An object representing a temporary file that should be cleaned up when this
// object goes out of scope. Note that since deletion occurs during the
// destructor, no further error handling is possible if the directory fails to
// be deleted. As a result, deletion is not guaranteed by this class.
class ScopedTemporaryFile {
public:
ScopedTemporaryFile();
virtual ~ScopedTemporaryFile();
// Init an empty temporary file with a certain extension.
bool Init(const base::FilePath::StringType& ext);
// Init an temporary file and fill it with content of |path|.
bool InitFromFile(base::File* src,
const base::FilePath::StringType& ext,
uint64_t offset,
uint64_t size);
base::FilePath path() const { return path_; }
private:
base::FilePath path_;
DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
};
} // namespace asar
#endif // ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_

View file

@ -0,0 +1,36 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/atom_command_line.h"
#include "base/command_line.h"
#include "uv.h" // NOLINT(build/include)
namespace atom {
// static
base::CommandLine::StringVector AtomCommandLine::argv_;
// static
void AtomCommandLine::Init(int argc, base::CommandLine::CharType** argv) {
DCHECK(argv_.empty());
// NOTE: uv_setup_args does nothing on Windows, so we don't need to call it.
// Otherwise we'd have to convert the arguments from UTF16.
#if !defined(OS_WIN)
// Hack around with the argv pointer. Used for process.title = "blah"
argv = uv_setup_args(argc, argv);
#endif
argv_.assign(argv, argv + argc);
}
#if defined(OS_LINUX)
// static
void AtomCommandLine::InitializeFromCommandLine() {
argv_ = base::CommandLine::ForCurrentProcess()->argv();
}
#endif
} // namespace atom

View file

@ -0,0 +1,38 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_ATOM_COMMAND_LINE_H_
#define ATOM_COMMON_ATOM_COMMAND_LINE_H_
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "build/build_config.h"
namespace atom {
// Singleton to remember the original "argc" and "argv".
class AtomCommandLine {
public:
static const base::CommandLine::StringVector& argv() { return argv_; }
static void Init(int argc, base::CommandLine::CharType** argv);
#if defined(OS_LINUX)
// On Linux the command line has to be read from base::CommandLine since
// it is using zygote.
static void InitializeFromCommandLine();
#endif
private:
static base::CommandLine::StringVector argv_;
DISALLOW_IMPLICIT_CONSTRUCTORS(AtomCommandLine);
};
} // namespace atom
#endif // ATOM_COMMON_ATOM_COMMAND_LINE_H_

View file

@ -0,0 +1,47 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/atom_constants.h"
namespace atom {
const char kBrowserForward[] = "browser-forward";
const char kBrowserBackward[] = "browser-backward";
const char kCORSHeader[] = "Access-Control-Allow-Origin: *";
const char kSHA1Certificate[] = "SHA-1 Certificate";
const char kSHA1MajorDescription[] =
"The certificate for this site expires in 2017 or later, "
"and the certificate chain contains a certificate signed using SHA-1.";
const char kSHA1MinorDescription[] =
"The certificate for this site expires in 2016, "
"and the certificate chain contains a certificate signed using SHA-1.";
const char kCertificateError[] = "Certificate Error";
const char kValidCertificate[] = "Valid Certificate";
const char kValidCertificateDescription[] =
"The connection to this site is using a valid, trusted server certificate.";
const char kSecureProtocol[] = "Secure TLS connection";
const char kSecureProtocolDescription[] =
"The connection to this site is using a strong protocol version "
"and cipher suite.";
#if defined(OS_WIN)
const char kCrashpadPipeName[] = "ELECTRON_CRASHPAD_PIPE_NAME";
#endif
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
const char kRunAsNode[] = "ELECTRON_RUN_AS_NODE";
#endif
#if BUILDFLAG(ENABLE_PDF_VIEWER)
const char kPdfPluginMimeType[] = "application/x-google-chrome-pdf";
const char kPdfPluginPath[] = "chrome://pdf-viewer/";
const char kPdfPluginSrc[] = "src";
const char kPdfViewerUIOrigin[] = "chrome://pdf-viewer/";
const char kPdfViewerUIHost[] = "pdf-viewer";
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
} // namespace atom

View file

@ -0,0 +1,52 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_ATOM_CONSTANTS_H_
#define ATOM_COMMON_ATOM_CONSTANTS_H_
#include "build/build_config.h"
#include "electron/buildflags/buildflags.h"
namespace atom {
// The app-command in NativeWindow.
extern const char kBrowserForward[];
extern const char kBrowserBackward[];
// Header to ignore CORS.
extern const char kCORSHeader[];
// Strings describing Chrome security policy for DevTools security panel.
extern const char kSHA1Certificate[];
extern const char kSHA1MajorDescription[];
extern const char kSHA1MinorDescription[];
extern const char kCertificateError[];
extern const char kValidCertificate[];
extern const char kValidCertificateDescription[];
extern const char kSecureProtocol[];
extern const char kSecureProtocolDescription[];
#if defined(OS_WIN)
// Crashpad pipe name.
extern const char kCrashpadPipeName[];
#endif
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
extern const char kRunAsNode[];
#endif
#if BUILDFLAG(ENABLE_PDF_VIEWER)
// The MIME type used for the PDF plugin.
extern const char kPdfPluginMimeType[];
extern const char kPdfPluginPath[];
extern const char kPdfPluginSrc[];
// Constants for PDF viewer webui.
extern const char kPdfViewerUIOrigin[];
extern const char kPdfViewerUIHost[];
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
} // namespace atom
#endif // ATOM_COMMON_ATOM_CONSTANTS_H_

View file

@ -0,0 +1,54 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/color_util.h"
#include <vector>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
namespace atom {
SkColor ParseHexColor(const std::string& color_string) {
// Check the string for incorrect formatting.
if (color_string.empty() || color_string[0] != '#')
return SK_ColorWHITE;
// Prepend FF if alpha channel is not specified.
std::string source = color_string.substr(1);
if (source.size() == 3)
source.insert(0, "F");
else if (source.size() == 6)
source.insert(0, "FF");
// Convert the string from #FFF format to #FFFFFF format.
std::string formatted_color;
if (source.size() == 4) {
for (size_t i = 0; i < 4; ++i) {
formatted_color += source[i];
formatted_color += source[i];
}
} else if (source.size() == 8) {
formatted_color = source;
} else {
return SK_ColorWHITE;
}
// Convert the string to an integer and make sure it is in the correct value
// range.
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(formatted_color, &bytes))
return SK_ColorWHITE;
return SkColorSetARGB(bytes[0], bytes[1], bytes[2], bytes[3]);
}
std::string ToRGBHex(SkColor color) {
return base::StringPrintf("#%02X%02X%02X", SkColorGetR(color),
SkColorGetG(color), SkColorGetB(color));
}
} // namespace atom

22
shell/common/color_util.h Normal file
View file

@ -0,0 +1,22 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_COLOR_UTIL_H_
#define ATOM_COMMON_COLOR_UTIL_H_
#include <string>
#include "third_party/skia/include/core/SkColor.h"
namespace atom {
// Parse hex color like "#FFF" or "#EFEFEF"
SkColor ParseHexColor(const std::string& name);
// Convert color to RGB hex value like "#ABCDEF"
std::string ToRGBHex(SkColor color);
} // namespace atom
#endif // ATOM_COMMON_COLOR_UTIL_H_

View file

@ -0,0 +1,153 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/crash_reporter/crash_reporter.h"
#include <memory>
#include "atom/browser/browser.h"
#include "atom/common/atom_constants.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/map_converter.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/common/content_switches.h"
#include "electron/electron_version.h"
namespace crash_reporter {
const char kCrashpadProcess[] = "crash-handler";
const char kCrashesDirectoryKey[] = "crashes-directory";
CrashReporter::CrashReporter() {
#if BUILDFLAG(ENABLE_RUN_AS_NODE)
bool run_as_node = base::Environment::Create()->HasVar(atom::kRunAsNode);
#else
bool run_as_node = false;
#endif
if (run_as_node) {
process_type_ = "node";
} else {
auto* cmd = base::CommandLine::ForCurrentProcess();
process_type_ = cmd->GetSwitchValueASCII(switches::kProcessType);
}
// process_type_ will be empty for browser process
}
CrashReporter::~CrashReporter() {}
bool CrashReporter::IsInitialized() {
return is_initialized_;
}
void CrashReporter::Start(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
const StringMap& extra_parameters) {
is_initialized_ = true;
SetUploadParameters(extra_parameters);
Init(product_name, company_name, submit_url, crashes_dir, upload_to_server,
skip_system_crash_handler);
}
void CrashReporter::SetUploadParameters(const StringMap& parameters) {
upload_parameters_ = parameters;
upload_parameters_["process_type"] =
process_type_.empty() ? "browser" : process_type_;
upload_parameters_["prod"] = ELECTRON_PRODUCT_NAME;
upload_parameters_["ver"] = ELECTRON_VERSION_STRING;
// Setting platform dependent parameters.
SetUploadParameters();
}
void CrashReporter::SetUploadToServer(const bool upload_to_server) {}
bool CrashReporter::GetUploadToServer() {
return true;
}
std::vector<CrashReporter::UploadReportResult>
CrashReporter::GetUploadedReports(const base::FilePath& crashes_dir) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string file_content;
std::vector<CrashReporter::UploadReportResult> result;
base::FilePath uploads_path =
crashes_dir.Append(FILE_PATH_LITERAL("uploads.log"));
if (base::ReadFileToString(uploads_path, &file_content)) {
std::vector<std::string> reports = base::SplitString(
file_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const std::string& report : reports) {
std::vector<std::string> report_item = base::SplitString(
report, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
int report_time = 0;
if (report_item.size() >= 2 &&
base::StringToInt(report_item[0], &report_time)) {
result.push_back(
CrashReporter::UploadReportResult(report_time, report_item[1]));
}
}
}
return result;
}
void CrashReporter::Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool auto_submit,
bool skip_system_crash_handler) {}
void CrashReporter::SetUploadParameters() {}
void CrashReporter::AddExtraParameter(const std::string& key,
const std::string& value) {}
void CrashReporter::RemoveExtraParameter(const std::string& key) {}
std::map<std::string, std::string> CrashReporter::GetParameters() const {
return upload_parameters_;
}
#if defined(OS_MACOSX) && defined(MAS_BUILD)
// static
CrashReporter* CrashReporter::GetInstance() {
static CrashReporter crash_reporter;
return &crash_reporter;
}
#endif
void CrashReporter::StartInstance(const mate::Dictionary& options) {
auto* reporter = GetInstance();
if (!reporter)
return;
std::string product_name;
options.Get("productName", &product_name);
std::string company_name;
options.Get("companyName", &company_name);
std::string submit_url;
options.Get("submitURL", &submit_url);
base::FilePath crashes_dir;
options.Get("crashesDirectory", &crashes_dir);
StringMap extra_parameters;
options.Get("extra", &extra_parameters);
extra_parameters["_productName"] = product_name;
extra_parameters["_companyName"] = company_name;
reporter->Start(product_name, company_name, submit_url, crashes_dir, true,
false, extra_parameters);
}
} // namespace crash_reporter

View file

@ -0,0 +1,73 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT 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 <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "native_mate/dictionary.h"
namespace crash_reporter {
extern const char kCrashpadProcess[];
extern const char kCrashesDirectoryKey[];
class CrashReporter {
public:
typedef std::map<std::string, std::string> StringMap;
typedef std::pair<int, std::string> UploadReportResult; // upload-date, id
static CrashReporter* GetInstance();
static void StartInstance(const mate::Dictionary& options);
bool IsInitialized();
void Start(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler,
const StringMap& extra_parameters);
virtual std::vector<CrashReporter::UploadReportResult> GetUploadedReports(
const base::FilePath& crashes_dir);
virtual void SetUploadToServer(bool upload_to_server);
virtual bool GetUploadToServer();
virtual void AddExtraParameter(const std::string& key,
const std::string& value);
virtual void RemoveExtraParameter(const std::string& key);
virtual std::map<std::string, std::string> GetParameters() const;
protected:
CrashReporter();
virtual ~CrashReporter();
virtual void Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler);
virtual void SetUploadParameters();
StringMap upload_parameters_;
std::string process_type_;
private:
bool is_initialized_ = false;
void SetUploadParameters(const StringMap& parameters);
DISALLOW_COPY_AND_ASSIGN(CrashReporter);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_

View file

@ -0,0 +1,118 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/crash_reporter/crash_reporter_crashpad.h"
#include <algorithm>
#include <memory>
#include "base/files/file_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "third_party/crashpad/crashpad/client/settings.h"
namespace crash_reporter {
CrashReporterCrashpad::CrashReporterCrashpad() {}
CrashReporterCrashpad::~CrashReporterCrashpad() {}
bool CrashReporterCrashpad::GetUploadToServer() {
bool enabled = true;
if (database_) {
database_->GetSettings()->GetUploadsEnabled(&enabled);
}
return enabled;
}
void CrashReporterCrashpad::SetUploadToServer(const bool upload_to_server) {
if (database_) {
database_->GetSettings()->SetUploadsEnabled(upload_to_server);
}
}
void CrashReporterCrashpad::SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value) {
simple_string_dictionary_->SetKeyValue(key.data(), value.data());
}
void CrashReporterCrashpad::SetInitialCrashKeyValues() {
for (const auto& upload_parameter : upload_parameters_)
SetCrashKeyValue(upload_parameter.first, upload_parameter.second);
}
void CrashReporterCrashpad::AddExtraParameter(const std::string& key,
const std::string& value) {
if (simple_string_dictionary_) {
SetCrashKeyValue(key, value);
} else {
upload_parameters_[key] = value;
}
}
void CrashReporterCrashpad::RemoveExtraParameter(const std::string& key) {
if (simple_string_dictionary_)
simple_string_dictionary_->RemoveKey(key.data());
else
upload_parameters_.erase(key);
}
std::map<std::string, std::string> CrashReporterCrashpad::GetParameters()
const {
if (simple_string_dictionary_) {
std::map<std::string, std::string> ret;
crashpad::SimpleStringDictionary::Iterator iter(*simple_string_dictionary_);
for (;;) {
auto* const entry = iter.Next();
if (!entry)
break;
ret[entry->key] = entry->value;
}
return ret;
}
return upload_parameters_;
}
std::vector<CrashReporter::UploadReportResult>
CrashReporterCrashpad::GetUploadedReports(const base::FilePath& crashes_dir) {
std::vector<CrashReporter::UploadReportResult> uploaded_reports;
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!base::PathExists(crashes_dir)) {
return uploaded_reports;
}
}
// Load crashpad database.
std::unique_ptr<crashpad::CrashReportDatabase> database =
crashpad::CrashReportDatabase::Initialize(crashes_dir);
DCHECK(database);
std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
crashpad::CrashReportDatabase::OperationStatus status =
database->GetCompletedReports(&completed_reports);
if (status != crashpad::CrashReportDatabase::kNoError) {
return uploaded_reports;
}
for (const crashpad::CrashReportDatabase::Report& completed_report :
completed_reports) {
if (completed_report.uploaded) {
uploaded_reports.push_back(
UploadReportResult(static_cast<int>(completed_report.creation_time),
completed_report.id));
}
}
auto sort_by_time = [](const UploadReportResult& a,
const UploadReportResult& b) {
return a.first >= b.first;
};
std::sort(uploaded_reports.begin(), uploaded_reports.end(), sort_by_time);
return uploaded_reports;
}
} // namespace crash_reporter

View file

@ -0,0 +1,50 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "atom/common/crash_reporter/crash_reporter.h"
#include "base/compiler_specific.h"
#include "base/strings/string_piece.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
namespace crash_reporter {
class CrashReporterCrashpad : public CrashReporter {
public:
void SetUploadToServer(bool upload_to_server) override;
bool GetUploadToServer() override;
void AddExtraParameter(const std::string& key,
const std::string& value) override;
void RemoveExtraParameter(const std::string& key) override;
std::map<std::string, std::string> GetParameters() const override;
protected:
CrashReporterCrashpad();
~CrashReporterCrashpad() override;
void SetUploadsEnabled(bool enable_uploads);
void SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value);
void SetInitialCrashKeyValues();
std::vector<UploadReportResult> GetUploadedReports(
const base::FilePath& crashes_dir) override;
std::unique_ptr<crashpad::SimpleStringDictionary> simple_string_dictionary_;
std::unique_ptr<crashpad::CrashReportDatabase> database_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterCrashpad);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_

View file

@ -0,0 +1,140 @@
// Copyright (c) 2014 GitHub, Inc.
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT 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 <string>
#include "base/debug/crash_logging.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/process/memory.h"
#include "base/threading/thread_restrictions.h"
#include "breakpad/src/client/linux/handler/exception_handler.h"
#include "breakpad/src/common/linux/linux_libc_support.h"
using google_breakpad::ExceptionHandler;
using google_breakpad::MinidumpDescriptor;
namespace crash_reporter {
namespace {
// 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() : 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::Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler) {
EnableCrashDumping(crashes_dir);
upload_url_ = submit_url;
upload_to_server_ = upload_to_server;
crash_keys_.reset(new CrashKeyStorage());
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::SetUploadToServer(const bool upload_to_server) {
upload_to_server_ = upload_to_server;
}
bool CrashReporterLinux::GetUploadToServer() {
return upload_to_server_;
}
void CrashReporterLinux::EnableCrashDumping(const base::FilePath& crashes_dir) {
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
base::CreateDirectory(crashes_dir);
}
std::string log_file = crashes_dir.Append("uploads.log").value();
strncpy(g_crash_log_path, log_file.c_str(), sizeof(g_crash_log_path));
MinidumpDescriptor minidump_descriptor(crashes_dir.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 = self->upload_to_server_;
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_.get();
HandleCrashDump(info);
return true;
}
// static
CrashReporterLinux* CrashReporterLinux::GetInstance() {
return base::Singleton<CrashReporterLinux>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterLinux::GetInstance();
}
} // namespace crash_reporter

View file

@ -0,0 +1,65 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT 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 <memory>
#include <string>
#include "atom/common/crash_reporter/crash_reporter.h"
#include "atom/common/crash_reporter/linux/crash_dump_handler.h"
#include "base/compiler_specific.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace google_breakpad {
class ExceptionHandler;
class MinidumpDescriptor;
} // namespace google_breakpad
namespace crash_reporter {
class CrashReporterLinux : public CrashReporter {
public:
static CrashReporterLinux* GetInstance();
void Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler) override;
void SetUploadToServer(bool upload_to_server) override;
void SetUploadParameters() override;
bool GetUploadToServer() override;
private:
friend struct base::DefaultSingletonTraits<CrashReporterLinux>;
CrashReporterLinux();
~CrashReporterLinux() override;
void EnableCrashDumping(const base::FilePath& crashes_dir);
static bool CrashDone(const google_breakpad::MinidumpDescriptor& minidump,
void* context,
const bool succeeded);
std::unique_ptr<google_breakpad::ExceptionHandler> breakpad_;
std::unique_ptr<CrashKeyStorage> crash_keys_;
uint64_t process_start_time_ = 0;
pid_t pid_ = 0;
std::string upload_url_;
bool upload_to_server_ = true;
DISALLOW_COPY_AND_ASSIGN(CrashReporterLinux);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_

View file

@ -0,0 +1,43 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT 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 <memory>
#include <string>
#include "atom/common/crash_reporter/crash_reporter_crashpad.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace crash_reporter {
class CrashReporterMac : public CrashReporterCrashpad {
public:
static CrashReporterMac* GetInstance();
void Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler) override;
void SetUploadParameters() override;
private:
friend struct base::DefaultSingletonTraits<CrashReporterMac>;
CrashReporterMac();
~CrashReporterMac() override;
DISALLOW_COPY_AND_ASSIGN(CrashReporterMac);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_

View file

@ -0,0 +1,84 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/crash_reporter/crash_reporter_mac.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h"
#include "base/memory/singleton.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
namespace crash_reporter {
CrashReporterMac::CrashReporterMac() {}
CrashReporterMac::~CrashReporterMac() {}
void CrashReporterMac::Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler) {
// check whether crashpad has been initialized.
// Only need to initialize once.
if (simple_string_dictionary_)
return;
if (process_type_.empty()) { // browser process
@autoreleasepool {
base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
base::FilePath handler_path =
framework_bundle_path.Append("Resources").Append("crashpad_handler");
std::vector<std::string> args = {
"--no-rate-limit",
"--no-upload-gzip", // not all servers accept gzip
};
crashpad::CrashpadClient crashpad_client;
crashpad_client.StartHandler(handler_path, crashes_dir, crashes_dir,
submit_url, StringMap(), args, true, false);
} // @autoreleasepool
}
crashpad::CrashpadInfo* crashpad_info =
crashpad::CrashpadInfo::GetCrashpadInfo();
if (skip_system_crash_handler) {
crashpad_info->set_system_crash_reporter_forwarding(
crashpad::TriState::kDisabled);
}
simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
SetInitialCrashKeyValues();
if (process_type_.empty()) { // browser process
database_ = crashpad::CrashReportDatabase::Initialize(crashes_dir);
SetUploadToServer(upload_to_server);
}
}
void CrashReporterMac::SetUploadParameters() {
upload_parameters_["platform"] = "darwin";
}
// static
CrashReporterMac* CrashReporterMac::GetInstance() {
return base::Singleton<CrashReporterMac>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterMac::GetInstance();
}
} // namespace crash_reporter

View file

@ -0,0 +1,141 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/crash_reporter/crash_reporter_win.h"
#include <memory>
#include <vector>
#include "atom/browser/ui/inspectable_web_contents_impl.h"
#include "atom/common/atom_constants.h"
#include "base/environment.h"
#include "base/memory/singleton.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "electron/atom/common/api/api.mojom.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
#if defined(_WIN64)
#include "gin/public/debug.h"
#endif
namespace {
#if defined(_WIN64)
int CrashForException(EXCEPTION_POINTERS* info) {
auto* reporter = crash_reporter::CrashReporterWin::GetInstance();
if (reporter->IsInitialized())
reporter->GetCrashpadClient().DumpAndCrash(info);
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
} // namespace
namespace crash_reporter {
CrashReporterWin::CrashReporterWin() {}
CrashReporterWin::~CrashReporterWin() {}
#if defined(_WIN64)
void CrashReporterWin::SetUnhandledExceptionFilter() {
gin::Debug::SetUnhandledExceptionCallback(&CrashForException);
}
#endif
void CrashReporterWin::Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler) {
// check whether crashpad has been initialized.
// Only need to initialize once.
if (simple_string_dictionary_)
return;
if (process_type_.empty()) { // browser process
base::FilePath handler_path;
base::PathService::Get(base::FILE_EXE, &handler_path);
std::vector<std::string> args = {
"--no-rate-limit",
"--no-upload-gzip", // not all servers accept gzip
};
args.push_back(base::StringPrintf("--type=%s", kCrashpadProcess));
args.push_back(
base::StringPrintf("--%s=%s", kCrashesDirectoryKey,
base::UTF16ToUTF8(crashes_dir.value()).c_str()));
crashpad_client_.StartHandler(handler_path, crashes_dir, crashes_dir,
submit_url, StringMap(), args, true, false);
UpdatePipeName();
} else {
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string pipe_name_utf8;
if (env->GetVar(atom::kCrashpadPipeName, &pipe_name_utf8)) {
base::string16 pipe_name = base::UTF8ToUTF16(pipe_name_utf8);
if (!crashpad_client_.SetHandlerIPCPipe(pipe_name))
LOG(ERROR) << "Failed to set handler IPC pipe name: " << pipe_name;
} else {
LOG(ERROR) << "Unable to get pipe name for crashpad";
}
}
crashpad::CrashpadInfo* crashpad_info =
crashpad::CrashpadInfo::GetCrashpadInfo();
if (skip_system_crash_handler) {
crashpad_info->set_system_crash_reporter_forwarding(
crashpad::TriState::kDisabled);
}
simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
SetInitialCrashKeyValues();
if (process_type_.empty()) { // browser process
database_ = crashpad::CrashReportDatabase::Initialize(crashes_dir);
SetUploadToServer(upload_to_server);
}
}
void CrashReporterWin::SetUploadParameters() {
upload_parameters_["platform"] = "win32";
}
crashpad::CrashpadClient& CrashReporterWin::GetCrashpadClient() {
return crashpad_client_;
}
void CrashReporterWin::UpdatePipeName() {
std::string pipe_name =
base::UTF16ToUTF8(crashpad_client_.GetHandlerIPCPipe());
std::unique_ptr<base::Environment> env(base::Environment::Create());
env->SetVar(atom::kCrashpadPipeName, pipe_name);
// Notify all WebContents of the pipe name.
const auto& pages = atom::InspectableWebContentsImpl::GetAll();
for (auto* page : pages) {
auto* frame_host = page->GetWebContents()->GetMainFrame();
if (!frame_host)
continue;
atom::mojom::ElectronRendererAssociatedPtr electron_ptr;
frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
mojo::MakeRequest(&electron_ptr));
electron_ptr->UpdateCrashpadPipeName(pipe_name);
}
}
// static
CrashReporterWin* CrashReporterWin::GetInstance() {
return base::Singleton<CrashReporterWin>::get();
}
// static
CrashReporter* CrashReporter::GetInstance() {
return CrashReporterWin::GetInstance();
}
} // namespace crash_reporter

View file

@ -0,0 +1,52 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT 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 <memory>
#include <string>
#include "atom/common/crash_reporter/crash_reporter_crashpad.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace crash_reporter {
class CrashReporterWin : public CrashReporterCrashpad {
public:
static CrashReporterWin* GetInstance();
#if defined(_WIN64)
static void SetUnhandledExceptionFilter();
#endif
void Init(const std::string& product_name,
const std::string& company_name,
const std::string& submit_url,
const base::FilePath& crashes_dir,
bool upload_to_server,
bool skip_system_crash_handler) override;
void SetUploadParameters() override;
crashpad::CrashpadClient& GetCrashpadClient();
private:
friend struct base::DefaultSingletonTraits<CrashReporterWin>;
CrashReporterWin();
~CrashReporterWin() override;
void UpdatePipeName();
crashpad::CrashpadClient crashpad_client_;
DISALLOW_COPY_AND_ASSIGN(CrashReporterWin);
};
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_

View file

@ -0,0 +1,753 @@
// Copyright (c) 2014 GitHub, Inc.
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT 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 "breakpad/src/client/linux/minidump_writer/directory_reader.h"
#include "breakpad/src/common/linux/linux_libc_support.h"
#include "breakpad/src/common/memory_allocator.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;
}
bool my_isxdigit(char c) {
return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
}
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_ = 0;
// Output file descriptor.
int fd_ = -1;
const char* const mime_boundary_;
private:
DISALLOW_COPY_AND_ASSIGN(MimeWriter);
};
MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
: 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, info.upload_url,
"--timeout=60", // Set a timeout so we don't hang forever.
"--tries=1", // Don't retry if the upload fails.
"--quiet", // Be silent.
"-O", // output reply to /dev/null.
"/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);
}
// Runs in the helper process to wait for the upload process running
// ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written
// to |fd| and save the written contents to |buf|.
// |buf| needs to be big enough to hold |bytes_to_read| + 1 characters.
size_t WaitForCrashReportUploadProcess(int fd,
size_t bytes_to_read,
char* buf) {
size_t bytes_read = 0;
// 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 = fd;
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.
ssize_t len = HANDLE_EINTR(
sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read));
if (len < 0)
break;
bytes_read += len;
if (bytes_read == bytes_to_read)
break;
}
// |ret| == 0 -> timed out, continue waiting.
// or |bytes_read| < |bytes_to_read| still, keep reading.
}
buf[bytes_to_read] = 0; // Always NUL terminate the buffer.
return bytes_read;
}
// |buf| should be |expected_len| + 1 characters in size and NULL terminated.
bool IsValidCrashReportId(const char* buf,
size_t bytes_read,
size_t expected_len) {
if (bytes_read != expected_len)
return false;
for (size_t i = 0; i < bytes_read; ++i) {
if (!my_isxdigit(buf[i]) && buf[i] != '-')
return false;
}
return true;
}
// |buf| should be |expected_len| + 1 characters in size and NULL terminated.
void HandleCrashReportId(const char* buf,
size_t bytes_read,
size_t expected_len) {
if (!IsValidCrashReportId(buf, bytes_read, expected_len)) {
static const char msg[] = "Failed to get crash dump id.";
WriteLog(msg, sizeof(msg) - 1);
WriteNewline();
static const char id_msg[] = "Report Id: ";
WriteLog(id_msg, sizeof(id_msg) - 1);
WriteLog(buf, bytes_read);
WriteNewline();
return;
}
// Write crash dump id to stderr.
static const char msg[] = "Crash dump id: ";
WriteLog(msg, sizeof(msg) - 1);
WriteLog(buf, my_strlen(buf));
WriteNewline();
// Write crash dump id to crash log as: seconds_since_epoch,crash_id
struct kernel_timeval tv;
if (!sys_gettimeofday(&tv, NULL)) {
uint64_t time = kernel_timeval_to_ms(&tv) / 1000;
char time_str[kUint64StringSize];
const unsigned time_len = my_uint64_len(time);
my_uint64tos(time_str, time, time_len);
const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC;
int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600);
if (log_fd > 0) {
sys_write(log_fd, time_str, time_len);
sys_write(log_fd, ",", 1);
sys_write(log_fd, buf, my_strlen(buf));
sys_write(log_fd, "\n", 1);
IGNORE_RET(sys_close(log_fd));
}
}
}
} // namespace
char g_crash_log_path[256];
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]));
const size_t kCrashIdLength = 36;
char id_buf[kCrashIdLength + 1];
size_t bytes_read =
WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf);
HandleCrashReportId(id_buf, bytes_read, kCrashIdLength);
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);
}
size_t WriteNewline() {
return WriteLog("\n", 1);
}
} // namespace crash_reporter

View file

@ -0,0 +1,46 @@
// Copyright (c) 2014 GitHub, Inc.
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT 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 <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include "base/macros.h"
#include "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);
size_t WriteNewline();
// Global variable storing the path of upload log.
extern char g_crash_log_path[256];
} // namespace crash_reporter
#endif // ATOM_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_

View file

@ -0,0 +1,508 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT 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/files/file_util.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/win/windows_version.h"
#include "breakpad/src/client/windows/crash_generation/client_info.h"
#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h"
#include "breakpad/src/client/windows/sender/crash_report_sender.h"
namespace breakpad {
namespace {
const wchar_t kWaitEventFormat[] = L"$1CrashServiceWaitEvent";
const wchar_t kClassNameFormat[] = L"$1CrashServiceWindow";
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;
}
bool WriteReportIDToFile(const std::wstring& dump_path,
const std::wstring& report_id) {
std::wstring file_path(dump_path);
size_t last_slash = file_path.rfind(L'\\');
if (last_slash == std::wstring::npos)
return false;
file_path.resize(last_slash);
file_path += L"\\uploads.log";
std::wofstream file(file_path.c_str(), std::ios_base::out |
std::ios_base::app |
std::ios::binary);
if (!file.is_open())
return false;
int64_t seconds_since_epoch =
(base::Time::Now() - base::Time::UnixEpoch()).InSeconds();
std::wstring line = base::NumberToString16(seconds_since_epoch);
line += L',';
line += report_id;
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,
const base::string16& application_name,
bool visible) {
base::string16 class_name =
base::ReplaceStringPlaceholders(kClassNameFormat, application_name, NULL);
WNDCLASSEXW wcx = {0};
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = CrashSvcWndProc;
wcx.hInstance = instance;
wcx.lpszClassName = class_name.c_str();
::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() {}
CrashService::~CrashService() {
base::AutoLock lock(sending_);
delete dumper_;
delete sender_;
}
bool CrashService::Initialize(const base::string16& application_name,
const base::FilePath& operating_dir,
const base::FilePath& dumps_path) {
using google_breakpad::CrashGenerationServer;
using google_breakpad::CrashReportSender;
std::wstring pipe_name = kTestPipeName;
int max_reports = -1;
// The checkpoint file allows CrashReportSender to enforce the maximum
// reports per day quota. Does not seem to serve any other purpose.
base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile);
base::CommandLine& cmd_line = *base::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_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;
// Create the OOP crash generator object.
dumper_ = new CrashGenerationServer(
pipe_name, &security_attributes, &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), application_name,
!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.
base::string16 wait_name =
base::ReplaceStringPlaceholders(kWaitEventFormat, application_name, NULL);
HANDLE wait_event = ::CreateEventW(NULL, TRUE, TRUE, wait_name.c_str());
::SetEvent(wait_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 processing_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);
base::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_ || map.find(L"skip_upload") != map.end())
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 = base::size(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;
std::map<std::wstring, std::wstring> file_map;
file_map[L"upload_file_minidump"] = info->dump_path;
google_breakpad::ReportResult send_result =
info->self->sender_->SendCrashReport(info->self->reporter_url_,
info->map, file_map, &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;
WriteReportIDToFile(info->dump_path, report_id);
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)";
PSECURITY_DESCRIPTOR sec_desc = NULL;
PACL sacl = NULL;
BOOL sacl_present = FALSE;
BOOL sacl_defaulted = FALSE;
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl.c_str(), SDDL_REVISION, &sec_desc, NULL)) {
if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
&sacl_defaulted)) {
return sec_desc;
}
}
return NULL;
}
} // namespace breakpad

View file

@ -0,0 +1,130 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
#define ATOM_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_
#include <string>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
#if defined(OS_WIN)
#include <windows.h>
#endif // defined(OS_WIN)
namespace google_breakpad {
class CrashReportSender;
class CrashGenerationServer;
class ClientInfo;
} // namespace google_breakpad
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::string16& application_name,
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_ = nullptr;
google_breakpad::CrashReportSender* sender_ = nullptr;
// 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_ = 0;
int requests_sent_ = 0;
volatile LONG clients_connected_ = 0;
volatile LONG clients_terminated_ = 0;
base::Lock sending_;
DISALLOW_COPY_AND_ASSIGN(CrashService);
};
} // namespace breakpad
#endif // ATOM_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_

View file

@ -0,0 +1,81 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/crash_reporter/win/crash_service_main.h"
#include <string>
#include "atom/common/crash_reporter/crash_reporter.h"
#include "atom/common/crash_reporter/win/crash_service.h"
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/crashpad/crashpad/handler/handler_main.h"
namespace crash_service {
namespace {
const wchar_t kStandardLogFile[] = L"operation_log.txt";
void InvalidParameterHandler(const wchar_t*,
const wchar_t*,
const wchar_t*,
unsigned int,
uintptr_t) {
// noop.
}
bool CreateCrashServiceDirectory(const base::FilePath& temp_dir) {
if (!base::PathExists(temp_dir)) {
if (!base::CreateDirectory(temp_dir))
return false;
}
return true;
}
void RemoveArgs(std::vector<char*>* args) {
args->erase(
std::remove_if(args->begin(), args->end(), [](const std::string& str) {
return base::StartsWith(str, "--type", base::CompareCase::SENSITIVE) ||
base::StartsWith(
str,
std::string("--") + crash_reporter::kCrashesDirectoryKey,
base::CompareCase::INSENSITIVE_ASCII);
}));
}
} // namespace.
int Main(std::vector<char*>* args) {
// Ignore invalid parameter errors.
_set_invalid_parameter_handler(InvalidParameterHandler);
// Initialize all Chromium things.
base::AtExitManager exit_manager;
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
// We use/create a directory under the user's temp folder, for logging.
base::FilePath operating_dir(
cmd_line->GetSwitchValueNative(crash_reporter::kCrashesDirectoryKey));
CreateCrashServiceDirectory(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);
// Crashpad cannot handle unknown arguments, so we need to remove it
RemoveArgs(args);
return crashpad::HandlerMain(args->size(), args->data(), nullptr);
}
} // namespace crash_service

View file

@ -0,0 +1,17 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
#define ATOM_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
#include <vector>
namespace crash_service {
// Program entry, should be called by main();
int Main(std::vector<char*>* args);
} // namespace crash_service
#endif // ATOM_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_

View file

@ -0,0 +1,27 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/deprecate_util.h"
#include "atom/common/native_mate_converters/callback.h"
#include "base/callback.h"
#include "native_mate/converter.h"
#include "native_mate/dictionary.h"
namespace atom {
void EmitDeprecationWarning(node::Environment* env,
const std::string& warning_msg,
const std::string& warning_type) {
mate::Dictionary process(env->isolate(), env->process_object());
base::RepeatingCallback<void(base::StringPiece, base::StringPiece,
base::StringPiece)>
emit_warning;
process.Get("emitWarning", &emit_warning);
emit_warning.Run(warning_msg, warning_type, "");
}
} // namespace atom

View file

@ -0,0 +1,20 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_DEPRECATE_UTIL_H_
#define ATOM_COMMON_DEPRECATE_UTIL_H_
#include <string>
#include "atom/common/node_includes.h"
namespace atom {
void EmitDeprecationWarning(node::Environment* env,
const std::string& warning_msg,
const std::string& warning_type);
} // namespace atom
#endif // ATOM_COMMON_DEPRECATE_UTIL_H_

29
shell/common/gin_util.h Normal file
View file

@ -0,0 +1,29 @@
// Copyright (c) 2018 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_GIN_UTIL_H_
#define ATOM_COMMON_GIN_UTIL_H_
#include "gin/converter.h"
#include "gin/function_template.h"
namespace gin_util {
template <typename T>
bool SetMethod(v8::Local<v8::Object> recv,
const base::StringPiece& key,
const T& callback) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
return recv
->Set(context, gin::StringToV8(isolate, key),
gin::CreateFunctionTemplate(isolate, callback)
->GetFunction(context)
.ToLocalChecked())
.ToChecked();
}
} // namespace gin_util
#endif // ATOM_COMMON_GIN_UTIL_H_

View file

@ -0,0 +1,56 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/heap_snapshot.h"
#include "v8/include/v8-profiler.h"
namespace {
class HeapSnapshotOutputStream : public v8::OutputStream {
public:
explicit HeapSnapshotOutputStream(base::File* file) : file_(file) {
DCHECK(file_);
}
bool IsComplete() const { return is_complete_; }
// v8::OutputStream
int GetChunkSize() override { return 65536; }
void EndOfStream() override { is_complete_ = true; }
v8::OutputStream::WriteResult WriteAsciiChunk(char* data, int size) override {
auto bytes_written = file_->WriteAtCurrentPos(data, size);
return bytes_written == size ? kContinue : kAbort;
}
private:
base::File* file_ = nullptr;
bool is_complete_ = false;
};
} // namespace
namespace atom {
bool TakeHeapSnapshot(v8::Isolate* isolate, base::File* file) {
DCHECK(isolate);
DCHECK(file);
if (!file->IsValid())
return false;
auto* snapshot = isolate->GetHeapProfiler()->TakeHeapSnapshot();
if (!snapshot)
return false;
HeapSnapshotOutputStream stream(file);
snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
return stream.IsComplete();
}
} // namespace atom

View file

@ -0,0 +1,17 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_HEAP_SNAPSHOT_H_
#define ATOM_COMMON_HEAP_SNAPSHOT_H_
#include "base/files/file.h"
#include "v8/include/v8.h"
namespace atom {
bool TakeHeapSnapshot(v8::Isolate* isolate, base::File* file);
} // namespace atom
#endif // ATOM_COMMON_HEAP_SNAPSHOT_H_

View file

@ -0,0 +1,87 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_KEY_WEAK_MAP_H_
#define ATOM_COMMON_KEY_WEAK_MAP_H_
#include <unordered_map>
#include <utility>
#include <vector>
#include "base/macros.h"
#include "v8/include/v8.h"
namespace atom {
// Like ES6's WeakMap, but the key is Integer and the value is Weak Pointer.
template <typename K>
class KeyWeakMap {
public:
// Records the key and self, used by SetWeak.
struct KeyObject {
K key;
KeyWeakMap* self;
};
KeyWeakMap() {}
virtual ~KeyWeakMap() {
for (auto& p : map_)
p.second.second.ClearWeak();
}
// Sets the object to WeakMap with the given |key|.
void Set(v8::Isolate* isolate, const K& key, v8::Local<v8::Object> object) {
KeyObject key_object = {key, this};
auto& p = map_[key] =
std::make_pair(key_object, v8::Global<v8::Object>(isolate, object));
p.second.SetWeak(&(p.first), OnObjectGC, v8::WeakCallbackType::kParameter);
}
// Gets the object from WeakMap by its |key|.
v8::MaybeLocal<v8::Object> Get(v8::Isolate* isolate, const K& key) {
auto iter = map_.find(key);
if (iter == map_.end())
return v8::MaybeLocal<v8::Object>();
else
return v8::Local<v8::Object>::New(isolate, iter->second.second);
}
// Whethere there is an object with |key| in this WeakMap.
bool Has(const K& key) const { return map_.find(key) != map_.end(); }
// Returns all objects.
std::vector<v8::Local<v8::Object>> Values(v8::Isolate* isolate) const {
std::vector<v8::Local<v8::Object>> keys;
keys.reserve(map_.size());
for (const auto& it : map_)
keys.emplace_back(v8::Local<v8::Object>::New(isolate, it.second.second));
return keys;
}
// Remove object with |key| in the WeakMap.
void Remove(const K& key) {
auto iter = map_.find(key);
if (iter == map_.end())
return;
iter->second.second.ClearWeak();
map_.erase(iter);
}
private:
static void OnObjectGC(
const v8::WeakCallbackInfo<typename KeyWeakMap<K>::KeyObject>& data) {
KeyWeakMap<K>::KeyObject* key_object = data.GetParameter();
key_object->self->Remove(key_object->key);
}
// Map of stored objects.
std::unordered_map<K, std::pair<KeyObject, v8::Global<v8::Object>>> map_;
DISALLOW_COPY_AND_ASSIGN(KeyWeakMap);
};
} // namespace atom
#endif // ATOM_COMMON_KEY_WEAK_MAP_H_

View file

@ -0,0 +1,358 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <string>
#include "atom/common/keyboard_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "ui/events/event_constants.h"
namespace atom {
namespace {
// Return key code represented by |str|.
ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s,
bool* shifted) {
std::string str = base::ToLowerASCII(s);
if (str == "ctrl" || str == "control") {
return ui::VKEY_CONTROL;
} else if (str == "super" || str == "cmd" || str == "command" ||
str == "meta") {
return ui::VKEY_COMMAND;
} else if (str == "commandorcontrol" || str == "cmdorctrl") {
#if defined(OS_MACOSX)
return ui::VKEY_COMMAND;
#else
return ui::VKEY_CONTROL;
#endif
} else if (str == "alt" || str == "option") {
return ui::VKEY_MENU;
} else if (str == "shift") {
return ui::VKEY_SHIFT;
} else if (str == "altgr") {
return ui::VKEY_ALTGR;
} else if (str == "plus") {
*shifted = true;
return ui::VKEY_OEM_PLUS;
} else if (str == "capslock") {
return ui::VKEY_CAPITAL;
} else if (str == "numlock") {
return ui::VKEY_NUMLOCK;
} else if (str == "scrolllock") {
return ui::VKEY_SCROLL;
} else if (str == "tab") {
return ui::VKEY_TAB;
} else if (str == "num0") {
return ui::VKEY_NUMPAD0;
} else if (str == "num1") {
return ui::VKEY_NUMPAD1;
} else if (str == "num2") {
return ui::VKEY_NUMPAD2;
} else if (str == "num3") {
return ui::VKEY_NUMPAD3;
} else if (str == "num4") {
return ui::VKEY_NUMPAD4;
} else if (str == "num5") {
return ui::VKEY_NUMPAD5;
} else if (str == "num6") {
return ui::VKEY_NUMPAD6;
} else if (str == "num7") {
return ui::VKEY_NUMPAD7;
} else if (str == "num8") {
return ui::VKEY_NUMPAD8;
} else if (str == "num9") {
return ui::VKEY_NUMPAD9;
} else if (str == "numadd") {
return ui::VKEY_ADD;
} else if (str == "nummult") {
return ui::VKEY_MULTIPLY;
} else if (str == "numdec") {
return ui::VKEY_DECIMAL;
} else if (str == "numsub") {
return ui::VKEY_SUBTRACT;
} else if (str == "numdiv") {
return ui::VKEY_DIVIDE;
} else if (str == "space") {
return ui::VKEY_SPACE;
} else if (str == "backspace") {
return ui::VKEY_BACK;
} else if (str == "delete") {
return ui::VKEY_DELETE;
} else if (str == "insert") {
return ui::VKEY_INSERT;
} else if (str == "enter" || str == "return") {
return ui::VKEY_RETURN;
} else if (str == "up") {
return ui::VKEY_UP;
} else if (str == "down") {
return ui::VKEY_DOWN;
} else if (str == "left") {
return ui::VKEY_LEFT;
} else if (str == "right") {
return ui::VKEY_RIGHT;
} else if (str == "home") {
return ui::VKEY_HOME;
} else if (str == "end") {
return ui::VKEY_END;
} else if (str == "pageup") {
return ui::VKEY_PRIOR;
} else if (str == "pagedown") {
return ui::VKEY_NEXT;
} else if (str == "esc" || str == "escape") {
return ui::VKEY_ESCAPE;
} else if (str == "volumemute") {
return ui::VKEY_VOLUME_MUTE;
} else if (str == "volumeup") {
return ui::VKEY_VOLUME_UP;
} else if (str == "volumedown") {
return ui::VKEY_VOLUME_DOWN;
} else if (str == "medianexttrack") {
return ui::VKEY_MEDIA_NEXT_TRACK;
} else if (str == "mediaprevioustrack") {
return ui::VKEY_MEDIA_PREV_TRACK;
} else if (str == "mediastop") {
return ui::VKEY_MEDIA_STOP;
} else if (str == "mediaplaypause") {
return ui::VKEY_MEDIA_PLAY_PAUSE;
} else if (str == "printscreen") {
return ui::VKEY_SNAPSHOT;
} else if (str.size() > 1 && str[0] == 'f') {
// F1 - F24.
int n;
if (base::StringToInt(str.c_str() + 1, &n) && n > 0 && n < 25) {
return static_cast<ui::KeyboardCode>(ui::VKEY_F1 + n - 1);
} else {
LOG(WARNING) << str << "is not available on keyboard";
return ui::VKEY_UNKNOWN;
}
} else {
if (str.size() > 2)
LOG(WARNING) << "Invalid accelerator token: " << str;
return ui::VKEY_UNKNOWN;
}
}
} // namespace
ui::KeyboardCode KeyboardCodeFromCharCode(base::char16 c, bool* shifted) {
c = base::ToLowerASCII(c);
*shifted = false;
switch (c) {
case 0x08:
return ui::VKEY_BACK;
case 0x7F:
return ui::VKEY_DELETE;
case 0x09:
return ui::VKEY_TAB;
case 0x0D:
return ui::VKEY_RETURN;
case 0x1B:
return ui::VKEY_ESCAPE;
case ' ':
return ui::VKEY_SPACE;
case 'a':
return ui::VKEY_A;
case 'b':
return ui::VKEY_B;
case 'c':
return ui::VKEY_C;
case 'd':
return ui::VKEY_D;
case 'e':
return ui::VKEY_E;
case 'f':
return ui::VKEY_F;
case 'g':
return ui::VKEY_G;
case 'h':
return ui::VKEY_H;
case 'i':
return ui::VKEY_I;
case 'j':
return ui::VKEY_J;
case 'k':
return ui::VKEY_K;
case 'l':
return ui::VKEY_L;
case 'm':
return ui::VKEY_M;
case 'n':
return ui::VKEY_N;
case 'o':
return ui::VKEY_O;
case 'p':
return ui::VKEY_P;
case 'q':
return ui::VKEY_Q;
case 'r':
return ui::VKEY_R;
case 's':
return ui::VKEY_S;
case 't':
return ui::VKEY_T;
case 'u':
return ui::VKEY_U;
case 'v':
return ui::VKEY_V;
case 'w':
return ui::VKEY_W;
case 'x':
return ui::VKEY_X;
case 'y':
return ui::VKEY_Y;
case 'z':
return ui::VKEY_Z;
case ')':
*shifted = true;
FALLTHROUGH;
case '0':
return ui::VKEY_0;
case '!':
*shifted = true;
FALLTHROUGH;
case '1':
return ui::VKEY_1;
case '@':
*shifted = true;
FALLTHROUGH;
case '2':
return ui::VKEY_2;
case '#':
*shifted = true;
FALLTHROUGH;
case '3':
return ui::VKEY_3;
case '$':
*shifted = true;
FALLTHROUGH;
case '4':
return ui::VKEY_4;
case '%':
*shifted = true;
FALLTHROUGH;
case '5':
return ui::VKEY_5;
case '^':
*shifted = true;
FALLTHROUGH;
case '6':
return ui::VKEY_6;
case '&':
*shifted = true;
FALLTHROUGH;
case '7':
return ui::VKEY_7;
case '*':
*shifted = true;
FALLTHROUGH;
case '8':
return ui::VKEY_8;
case '(':
*shifted = true;
FALLTHROUGH;
case '9':
return ui::VKEY_9;
case ':':
*shifted = true;
FALLTHROUGH;
case ';':
return ui::VKEY_OEM_1;
case '+':
*shifted = true;
FALLTHROUGH;
case '=':
return ui::VKEY_OEM_PLUS;
case '<':
*shifted = true;
FALLTHROUGH;
case ',':
return ui::VKEY_OEM_COMMA;
case '_':
*shifted = true;
FALLTHROUGH;
case '-':
return ui::VKEY_OEM_MINUS;
case '>':
*shifted = true;
FALLTHROUGH;
case '.':
return ui::VKEY_OEM_PERIOD;
case '?':
*shifted = true;
FALLTHROUGH;
case '/':
return ui::VKEY_OEM_2;
case '~':
*shifted = true;
FALLTHROUGH;
case '`':
return ui::VKEY_OEM_3;
case '{':
*shifted = true;
FALLTHROUGH;
case '[':
return ui::VKEY_OEM_4;
case '|':
*shifted = true;
FALLTHROUGH;
case '\\':
return ui::VKEY_OEM_5;
case '}':
*shifted = true;
FALLTHROUGH;
case ']':
return ui::VKEY_OEM_6;
case '"':
*shifted = true;
FALLTHROUGH;
case '\'':
return ui::VKEY_OEM_7;
default:
return ui::VKEY_UNKNOWN;
}
}
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted) {
if (str.size() == 1)
return KeyboardCodeFromCharCode(str[0], shifted);
else
return KeyboardCodeFromKeyIdentifier(str, shifted);
}
int WebEventModifiersToEventFlags(int modifiers) {
int flags = 0;
if (modifiers & blink::WebInputEvent::kShiftKey)
flags |= ui::EF_SHIFT_DOWN;
if (modifiers & blink::WebInputEvent::kControlKey)
flags |= ui::EF_CONTROL_DOWN;
if (modifiers & blink::WebInputEvent::kAltKey)
flags |= ui::EF_ALT_DOWN;
if (modifiers & blink::WebInputEvent::kMetaKey)
flags |= ui::EF_COMMAND_DOWN;
if (modifiers & blink::WebInputEvent::kCapsLockOn)
flags |= ui::EF_CAPS_LOCK_ON;
if (modifiers & blink::WebInputEvent::kNumLockOn)
flags |= ui::EF_NUM_LOCK_ON;
if (modifiers & blink::WebInputEvent::kScrollLockOn)
flags |= ui::EF_SCROLL_LOCK_ON;
if (modifiers & blink::WebInputEvent::kLeftButtonDown)
flags |= ui::EF_LEFT_MOUSE_BUTTON;
if (modifiers & blink::WebInputEvent::kMiddleButtonDown)
flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
if (modifiers & blink::WebInputEvent::kRightButtonDown)
flags |= ui::EF_RIGHT_MOUSE_BUTTON;
if (modifiers & blink::WebInputEvent::kIsAutoRepeat)
flags |= ui::EF_IS_REPEAT;
return flags;
}
} // namespace atom

View file

@ -0,0 +1,28 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_KEYBOARD_UTIL_H_
#define ATOM_COMMON_KEYBOARD_UTIL_H_
#include <string>
#include "base/strings/string16.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace atom {
// Return key code of the char, and also determine whether the SHIFT key is
// pressed.
ui::KeyboardCode KeyboardCodeFromCharCode(base::char16 c, bool* shifted);
// Return key code of the |str|, and also determine whether the SHIFT key is
// pressed.
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted);
// Ported from ui/events/blink/blink_event_util.h
int WebEventModifiersToEventFlags(int modifiers);
} // namespace atom
#endif // ATOM_COMMON_KEYBOARD_UTIL_H_

View file

@ -0,0 +1,26 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright (c) 2013 Adam Roben <adam@roben.org>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.
#ifndef ATOM_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_
#define ATOM_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_
@class NSBundle;
namespace base {
class FilePath;
}
namespace atom {
// The "main" application bundle is the outermost bundle for this logical
// application. E.g., if you have MyApp.app and
// MyApp.app/Contents/Frameworks/MyApp Helper.app, the main application bundle
// is MyApp.app, no matter which executable is currently running.
NSBundle* MainApplicationBundle();
base::FilePath MainApplicationBundlePath();
} // namespace atom
#endif // ATOM_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_

View file

@ -0,0 +1,56 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright (c) 2013 Adam Roben <adam@roben.org>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.
#import "atom/common/mac/main_application_bundle.h"
#include "base/files/file_path.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
namespace atom {
namespace {
bool HasMainProcessKey() {
NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary];
return
[[info_dictionary objectForKey:@"ElectronMainProcess"] boolValue] != NO;
}
} // namespace
base::FilePath MainApplicationBundlePath() {
// Start out with the path to the running executable.
base::FilePath path;
base::PathService::Get(base::FILE_EXE, &path);
// Up to Contents.
if (!HasMainProcessKey() &&
base::EndsWith(path.value(), " Helper", base::CompareCase::SENSITIVE)) {
// The running executable is the helper. Go up five steps:
// Contents/Frameworks/Helper.app/Contents/MacOS/Helper
// ^ to here ^ from here
path = path.DirName().DirName().DirName().DirName().DirName();
} else {
// One step up to MacOS, another to Contents.
path = path.DirName().DirName();
}
DCHECK_EQ(path.BaseName().value(), "Contents");
// Up one more level to the .app.
path = path.DirName();
DCHECK_EQ(path.BaseName().Extension(), ".app");
return path;
}
NSBundle* MainApplicationBundle() {
return [NSBundle bundleWithPath:base::mac::FilePathToNSString(
MainApplicationBundlePath())];
}
} // namespace atom

107
shell/common/mouse_util.cc Normal file
View file

@ -0,0 +1,107 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/mouse_util.h"
#include <string>
using Cursor = blink::WebCursorInfo::Type;
namespace atom {
std::string CursorTypeToString(const content::CursorInfo& info) {
switch (info.type) {
case Cursor::kTypePointer:
return "default";
case Cursor::kTypeCross:
return "crosshair";
case Cursor::kTypeHand:
return "pointer";
case Cursor::kTypeIBeam:
return "text";
case Cursor::kTypeWait:
return "wait";
case Cursor::kTypeHelp:
return "help";
case Cursor::kTypeEastResize:
return "e-resize";
case Cursor::kTypeNorthResize:
return "n-resize";
case Cursor::kTypeNorthEastResize:
return "ne-resize";
case Cursor::kTypeNorthWestResize:
return "nw-resize";
case Cursor::kTypeSouthResize:
return "s-resize";
case Cursor::kTypeSouthEastResize:
return "se-resize";
case Cursor::kTypeSouthWestResize:
return "sw-resize";
case Cursor::kTypeWestResize:
return "w-resize";
case Cursor::kTypeNorthSouthResize:
return "ns-resize";
case Cursor::kTypeEastWestResize:
return "ew-resize";
case Cursor::kTypeNorthEastSouthWestResize:
return "nesw-resize";
case Cursor::kTypeNorthWestSouthEastResize:
return "nwse-resize";
case Cursor::kTypeColumnResize:
return "col-resize";
case Cursor::kTypeRowResize:
return "row-resize";
case Cursor::kTypeMiddlePanning:
return "m-panning";
case Cursor::kTypeEastPanning:
return "e-panning";
case Cursor::kTypeNorthPanning:
return "n-panning";
case Cursor::kTypeNorthEastPanning:
return "ne-panning";
case Cursor::kTypeNorthWestPanning:
return "nw-panning";
case Cursor::kTypeSouthPanning:
return "s-panning";
case Cursor::kTypeSouthEastPanning:
return "se-panning";
case Cursor::kTypeSouthWestPanning:
return "sw-panning";
case Cursor::kTypeWestPanning:
return "w-panning";
case Cursor::kTypeMove:
return "move";
case Cursor::kTypeVerticalText:
return "vertical-text";
case Cursor::kTypeCell:
return "cell";
case Cursor::kTypeContextMenu:
return "context-menu";
case Cursor::kTypeAlias:
return "alias";
case Cursor::kTypeProgress:
return "progress";
case Cursor::kTypeNoDrop:
return "nodrop";
case Cursor::kTypeCopy:
return "copy";
case Cursor::kTypeNone:
return "none";
case Cursor::kTypeNotAllowed:
return "not-allowed";
case Cursor::kTypeZoomIn:
return "zoom-in";
case Cursor::kTypeZoomOut:
return "zoom-out";
case Cursor::kTypeGrab:
return "grab";
case Cursor::kTypeGrabbing:
return "grabbing";
case Cursor::kTypeCustom:
return "custom";
default:
return "default";
}
}
} // namespace atom

34
shell/common/mouse_util.h Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_MOUSE_UTIL_H_
#define ATOM_COMMON_MOUSE_UTIL_H_
#include <string>
#include "content/common/cursors/webcursor.h"
#include "ipc/ipc_message_macros.h"
// IPC macros similar to the already existing ones in the chromium source.
// We need these to listen to the cursor change IPC message while still
// letting chromium handle the actual cursor change by setting handled = false.
#define IPC_MESSAGE_HANDLER_CODE(msg_class, member_func, code) \
IPC_MESSAGE_FORWARD_CODE(msg_class, this, \
_IpcMessageHandlerClass::member_func, code)
#define IPC_MESSAGE_FORWARD_CODE(msg_class, obj, member_func, code) \
case msg_class::ID: { \
if (!msg_class::Dispatch(&ipc_message__, obj, this, param__, \
&member_func)) \
ipc_message__.set_dispatch_error(); \
code; \
} break;
namespace atom {
// Returns the cursor's type as a string.
std::string CursorTypeToString(const content::CursorInfo& info);
} // namespace atom
#endif // ATOM_COMMON_MOUSE_UTIL_H_

View file

@ -0,0 +1,23 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/accelerator_converter.h"
#include <string>
#include "atom/browser/ui/accelerator_util.h"
namespace mate {
// static
bool Converter<ui::Accelerator>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
ui::Accelerator* out) {
std::string keycode;
if (!ConvertFromV8(isolate, val, &keycode))
return false;
return accelerator_util::StringToAccelerator(keycode, out);
}
} // namespace mate

View file

@ -0,0 +1,25 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_ACCELERATOR_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_ACCELERATOR_CONVERTER_H_
#include "native_mate/converter.h"
namespace ui {
class Accelerator;
}
namespace mate {
template <>
struct Converter<ui::Accelerator> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
ui::Accelerator* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_ACCELERATOR_CONVERTER_H_

View file

@ -0,0 +1,530 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/blink_converter.h"
#include <algorithm>
#include <string>
#include <vector>
#include "atom/common/keyboard_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "gin/converter.h"
#include "native_mate/dictionary.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/public/platform/web_mouse_event.h"
#include "third_party/blink/public/platform/web_mouse_wheel_event.h"
#include "third_party/blink/public/web/web_device_emulation_params.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
namespace {
template <typename T>
int VectorToBitArray(const std::vector<T>& vec) {
int bits = 0;
for (const T& item : vec)
bits |= item;
return bits;
}
} // namespace
namespace mate {
template <>
struct Converter<base::char16> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
base::char16* out) {
base::string16 code = base::UTF8ToUTF16(gin::V8ToString(isolate, val));
if (code.length() != 1)
return false;
*out = code[0];
return true;
}
};
template <>
struct Converter<blink::WebInputEvent::Type> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
blink::WebInputEvent::Type* out) {
std::string type = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (type == "mousedown")
*out = blink::WebInputEvent::kMouseDown;
else if (type == "mouseup")
*out = blink::WebInputEvent::kMouseUp;
else if (type == "mousemove")
*out = blink::WebInputEvent::kMouseMove;
else if (type == "mouseenter")
*out = blink::WebInputEvent::kMouseEnter;
else if (type == "mouseleave")
*out = blink::WebInputEvent::kMouseLeave;
else if (type == "contextmenu")
*out = blink::WebInputEvent::kContextMenu;
else if (type == "mousewheel")
*out = blink::WebInputEvent::kMouseWheel;
else if (type == "keydown")
*out = blink::WebInputEvent::kRawKeyDown;
else if (type == "keyup")
*out = blink::WebInputEvent::kKeyUp;
else if (type == "char")
*out = blink::WebInputEvent::kChar;
else if (type == "touchstart")
*out = blink::WebInputEvent::kTouchStart;
else if (type == "touchmove")
*out = blink::WebInputEvent::kTouchMove;
else if (type == "touchend")
*out = blink::WebInputEvent::kTouchEnd;
else if (type == "touchcancel")
*out = blink::WebInputEvent::kTouchCancel;
return true;
}
};
template <>
struct Converter<blink::WebMouseEvent::Button> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
blink::WebMouseEvent::Button* out) {
std::string button = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (button == "left")
*out = blink::WebMouseEvent::Button::kLeft;
else if (button == "middle")
*out = blink::WebMouseEvent::Button::kMiddle;
else if (button == "right")
*out = blink::WebMouseEvent::Button::kRight;
else
return false;
return true;
}
};
template <>
struct Converter<blink::WebInputEvent::Modifiers> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
blink::WebInputEvent::Modifiers* out) {
std::string modifier = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (modifier == "shift")
*out = blink::WebInputEvent::kShiftKey;
else if (modifier == "control" || modifier == "ctrl")
*out = blink::WebInputEvent::kControlKey;
else if (modifier == "alt")
*out = blink::WebInputEvent::kAltKey;
else if (modifier == "meta" || modifier == "command" || modifier == "cmd")
*out = blink::WebInputEvent::kMetaKey;
else if (modifier == "iskeypad")
*out = blink::WebInputEvent::kIsKeyPad;
else if (modifier == "isautorepeat")
*out = blink::WebInputEvent::kIsAutoRepeat;
else if (modifier == "leftbuttondown")
*out = blink::WebInputEvent::kLeftButtonDown;
else if (modifier == "middlebuttondown")
*out = blink::WebInputEvent::kMiddleButtonDown;
else if (modifier == "rightbuttondown")
*out = blink::WebInputEvent::kRightButtonDown;
else if (modifier == "capslock")
*out = blink::WebInputEvent::kCapsLockOn;
else if (modifier == "numlock")
*out = blink::WebInputEvent::kNumLockOn;
else if (modifier == "left")
*out = blink::WebInputEvent::kIsLeft;
else if (modifier == "right")
*out = blink::WebInputEvent::kIsRight;
return true;
}
};
blink::WebInputEvent::Type GetWebInputEventType(v8::Isolate* isolate,
v8::Local<v8::Value> val) {
blink::WebInputEvent::Type type = blink::WebInputEvent::kUndefined;
mate::Dictionary dict;
ConvertFromV8(isolate, val, &dict) && dict.Get("type", &type);
return type;
}
bool Converter<blink::WebInputEvent>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebInputEvent* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
blink::WebInputEvent::Type type;
if (!dict.Get("type", &type))
return false;
out->SetType(type);
std::vector<blink::WebInputEvent::Modifiers> modifiers;
if (dict.Get("modifiers", &modifiers))
out->SetModifiers(VectorToBitArray(modifiers));
out->SetTimeStamp(base::TimeTicks::Now());
return true;
}
bool Converter<blink::WebKeyboardEvent>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebKeyboardEvent* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!ConvertFromV8(isolate, val, static_cast<blink::WebInputEvent*>(out)))
return false;
std::string str;
if (!dict.Get("keyCode", &str))
return false;
bool shifted = false;
ui::KeyboardCode keyCode = atom::KeyboardCodeFromStr(str, &shifted);
out->windows_key_code = keyCode;
if (shifted)
out->SetModifiers(out->GetModifiers() | blink::WebInputEvent::kShiftKey);
ui::DomCode domCode = ui::UsLayoutKeyboardCodeToDomCode(keyCode);
out->dom_code = static_cast<int>(domCode);
ui::DomKey domKey;
ui::KeyboardCode dummy_code;
int flags = atom::WebEventModifiersToEventFlags(out->GetModifiers());
if (ui::DomCodeToUsLayoutDomKey(domCode, flags, &domKey, &dummy_code))
out->dom_key = static_cast<int>(domKey);
if ((out->GetType() == blink::WebInputEvent::kChar ||
out->GetType() == blink::WebInputEvent::kRawKeyDown)) {
// Make sure to not read beyond the buffer in case some bad code doesn't
// NULL-terminate it (this is called from plugins).
size_t text_length_cap = blink::WebKeyboardEvent::kTextLengthCap;
base::string16 text16 = base::UTF8ToUTF16(str);
memset(out->text, 0, text_length_cap);
memset(out->unmodified_text, 0, text_length_cap);
for (size_t i = 0; i < std::min(text_length_cap, text16.size()); ++i) {
out->text[i] = text16[i];
out->unmodified_text[i] = text16[i];
}
}
return true;
}
bool Converter<content::NativeWebKeyboardEvent>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::NativeWebKeyboardEvent* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!ConvertFromV8(isolate, val, static_cast<blink::WebKeyboardEvent*>(out)))
return false;
dict.Get("skipInBrowser", &out->skip_in_browser);
return true;
}
v8::Local<v8::Value> Converter<content::NativeWebKeyboardEvent>::ToV8(
v8::Isolate* isolate,
const content::NativeWebKeyboardEvent& in) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
if (in.GetType() == blink::WebInputEvent::Type::kRawKeyDown)
dict.Set("type", "keyDown");
else if (in.GetType() == blink::WebInputEvent::Type::kKeyUp)
dict.Set("type", "keyUp");
dict.Set("key", ui::KeycodeConverter::DomKeyToKeyString(in.dom_key));
dict.Set("code", ui::KeycodeConverter::DomCodeToCodeString(
static_cast<ui::DomCode>(in.dom_code)));
using Modifiers = blink::WebInputEvent::Modifiers;
dict.Set("isAutoRepeat", (in.GetModifiers() & Modifiers::kIsAutoRepeat) != 0);
dict.Set("shift", (in.GetModifiers() & Modifiers::kShiftKey) != 0);
dict.Set("control", (in.GetModifiers() & Modifiers::kControlKey) != 0);
dict.Set("alt", (in.GetModifiers() & Modifiers::kAltKey) != 0);
dict.Set("meta", (in.GetModifiers() & Modifiers::kMetaKey) != 0);
return dict.GetHandle();
}
bool Converter<blink::WebMouseEvent>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebMouseEvent* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!ConvertFromV8(isolate, val, static_cast<blink::WebInputEvent*>(out)))
return false;
float x = 0.f;
float y = 0.f;
if (!dict.Get("x", &x) || !dict.Get("y", &y))
return false;
out->SetPositionInWidget(x, y);
if (!dict.Get("button", &out->button))
out->button = blink::WebMouseEvent::Button::kLeft;
float global_x = 0.f;
float global_y = 0.f;
dict.Get("globalX", &global_x);
dict.Get("globalY", &global_y);
out->SetPositionInScreen(global_x, global_y);
dict.Get("movementX", &out->movement_x);
dict.Get("movementY", &out->movement_y);
dict.Get("clickCount", &out->click_count);
return true;
}
bool Converter<blink::WebMouseWheelEvent>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebMouseWheelEvent* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!ConvertFromV8(isolate, val, static_cast<blink::WebMouseEvent*>(out)))
return false;
dict.Get("deltaX", &out->delta_x);
dict.Get("deltaY", &out->delta_y);
dict.Get("wheelTicksX", &out->wheel_ticks_x);
dict.Get("wheelTicksY", &out->wheel_ticks_y);
dict.Get("accelerationRatioX", &out->acceleration_ratio_x);
dict.Get("accelerationRatioY", &out->acceleration_ratio_y);
dict.Get("hasPreciseScrollingDeltas", &out->has_precise_scrolling_deltas);
#if defined(USE_AURA)
// Matches the behavior of ui/events/blink/web_input_event_traits.cc:
bool can_scroll = true;
if (dict.Get("canScroll", &can_scroll) && !can_scroll) {
out->has_precise_scrolling_deltas = false;
out->SetModifiers(out->GetModifiers() & ~blink::WebInputEvent::kControlKey);
}
#endif
return true;
}
bool Converter<blink::WebFloatPoint>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebFloatPoint* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
return dict.Get("x", &out->x) && dict.Get("y", &out->y);
}
template <>
struct Converter<base::Optional<blink::WebPoint>> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::Optional<blink::WebPoint>* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
blink::WebPoint point;
bool success = dict.Get("x", &point.x) && dict.Get("y", &point.y);
if (!success)
return false;
out->emplace(point);
return true;
}
};
bool Converter<blink::WebSize>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebSize* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
return dict.Get("width", &out->width) && dict.Get("height", &out->height);
}
bool Converter<blink::WebDeviceEmulationParams>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebDeviceEmulationParams* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
std::string screen_position;
if (dict.Get("screenPosition", &screen_position)) {
screen_position = base::ToLowerASCII(screen_position);
if (screen_position == "mobile")
out->screen_position = blink::WebDeviceEmulationParams::kMobile;
else if (screen_position == "desktop")
out->screen_position = blink::WebDeviceEmulationParams::kDesktop;
else
return false;
}
dict.Get("screenSize", &out->screen_size);
dict.Get("viewPosition", &out->view_position);
dict.Get("deviceScaleFactor", &out->device_scale_factor);
dict.Get("viewSize", &out->view_size);
dict.Get("scale", &out->scale);
return true;
}
// static
v8::Local<v8::Value> Converter<blink::WebContextMenuData::MediaType>::ToV8(
v8::Isolate* isolate,
const blink::WebContextMenuData::MediaType& in) {
switch (in) {
case blink::WebContextMenuData::kMediaTypeImage:
return mate::StringToV8(isolate, "image");
case blink::WebContextMenuData::kMediaTypeVideo:
return mate::StringToV8(isolate, "video");
case blink::WebContextMenuData::kMediaTypeAudio:
return mate::StringToV8(isolate, "audio");
case blink::WebContextMenuData::kMediaTypeCanvas:
return mate::StringToV8(isolate, "canvas");
case blink::WebContextMenuData::kMediaTypeFile:
return mate::StringToV8(isolate, "file");
case blink::WebContextMenuData::kMediaTypePlugin:
return mate::StringToV8(isolate, "plugin");
default:
return mate::StringToV8(isolate, "none");
}
}
// static
v8::Local<v8::Value> Converter<blink::WebContextMenuData::InputFieldType>::ToV8(
v8::Isolate* isolate,
const blink::WebContextMenuData::InputFieldType& in) {
switch (in) {
case blink::WebContextMenuData::kInputFieldTypePlainText:
return mate::StringToV8(isolate, "plainText");
case blink::WebContextMenuData::kInputFieldTypePassword:
return mate::StringToV8(isolate, "password");
case blink::WebContextMenuData::kInputFieldTypeOther:
return mate::StringToV8(isolate, "other");
default:
return mate::StringToV8(isolate, "none");
}
}
v8::Local<v8::Value> EditFlagsToV8(v8::Isolate* isolate, int editFlags) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("canUndo", !!(editFlags & blink::WebContextMenuData::kCanUndo));
dict.Set("canRedo", !!(editFlags & blink::WebContextMenuData::kCanRedo));
dict.Set("canCut", !!(editFlags & blink::WebContextMenuData::kCanCut));
dict.Set("canCopy", !!(editFlags & blink::WebContextMenuData::kCanCopy));
bool pasteFlag = false;
if (editFlags & blink::WebContextMenuData::kCanPaste) {
std::vector<base::string16> types;
bool ignore;
ui::Clipboard::GetForCurrentThread()->ReadAvailableTypes(
ui::CLIPBOARD_TYPE_COPY_PASTE, &types, &ignore);
pasteFlag = !types.empty();
}
dict.Set("canPaste", pasteFlag);
dict.Set("canDelete", !!(editFlags & blink::WebContextMenuData::kCanDelete));
dict.Set("canSelectAll",
!!(editFlags & blink::WebContextMenuData::kCanSelectAll));
return mate::ConvertToV8(isolate, dict);
}
v8::Local<v8::Value> MediaFlagsToV8(v8::Isolate* isolate, int mediaFlags) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("inError",
!!(mediaFlags & blink::WebContextMenuData::kMediaInError));
dict.Set("isPaused",
!!(mediaFlags & blink::WebContextMenuData::kMediaPaused));
dict.Set("isMuted", !!(mediaFlags & blink::WebContextMenuData::kMediaMuted));
dict.Set("hasAudio",
!!(mediaFlags & blink::WebContextMenuData::kMediaHasAudio));
dict.Set("isLooping",
(mediaFlags & blink::WebContextMenuData::kMediaLoop) != 0);
dict.Set("isControlsVisible",
(mediaFlags & blink::WebContextMenuData::kMediaControls) != 0);
dict.Set("canToggleControls",
!!(mediaFlags & blink::WebContextMenuData::kMediaCanToggleControls));
dict.Set("canRotate",
!!(mediaFlags & blink::WebContextMenuData::kMediaCanRotate));
return mate::ConvertToV8(isolate, dict);
}
v8::Local<v8::Value> Converter<blink::WebCache::ResourceTypeStat>::ToV8(
v8::Isolate* isolate,
const blink::WebCache::ResourceTypeStat& stat) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("count", static_cast<uint32_t>(stat.count));
dict.Set("size", static_cast<double>(stat.size));
dict.Set("liveSize", static_cast<double>(stat.decoded_size));
return dict.GetHandle();
}
v8::Local<v8::Value> Converter<blink::WebCache::ResourceTypeStats>::ToV8(
v8::Isolate* isolate,
const blink::WebCache::ResourceTypeStats& stats) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("images", stats.images);
dict.Set("scripts", stats.scripts);
dict.Set("cssStyleSheets", stats.css_style_sheets);
dict.Set("xslStyleSheets", stats.xsl_style_sheets);
dict.Set("fonts", stats.fonts);
dict.Set("other", stats.other);
return dict.GetHandle();
}
// static
v8::Local<v8::Value> Converter<network::mojom::ReferrerPolicy>::ToV8(
v8::Isolate* isolate,
const network::mojom::ReferrerPolicy& in) {
switch (in) {
case network::mojom::ReferrerPolicy::kDefault:
return mate::StringToV8(isolate, "default");
case network::mojom::ReferrerPolicy::kAlways:
return mate::StringToV8(isolate, "unsafe-url");
case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade:
return mate::StringToV8(isolate, "no-referrer-when-downgrade");
case network::mojom::ReferrerPolicy::kNever:
return mate::StringToV8(isolate, "no-referrer");
case network::mojom::ReferrerPolicy::kOrigin:
return mate::StringToV8(isolate, "origin");
case network::mojom::ReferrerPolicy::
kNoReferrerWhenDowngradeOriginWhenCrossOrigin:
return mate::StringToV8(isolate, "strict-origin-when-cross-origin");
case network::mojom::ReferrerPolicy::kSameOrigin:
return mate::StringToV8(isolate, "same-origin");
case network::mojom::ReferrerPolicy::kStrictOrigin:
return mate::StringToV8(isolate, "strict-origin");
default:
return mate::StringToV8(isolate, "no-referrer");
}
}
// static
bool Converter<network::mojom::ReferrerPolicy>::FromV8(
v8::Isolate* isolate,
v8::Handle<v8::Value> val,
network::mojom::ReferrerPolicy* out) {
std::string policy = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (policy == "default")
*out = network::mojom::ReferrerPolicy::kDefault;
else if (policy == "unsafe-url")
*out = network::mojom::ReferrerPolicy::kAlways;
else if (policy == "no-referrer-when-downgrade")
*out = network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade;
else if (policy == "no-referrer")
*out = network::mojom::ReferrerPolicy::kNever;
else if (policy == "origin")
*out = network::mojom::ReferrerPolicy::kOrigin;
else if (policy == "strict-origin-when-cross-origin")
*out = network::mojom::ReferrerPolicy::
kNoReferrerWhenDowngradeOriginWhenCrossOrigin;
else if (policy == "same-origin")
*out = network::mojom::ReferrerPolicy::kSameOrigin;
else if (policy == "strict-origin")
*out = network::mojom::ReferrerPolicy::kStrictOrigin;
else
return false;
return true;
}
} // namespace mate

View file

@ -0,0 +1,139 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_
#include "native_mate/converter.h"
#include "third_party/blink/public/platform/web_cache.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/public/web/web_context_menu_data.h"
namespace blink {
class WebMouseEvent;
class WebMouseWheelEvent;
class WebKeyboardEvent;
struct WebDeviceEmulationParams;
struct WebFloatPoint;
struct WebPoint;
struct WebSize;
} // namespace blink
namespace content {
struct NativeWebKeyboardEvent;
}
namespace mate {
blink::WebInputEvent::Type GetWebInputEventType(v8::Isolate* isolate,
v8::Local<v8::Value> val);
template <>
struct Converter<blink::WebInputEvent> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebInputEvent* out);
};
template <>
struct Converter<blink::WebKeyboardEvent> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebKeyboardEvent* out);
};
template <>
struct Converter<content::NativeWebKeyboardEvent> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::NativeWebKeyboardEvent* out);
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const content::NativeWebKeyboardEvent& in);
};
template <>
struct Converter<blink::WebMouseEvent> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebMouseEvent* out);
};
template <>
struct Converter<blink::WebMouseWheelEvent> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebMouseWheelEvent* out);
};
template <>
struct Converter<blink::WebFloatPoint> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebFloatPoint* out);
};
template <>
struct Converter<blink::WebPoint> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebPoint* out);
};
template <>
struct Converter<blink::WebSize> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebSize* out);
};
template <>
struct Converter<blink::WebDeviceEmulationParams> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebDeviceEmulationParams* out);
};
template <>
struct Converter<blink::WebContextMenuData::MediaType> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const blink::WebContextMenuData::MediaType& in);
};
template <>
struct Converter<blink::WebContextMenuData::InputFieldType> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const blink::WebContextMenuData::InputFieldType& in);
};
template <>
struct Converter<blink::WebCache::ResourceTypeStat> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const blink::WebCache::ResourceTypeStat& stat);
};
template <>
struct Converter<blink::WebCache::ResourceTypeStats> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const blink::WebCache::ResourceTypeStats& stats);
};
template <>
struct Converter<network::mojom::ReferrerPolicy> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const network::mojom::ReferrerPolicy& in);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::ReferrerPolicy* out);
};
v8::Local<v8::Value> EditFlagsToV8(v8::Isolate* isolate, int editFlags);
v8::Local<v8::Value> MediaFlagsToV8(v8::Isolate* isolate, int mediaFlags);
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_

View file

@ -0,0 +1,163 @@
// Copyright (c) 2015 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/callback.h"
#include "base/stl_util.h"
#include "content/public/browser/browser_thread.h"
#include "native_mate/dictionary.h"
using content::BrowserThread;
namespace mate {
namespace internal {
namespace {
struct TranslaterHolder {
explicit TranslaterHolder(v8::Isolate* isolate)
: handle(isolate, v8::External::New(isolate, this)) {
handle.SetWeak(this, &GC, v8::WeakCallbackType::kFinalizer);
}
~TranslaterHolder() {
if (!handle.IsEmpty()) {
handle.ClearWeak();
handle.Reset();
}
}
static void GC(const v8::WeakCallbackInfo<TranslaterHolder>& data) {
delete data.GetParameter();
}
v8::Global<v8::External> handle;
Translater translater;
};
// Cached JavaScript version of |CallTranslater|.
v8::Persistent<v8::FunctionTemplate> g_call_translater;
void CallTranslater(v8::Local<v8::External> external,
v8::Local<v8::Object> state,
mate::Arguments* args) {
// Whether the callback should only be called for once.
v8::Isolate* isolate = args->isolate();
auto context = isolate->GetCurrentContext();
bool one_time =
state->Has(context, mate::StringToSymbol(isolate, "oneTime")).ToChecked();
// Check if the callback has already been called.
if (one_time) {
auto called_symbol = mate::StringToSymbol(isolate, "called");
if (state->Has(context, called_symbol).ToChecked()) {
args->ThrowError("callback can only be called for once");
return;
} else {
state->Set(context, called_symbol, v8::Boolean::New(isolate, true))
.ToChecked();
}
}
TranslaterHolder* holder = static_cast<TranslaterHolder*>(external->Value());
holder->translater.Run(args);
// Free immediately for one-time callback.
if (one_time)
delete holder;
}
} // namespace
// Destroy the class on UI thread when possible.
struct DeleteOnUIThread {
template <typename T>
static void Destruct(const T* x) {
if (Locker::IsBrowserProcess() &&
!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, x);
} else {
delete x;
}
}
};
// Like v8::Global, but ref-counted.
template <typename T>
class RefCountedGlobal
: public base::RefCountedThreadSafe<RefCountedGlobal<T>, DeleteOnUIThread> {
public:
RefCountedGlobal(v8::Isolate* isolate, v8::Local<v8::Value> value)
: handle_(isolate, v8::Local<T>::Cast(value)) {}
bool IsAlive() const { return !handle_.IsEmpty(); }
v8::Local<T> NewHandle(v8::Isolate* isolate) const {
return v8::Local<T>::New(isolate, handle_);
}
private:
v8::Global<T> handle_;
DISALLOW_COPY_AND_ASSIGN(RefCountedGlobal);
};
SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value)
: v8_function_(new RefCountedGlobal<v8::Function>(isolate, value)) {}
SafeV8Function::SafeV8Function(const SafeV8Function& other)
: v8_function_(other.v8_function_) {}
SafeV8Function::~SafeV8Function() {}
bool SafeV8Function::IsAlive() const {
return v8_function_.get() && v8_function_->IsAlive();
}
v8::Local<v8::Function> SafeV8Function::NewHandle(v8::Isolate* isolate) const {
return v8_function_->NewHandle(isolate);
}
v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
const Translater& translater,
bool one_time) {
// The FunctionTemplate is cached.
if (g_call_translater.IsEmpty())
g_call_translater.Reset(isolate,
mate::CreateFunctionTemplate(
isolate, base::BindRepeating(&CallTranslater)));
v8::Local<v8::FunctionTemplate> call_translater =
v8::Local<v8::FunctionTemplate>::New(isolate, g_call_translater);
auto* holder = new TranslaterHolder(isolate);
holder->translater = translater;
Dictionary state = mate::Dictionary::CreateEmpty(isolate);
if (one_time)
state.Set("oneTime", true);
auto context = isolate->GetCurrentContext();
return BindFunctionWith(
isolate, context, call_translater->GetFunction(context).ToLocalChecked(),
holder->handle.Get(isolate), state.GetHandle());
}
// func.bind(func, arg1).
// NB(zcbenz): Using C++11 version crashes VS.
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2) {
v8::MaybeLocal<v8::Value> bind =
func->Get(context, mate::StringToV8(isolate, "bind"));
CHECK(!bind.IsEmpty());
v8::Local<v8::Function> bind_func =
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
v8::Local<v8::Value> converted[] = {func, arg1, arg2};
return bind_func->Call(context, func, base::size(converted), converted)
.ToLocalChecked();
}
} // namespace internal
} // namespace mate

View file

@ -0,0 +1,181 @@
// Copyright (c) 2015 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_CALLBACK_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_CALLBACK_H_
#include <utility>
#include <vector>
#include "atom/common/api/locker.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "native_mate/function_template.h"
#include "native_mate/scoped_persistent.h"
namespace mate {
namespace internal {
template <typename T>
class RefCountedGlobal;
// Manages the V8 function with RAII.
class SafeV8Function {
public:
SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value);
SafeV8Function(const SafeV8Function& other);
~SafeV8Function();
bool IsAlive() const;
v8::Local<v8::Function> NewHandle(v8::Isolate* isolate) const;
private:
scoped_refptr<RefCountedGlobal<v8::Function>> v8_function_;
};
// Helper to invoke a V8 function with C++ parameters.
template <typename Sig>
struct V8FunctionInvoker {};
template <typename... ArgTypes>
struct V8FunctionInvoker<v8::Local<v8::Value>(ArgTypes...)> {
static v8::Local<v8::Value> Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
Locker locker(isolate);
v8::EscapableHandleScope handle_scope(isolate);
if (!function.IsAlive())
return v8::Null(isolate);
v8::MicrotasksScope script_scope(isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Local<v8::Function> holder = function.NewHandle(isolate);
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args{
ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
v8::MaybeLocal<v8::Value> ret = holder->Call(
context, holder, args.size(), args.empty() ? nullptr : &args.front());
if (ret.IsEmpty())
return v8::Undefined(isolate);
else
return handle_scope.Escape(ret.ToLocalChecked());
}
};
template <typename... ArgTypes>
struct V8FunctionInvoker<void(ArgTypes...)> {
static void Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
if (!function.IsAlive())
return;
v8::MicrotasksScope script_scope(isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Local<v8::Function> holder = function.NewHandle(isolate);
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args{
ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
holder
->Call(context, holder, args.size(),
args.empty() ? nullptr : &args.front())
.IsEmpty();
}
};
template <typename ReturnType, typename... ArgTypes>
struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
static ReturnType Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
ReturnType ret = ReturnType();
if (!function.IsAlive())
return ret;
v8::MicrotasksScope script_scope(isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Local<v8::Function> holder = function.NewHandle(isolate);
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args{
ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
v8::Local<v8::Value> result;
auto maybe_result = holder->Call(context, holder, args.size(),
args.empty() ? nullptr : &args.front());
if (maybe_result.ToLocal(&result))
Converter<ReturnType>::FromV8(isolate, result, &ret);
return ret;
}
};
// Helper to pass a C++ funtion to JavaScript.
using Translater = base::Callback<void(Arguments* args)>;
v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
const Translater& translater,
bool one_time);
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2);
// Calls callback with Arguments.
template <typename Sig>
struct NativeFunctionInvoker {};
template <typename ReturnType, typename... ArgTypes>
struct NativeFunctionInvoker<ReturnType(ArgTypes...)> {
static void Go(base::Callback<ReturnType(ArgTypes...)> val, Arguments* args) {
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
Invoker<Indices, ArgTypes...> invoker(args, 0);
if (invoker.IsOK())
invoker.DispatchToCallback(val);
}
};
} // namespace internal
template <typename Sig>
struct Converter<base::RepeatingCallback<Sig>> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::RepeatingCallback<Sig>& val) {
// We don't use CreateFunctionTemplate here because it creates a new
// FunctionTemplate everytime, which is cached by V8 and causes leaks.
internal::Translater translater =
base::Bind(&internal::NativeFunctionInvoker<Sig>::Go, val);
// To avoid memory leak, we ensure that the callback can only be called
// for once.
return internal::CreateFunctionFromTranslater(isolate, translater, true);
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::RepeatingCallback<Sig>* out) {
if (!val->IsFunction())
return false;
*out = base::BindRepeating(&internal::V8FunctionInvoker<Sig>::Go, isolate,
internal::SafeV8Function(isolate, val));
return true;
}
};
// Convert a callback to V8 without the call number limitation, this can easily
// cause memory leaks so use it with caution.
template <typename Sig>
v8::Local<v8::Value> CallbackToV8(v8::Isolate* isolate,
const base::Callback<Sig>& val) {
internal::Translater translater =
base::Bind(&internal::NativeFunctionInvoker<Sig>::Go, val);
return internal::CreateFunctionFromTranslater(isolate, translater, false);
}
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CALLBACK_H_

View file

@ -0,0 +1,251 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/content_converter.h"
#include <string>
#include <vector>
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/web_contents_permission_helper.h"
#include "atom/common/native_mate_converters/blink_converter.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/ui_base_types_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/context_menu_params.h"
#include "native_mate/dictionary.h"
namespace {
void ExecuteCommand(content::WebContents* web_contents,
int action,
const content::CustomContextMenuContext& context) {
web_contents->ExecuteCustomContextMenuCommand(action, context);
}
// Forward declaration for nested recursive call.
v8::Local<v8::Value> MenuToV8(v8::Isolate* isolate,
content::WebContents* web_contents,
const content::CustomContextMenuContext& context,
const std::vector<content::MenuItem>& menu);
v8::Local<v8::Value> MenuItemToV8(
v8::Isolate* isolate,
content::WebContents* web_contents,
const content::CustomContextMenuContext& context,
const content::MenuItem& item) {
mate::Dictionary v8_item = mate::Dictionary::CreateEmpty(isolate);
switch (item.type) {
case content::MenuItem::CHECKABLE_OPTION:
case content::MenuItem::GROUP:
v8_item.Set("checked", item.checked);
FALLTHROUGH;
case content::MenuItem::OPTION:
case content::MenuItem::SUBMENU:
v8_item.Set("label", item.label);
v8_item.Set("enabled", item.enabled);
FALLTHROUGH;
default:
v8_item.Set("type", item.type);
}
if (item.type == content::MenuItem::SUBMENU)
v8_item.Set("submenu",
MenuToV8(isolate, web_contents, context, item.submenu));
else if (item.action > 0)
v8_item.Set("click", base::BindRepeating(ExecuteCommand, web_contents,
item.action, context));
return v8_item.GetHandle();
}
v8::Local<v8::Value> MenuToV8(v8::Isolate* isolate,
content::WebContents* web_contents,
const content::CustomContextMenuContext& context,
const std::vector<content::MenuItem>& menu) {
std::vector<v8::Local<v8::Value>> v8_menu;
for (const auto& menu_item : menu)
v8_menu.push_back(MenuItemToV8(isolate, web_contents, context, menu_item));
return mate::ConvertToV8(isolate, v8_menu);
}
} // namespace
namespace mate {
// static
v8::Local<v8::Value> Converter<content::MenuItem::Type>::ToV8(
v8::Isolate* isolate,
const content::MenuItem::Type& val) {
switch (val) {
case content::MenuItem::CHECKABLE_OPTION:
return StringToV8(isolate, "checkbox");
case content::MenuItem::GROUP:
return StringToV8(isolate, "radio");
case content::MenuItem::SEPARATOR:
return StringToV8(isolate, "separator");
case content::MenuItem::SUBMENU:
return StringToV8(isolate, "submenu");
case content::MenuItem::OPTION:
default:
return StringToV8(isolate, "normal");
}
}
// static
v8::Local<v8::Value> Converter<ContextMenuParamsWithWebContents>::ToV8(
v8::Isolate* isolate,
const ContextMenuParamsWithWebContents& val) {
const auto& params = val.first;
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("x", params.x);
dict.Set("y", params.y);
dict.Set("linkURL", params.link_url);
dict.Set("linkText", params.link_text);
dict.Set("pageURL", params.page_url);
dict.Set("frameURL", params.frame_url);
dict.Set("srcURL", params.src_url);
dict.Set("mediaType", params.media_type);
dict.Set("mediaFlags", MediaFlagsToV8(isolate, params.media_flags));
bool has_image_contents =
(params.media_type == blink::WebContextMenuData::kMediaTypeImage) &&
params.has_image_contents;
dict.Set("hasImageContents", has_image_contents);
dict.Set("isEditable", params.is_editable);
dict.Set("editFlags", EditFlagsToV8(isolate, params.edit_flags));
dict.Set("selectionText", params.selection_text);
dict.Set("titleText", params.title_text);
dict.Set("misspelledWord", params.misspelled_word);
dict.Set("frameCharset", params.frame_charset);
dict.Set("inputFieldType", params.input_field_type);
dict.Set("menuSourceType", params.source_type);
if (params.custom_context.is_pepper_menu)
dict.Set("menu", MenuToV8(isolate, val.second, params.custom_context,
params.custom_items));
return mate::ConvertToV8(isolate, dict);
}
// static
bool Converter<blink::mojom::PermissionStatus>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::mojom::PermissionStatus* out) {
bool result;
if (!ConvertFromV8(isolate, val, &result))
return false;
if (result)
*out = blink::mojom::PermissionStatus::GRANTED;
else
*out = blink::mojom::PermissionStatus::DENIED;
return true;
}
// static
v8::Local<v8::Value> Converter<content::PermissionType>::ToV8(
v8::Isolate* isolate,
const content::PermissionType& val) {
using PermissionType = atom::WebContentsPermissionHelper::PermissionType;
switch (val) {
case content::PermissionType::MIDI_SYSEX:
return StringToV8(isolate, "midiSysex");
case content::PermissionType::NOTIFICATIONS:
return StringToV8(isolate, "notifications");
case content::PermissionType::GEOLOCATION:
return StringToV8(isolate, "geolocation");
case content::PermissionType::AUDIO_CAPTURE:
case content::PermissionType::VIDEO_CAPTURE:
return StringToV8(isolate, "media");
case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
return StringToV8(isolate, "mediaKeySystem");
case content::PermissionType::MIDI:
return StringToV8(isolate, "midi");
default:
break;
}
if (val == static_cast<content::PermissionType>(PermissionType::POINTER_LOCK))
return StringToV8(isolate, "pointerLock");
else if (val ==
static_cast<content::PermissionType>(PermissionType::FULLSCREEN))
return StringToV8(isolate, "fullscreen");
else if (val ==
static_cast<content::PermissionType>(PermissionType::OPEN_EXTERNAL))
return StringToV8(isolate, "openExternal");
return StringToV8(isolate, "unknown");
}
// static
bool Converter<content::StopFindAction>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::StopFindAction* out) {
std::string action;
if (!ConvertFromV8(isolate, val, &action))
return false;
if (action == "clearSelection")
*out = content::STOP_FIND_ACTION_CLEAR_SELECTION;
else if (action == "keepSelection")
*out = content::STOP_FIND_ACTION_KEEP_SELECTION;
else if (action == "activateSelection")
*out = content::STOP_FIND_ACTION_ACTIVATE_SELECTION;
else
return false;
return true;
}
// static
v8::Local<v8::Value> Converter<content::WebContents*>::ToV8(
v8::Isolate* isolate,
content::WebContents* val) {
if (!val)
return v8::Null(isolate);
return atom::api::WebContents::FromOrCreate(isolate, val).ToV8();
}
// static
bool Converter<content::WebContents*>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::WebContents** out) {
atom::api::WebContents* web_contents = nullptr;
if (!ConvertFromV8(isolate, val, &web_contents) || !web_contents)
return false;
*out = web_contents->web_contents();
return true;
}
// static
v8::Local<v8::Value> Converter<content::Referrer>::ToV8(
v8::Isolate* isolate,
const content::Referrer& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("url", ConvertToV8(isolate, val.url));
dict.Set("policy", ConvertToV8(isolate, val.policy));
return mate::ConvertToV8(isolate, dict);
}
// static
bool Converter<content::Referrer>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::Referrer* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!dict.Get("url", &out->url))
return false;
if (!dict.Get("policy", &out->policy))
return false;
return true;
}
} // namespace mate

View file

@ -0,0 +1,79 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
#include <utility>
#include "content/public/browser/permission_type.h"
#include "content/public/common/menu_item.h"
#include "content/public/common/referrer.h"
#include "content/public/common/stop_find_action.h"
#include "native_mate/converter.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
namespace content {
struct ContextMenuParams;
class WebContents;
} // namespace content
using ContextMenuParamsWithWebContents =
std::pair<content::ContextMenuParams, content::WebContents*>;
namespace mate {
template <>
struct Converter<content::MenuItem::Type> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const content::MenuItem::Type& val);
};
template <>
struct Converter<ContextMenuParamsWithWebContents> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const ContextMenuParamsWithWebContents& val);
};
template <>
struct Converter<blink::mojom::PermissionStatus> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::mojom::PermissionStatus* out);
};
template <>
struct Converter<content::PermissionType> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const content::PermissionType& val);
};
template <>
struct Converter<content::StopFindAction> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::StopFindAction* out);
};
template <>
struct Converter<content::WebContents*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
content::WebContents* val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::WebContents** out);
};
template <>
struct Converter<content::Referrer> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const content::Referrer& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::Referrer* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_

View file

@ -0,0 +1,76 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/file_dialog_converter.h"
#include "atom/browser/api/atom_api_browser_window.h"
#include "atom/browser/ui/file_dialog.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "native_mate/dictionary.h"
namespace mate {
bool Converter<file_dialog::Filter>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
file_dialog::Filter* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!dict.Get("name", &(out->first)))
return false;
if (!dict.Get("extensions", &(out->second)))
return false;
return true;
}
v8::Local<v8::Value> Converter<file_dialog::Filter>::ToV8(
v8::Isolate* isolate,
const file_dialog::Filter& in) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("name", in.first);
dict.Set("extensions", in.second);
return dict.GetHandle();
}
bool Converter<file_dialog::DialogSettings>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
file_dialog::DialogSettings* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
dict.Get("window", &(out->parent_window));
dict.Get("title", &(out->title));
dict.Get("message", &(out->message));
dict.Get("buttonLabel", &(out->button_label));
dict.Get("nameFieldLabel", &(out->name_field_label));
dict.Get("defaultPath", &(out->default_path));
dict.Get("filters", &(out->filters));
dict.Get("properties", &(out->properties));
dict.Get("showsTagField", &(out->shows_tag_field));
dict.Get("securityScopedBookmarks", &(out->security_scoped_bookmarks));
return true;
}
v8::Local<v8::Value> Converter<file_dialog::DialogSettings>::ToV8(
v8::Isolate* isolate,
const file_dialog::DialogSettings& in) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("window", atom::api::BrowserWindow::From(isolate, in.parent_window));
dict.Set("title", in.title);
dict.Set("message", in.message);
dict.Set("buttonLabel", in.button_label);
dict.Set("nameFieldLabel", in.name_field_label);
dict.Set("defaultPath", in.default_path);
dict.Set("filters", in.filters);
dict.Set("showsTagField", in.shows_tag_field);
dict.Set("securityScopedBookmarks", in.security_scoped_bookmarks);
return dict.GetHandle();
}
} // namespace mate

View file

@ -0,0 +1,33 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_FILE_DIALOG_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_FILE_DIALOG_CONVERTER_H_
#include "atom/browser/ui/file_dialog.h"
#include "native_mate/converter.h"
namespace mate {
template <>
struct Converter<file_dialog::Filter> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const file_dialog::Filter& in);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
file_dialog::Filter* out);
};
template <>
struct Converter<file_dialog::DialogSettings> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const file_dialog::DialogSettings& in);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
file_dialog::DialogSettings* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_FILE_DIALOG_CONVERTER_H_

View file

@ -0,0 +1,57 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_FILE_PATH_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_FILE_PATH_CONVERTER_H_
#include <string>
#include "atom/common/native_mate_converters/string16_converter.h"
#include "base/files/file_path.h"
namespace gin {
template <>
struct Converter<base::FilePath> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::FilePath& val) {
return Converter<base::FilePath::StringType>::ToV8(isolate, val.value());
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::FilePath* out) {
if (val->IsNull())
return true;
base::FilePath::StringType path;
if (Converter<base::FilePath::StringType>::FromV8(isolate, val, &path)) {
*out = base::FilePath(path);
return true;
} else {
return false;
}
}
};
} // namespace gin
namespace mate {
template <>
struct Converter<base::FilePath> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::FilePath& val) {
return gin::Converter<base::FilePath::StringType>::ToV8(isolate,
val.value());
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::FilePath* out) {
return gin::Converter<base::FilePath>::FromV8(isolate, val, out);
}
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_FILE_PATH_CONVERTER_H_

View file

@ -0,0 +1,161 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "native_mate/dictionary.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace mate {
v8::Local<v8::Value> Converter<gfx::Point>::ToV8(v8::Isolate* isolate,
const gfx::Point& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("x", val.x());
dict.Set("y", val.y());
return dict.GetHandle();
}
bool Converter<gfx::Point>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Point* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
double x, y;
if (!dict.Get("x", &x) || !dict.Get("y", &y))
return false;
*out = gfx::Point(static_cast<int>(std::round(x)),
static_cast<int>(std::round(y)));
return true;
}
v8::Local<v8::Value> Converter<gfx::PointF>::ToV8(v8::Isolate* isolate,
const gfx::PointF& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("x", val.x());
dict.Set("y", val.y());
return dict.GetHandle();
}
bool Converter<gfx::PointF>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::PointF* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
float x, y;
if (!dict.Get("x", &x) || !dict.Get("y", &y))
return false;
*out = gfx::PointF(x, y);
return true;
}
v8::Local<v8::Value> Converter<gfx::Size>::ToV8(v8::Isolate* isolate,
const gfx::Size& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
bool Converter<gfx::Size>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Size* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int width, height;
if (!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Size(width, height);
return true;
}
v8::Local<v8::Value> Converter<gfx::Rect>::ToV8(v8::Isolate* isolate,
const gfx::Rect& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("x", val.x());
dict.Set("y", val.y());
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
bool Converter<gfx::Rect>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Rect* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y, width, height;
if (!dict.Get("x", &x) || !dict.Get("y", &y) || !dict.Get("width", &width) ||
!dict.Get("height", &height))
return false;
*out = gfx::Rect(x, y, width, height);
return true;
}
template <>
struct Converter<display::Display::AccelerometerSupport> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const display::Display::AccelerometerSupport& val) {
switch (val) {
case display::Display::AccelerometerSupport::AVAILABLE:
return StringToV8(isolate, "available");
case display::Display::AccelerometerSupport::UNAVAILABLE:
return StringToV8(isolate, "unavailable");
default:
return StringToV8(isolate, "unknown");
}
}
};
template <>
struct Converter<display::Display::TouchSupport> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const display::Display::TouchSupport& val) {
switch (val) {
case display::Display::TouchSupport::AVAILABLE:
return StringToV8(isolate, "available");
case display::Display::TouchSupport::UNAVAILABLE:
return StringToV8(isolate, "unavailable");
default:
return StringToV8(isolate, "unknown");
}
}
};
v8::Local<v8::Value> Converter<display::Display>::ToV8(
v8::Isolate* isolate,
const display::Display& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true);
dict.Set("id", val.id());
dict.Set("bounds", val.bounds());
dict.Set("workArea", val.work_area());
dict.Set("accelerometerSupport", val.accelerometer_support());
dict.Set("monochrome", val.is_monochrome());
dict.Set("colorDepth", val.color_depth());
dict.Set("colorSpace", val.color_space().ToString());
dict.Set("depthPerComponent", val.depth_per_component());
dict.Set("size", val.size());
dict.Set("workAreaSize", val.work_area_size());
dict.Set("scaleFactor", val.device_scale_factor());
dict.Set("rotation", val.RotationAsDegree());
dict.Set("internal", val.IsInternal());
dict.Set("touchSupport", val.touch_support());
return dict.GetHandle();
}
} // namespace mate

View file

@ -0,0 +1,67 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_
#include "native_mate/converter.h"
#include "ui/gfx/geometry/point_f.h"
namespace display {
class Display;
}
namespace gfx {
class Point;
class Size;
class Rect;
} // namespace gfx
namespace mate {
template <>
struct Converter<gfx::Point> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const gfx::Point& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Point* out);
};
template <>
struct Converter<gfx::PointF> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::PointF& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::PointF* out);
};
template <>
struct Converter<gfx::Size> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const gfx::Size& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Size* out);
};
template <>
struct Converter<gfx::Rect> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const gfx::Rect& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Rect* out);
};
template <>
struct Converter<display::Display> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const display::Display& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
display::Display* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_

View file

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

View file

@ -0,0 +1,43 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/api/atom_api_native_image.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "ui/gfx/image/image_skia.h"
namespace mate {
bool Converter<gfx::ImageSkia>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::ImageSkia* out) {
gfx::Image image;
if (!ConvertFromV8(isolate, val, &image))
return false;
*out = image.AsImageSkia();
return true;
}
bool Converter<gfx::Image>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Image* out) {
if (val->IsNull())
return true;
Handle<atom::api::NativeImage> native_image;
if (!ConvertFromV8(isolate, val, &native_image))
return false;
*out = native_image->image();
return true;
}
v8::Local<v8::Value> Converter<gfx::Image>::ToV8(v8::Isolate* isolate,
const gfx::Image& val) {
return ConvertToV8(isolate, atom::api::NativeImage::Create(isolate, val));
}
} // namespace mate

View file

@ -0,0 +1,34 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_IMAGE_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_IMAGE_CONVERTER_H_
#include "native_mate/converter.h"
namespace gfx {
class Image;
class ImageSkia;
} // namespace gfx
namespace mate {
template <>
struct Converter<gfx::ImageSkia> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::ImageSkia* out);
};
template <>
struct Converter<gfx::Image> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gfx::Image* out);
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const gfx::Image& val);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_IMAGE_CONVERTER_H_

View file

@ -0,0 +1,70 @@
// Copyright (c) 2019 Slack Technologies, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_MAP_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_MAP_CONVERTER_H_
#include <map>
#include <string>
#include <utility>
#include "gin/converter.h"
namespace gin {
template <typename T>
struct Converter<std::map<std::string, T>> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
std::map<std::string, T>* out) {
if (!val->IsObject())
return false;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> dict = val->ToObject(context).ToLocalChecked();
v8::Local<v8::Array> keys =
dict->GetOwnPropertyNames(context).ToLocalChecked();
for (uint32_t i = 0; i < keys->Length(); ++i) {
v8::Local<v8::Value> key = keys->Get(context, i).ToLocalChecked();
T value;
if (Converter<T>::FromV8(
isolate, dict->Get(context, key).ToLocalChecked(), &value))
(*out)[gin::V8ToString(isolate, key)] = std::move(value);
}
return true;
}
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const std::map<std::string, T>& val) {
v8::Local<v8::Object> result = v8::Object::New(isolate);
auto context = isolate->GetCurrentContext();
for (auto i = val.begin(); i != val.end(); i++) {
result
->Set(context, Converter<T>::ToV8(isolate, i->first),
Converter<T>::ToV8(isolate, i->second))
.Check();
}
return result;
}
};
} // namespace gin
namespace mate {
template <typename T>
struct Converter<std::map<std::string, T>> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
std::map<std::string, T>* out) {
return gin::Converter<std::map<std::string, T>>::FromV8(isolate, val, out);
}
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const std::map<std::string, T>& val) {
return gin::Converter<std::map<std::string, T>>::ToV8(isolate, val);
}
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_MAP_CONVERTER_H_

View file

@ -0,0 +1,38 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/message_box_converter.h"
#include "atom/browser/api/atom_api_browser_window.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "native_mate/dictionary.h"
namespace mate {
bool Converter<atom::MessageBoxSettings>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
atom::MessageBoxSettings* out) {
mate::Dictionary dict;
int type = 0;
if (!ConvertFromV8(isolate, val, &dict))
return false;
dict.Get("window", &out->parent_window);
dict.Get("type", &type);
out->type = static_cast<atom::MessageBoxType>(type);
dict.Get("buttons", &out->buttons);
dict.Get("defaultId", &out->default_id);
dict.Get("cancelId", &out->cancel_id);
dict.Get("options", &out->options);
dict.Get("title", &out->title);
dict.Get("message", &out->message);
dict.Get("detail", &out->detail);
dict.Get("checkboxLabel", &out->checkbox_label);
dict.Get("checkboxChecked", &out->checkbox_checked);
dict.Get("icon", &out->icon);
return true;
}
} // namespace mate

View file

@ -0,0 +1,22 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_MESSAGE_BOX_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_MESSAGE_BOX_CONVERTER_H_
#include "atom/browser/ui/message_box.h"
#include "native_mate/converter.h"
namespace mate {
template <>
struct Converter<atom::MessageBoxSettings> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
atom::MessageBoxSettings* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_MESSAGE_BOX_CONVERTER_H_

View file

@ -0,0 +1,324 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/net_converter.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/node_includes.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "native_mate/dictionary.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_element_reader.h"
#include "net/base/upload_file_element_reader.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
#include "services/network/public/cpp/resource_request.h"
#include "storage/browser/blob/upload_blob_element_reader.h"
namespace mate {
namespace {
bool CertFromData(const std::string& data,
scoped_refptr<net::X509Certificate>* out) {
auto cert_list = net::X509Certificate::CreateCertificateListFromBytes(
data.c_str(), data.length(),
net::X509Certificate::FORMAT_SINGLE_CERTIFICATE);
if (cert_list.empty())
return false;
auto leaf_cert = cert_list.front();
if (!leaf_cert)
return false;
*out = leaf_cert;
return true;
}
} // namespace
// static
v8::Local<v8::Value> Converter<net::AuthChallengeInfo>::ToV8(
v8::Isolate* isolate,
const net::AuthChallengeInfo& val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("isProxy", val.is_proxy);
dict.Set("scheme", val.scheme);
dict.Set("host", val.challenger.host());
dict.Set("port", static_cast<uint32_t>(val.challenger.port()));
dict.Set("realm", val.realm);
return mate::ConvertToV8(isolate, dict);
}
// static
v8::Local<v8::Value> Converter<scoped_refptr<net::X509Certificate>>::ToV8(
v8::Isolate* isolate,
const scoped_refptr<net::X509Certificate>& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
std::string encoded_data;
net::X509Certificate::GetPEMEncoded(val->cert_buffer(), &encoded_data);
dict.Set("data", encoded_data);
dict.Set("issuer", val->issuer());
dict.Set("issuerName", val->issuer().GetDisplayName());
dict.Set("subject", val->subject());
dict.Set("subjectName", val->subject().GetDisplayName());
dict.Set("serialNumber", base::HexEncode(val->serial_number().data(),
val->serial_number().size()));
dict.Set("validStart", val->valid_start().ToDoubleT());
dict.Set("validExpiry", val->valid_expiry().ToDoubleT());
dict.Set("fingerprint",
net::HashValue(val->CalculateFingerprint256(val->cert_buffer()))
.ToString());
const auto& intermediate_buffers = val->intermediate_buffers();
if (!intermediate_buffers.empty()) {
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> issuer_intermediates;
issuer_intermediates.reserve(intermediate_buffers.size() - 1);
for (size_t i = 1; i < intermediate_buffers.size(); ++i) {
issuer_intermediates.push_back(
bssl::UpRef(intermediate_buffers[i].get()));
}
const scoped_refptr<net::X509Certificate>& issuer_cert =
net::X509Certificate::CreateFromBuffer(
bssl::UpRef(intermediate_buffers[0].get()),
std::move(issuer_intermediates));
dict.Set("issuerCert", issuer_cert);
}
return dict.GetHandle();
}
bool Converter<scoped_refptr<net::X509Certificate>>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
scoped_refptr<net::X509Certificate>* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
std::string data;
dict.Get("data", &data);
scoped_refptr<net::X509Certificate> leaf_cert;
if (!CertFromData(data, &leaf_cert))
return false;
scoped_refptr<net::X509Certificate> issuer_cert;
if (dict.Get("issuerCert", &issuer_cert)) {
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
intermediates.push_back(bssl::UpRef(issuer_cert->cert_buffer()));
auto cert = net::X509Certificate::CreateFromBuffer(
bssl::UpRef(leaf_cert->cert_buffer()), std::move(intermediates));
if (!cert)
return false;
*out = cert;
} else {
*out = leaf_cert;
}
return true;
}
// static
v8::Local<v8::Value> Converter<net::CertPrincipal>::ToV8(
v8::Isolate* isolate,
const net::CertPrincipal& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("commonName", val.common_name);
dict.Set("organizations", val.organization_names);
dict.Set("organizationUnits", val.organization_unit_names);
dict.Set("locality", val.locality_name);
dict.Set("state", val.state_or_province_name);
dict.Set("country", val.country_name);
return dict.GetHandle();
}
// static
v8::Local<v8::Value> Converter<net::HttpResponseHeaders*>::ToV8(
v8::Isolate* isolate,
net::HttpResponseHeaders* headers) {
base::DictionaryValue response_headers;
if (headers) {
size_t iter = 0;
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
key = base::ToLowerASCII(key);
if (response_headers.FindKey(key)) {
base::ListValue* values = nullptr;
if (response_headers.GetList(key, &values))
values->AppendString(value);
} else {
auto values = std::make_unique<base::ListValue>();
values->AppendString(value);
response_headers.Set(key, std::move(values));
}
}
}
return ConvertToV8(isolate, response_headers);
}
bool Converter<net::HttpResponseHeaders*>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::HttpResponseHeaders* out) {
if (!val->IsObject()) {
return false;
}
auto addHeaderFromValue = [&isolate, &out](
const std::string& key,
const v8::Local<v8::Value>& localVal) {
auto context = isolate->GetCurrentContext();
v8::Local<v8::String> localStrVal;
if (!localVal->ToString(context).ToLocal(&localStrVal)) {
return false;
}
std::string value;
mate::ConvertFromV8(isolate, localStrVal, &value);
out->AddHeader(key + ": " + value);
return true;
};
auto context = isolate->GetCurrentContext();
auto headers = v8::Local<v8::Object>::Cast(val);
auto keys = headers->GetOwnPropertyNames(context).ToLocalChecked();
for (uint32_t i = 0; i < keys->Length(); i++) {
v8::Local<v8::Value> keyVal;
if (!keys->Get(context, i).ToLocal(&keyVal)) {
return false;
}
std::string key;
mate::ConvertFromV8(isolate, keyVal, &key);
auto localVal = headers->Get(context, keyVal).ToLocalChecked();
if (localVal->IsArray()) {
auto values = v8::Local<v8::Array>::Cast(localVal);
for (uint32_t j = 0; j < values->Length(); j++) {
if (!addHeaderFromValue(key,
values->Get(context, j).ToLocalChecked())) {
return false;
}
}
} else {
if (!addHeaderFromValue(key, localVal)) {
return false;
}
}
}
return true;
}
// static
v8::Local<v8::Value> Converter<network::ResourceRequest>::ToV8(
v8::Isolate* isolate,
const network::ResourceRequest& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("method", val.method);
dict.Set("url", val.url.spec());
dict.Set("referrer", val.referrer.spec());
mate::Dictionary headers(isolate, v8::Object::New(isolate));
for (net::HttpRequestHeaders::Iterator it(val.headers); it.GetNext();)
headers.Set(it.name(), it.value());
dict.Set("headers", headers);
if (val.request_body) {
const auto& elements = *val.request_body->elements();
v8::Local<v8::Array> arr = v8::Array::New(isolate, elements.size());
for (size_t i = 0; i < elements.size(); ++i) {
const auto& element = elements[i];
mate::Dictionary upload_data(isolate, v8::Object::New(isolate));
switch (element.type()) {
case network::mojom::DataElementType::kFile:
upload_data.Set("file", element.path().value());
break;
case network::mojom::DataElementType::kBytes:
upload_data.Set("bytes", node::Buffer::Copy(isolate, element.bytes(),
element.length())
.ToLocalChecked());
break;
case network::mojom::DataElementType::kBlob:
upload_data.Set("blobUUID", element.blob_uuid());
break;
default:
NOTREACHED() << "Found unsupported data element";
}
arr->Set(isolate->GetCurrentContext(), static_cast<uint32_t>(i),
upload_data.GetHandle())
.Check();
}
dict.Set("uploadData", arr);
}
return dict.GetHandle();
}
} // namespace mate
namespace atom {
void FillRequestDetails(base::DictionaryValue* details,
const net::URLRequest* request) {
details->SetString("method", request->method());
std::string url;
if (!request->url_chain().empty())
url = request->url().spec();
details->SetKey("url", base::Value(url));
details->SetString("referrer", request->referrer());
auto list = std::make_unique<base::ListValue>();
GetUploadData(list.get(), request);
if (!list->empty())
details->Set("uploadData", std::move(list));
auto headers_value = std::make_unique<base::DictionaryValue>();
for (net::HttpRequestHeaders::Iterator it(request->extra_request_headers());
it.GetNext();) {
headers_value->SetString(it.name(), it.value());
}
details->Set("headers", std::move(headers_value));
}
void GetUploadData(base::ListValue* upload_data_list,
const net::URLRequest* request) {
const net::UploadDataStream* upload_data = request->get_upload();
if (!upload_data)
return;
const std::vector<std::unique_ptr<net::UploadElementReader>>* readers =
upload_data->GetElementReaders();
for (const auto& reader : *readers) {
auto upload_data_dict = std::make_unique<base::DictionaryValue>();
if (reader->AsBytesReader()) {
const net::UploadBytesElementReader* bytes_reader =
reader->AsBytesReader();
auto bytes = std::make_unique<base::Value>(
std::vector<char>(bytes_reader->bytes(),
bytes_reader->bytes() + bytes_reader->length()));
upload_data_dict->Set("bytes", std::move(bytes));
} else if (reader->AsFileReader()) {
const net::UploadFileElementReader* file_reader = reader->AsFileReader();
auto file_path = file_reader->path().AsUTF8Unsafe();
upload_data_dict->SetKey("file", base::Value(file_path));
} else {
const storage::UploadBlobElementReader* blob_reader =
static_cast<storage::UploadBlobElementReader*>(reader.get());
upload_data_dict->SetString("blobUUID", blob_reader->uuid());
}
upload_data_list->Append(std::move(upload_data_dict));
}
}
} // namespace atom

View file

@ -0,0 +1,80 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_NET_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_NET_CONVERTER_H_
#include "base/memory/ref_counted.h"
#include "native_mate/converter.h"
namespace base {
class DictionaryValue;
class ListValue;
} // namespace base
namespace net {
class AuthChallengeInfo;
class URLRequest;
class X509Certificate;
class HttpResponseHeaders;
struct CertPrincipal;
} // namespace net
namespace network {
struct ResourceRequest;
}
namespace mate {
template <>
struct Converter<net::AuthChallengeInfo> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::AuthChallengeInfo& val);
};
template <>
struct Converter<scoped_refptr<net::X509Certificate>> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const scoped_refptr<net::X509Certificate>& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
scoped_refptr<net::X509Certificate>* out);
};
template <>
struct Converter<net::CertPrincipal> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::CertPrincipal& val);
};
template <>
struct Converter<net::HttpResponseHeaders*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
net::HttpResponseHeaders* headers);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::HttpResponseHeaders* out);
};
template <>
struct Converter<network::ResourceRequest> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const network::ResourceRequest& val);
};
} // namespace mate
namespace atom {
void FillRequestDetails(base::DictionaryValue* details,
const net::URLRequest* request);
void GetUploadData(base::ListValue* upload_data_list,
const net::URLRequest* request);
} // namespace atom
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_NET_CONVERTER_H_

View file

@ -0,0 +1,94 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/network_converter.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "atom/common/native_mate_converters/value_converter.h"
#include "base/numerics/safe_conversions.h"
#include "native_mate/dictionary.h"
#include "services/network/public/cpp/resource_request_body.h"
namespace mate {
// static
v8::Local<v8::Value>
Converter<scoped_refptr<network::ResourceRequestBody>>::ToV8(
v8::Isolate* isolate,
const scoped_refptr<network::ResourceRequestBody>& val) {
if (!val)
return v8::Null(isolate);
auto list = std::make_unique<base::ListValue>();
for (const auto& element : *(val->elements())) {
auto post_data_dict = std::make_unique<base::DictionaryValue>();
auto type = element.type();
if (type == network::mojom::DataElementType::kBytes) {
auto bytes = std::make_unique<base::Value>(std::vector<char>(
element.bytes(), element.bytes() + (element.length())));
post_data_dict->SetString("type", "rawData");
post_data_dict->Set("bytes", std::move(bytes));
} else if (type == network::mojom::DataElementType::kFile) {
post_data_dict->SetString("type", "file");
post_data_dict->SetKey("filePath",
base::Value(element.path().AsUTF8Unsafe()));
post_data_dict->SetInteger("offset", static_cast<int>(element.offset()));
post_data_dict->SetInteger("length", static_cast<int>(element.length()));
post_data_dict->SetDouble(
"modificationTime", element.expected_modification_time().ToDoubleT());
} else if (type == network::mojom::DataElementType::kBlob) {
post_data_dict->SetString("type", "blob");
post_data_dict->SetString("blobUUID", element.blob_uuid());
}
list->Append(std::move(post_data_dict));
}
return ConvertToV8(isolate, *list);
}
// static
bool Converter<scoped_refptr<network::ResourceRequestBody>>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
scoped_refptr<network::ResourceRequestBody>* out) {
auto list = std::make_unique<base::ListValue>();
if (!ConvertFromV8(isolate, val, list.get()))
return false;
*out = new network::ResourceRequestBody();
for (size_t i = 0; i < list->GetSize(); ++i) {
base::DictionaryValue* dict = nullptr;
std::string type;
if (!list->GetDictionary(i, &dict))
return false;
dict->GetString("type", &type);
if (type == "rawData") {
base::Value* bytes = nullptr;
dict->GetBinary("bytes", &bytes);
(*out)->AppendBytes(
reinterpret_cast<const char*>(bytes->GetBlob().data()),
base::checked_cast<int>(bytes->GetBlob().size()));
} else if (type == "file") {
std::string file;
int offset = 0, length = -1;
double modification_time = 0.0;
dict->GetStringWithoutPathExpansion("filePath", &file);
dict->GetInteger("offset", &offset);
dict->GetInteger("file", &length);
dict->GetDouble("modificationTime", &modification_time);
(*out)->AppendFileRange(base::FilePath::FromUTF8Unsafe(file),
static_cast<uint64_t>(offset),
static_cast<uint64_t>(length),
base::Time::FromDoubleT(modification_time));
} else if (type == "blob") {
std::string uuid;
dict->GetString("blobUUID", &uuid);
(*out)->AppendBlob(uuid);
}
}
return true;
}
} // namespace mate

View file

@ -0,0 +1,29 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_NETWORK_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_NETWORK_CONVERTER_H_
#include "base/memory/scoped_refptr.h"
#include "native_mate/converter.h"
namespace network {
class ResourceRequestBody;
}
namespace mate {
template <>
struct Converter<scoped_refptr<network::ResourceRequestBody>> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const scoped_refptr<network::ResourceRequestBody>& val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
scoped_refptr<network::ResourceRequestBody>* out);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_NETWORK_CONVERTER_H_

View file

@ -0,0 +1,87 @@
// Copyright (c) 2019 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_ONCE_CALLBACK_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_ONCE_CALLBACK_H_
#include <utility>
#include "atom/common/native_mate_converters/callback.h"
namespace mate {
namespace internal {
// Manages the OnceCallback with ref-couting.
template <typename Sig>
class RefCountedOnceCallback
: public base::RefCounted<RefCountedOnceCallback<Sig>> {
public:
explicit RefCountedOnceCallback(base::OnceCallback<Sig> callback)
: callback_(std::move(callback)) {}
base::OnceCallback<Sig> GetCallback() { return std::move(callback_); }
private:
friend class base::RefCounted<RefCountedOnceCallback<Sig>>;
~RefCountedOnceCallback() = default;
base::OnceCallback<Sig> callback_;
};
// Invokes the OnceCallback.
template <typename Sig>
struct InvokeOnceCallback {};
template <typename... ArgTypes>
struct InvokeOnceCallback<void(ArgTypes...)> {
static void Go(
scoped_refptr<RefCountedOnceCallback<void(ArgTypes...)>> holder,
ArgTypes... args) {
base::OnceCallback<void(ArgTypes...)> callback = holder->GetCallback();
DCHECK(!callback.is_null());
std::move(callback).Run(std::move(args)...);
}
};
template <typename ReturnType, typename... ArgTypes>
struct InvokeOnceCallback<ReturnType(ArgTypes...)> {
static ReturnType Go(
scoped_refptr<RefCountedOnceCallback<ReturnType(ArgTypes...)>> holder,
ArgTypes... args) {
base::OnceCallback<void(ArgTypes...)> callback = holder->GetCallback();
DCHECK(!callback.is_null());
return std::move(callback).Run(std::move(args)...);
}
};
} // namespace internal
template <typename Sig>
struct Converter<base::OnceCallback<Sig>> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
base::OnceCallback<Sig> val) {
// Reuse the converter of base::RepeatingCallback by storing the callback
// with a RefCounted.
auto holder = base::MakeRefCounted<internal::RefCountedOnceCallback<Sig>>(
std::move(val));
return Converter<base::RepeatingCallback<Sig>>::ToV8(
isolate,
base::BindRepeating(&internal::InvokeOnceCallback<Sig>::Go, holder));
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::OnceCallback<Sig>* out) {
if (!val->IsFunction())
return false;
*out = base::BindOnce(&internal::V8FunctionInvoker<Sig>::Go, isolate,
internal::SafeV8Function(isolate, val));
return true;
}
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_ONCE_CALLBACK_H_

View file

@ -0,0 +1,64 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING16_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING16_CONVERTER_H_
#include "base/strings/string16.h"
#include "gin/converter.h"
#include "native_mate/converter.h"
namespace gin {
template <>
struct Converter<base::string16> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::string16& val) {
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(val.data()),
v8::NewStringType::kNormal, val.size())
.ToLocalChecked();
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::string16* out) {
if (!val->IsString())
return false;
v8::String::Value s(isolate, val);
out->assign(reinterpret_cast<const base::char16*>(*s), s.length());
return true;
}
};
inline v8::Local<v8::String> StringToV8(v8::Isolate* isolate,
const base::string16& input) {
return ConvertToV8(isolate, input).As<v8::String>();
}
} // namespace gin
namespace mate {
template <>
struct Converter<base::string16> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::string16& val) {
return gin::Converter<base::string16>::ToV8(isolate, val);
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::string16* out) {
return gin::Converter<base::string16>::FromV8(isolate, val, out);
}
};
inline v8::Local<v8::String> StringToV8(v8::Isolate* isolate,
const base::string16& input) {
return ConvertToV8(isolate, input).As<v8::String>();
}
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING16_CONVERTER_H_

View file

@ -0,0 +1,34 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_UI_BASE_TYPES_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_UI_BASE_TYPES_CONVERTER_H_
#include "native_mate/converter.h"
#include "ui/base/ui_base_types.h"
namespace mate {
template <>
struct Converter<ui::MenuSourceType> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const ui::MenuSourceType& in) {
switch (in) {
case ui::MENU_SOURCE_MOUSE:
return mate::StringToV8(isolate, "mouse");
case ui::MENU_SOURCE_KEYBOARD:
return mate::StringToV8(isolate, "keyboard");
case ui::MENU_SOURCE_TOUCH:
return mate::StringToV8(isolate, "touch");
case ui::MENU_SOURCE_TOUCH_EDIT_MENU:
return mate::StringToV8(isolate, "touchMenu");
default:
return mate::StringToV8(isolate, "none");
}
}
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_UI_BASE_TYPES_CONVERTER_H_

View file

@ -0,0 +1,517 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/v8_value_converter.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/values.h"
#include "native_mate/dictionary.h"
#include "atom/common/node_bindings.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace {
const int kMaxRecursionDepth = 100;
} // namespace
// The state of a call to FromV8Value.
class V8ValueConverter::FromV8ValueState {
public:
// Level scope which updates the current depth of some FromV8ValueState.
class Level {
public:
explicit Level(FromV8ValueState* state) : state_(state) {
state_->max_recursion_depth_--;
}
~Level() { state_->max_recursion_depth_++; }
private:
FromV8ValueState* state_;
};
FromV8ValueState() : max_recursion_depth_(kMaxRecursionDepth) {}
// If |handle| is not in |unique_map_|, then add it to |unique_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 AddToUniquenessCheck(v8::Local<v8::Object> handle) {
int hash;
auto iter = GetIteratorInMap(handle, &hash);
if (iter != unique_map_.end())
return false;
unique_map_.insert(std::make_pair(hash, handle));
return true;
}
bool RemoveFromUniquenessCheck(v8::Local<v8::Object> handle) {
int unused_hash;
auto iter = GetIteratorInMap(handle, &unused_hash);
if (iter == unique_map_.end())
return false;
unique_map_.erase(iter);
return true;
}
bool HasReachedMaxRecursionDepth() { return max_recursion_depth_ < 0; }
private:
using HashToHandleMap = std::multimap<int, v8::Local<v8::Object>>;
using Iterator = HashToHandleMap::const_iterator;
Iterator GetIteratorInMap(v8::Local<v8::Object> handle, int* hash) {
*hash = 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 = unique_map_.equal_range(*hash);
for (auto it = range.first; it != range.second; ++it) {
// Operator == for handles actually compares the underlying objects.
if (it->second == handle)
return it;
}
// Not found.
return unique_map_.end();
}
HashToHandleMap unique_map_;
int max_recursion_depth_;
};
// A class to ensure that objects/arrays that are being converted by
// this V8ValueConverterImpl do not have cycles.
//
// An example of cycle: var v = {}; v = {key: v};
// Not an example of cycle: var v = {}; a = [v, v]; or w = {a: v, b: v};
class V8ValueConverter::ScopedUniquenessGuard {
public:
ScopedUniquenessGuard(V8ValueConverter::FromV8ValueState* state,
v8::Local<v8::Object> value)
: state_(state),
value_(value),
is_valid_(state_->AddToUniquenessCheck(value_)) {}
~ScopedUniquenessGuard() {
if (is_valid_) {
bool removed = state_->RemoveFromUniquenessCheck(value_);
DCHECK(removed);
}
}
bool is_valid() const { return is_valid_; }
private:
typedef std::multimap<int, v8::Local<v8::Object>> HashToHandleMap;
V8ValueConverter::FromV8ValueState* state_;
v8::Local<v8::Object> value_;
bool is_valid_;
DISALLOW_COPY_AND_ASSIGN(ScopedUniquenessGuard);
};
V8ValueConverter::V8ValueConverter() {}
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::Local<v8::Value> V8ValueConverter::ToV8Value(
const base::Value* value,
v8::Local<v8::Context> context) const {
v8::Context::Scope context_scope(context);
v8::EscapableHandleScope handle_scope(context->GetIsolate());
return handle_scope.Escape(ToV8ValueImpl(context->GetIsolate(), value));
}
std::unique_ptr<base::Value> V8ValueConverter::FromV8Value(
v8::Local<v8::Value> val,
v8::Local<v8::Context> context) const {
v8::Context::Scope context_scope(context);
v8::HandleScope handle_scope(context->GetIsolate());
FromV8ValueState state;
return FromV8ValueImpl(&state, val, context->GetIsolate());
}
v8::Local<v8::Value> V8ValueConverter::ToV8ValueImpl(
v8::Isolate* isolate,
const base::Value* value) const {
switch (value->type()) {
case base::Value::Type::NONE:
return v8::Null(isolate);
case base::Value::Type::BOOLEAN: {
bool val = value->GetBool();
return v8::Boolean::New(isolate, val);
}
case base::Value::Type::INTEGER: {
int val = value->GetInt();
return v8::Integer::New(isolate, val);
}
case base::Value::Type::DOUBLE: {
double val = value->GetDouble();
return v8::Number::New(isolate, val);
}
case base::Value::Type::STRING: {
std::string val = value->GetString();
return v8::String::NewFromUtf8(isolate, val.c_str(),
v8::NewStringType::kNormal, val.length())
.ToLocalChecked();
}
case base::Value::Type::LIST:
return ToV8Array(isolate, static_cast<const base::ListValue*>(value));
case base::Value::Type::DICTIONARY:
return ToV8Object(isolate,
static_cast<const base::DictionaryValue*>(value));
case base::Value::Type::BINARY:
return ToArrayBuffer(isolate, static_cast<const base::Value*>(value));
default:
LOG(ERROR) << "Unexpected value type: " << value->type();
return v8::Null(isolate);
}
}
v8::Local<v8::Value> V8ValueConverter::ToV8Array(
v8::Isolate* isolate,
const base::ListValue* val) const {
v8::Local<v8::Array> result(v8::Array::New(isolate, val->GetSize()));
auto context = isolate->GetCurrentContext();
for (size_t i = 0; i < val->GetSize(); ++i) {
const base::Value* child = nullptr;
val->Get(i, &child);
v8::Local<v8::Value> child_v8 = ToV8ValueImpl(isolate, child);
v8::TryCatch try_catch(isolate);
result->Set(context, static_cast<uint32_t>(i), child_v8).Check();
if (try_catch.HasCaught())
LOG(ERROR) << "Setter for index " << i << " threw an exception.";
}
return result;
}
v8::Local<v8::Value> V8ValueConverter::ToV8Object(
v8::Isolate* isolate,
const base::DictionaryValue* val) const {
mate::Dictionary result = mate::Dictionary::CreateEmpty(isolate);
result.SetHidden("simple", true);
for (base::DictionaryValue::Iterator iter(*val); !iter.IsAtEnd();
iter.Advance()) {
const std::string& key = iter.key();
v8::Local<v8::Value> child_v8 = ToV8ValueImpl(isolate, &iter.value());
v8::TryCatch try_catch(isolate);
result.Set(key, child_v8);
if (try_catch.HasCaught()) {
LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
<< "exception.";
}
}
return result.GetHandle();
}
v8::Local<v8::Value> V8ValueConverter::ToArrayBuffer(
v8::Isolate* isolate,
const base::Value* value) const {
const auto* data = reinterpret_cast<const char*>(value->GetBlob().data());
size_t length = value->GetBlob().size();
if (NodeBindings::IsInitialized()) {
return node::Buffer::Copy(isolate, data, length).ToLocalChecked();
}
if (length > node::Buffer::kMaxLength) {
return v8::Local<v8::Object>();
}
auto context = isolate->GetCurrentContext();
auto array_buffer = v8::ArrayBuffer::New(isolate, length);
memcpy(array_buffer->GetContents().Data(), data, length);
// From this point, if something goes wrong(can't find Buffer class for
// example) we'll simply return a Uint8Array based on the created ArrayBuffer.
// This can happen if no preload script was specified to the renderer.
mate::Dictionary global(isolate, context->Global());
v8::Local<v8::Value> buffer_value;
// Get the Buffer class stored as a hidden value in the global object. We'll
// use it return a browserified Buffer.
if (!global.GetHidden("Buffer", &buffer_value) ||
!buffer_value->IsFunction()) {
return v8::Uint8Array::New(array_buffer, 0, length);
}
mate::Dictionary buffer_class(
isolate,
buffer_value->ToObject(isolate->GetCurrentContext()).ToLocalChecked());
v8::Local<v8::Value> from_value;
if (!buffer_class.Get("from", &from_value) || !from_value->IsFunction()) {
return v8::Uint8Array::New(array_buffer, 0, length);
}
v8::Local<v8::Value> args[] = {array_buffer};
auto func = v8::Local<v8::Function>::Cast(from_value);
auto result = func->Call(context, v8::Null(isolate), 1, args);
if (!result.IsEmpty()) {
return result.ToLocalChecked();
}
return v8::Uint8Array::New(array_buffer, 0, length);
}
std::unique_ptr<base::Value> V8ValueConverter::FromV8ValueImpl(
FromV8ValueState* state,
v8::Local<v8::Value> val,
v8::Isolate* isolate) const {
FromV8ValueState::Level state_level(state);
if (state->HasReachedMaxRecursionDepth())
return nullptr;
if (val->IsExternal())
return std::make_unique<base::Value>();
if (val->IsNull())
return std::make_unique<base::Value>();
auto context = isolate->GetCurrentContext();
if (val->IsBoolean())
return std::make_unique<base::Value>(val->ToBoolean(isolate)->Value());
if (val->IsInt32())
return std::make_unique<base::Value>(val.As<v8::Int32>()->Value());
if (val->IsNumber()) {
double val_as_double = val.As<v8::Number>()->Value();
if (!std::isfinite(val_as_double))
return nullptr;
return std::make_unique<base::Value>(val_as_double);
}
if (val->IsString()) {
v8::String::Utf8Value utf8(isolate, val);
return std::make_unique<base::Value>(std::string(*utf8, utf8.length()));
}
if (val->IsUndefined())
// JSON.stringify ignores undefined.
return nullptr;
if (val->IsDate()) {
v8::Date* date = v8::Date::Cast(*val);
v8::Local<v8::Value> toISOString =
date->Get(context, v8::String::NewFromUtf8(isolate, "toISOString",
v8::NewStringType::kNormal)
.ToLocalChecked())
.ToLocalChecked();
if (toISOString->IsFunction()) {
v8::MaybeLocal<v8::Value> result =
toISOString.As<v8::Function>()->Call(context, val, 0, nullptr);
if (!result.IsEmpty()) {
v8::String::Utf8Value utf8(isolate, result.ToLocalChecked());
return std::make_unique<base::Value>(std::string(*utf8, utf8.length()));
}
}
}
if (val->IsRegExp()) {
if (!reg_exp_allowed_)
// JSON.stringify converts to an object.
return FromV8Object(val.As<v8::Object>(), state, isolate);
return std::make_unique<base::Value>(*v8::String::Utf8Value(isolate, val));
}
// v8::Value doesn't have a ToArray() method for some reason.
if (val->IsArray())
return FromV8Array(val.As<v8::Array>(), state, isolate);
if (val->IsFunction()) {
if (!function_allowed_)
// JSON.stringify refuses to convert function(){}.
return nullptr;
return FromV8Object(val.As<v8::Object>(), state, isolate);
}
if (node::Buffer::HasInstance(val)) {
return FromNodeBuffer(val, state, isolate);
}
if (val->IsObject()) {
return FromV8Object(val.As<v8::Object>(), state, isolate);
}
LOG(ERROR) << "Unexpected v8 value type encountered.";
return nullptr;
}
std::unique_ptr<base::Value> V8ValueConverter::FromV8Array(
v8::Local<v8::Array> val,
FromV8ValueState* state,
v8::Isolate* isolate) const {
ScopedUniquenessGuard uniqueness_guard(state, val);
if (!uniqueness_guard.is_valid())
return std::make_unique<base::Value>();
std::unique_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() != isolate->GetCurrentContext())
scope.reset(new v8::Context::Scope(val->CreationContext()));
std::unique_ptr<base::ListValue> result(new base::ListValue());
// Only fields with integer keys are carried over to the ListValue.
for (uint32_t i = 0; i < val->Length(); ++i) {
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> child_v8;
v8::MaybeLocal<v8::Value> maybe_child =
val->Get(isolate->GetCurrentContext(), i);
if (try_catch.HasCaught() || !maybe_child.ToLocal(&child_v8)) {
LOG(ERROR) << "Getter for index " << i << " threw an exception.";
child_v8 = v8::Null(isolate);
}
if (!val->HasRealIndexedProperty(isolate->GetCurrentContext(), i)
.FromMaybe(false)) {
result->Append(std::make_unique<base::Value>());
continue;
}
std::unique_ptr<base::Value> child =
FromV8ValueImpl(state, child_v8, isolate);
if (child)
result->Append(std::move(child));
else
// JSON.stringify puts null in places where values don't serialize, for
// example undefined and functions. Emulate that behavior.
result->Append(std::make_unique<base::Value>());
}
return std::move(result);
}
std::unique_ptr<base::Value> V8ValueConverter::FromNodeBuffer(
v8::Local<v8::Value> value,
FromV8ValueState* state,
v8::Isolate* isolate) const {
std::vector<char> buffer(
node::Buffer::Data(value),
node::Buffer::Data(value) + node::Buffer::Length(value));
return std::make_unique<base::Value>(std::move(buffer));
}
std::unique_ptr<base::Value> V8ValueConverter::FromV8Object(
v8::Local<v8::Object> val,
FromV8ValueState* state,
v8::Isolate* isolate) const {
ScopedUniquenessGuard uniqueness_guard(state, val);
if (!uniqueness_guard.is_valid())
return std::make_unique<base::Value>();
std::unique_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() != isolate->GetCurrentContext())
scope.reset(new v8::Context::Scope(val->CreationContext()));
auto result = std::make_unique<base::DictionaryValue>();
v8::Local<v8::Array> property_names;
if (!val->GetOwnPropertyNames(isolate->GetCurrentContext())
.ToLocal(&property_names)) {
return std::move(result);
}
for (uint32_t i = 0; i < property_names->Length(); ++i) {
v8::Local<v8::Value> key =
property_names->Get(isolate->GetCurrentContext(), i).ToLocalChecked();
// Extend this test to cover more types as necessary and if sensible.
if (!key->IsString() && !key->IsNumber()) {
NOTREACHED() << "Key \"" << *v8::String::Utf8Value(isolate, key)
<< "\" "
"is neither a string nor a number";
continue;
}
v8::String::Utf8Value name_utf8(isolate, key);
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> child_v8;
v8::MaybeLocal<v8::Value> maybe_child =
val->Get(isolate->GetCurrentContext(), key);
if (try_catch.HasCaught() || !maybe_child.ToLocal(&child_v8)) {
LOG(ERROR) << "Getter for property " << *name_utf8
<< " threw an exception.";
child_v8 = v8::Null(isolate);
}
std::unique_ptr<base::Value> child =
FromV8ValueImpl(state, child_v8, isolate);
if (!child)
// 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->is_none())
continue;
result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()),
std::move(child));
}
return std::move(result);
}
} // namespace atom

Some files were not shown because too many files have changed in this diff Show more