Files
atom/Atom-Linux/native_handler.cpp
Kevin Sawicki b3200a944f Move native function declarations to js file
This allows multiple native handlers to load it
as an extension without duplicating all the expected
native function calls names internally.
2012-08-22 15:10:37 -07:00

532 lines
18 KiB
C++

#include "native_handler.h"
#include "include/cef_base.h"
#include "include/cef_runnable.h"
#include "client_handler.h"
#include "io_utils.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <vector>
#include <gtk/gtk.h>
#include <sys/inotify.h>
#include <pthread.h>
#include <openssl/evp.h>
#define BUFFER_SIZE 8192
using namespace std;
void *NotifyWatchersCallback(void* pointer) {
NativeHandler* handler = (NativeHandler*) pointer;
handler->NotifyWatchers();
return NULL;
}
void ExecuteWatchCallback(NotifyContext notifyContext) {
map<string, CallbackContext> callbacks =
notifyContext.callbacks[notifyContext.descriptor];
map<string, CallbackContext>::iterator callback;
for (callback = callbacks.begin(); callback != callbacks.end(); callback++) {
CallbackContext callbackContext = callback->second;
CefRefPtr<CefV8Context> context = callbackContext.context;
CefRefPtr<CefV8Value> function = callbackContext.function;
context->Enter();
CefV8ValueList args;
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Exception> e;
args.push_back(callbackContext.eventTypes);
function->ExecuteFunction(function, args, retval, e, true);
context->Exit();
}
}
NativeHandler::NativeHandler() :
CefV8Handler() {
object = CefV8Value::CreateObject(NULL, NULL);
const char *functionNames[] = { "exists", "alert", "read", "write",
"absolute", "list", "isFile", "isDirectory", "remove", "asyncList",
"open", "openDialog", "quit", "writeToPasteboard", "readFromPasteboard",
"showDevTools", "newWindow", "saveDialog", "exit", "watchPath",
"unwatchPath", "makeDirectory", "move", "moveToTrash", "md5ForPath",
"getPlatform" };
int arrayLength = sizeof(functionNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = functionNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName,
this);
object->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
string nativePath = io_utils_real_app_path(
"/../src/stdlib/native-handler.js");
if (!nativePath.empty()) {
string extensionCode;
if (io_utils_read(nativePath, &extensionCode) > 0)
CefRegisterExtension("v8/native-handler", extensionCode, this);
}
notifyFd = inotify_init();
if (notifyFd != -1)
g_thread_create_full(NotifyWatchersCallback, this, 0, true, false,
G_THREAD_PRIORITY_NORMAL, NULL);
}
void NativeHandler::NotifyWatchers() {
char buffer[BUFFER_SIZE];
ssize_t bufferRead;
size_t eventSize;
ssize_t bufferIndex;
struct inotify_event *event;
bufferRead = read(notifyFd, buffer, BUFFER_SIZE);
while (bufferRead > 0) {
bufferIndex = 0;
while (bufferIndex < bufferRead) {
event = (struct inotify_event *) &buffer[bufferIndex];
eventSize = offsetof (struct inotify_event, name) + event->len;
NotifyContext context;
context.descriptor = event->wd;
context.callbacks = pathCallbacks;
CefPostTask(TID_UI,
NewCefRunnableFunction(&ExecuteWatchCallback, context));
bufferIndex += eventSize;
}
bufferRead = read(notifyFd, buffer, BUFFER_SIZE);
}
}
void NativeHandler::Exists(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
struct stat statInfo;
int result = stat(path.c_str(), &statInfo);
retval = CefV8Value::CreateBool(result == 0);
}
void NativeHandler::Read(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
string value;
io_utils_read(path, &value);
retval = CefV8Value::CreateString(value);
}
void NativeHandler::Absolute(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
string relativePath;
if (path[0] != '~')
relativePath.append(path);
else {
relativePath.append(getenv("HOME"));
relativePath.append(path, 1, path.length() - 1);
}
vector < string > segments;
char allSegments[relativePath.length() + 1];
strcpy(allSegments, relativePath.c_str());
const char* segment;
for (segment = strtok(allSegments, "/"); segment;
segment = strtok(NULL, "/")) {
if (strcmp(segment, ".") == 0)
continue;
if (strcmp(segment, "..") == 0) {
if (segments.empty()) {
retval = CefV8Value::CreateString("/");
return;
}
segments.pop_back();
} else
segments.push_back(segment);
}
string absolutePath;
unsigned int i;
for (i = 0; i < segments.size(); i++) {
absolutePath.append("/");
absolutePath.append(segments.at(i));
}
retval = CefV8Value::CreateString(absolutePath);
}
void ListDirectory(string path, vector<string>* paths, bool recursive) {
dirent **children;
int childrenCount = scandir(path.c_str(), &children, 0, alphasort);
struct stat statInfo;
int result;
for (int i = 0; i < childrenCount; i++) {
if (strcmp(children[i]->d_name, ".") == 0
|| strcmp(children[i]->d_name, "..") == 0) {
free(children[i]);
continue;
}
string entryPath(path + "/" + children[i]->d_name);
paths->push_back(entryPath);
if (recursive) {
result = stat(entryPath.c_str(), &statInfo);
if (result == 0 && S_ISDIR(statInfo.st_mode))
ListDirectory(entryPath, paths, recursive);
}
free(children[i]);
}
free(children);
}
void DeleteContents(string path) {
dirent **children;
const char* dirPath = path.c_str();
int childrenCount = scandir(dirPath, &children, 0, alphasort);
struct stat statInfo;
for (int i = 0; i < childrenCount; i++) {
if (strcmp(children[i]->d_name, ".") == 0
|| strcmp(children[i]->d_name, "..") == 0) {
free(children[i]);
continue;
}
string entryPath(path + "/" + children[i]->d_name);
if (stat(entryPath.c_str(), &statInfo) != 0) {
free(children[i]);
continue;
}
if (S_ISDIR(statInfo.st_mode))
DeleteContents(entryPath);
else if (S_ISREG(statInfo.st_mode))
remove(entryPath.c_str());
}
free(children);
rmdir(dirPath);
}
void NativeHandler::List(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
bool recursive = arguments[1]->GetBoolValue();
vector < string > *paths = new vector<string>;
ListDirectory(path, paths, recursive);
retval = CefV8Value::CreateArray();
for (uint i = 0; i < paths->size(); i++)
retval->SetValue(i, CefV8Value::CreateString(paths->at(i)));
free (paths);
}
void NativeHandler::IsFile(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
struct stat statInfo;
int result = stat(path.c_str(), &statInfo);
retval = CefV8Value::CreateBool(result == 0 && S_ISREG(statInfo.st_mode));
}
void NativeHandler::IsDirectory(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
struct stat statInfo;
int result = stat(path.c_str(), &statInfo);
retval = CefV8Value::CreateBool(result == 0 && S_ISDIR(statInfo.st_mode));
}
void NativeHandler::OpenDialog(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new("Open File", GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename;
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
retval = CefV8Value::CreateString(filename);
g_free(filename);
} else
retval = CefV8Value::CreateNull();
gtk_widget_destroy(dialog);
}
void NativeHandler::Open(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
path = arguments[0]->GetStringValue().ToString();
CefV8Context::GetCurrentContext()->GetBrowser()->Reload();
}
void NativeHandler::Write(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
string content = arguments[1]->GetStringValue().ToString();
ofstream file;
file.open(path.c_str());
file << content;
file.close();
}
void NativeHandler::WriteToPasteboard(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string content = arguments[0]->GetStringValue().ToString();
GtkClipboard* clipboard = gtk_clipboard_get_for_display(
gdk_display_get_default(), GDK_NONE);
gtk_clipboard_set_text(clipboard, content.c_str(), content.length());
gtk_clipboard_store(clipboard);
}
void NativeHandler::ReadFromPasteboard(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
GtkClipboard* clipboard = gtk_clipboard_get_for_display(
gdk_display_get_default(), GDK_NONE);
char* content = gtk_clipboard_wait_for_text(clipboard);
retval = CefV8Value::CreateString(content);
}
void NativeHandler::AsyncList(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
bool recursive = arguments[1]->GetBoolValue();
vector < string > *paths = new vector<string>;
ListDirectory(path, paths, recursive);
CefRefPtr<CefV8Value> callbackPaths = CefV8Value::CreateArray();
for (uint i = 0; i < paths->size(); i++)
callbackPaths->SetValue(i, CefV8Value::CreateString(paths->at(i)));
CefV8ValueList args;
args.push_back(callbackPaths);
CefRefPtr<CefV8Exception> e;
arguments[2]->ExecuteFunction(arguments[2], args, retval, e, true);
if (e)
exception = e->GetMessage();
free (paths);
}
void NativeHandler::MakeDirectory(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string content = arguments[0]->GetStringValue().ToString();
mkdir(content.c_str(), S_IRWXU);
}
void NativeHandler::Move(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string from = arguments[0]->GetStringValue().ToString();
string to = arguments[1]->GetStringValue().ToString();
rename(from.c_str(), to.c_str());
}
void NativeHandler::Remove(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string pathArgument = arguments[0]->GetStringValue().ToString();
const char* path = pathArgument.c_str();
struct stat statInfo;
if (stat(path, &statInfo) != 0)
return;
if (S_ISREG(statInfo.st_mode))
remove(path);
else if (S_ISDIR(statInfo.st_mode))
DeleteContents(pathArgument);
}
void NativeHandler::Alert(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
CefRefPtr<CefV8Value> buttonNamesAndCallbacks;
if (arguments.size() < 3)
buttonNamesAndCallbacks = CefV8Value::CreateArray();
else
buttonNamesAndCallbacks = arguments[2];
GtkWidget *dialog;
dialog = gtk_dialog_new_with_buttons("atom", GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT, NULL);
for (int i = 0; i < buttonNamesAndCallbacks->GetArrayLength(); i++) {
string title =
buttonNamesAndCallbacks->GetValue(i)->GetValue(0)->GetStringValue().ToString();
gtk_dialog_add_button(GTK_DIALOG(dialog), title.c_str(), i);
}
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
string dialogMessage(arguments[0]->GetStringValue().ToString());
dialogMessage.append("\n\n");
dialogMessage.append(arguments[1]->GetStringValue().ToString());
GtkWidget *label;
label = gtk_label_new(dialogMessage.c_str());
GtkWidget *contentArea;
contentArea = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_container_add(GTK_CONTAINER(contentArea), label);
gtk_widget_show_all(dialog);
int result = gtk_dialog_run(GTK_DIALOG(dialog));
if (result >= 0) {
CefRefPtr<CefV8Value> callback =
buttonNamesAndCallbacks->GetValue(result)->GetValue(1);
CefV8ValueList args;
CefRefPtr<CefV8Exception> e;
callback->ExecuteFunction(callback, args, retval, e, true);
if (e)
exception = e->GetMessage();
}
gtk_widget_destroy(dialog);
}
void NativeHandler::WatchPath(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
int descriptor = inotify_add_watch(notifyFd, path.c_str(),
IN_ALL_EVENTS & ~(IN_CLOSE | IN_OPEN | IN_ACCESS));
if (descriptor == -1)
return;
CallbackContext callbackContext;
callbackContext.context = CefV8Context::GetCurrentContext();
callbackContext.function = arguments[1];
CefRefPtr<CefV8Value> eventTypes = CefV8Value::CreateObject(NULL, NULL);
eventTypes->SetValue("modified", CefV8Value::CreateBool(true),
V8_PROPERTY_ATTRIBUTE_NONE);
callbackContext.eventTypes = eventTypes;
stringstream idStream;
idStream << "counter";
idStream << idCounter;
string id = idStream.str();
idCounter++;
pathDescriptors[path] = descriptor;
pathCallbacks[descriptor][id] = callbackContext;
retval = CefV8Value::CreateString(id);
}
void NativeHandler::UnwatchPath(const CefString& name,
CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval, CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
int descriptor = pathDescriptors[path];
if (descriptor == -1)
return;
map<string, CallbackContext> callbacks = pathCallbacks[descriptor];
string id = arguments[1]->GetStringValue().ToString();
callbacks.erase(id);
if (callbacks.empty())
inotify_rm_watch(notifyFd, descriptor);
}
void NativeHandler::Digest(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
string path = arguments[0]->GetStringValue().ToString();
int fd = open(path.c_str(), O_RDONLY);
if (fd < 0)
return;
const EVP_MD *md;
OpenSSL_add_all_digests();
md = EVP_get_digestbyname("md5");
if (!md)
return;
EVP_MD_CTX context;
EVP_MD_CTX_init(&context);
EVP_DigestInit_ex(&context, md, NULL);
char buffer[BUFFER_SIZE];
int r;
while ((r = read(fd, buffer, sizeof buffer)) > 0)
EVP_DigestUpdate(&context, buffer, r);
close(fd);
unsigned char value[EVP_MAX_MD_SIZE];
unsigned int length;
EVP_DigestFinal_ex(&context, value, &length);
EVP_MD_CTX_cleanup(&context);
stringstream md5;
char hex[3];
for (uint i = 0; i < length; i++) {
sprintf(hex, "%02x", value[i]);
md5 << hex;
}
retval = CefV8Value::CreateString(md5.str());
}
bool NativeHandler::Execute(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "exists")
Exists(name, object, arguments, retval, exception);
else if (name == "read")
Read(name, object, arguments, retval, exception);
else if (name == "absolute")
Absolute(name, object, arguments, retval, exception);
else if (name == "list")
List(name, object, arguments, retval, exception);
else if (name == "isFile")
IsFile(name, object, arguments, retval, exception);
else if (name == "isDirectory")
IsDirectory(name, object, arguments, retval, exception);
else if (name == "showDevTools")
CefV8Context::GetCurrentContext()->GetBrowser()->ShowDevTools();
else if (name == "openDialog")
OpenDialog(name, object, arguments, retval, exception);
else if (name == "open")
Open(name, object, arguments, retval, exception);
else if (name == "write")
Write(name, object, arguments, retval, exception);
else if (name == "writeToPasteboard")
WriteToPasteboard(name, object, arguments, retval, exception);
else if (name == "readFromPasteboard")
ReadFromPasteboard(name, object, arguments, retval, exception);
else if (name == "asyncList")
AsyncList(name, object, arguments, retval, exception);
else if (name == "makeDirectory")
MakeDirectory(name, object, arguments, retval, exception);
else if (name == "move")
Move(name, object, arguments, retval, exception);
else if (name == "remove")
Remove(name, object, arguments, retval, exception);
else if (name == "alert")
Alert(name, object, arguments, retval, exception);
else if (name == "watchPath")
WatchPath(name, object, arguments, retval, exception);
else if (name == "unwatchPath")
UnwatchPath(name, object, arguments, retval, exception);
else if (name == "md5ForPath")
Digest(name, object, arguments, retval, exception);
else if (name == "getPlatform")
retval = CefV8Value::CreateString("linux");
else
cout << "Unhandled -> " + name.ToString() << " : "
<< arguments[0]->GetStringValue().ToString() << endl;
return true;
}