#include "native_linux.h" #include "include/cef_base.h" #include "include/cef_runnable.h" #include "io_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUFFER_SIZE 8192 using namespace std; namespace v8_extensions { void *NotifyWatchersCallback(void* pointer) { NativeHandler* handler = (NativeHandler*) pointer; handler->NotifyWatchers(); return NULL; } void ExecuteWatchCallback(NotifyContext notifyContext) { map callbacks = notifyContext.callbacks[notifyContext.descriptor]; map::iterator callback; for (callback = callbacks.begin(); callback != callbacks.end(); callback++) { CallbackContext callbackContext = callback->second; CefRefPtr context = callbackContext.context; CefRefPtr function = callbackContext.function; context->Enter(); CefV8ValueList args; CefRefPtr retval; CefRefPtr e; args.push_back(callbackContext.eventTypes); function->ExecuteFunction(retval, args); context->Exit(); } } NativeHandler::NativeHandler() : CefV8Handler() { string nativePath = io_utils_real_app_path("/native/v8_extensions/native.js"); if (!nativePath.empty()) { string extensionCode; if (io_utils_read(nativePath, &extensionCode) > 0) CefRegisterExtension("v8/native", 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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* 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 object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { string path = arguments[0]->GetStringValue().ToString(); bool recursive = arguments[1]->GetBoolValue(); vector < string > *paths = new vector; ListDirectory(path, paths, recursive); retval = CefV8Value::CreateArray(paths->size()); 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { path = arguments[0]->GetStringValue().ToString(); CefV8Context::GetCurrentContext()->GetBrowser()->Reload(); } void NativeHandler::Write(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { string path = arguments[0]->GetStringValue().ToString(); bool recursive = arguments[1]->GetBoolValue(); vector < string > *paths = new vector; ListDirectory(path, paths, recursive); CefRefPtr callbackPaths = CefV8Value::CreateArray(paths->size()); for (uint i = 0; i < paths->size(); i++) callbackPaths->SetValue(i, CefV8Value::CreateString(paths->at(i))); CefV8ValueList args; args.push_back(callbackPaths); CefRefPtr e; arguments[2]->ExecuteFunction(retval, args); if (e) exception = e->GetMessage(); free (paths); } void NativeHandler::MakeDirectory(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { string content = arguments[0]->GetStringValue().ToString(); mkdir(content.c_str(), S_IRWXU); } void NativeHandler::Move(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& 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 object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { CefRefPtr buttonNamesAndCallbacks; if (arguments.size() < 3) buttonNamesAndCallbacks = CefV8Value::CreateArray(0); 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 callback = buttonNamesAndCallbacks->GetValue(result)->GetValue(1); CefV8ValueList args; CefRefPtr e; callback->ExecuteFunction(retval, args); if (e) exception = e->GetMessage(); } gtk_widget_destroy(dialog); } void NativeHandler::WatchPath(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& 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 eventTypes = CefV8Value::CreateObject(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 object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { string path = arguments[0]->GetStringValue().ToString(); int descriptor = pathDescriptors[path]; if (descriptor == -1) return; map 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 object, const CefV8ValueList& arguments, CefRefPtr& 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()); } void NativeHandler::LastModified(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { string path = arguments[0]->GetStringValue().ToString(); struct stat statInfo; if (stat(path.c_str(), &statInfo) == 0) { CefTime time(statInfo.st_mtime); retval = CefV8Value::CreateDate(time); } } bool NativeHandler::Execute(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& 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 == "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 if (name == "lastModified") LastModified(name, object, arguments, retval, exception); else cout << "Unhandled -> " + name.ToString() << " : " << arguments[0]->GetStringValue().ToString() << endl; return true; } }