Merge branch 'web-workers' into dev

This commit is contained in:
Corey Johnson & Nathan Sobo 2013-01-28 15:02:57 -07:00
commit 493ed5f006
53 changed files with 1388 additions and 1121 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -36,13 +36,13 @@
#define CEF_INCLUDE_CEF_VERSION_H_
#define CEF_VERSION_MAJOR 3
#define CEF_REVISION 1050
#define CEF_REVISION 1065
#define COPYRIGHT_YEAR 2013
#define CHROME_VERSION_MAJOR 25
#define CHROME_VERSION_MINOR 0
#define CHROME_VERSION_BUILD 1364
#define CHROME_VERSION_PATCH 29
#define CHROME_VERSION_PATCH 45
#define DO_MAKE_STRING(p) #p
#define MAKE_STRING(p) DO_MAKE_STRING(p)

View File

@ -16,10 +16,22 @@ class AtomCefRenderProcessHandler : public CefRenderProcessHandler {
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE;
virtual void OnWorkerContextCreated(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) OVERRIDE;
virtual void OnWorkerContextReleased(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) OVERRIDE;
virtual void OnWorkerUncaughtException(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE;
void Reload(CefRefPtr<CefBrowser> browser);
void Shutdown(CefRefPtr<CefBrowser> browser);
bool CallMessageReceivedHandler(CefRefPtr<CefV8Context> context, CefRefPtr<CefProcessMessage> message);
void InjectExtensionsIntoV8Context(CefRefPtr<CefV8Context> context);
IMPLEMENT_REFCOUNTING(AtomCefRenderProcessHandler);
};

View File

@ -9,26 +9,44 @@
#import "path_watcher.h"
#include <iostream>
void AtomCefRenderProcessHandler::OnWebKitInitialized() {
new v8_extensions::Atom();
new v8_extensions::Native();
new v8_extensions::OnigRegExp();
new v8_extensions::OnigScanner();
new v8_extensions::Git();
new v8_extensions::Tags();
}
void AtomCefRenderProcessHandler::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
InjectExtensionsIntoV8Context(context);
}
void AtomCefRenderProcessHandler::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
[PathWatcher removePathWatcherForContext:context];
}
void AtomCefRenderProcessHandler::OnWorkerContextCreated(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) {
InjectExtensionsIntoV8Context(context);
}
void AtomCefRenderProcessHandler::OnWorkerContextReleased(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) {
NSLog(@"Web worker context released");
}
void AtomCefRenderProcessHandler::OnWorkerUncaughtException(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {
std::string message = exception->GetMessage().ToString();
NSLog(@"Exception throw in worker thread %s", message.c_str());
}
bool AtomCefRenderProcessHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
@ -100,3 +118,13 @@ bool AtomCefRenderProcessHandler::CallMessageReceivedHandler(CefRefPtr<CefV8Cont
return true;
}
}
void AtomCefRenderProcessHandler::InjectExtensionsIntoV8Context(CefRefPtr<CefV8Context> context) {
// these objects are deleted when the context removes all references to them
(new v8_extensions::Atom())->CreateContextBinding(context);
(new v8_extensions::Native())->CreateContextBinding(context);
(new v8_extensions::Git())->CreateContextBinding(context);
(new v8_extensions::OnigRegExp())->CreateContextBinding(context);
(new v8_extensions::OnigScanner())->CreateContextBinding(context);
(new v8_extensions::Tags())->CreateContextBinding(context);
}

View File

@ -5,7 +5,7 @@ namespace v8_extensions {
class Atom : public CefV8Handler {
public:
Atom();
void CreateContextBinding(CefRefPtr<CefV8Context> context);
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
@ -14,5 +14,9 @@ namespace v8_extensions {
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Atom);
private:
Atom(Atom const&);
void operator=(Atom const&);
};
}

View File

@ -1,9 +0,0 @@
(function () {
native function sendMessageToBrowserProcess(name, array);
this.atom = {
sendMessageToBrowserProcess: sendMessageToBrowserProcess
};
})();

View File

@ -7,9 +7,14 @@
namespace v8_extensions {
Atom::Atom() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/atom.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
CefRegisterExtension("v8/atom", [extensionCode UTF8String], this);
}
void Atom::CreateContextBinding(CefRefPtr<CefV8Context> context) {
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction("sendMessageToBrowserProcess", this);
CefRefPtr<CefV8Value> atomObject = CefV8Value::CreateObject(NULL);
atomObject->SetValue("sendMessageToBrowserProcess", function, V8_PROPERTY_ATTRIBUTE_NONE);
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("atom", atomObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
bool Atom::Execute(const CefString& name,

View File

@ -2,19 +2,21 @@
#include "include/cef_v8.h"
namespace v8_extensions {
class Git : public CefV8Handler {
public:
Git();
void CreateContextBinding(CefRefPtr<CefV8Context> context);
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
class Git : public CefV8Handler {
public:
Git();
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Git);
};
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Git);
private:
Git(Git const&);
void operator=(Git const&);
};
}

View File

@ -1,35 +0,0 @@
var $git = {};
(function() {
native function getRepository(pathInRepo);
native function getHead();
native function getPath();
native function getStatus(path);
native function isIgnored(path);
native function checkoutHead(path);
native function getDiffStats(path);
native function isSubmodule(path);
native function refreshIndex();
native function destroy();
function GitRepository(path) {
var repo = getRepository(path);
if (!repo)
throw new Error("No Git repository found searching path: " + path);
repo.constructor = GitRepository;
repo.__proto__ = GitRepository.prototype;
return repo;
}
GitRepository.prototype.getHead = getHead;
GitRepository.prototype.getPath = getPath;
GitRepository.prototype.getStatus = getStatus;
GitRepository.prototype.isIgnored = isIgnored;
GitRepository.prototype.checkoutHead = checkoutHead;
GitRepository.prototype.getDiffStats = getDiffStats;
GitRepository.prototype.isSubmodule = isSubmodule;
GitRepository.prototype.refreshIndex = refreshIndex;
GitRepository.prototype.destroy = destroy;
this.GitRepository = GitRepository;
})();

View File

@ -4,264 +4,278 @@
namespace v8_extensions {
class GitRepository : public CefBase {
private:
git_repository *repo;
class GitRepository : public CefBase {
private:
git_repository *repo;
public:
GitRepository(const char *pathInRepo) {
if (git_repository_open_ext(&repo, pathInRepo, 0, NULL) != GIT_OK) {
repo = NULL;
public:
GitRepository(const char *pathInRepo) {
if (git_repository_open_ext(&repo, pathInRepo, 0, NULL) != GIT_OK) {
repo = NULL;
}
}
}
~GitRepository() {
Destroy();
}
void Destroy() {
if (Exists()) {
git_repository_free(repo);
repo = NULL;
~GitRepository() {
Destroy();
}
}
BOOL Exists() {
return repo != NULL;
}
void Destroy() {
if (Exists()) {
git_repository_free(repo);
repo = NULL;
}
}
CefRefPtr<CefV8Value> GetPath() {
return CefV8Value::CreateString(git_repository_path(repo));
}
BOOL Exists() {
return repo != NULL;
}
CefRefPtr<CefV8Value> GetHead() {
git_reference *head;
if (git_repository_head(&head, repo) == GIT_OK) {
if (git_repository_head_detached(repo) == 1) {
const git_oid* sha = git_reference_target(head);
if (sha) {
char oid[GIT_OID_HEXSZ + 1];
git_oid_tostr(oid, GIT_OID_HEXSZ + 1, sha);
git_reference_free(head);
return CefV8Value::CreateString(oid);
CefRefPtr<CefV8Value> GetPath() {
return CefV8Value::CreateString(git_repository_path(repo));
}
CefRefPtr<CefV8Value> GetHead() {
git_reference *head;
if (git_repository_head(&head, repo) == GIT_OK) {
if (git_repository_head_detached(repo) == 1) {
const git_oid* sha = git_reference_target(head);
if (sha) {
char oid[GIT_OID_HEXSZ + 1];
git_oid_tostr(oid, GIT_OID_HEXSZ + 1, sha);
git_reference_free(head);
return CefV8Value::CreateString(oid);
}
}
CefRefPtr<CefV8Value> result = CefV8Value::CreateString(git_reference_name(head));
git_reference_free(head);
return result;
}
CefRefPtr<CefV8Value> result = CefV8Value::CreateString(git_reference_name(head));
return CefV8Value::CreateNull();
}
CefRefPtr<CefV8Value> IsIgnored(const char *path) {
int ignored;
if (git_ignore_path_is_ignored(&ignored, repo, path) == GIT_OK) {
return CefV8Value::CreateBool(ignored == 1);
}
else {
return CefV8Value::CreateBool(false);
}
}
CefRefPtr<CefV8Value> GetStatus(const char *path) {
unsigned int status = 0;
if (git_status_file(&status, repo, path) == GIT_OK) {
return CefV8Value::CreateInt(status);
}
else {
return CefV8Value::CreateInt(0);
}
}
CefRefPtr<CefV8Value> CheckoutHead(const char *path) {
char *copiedPath = (char *)malloc(sizeof(char) * (strlen(path) + 1));
strcpy(copiedPath, path);
git_checkout_opts options = GIT_CHECKOUT_OPTS_INIT;
options.checkout_strategy = GIT_CHECKOUT_FORCE;
git_strarray paths;
paths.count = 1;
paths.strings = &copiedPath;
options.paths = paths;
int result = git_checkout_head(repo, &options);
free(copiedPath);
return CefV8Value::CreateBool(result == GIT_OK);
}
CefRefPtr<CefV8Value> GetDiffStats(const char *path) {
git_reference *head;
if (git_repository_head(&head, repo) != GIT_OK) {
return CefV8Value::CreateNull();
}
const git_oid* sha = git_reference_target(head);
git_commit *commit;
int commitStatus = git_commit_lookup(&commit, repo, sha);
git_reference_free(head);
return result;
}
if (commitStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
return CefV8Value::CreateNull();
}
git_tree *tree;
int treeStatus = git_commit_tree(&tree, commit);
git_commit_free(commit);
if (treeStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
CefRefPtr<CefV8Value> IsIgnored(const char *path) {
int ignored;
if (git_ignore_path_is_ignored(&ignored, repo, path) == GIT_OK) {
return CefV8Value::CreateBool(ignored == 1);
}
else {
return CefV8Value::CreateBool(false);
}
}
char *copiedPath = (char *)malloc(sizeof(char) * (strlen(path) + 1));
strcpy(copiedPath, path);
git_diff_options options = GIT_DIFF_OPTIONS_INIT;
git_strarray paths;
paths.count = 1;
paths.strings = &copiedPath;
options.pathspec = paths;
options.context_lines = 1;
options.flags = GIT_DIFF_DISABLE_PATHSPEC_MATCH;
CefRefPtr<CefV8Value> GetStatus(const char *path) {
unsigned int status = 0;
if (git_status_file(&status, repo, path) == GIT_OK) {
return CefV8Value::CreateInt(status);
}
else {
return CefV8Value::CreateInt(0);
}
}
git_diff_list *diffs;
int diffStatus = git_diff_tree_to_workdir(&diffs, repo, tree, &options);
free(copiedPath);
if (diffStatus != GIT_OK || git_diff_num_deltas(diffs) != 1) {
return CefV8Value::CreateNull();
}
CefRefPtr<CefV8Value> CheckoutHead(const char *path) {
char *copiedPath = (char *)malloc(sizeof(char) * (strlen(path) + 1));
strcpy(copiedPath, path);
git_checkout_opts options = GIT_CHECKOUT_OPTS_INIT;
options.checkout_strategy = GIT_CHECKOUT_FORCE;
git_strarray paths;
paths.count = 1;
paths.strings = &copiedPath;
options.paths = paths;
git_diff_patch *patch;
int patchStatus = git_diff_get_patch(&patch, NULL, diffs, 0);
git_diff_list_free(diffs);
if (patchStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
int result = git_checkout_head(repo, &options);
free(copiedPath);
return CefV8Value::CreateBool(result == GIT_OK);
}
CefRefPtr<CefV8Value> GetDiffStats(const char *path) {
git_reference *head;
if (git_repository_head(&head, repo) != GIT_OK) {
return CefV8Value::CreateNull();
}
const git_oid* sha = git_reference_target(head);
git_commit *commit;
int commitStatus = git_commit_lookup(&commit, repo, sha);
git_reference_free(head);
if (commitStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
git_tree *tree;
int treeStatus = git_commit_tree(&tree, commit);
git_commit_free(commit);
if (treeStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
char *copiedPath = (char *)malloc(sizeof(char) * (strlen(path) + 1));
strcpy(copiedPath, path);
git_diff_options options = GIT_DIFF_OPTIONS_INIT;
git_strarray paths;
paths.count = 1;
paths.strings = &copiedPath;
options.pathspec = paths;
options.context_lines = 1;
options.flags = GIT_DIFF_DISABLE_PATHSPEC_MATCH;
git_diff_list *diffs;
int diffStatus = git_diff_tree_to_workdir(&diffs, repo, tree, &options);
free(copiedPath);
if (diffStatus != GIT_OK || git_diff_num_deltas(diffs) != 1) {
return CefV8Value::CreateNull();
}
git_diff_patch *patch;
int patchStatus = git_diff_get_patch(&patch, NULL, diffs, 0);
git_diff_list_free(diffs);
if (patchStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
int added = 0;
int deleted = 0;
int hunks = git_diff_patch_num_hunks(patch);
for (int i = 0; i < hunks; i++) {
int lines = git_diff_patch_num_lines_in_hunk(patch, i);
for (int j = 0; j < lines; j++) {
char lineType;
if (git_diff_patch_get_line_in_hunk(&lineType, NULL, NULL, NULL, NULL, patch, i, j) == GIT_OK) {
switch (lineType) {
case GIT_DIFF_LINE_ADDITION:
added++;
break;
case GIT_DIFF_LINE_DELETION:
deleted++;
break;
int added = 0;
int deleted = 0;
int hunks = git_diff_patch_num_hunks(patch);
for (int i = 0; i < hunks; i++) {
int lines = git_diff_patch_num_lines_in_hunk(patch, i);
for (int j = 0; j < lines; j++) {
char lineType;
if (git_diff_patch_get_line_in_hunk(&lineType, NULL, NULL, NULL, NULL, patch, i, j) == GIT_OK) {
switch (lineType) {
case GIT_DIFF_LINE_ADDITION:
added++;
break;
case GIT_DIFF_LINE_DELETION:
deleted++;
break;
}
}
}
}
}
git_diff_patch_free(patch);
git_diff_patch_free(patch);
CefRefPtr<CefV8Value> result = CefV8Value::CreateObject(NULL);
result->SetValue("added", CefV8Value::CreateInt(added), V8_PROPERTY_ATTRIBUTE_NONE);
result->SetValue("deleted", CefV8Value::CreateInt(deleted), V8_PROPERTY_ATTRIBUTE_NONE);
return result;
CefRefPtr<CefV8Value> result = CefV8Value::CreateObject(NULL);
result->SetValue("added", CefV8Value::CreateInt(added), V8_PROPERTY_ATTRIBUTE_NONE);
result->SetValue("deleted", CefV8Value::CreateInt(deleted), V8_PROPERTY_ATTRIBUTE_NONE);
return result;
}
CefRefPtr<CefV8Value> IsSubmodule(const char *path) {
BOOL isSubmodule = false;
git_index* index;
if (git_repository_index(&index, repo) == GIT_OK) {
const git_index_entry *entry = git_index_get_bypath(index, path, 0);
isSubmodule = entry != NULL && (entry->mode & S_IFMT) == GIT_FILEMODE_COMMIT;
git_index_free(index);
}
return CefV8Value::CreateBool(isSubmodule);
}
void RefreshIndex() {
git_index* index;
if (git_repository_index(&index, repo) == GIT_OK) {
git_index_read(index);
git_index_free(index);
}
}
IMPLEMENT_REFCOUNTING(GitRepository);
};
Git::Git() : CefV8Handler() {
}
CefRefPtr<CefV8Value> IsSubmodule(const char *path) {
BOOL isSubmodule = false;
git_index* index;
if (git_repository_index(&index, repo) == GIT_OK) {
const git_index_entry *entry = git_index_get_bypath(index, path, 0);
isSubmodule = entry != NULL && (entry->mode & S_IFMT) == GIT_FILEMODE_COMMIT;
git_index_free(index);
void Git::CreateContextBinding(CefRefPtr<CefV8Context> context) {
const char* methodNames[] = {
"getRepository", "getHead", "getPath", "isIgnored", "getStatus", "checkoutHead",
"getDiffStats", "isSubmodule", "refreshIndex", "destroy"
};
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
int arrayLength = sizeof(methodNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = methodNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName, this);
nativeObject->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
return CefV8Value::CreateBool(isSubmodule);
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("$git", nativeObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
void RefreshIndex() {
git_index* index;
if (git_repository_index(&index, repo) == GIT_OK) {
git_index_read(index);
git_index_free(index);
bool Git::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "getRepository") {
GitRepository *repository = new GitRepository(arguments[0]->GetStringValue().ToString().c_str());
if (repository->Exists()) {
CefRefPtr<CefBase> userData = repository;
retval = CefV8Value::CreateObject(NULL);
retval->SetUserData(userData);
} else {
retval = CefV8Value::CreateNull();
}
return true;
}
if (name == "getHead") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetHead();
return true;
}
if (name == "getPath") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetPath();
return true;
}
if (name == "isIgnored") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->IsIgnored(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "getStatus") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetStatus(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "checkoutHead") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->CheckoutHead(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "getDiffStats") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetDiffStats(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "isSubmodule") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->IsSubmodule(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "refreshIndex") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
userData->RefreshIndex();
return true;
}
if (name == "destroy") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
userData->Destroy();
return true;
}
return false;
}
IMPLEMENT_REFCOUNTING(GitRepository);
};
Git::Git() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/git.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
CefRegisterExtension("v8/git", [extensionCode UTF8String], this);
}
bool Git::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "getRepository") {
GitRepository *repository = new GitRepository(arguments[0]->GetStringValue().ToString().c_str());
if (repository->Exists()) {
CefRefPtr<CefBase> userData = repository;
retval = CefV8Value::CreateObject(NULL);
retval->SetUserData(userData);
} else {
retval = CefV8Value::CreateNull();
}
return true;
}
if (name == "getHead") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetHead();
return true;
}
if (name == "getPath") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetPath();
return true;
}
if (name == "isIgnored") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->IsIgnored(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "getStatus") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetStatus(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "checkoutHead") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->CheckoutHead(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "getDiffStats") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetDiffStats(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "isSubmodule") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->IsSubmodule(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "refreshIndex") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
userData->RefreshIndex();
return true;
}
if (name == "destroy") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
userData->Destroy();
return true;
}
return false;
}
}

View File

@ -1,24 +1,23 @@
#include "include/cef_base.h"
#include "include/cef_v8.h"
namespace v8_extensions {
class Native : public CefV8Handler {
public:
Native();
class Native : public CefV8Handler {
public:
Native();
void CreateContextBinding(CefRefPtr<CefV8Context> context);
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Native);
private:
std::string windowState;
};
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Native);
private:
Native(Native const&);
void operator=(Native const&);
};
}

View File

@ -1,85 +0,0 @@
var $native = {};
(function() {
native function exists(path);
$native.exists = exists;
native function read(path);
$native.read = read;
native function write(path, content);
$native.write = write;
native function absolute(path);
$native.absolute = absolute;
native function traverseTree(path, onFile, onDirectory);
$native.traverseTree = traverseTree;
native function getAllFilePathsAsync(path, callback);
$native.getAllFilePathsAsync = getAllFilePathsAsync;
native function isFile(path);
$native.isFile = isFile;
native function isDirectory(path);
$native.isDirectory = isDirectory;
native function remove(path);
$native.remove = remove;
native function open(path);
$native.open = open;
native function quit();
$native.quit = quit;
native function writeToPasteboard(text);
$native.writeToPasteboard = writeToPasteboard;
native function readFromPasteboard();
$native.readFromPasteboard = readFromPasteboard;
native function watchPath(path);
$native.watchPath = watchPath;
native function unwatchPath(path, callbackId);
$native.unwatchPath = unwatchPath;
native function getWatchedPaths();
$native.getWatchedPaths = getWatchedPaths;
native function unwatchAllPaths();
$native.unwatchAllPaths = unwatchAllPaths;
native function makeDirectory(path);
$native.makeDirectory = makeDirectory;
native function move(sourcePath, targetPath);
$native.move = move;
native function moveToTrash(path);
$native.moveToTrash = moveToTrash;
native function reload();
$native.reload = reload;
native function lastModified(path);
$native.lastModified = lastModified;
native function md5ForPath(path);
$native.md5ForPath = md5ForPath;
native function exec(command, options, callback);
$native.exec = exec;
native function getPlatform();
$native.getPlatform = getPlatform;
native function setWindowState(state);
$native.setWindowState = setWindowState;
native function getWindowState();
$native.getWindowState = getWindowState;
})();

View File

@ -8,496 +8,522 @@
#import "path_watcher.h"
#import <iostream>
#include <fts.h>
static std::string windowState = "{}";
static NSLock *windowStateLock = [[NSLock alloc] init];
namespace v8_extensions {
using namespace std;
NSString *stringFromCefV8Value(const CefRefPtr<CefV8Value>& value) {
std::string cc_value = value->GetStringValue().ToString();
return [NSString stringWithUTF8String:cc_value.c_str()];
}
NSString *stringFromCefV8Value(const CefRefPtr<CefV8Value>& value);
void throwException(const CefRefPtr<CefV8Value>& global, CefRefPtr<CefV8Exception> exception, NSString *message);
void throwException(const CefRefPtr<CefV8Value>& global, CefRefPtr<CefV8Exception> exception, NSString *message) {
CefV8ValueList arguments;
message = [message stringByAppendingFormat:@"\n%s", exception->GetMessage().ToString().c_str()];
arguments.push_back(CefV8Value::CreateString(std::string([message UTF8String], [message lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
CefRefPtr<CefV8Value> console = global->GetValue("console");
console->GetValue("error")->ExecuteFunction(console, arguments);
}
Native::Native() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/native.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
windowState = "{}";
CefRegisterExtension("v8/native", [extensionCode UTF8String], this);
}
bool Native::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "exists") {
std::string cc_value = arguments[0]->GetStringValue().ToString();
const char *path = cc_value.c_str();
retval = CefV8Value::CreateBool(access(path, F_OK) == 0);
return true;
Native::Native() : CefV8Handler() {
}
else if (name == "read") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSError *error = nil;
NSStringEncoding *encoding = nil;
NSString *contents = [NSString stringWithContentsOfFile:path usedEncoding:encoding error:&error];
void Native::CreateContextBinding(CefRefPtr<CefV8Context> context) {
const char* methodNames[] = {
"exists", "read", "write", "absolute", "getAllFilePathsAsync", "traverseTree", "isDirectory",
"isFile", "remove", "writeToPasteboard", "readFromPasteboard", "quit", "watchPath", "unwatchPath",
"getWatchedPaths", "unwatchAllPaths", "makeDirectory", "move", "moveToTrash", "reload", "lastModified",
"md5ForPath", "exec", "getPlatform", "setWindowState", "getWindowState"
};
NSError *binaryFileError = nil;
if (error) {
contents = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:&binaryFileError];
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
int arrayLength = sizeof(methodNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = methodNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName, this);
nativeObject->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
if (binaryFileError) {
exception = [[binaryFileError localizedDescription] UTF8String];
}
else {
retval = CefV8Value::CreateString([contents UTF8String]);
}
return true;
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("$native", nativeObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
else if (name == "write") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSString *content = stringFromCefV8Value(arguments[1]);
NSFileManager *fm = [NSFileManager defaultManager];
bool Native::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "exists") {
std::string cc_value = arguments[0]->GetStringValue().ToString();
const char *path = cc_value.c_str();
retval = CefV8Value::CreateBool(access(path, F_OK) == 0);
// Create parent directories if they don't exist
BOOL exists = [fm fileExistsAtPath:[path stringByDeletingLastPathComponent] isDirectory:nil];
if (!exists) {
[fm createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
return true;
}
else if (name == "read") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSError *error = nil;
BOOL success = [content writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error];
NSError *error = nil;
NSStringEncoding *encoding = nil;
NSString *contents = [NSString stringWithContentsOfFile:path usedEncoding:encoding error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
NSError *binaryFileError = nil;
if (error) {
contents = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:&binaryFileError];
}
if (binaryFileError) {
exception = [[binaryFileError localizedDescription] UTF8String];
}
else {
retval = CefV8Value::CreateString([contents UTF8String]);
}
return true;
}
else if (!success) {
std::string exception = "Cannot write to '";
exception += [path UTF8String];
exception += "'";
else if (name == "write") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSString *content = stringFromCefV8Value(arguments[1]);
NSFileManager *fm = [NSFileManager defaultManager];
// Create parent directories if they don't exist
BOOL exists = [fm fileExistsAtPath:[path stringByDeletingLastPathComponent] isDirectory:nil];
if (!exists) {
[fm createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
}
NSError *error = nil;
BOOL success = [content writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
else if (!success) {
std::string exception = "Cannot write to '";
exception += [path UTF8String];
exception += "'";
}
return true;
}
else if (name == "absolute") {
NSString *path = stringFromCefV8Value(arguments[0]);
return true;
}
else if (name == "absolute") {
NSString *path = stringFromCefV8Value(arguments[0]);
path = [path stringByStandardizingPath];
if ([path characterAtIndex:0] == '/') {
retval = CefV8Value::CreateString([path UTF8String]);
}
path = [path stringByStandardizingPath];
if ([path characterAtIndex:0] == '/') {
retval = CefV8Value::CreateString([path UTF8String]);
return true;
}
else if (name == "getAllFilePathsAsync") {
std::string argument = arguments[0]->GetStringValue().ToString();
CefRefPtr<CefV8Value> callback = arguments[1];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
return true;
}
else if (name == "getAllFilePathsAsync") {
std::string argument = arguments[0]->GetStringValue().ToString();
CefRefPtr<CefV8Value> callback = arguments[1];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
int rootPathLength = argument.size() + 1;
char rootPath[rootPathLength];
strcpy(rootPath, argument.c_str());
char * const treePaths[] = {rootPath, NULL};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
FTS *tree = fts_open(treePaths, FTS_COMFOLLOW | FTS_PHYSICAL| FTS_NOCHDIR | FTS_NOSTAT, NULL);
std::vector<std::string> paths;
if (tree != NULL) {
FTSENT *entry;
int arrayIndex = 0;
while ((entry = fts_read(tree)) != NULL) {
if (entry->fts_level == 0) {
continue;
}
bool isFile = entry->fts_info == FTS_NSOK;
if (!isFile) {
continue;
}
int pathLength = entry->fts_pathlen - rootPathLength;
char relative[pathLength + 1];
relative[pathLength] = '\0';
strncpy(relative, entry->fts_path + rootPathLength, pathLength);
paths.push_back(relative);
}
}
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
context->Enter();
CefRefPtr<CefV8Value> v8Paths = CefV8Value::CreateArray(paths.size());
for (int i = 0; i < paths.size(); i++) {
v8Paths->SetValue(i, CefV8Value::CreateString(paths[i]));
}
CefV8ValueList callbackArgs;
callbackArgs.push_back(v8Paths);
callback->ExecuteFunction(callback, callbackArgs);
context->Exit();
});
});
return true;
}
else if (name == "traverseTree") {
std::string argument = arguments[0]->GetStringValue().ToString();
int rootPathLength = argument.size() + 1;
char rootPath[rootPathLength];
strcpy(rootPath, argument.c_str());
char * const treePaths[] = {rootPath, NULL};
char * const paths[] = {rootPath, NULL};
FTS *tree = fts_open(treePaths, FTS_COMFOLLOW | FTS_PHYSICAL| FTS_NOCHDIR | FTS_NOSTAT, NULL);
std::vector<std::string> paths;
FTS *tree = fts_open(paths, FTS_COMFOLLOW | FTS_PHYSICAL| FTS_NOCHDIR | FTS_NOSTAT, NULL);
if (tree == NULL) {
return true;
}
if (tree != NULL) {
FTSENT *entry;
int arrayIndex = 0;
while ((entry = fts_read(tree)) != NULL) {
if (entry->fts_level == 0) {
continue;
CefRefPtr<CefV8Value> onFile = arguments[1];
CefRefPtr<CefV8Value> onDir = arguments[2];
CefV8ValueList args;
FTSENT *entry;
while ((entry = fts_read(tree)) != NULL) {
if (entry->fts_level == 0) {
continue;
}
bool isFile = entry->fts_info == FTS_NSOK;
bool isDir = entry->fts_info == FTS_D;
if (!isFile && !isDir) {
continue;
}
int pathLength = entry->fts_pathlen - rootPathLength;
char relative[pathLength + 1];
relative[pathLength] = '\0';
strncpy(relative, entry->fts_path + rootPathLength, pathLength);
args.clear();
args.push_back(CefV8Value::CreateString(relative));
if (isFile) {
onFile->ExecuteFunction(onFile, args);
}
else {
CefRefPtr<CefV8Value> enterDir = onDir->ExecuteFunction(onDir, args);
if(enterDir != NULL && !enterDir->GetBoolValue()) {
fts_set(tree, entry, FTS_SKIP);
}
bool isFile = entry->fts_info == FTS_NSOK;
if (!isFile) {
continue;
}
int pathLength = entry->fts_pathlen - rootPathLength;
char relative[pathLength + 1];
relative[pathLength] = '\0';
strncpy(relative, entry->fts_path + rootPathLength, pathLength);
paths.push_back(relative);
}
}
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
context->Enter();
CefRefPtr<CefV8Value> v8Paths = CefV8Value::CreateArray(paths.size());
for (int i = 0; i < paths.size(); i++) {
v8Paths->SetValue(i, CefV8Value::CreateString(paths[i]));
}
CefV8ValueList callbackArgs;
callbackArgs.push_back(v8Paths);
callback->ExecuteFunction(callback, callbackArgs);
context->Exit();
});
});
return true;
}
else if (name == "traverseTree") {
std::string argument = arguments[0]->GetStringValue().ToString();
int rootPathLength = argument.size() + 1;
char rootPath[rootPathLength];
strcpy(rootPath, argument.c_str());
char * const paths[] = {rootPath, NULL};
return true;
}
else if (name == "isDirectory") {
NSString *path = stringFromCefV8Value(arguments[0]);
FTS *tree = fts_open(paths, FTS_COMFOLLOW | FTS_PHYSICAL| FTS_NOCHDIR | FTS_NOSTAT, NULL);
if (tree == NULL) {
BOOL isDir = false;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
retval = CefV8Value::CreateBool(exists && isDir);
return true;
}
else if (name == "isFile") {
NSString *path = stringFromCefV8Value(arguments[0]);
BOOL isDir = false;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
retval = CefV8Value::CreateBool(exists && !isDir);
return true;
}
else if (name == "remove") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "writeToPasteboard") {
NSString *text = stringFromCefV8Value(arguments[0]);
NSPasteboard *pb = [NSPasteboard generalPasteboard];
[pb declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil];
[pb setString:text forType:NSStringPboardType];
return true;
}
else if (name == "readFromPasteboard") {
NSPasteboard *pb = [NSPasteboard generalPasteboard];
NSArray *results = [pb readObjectsForClasses:[NSArray arrayWithObjects:[NSString class], nil] options:nil];
if (results) {
retval = CefV8Value::CreateString([[results objectAtIndex:0] UTF8String]);
}
return true;
}
else if (name == "quit") {
[NSApp terminate:nil];
return true;
}
else if (name == "watchPath") {
NSString *path = stringFromCefV8Value(arguments[0]);
CefRefPtr<CefV8Value> function = arguments[1];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
WatchCallback callback = ^(NSString *eventType, NSString *path) {
context->Enter();
CefV8ValueList args;
args.push_back(CefV8Value::CreateString(std::string([eventType UTF8String], [eventType lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
args.push_back(CefV8Value::CreateString(std::string([path UTF8String], [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
function->ExecuteFunction(function, args);
context->Exit();
};
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
NSString *watchId = [pathWatcher watchPath:path callback:[[callback copy] autorelease]];
if (watchId) {
retval = CefV8Value::CreateString([watchId UTF8String]);
}
else {
exception = std::string("Failed to watch path '") + std::string([path UTF8String]) + std::string("' (it may not exist)");
}
return true;
}
else if (name == "unwatchPath") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSString *callbackId = stringFromCefV8Value(arguments[1]);
NSError *error = nil;
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
[pathWatcher unwatchPath:path callbackId:callbackId error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "getWatchedPaths") {
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
NSArray *paths = [pathWatcher watchedPaths];
CefRefPtr<CefV8Value> pathsArray = CefV8Value::CreateArray([paths count]);
for (int i = 0; i < [paths count]; i++) {
CefRefPtr<CefV8Value> path = CefV8Value::CreateString([[paths objectAtIndex:i] UTF8String]);
pathsArray->SetValue(i, path);
}
retval = pathsArray;
return true;
}
else if (name == "unwatchAllPaths") {
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
[pathWatcher unwatchAllPaths];
return true;
}
else if (name == "makeDirectory") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
[fm createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "move") {
NSString *sourcePath = stringFromCefV8Value(arguments[0]);
NSString *targetPath = stringFromCefV8Value(arguments[1]);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
[fm moveItemAtPath:sourcePath toPath:targetPath error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "moveToTrash") {
NSString *sourcePath = stringFromCefV8Value(arguments[0]);
bool success = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
source:[sourcePath stringByDeletingLastPathComponent]
destination:@""
files:[NSArray arrayWithObject:[sourcePath lastPathComponent]]
tag:nil];
if (!success) {
std::string exception = "Can not move ";
exception += [sourcePath UTF8String];
exception += " to trash.";
}
return true;
}
else if (name == "reload") {
CefV8Context::GetCurrentContext()->GetBrowser()->ReloadIgnoreCache();
}
else if (name == "lastModified") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
NSDictionary *attributes = [fm attributesOfItemAtPath:path error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
NSDate *lastModified = [attributes objectForKey:NSFileModificationDate];
retval = CefV8Value::CreateDate(CefTime([lastModified timeIntervalSince1970]));
return true;
}
else if (name == "md5ForPath") {
NSString *path = stringFromCefV8Value(arguments[0]);
unsigned char outputData[CC_MD5_DIGEST_LENGTH];
NSData *inputData = [[NSData alloc] initWithContentsOfFile:path];
CC_MD5([inputData bytes], [inputData length], outputData);
[inputData release];
NSMutableString *hash = [[NSMutableString alloc] init];
for (NSUInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[hash appendFormat:@"%02x", outputData[i]];
}
retval = CefV8Value::CreateString([hash UTF8String]);
return true;
}
else if (name == "exec") {
NSString *command = stringFromCefV8Value(arguments[0]);
CefRefPtr<CefV8Value> options = arguments[1];
CefRefPtr<CefV8Value> callback = arguments[2];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
[task setStandardInput:[NSFileHandle fileHandleWithNullDevice]];
[task setArguments:[NSArray arrayWithObjects:@"-l", @"-c", command, nil]];
NSPipe *stdout = [NSPipe pipe];
NSPipe *stderr = [NSPipe pipe];
[task setStandardOutput:stdout];
[task setStandardError:stderr];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
void (^outputHandle)(NSString *contents, CefRefPtr<CefV8Value> function) = nil;
void (^taskTerminatedHandle)(NSString *output, NSString *errorOutput) = nil;
outputHandle = ^(NSString *contents, CefRefPtr<CefV8Value> function) {
context->Enter();
CefV8ValueList args;
args.push_back(CefV8Value::CreateString(std::string([contents UTF8String], [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
CefRefPtr<CefV8Value> retval = function->ExecuteFunction(function, args);
if (function->HasException()) {
throwException(context->GetGlobal(), function->GetException(), @"Error thrown in OutputHandle");
}
context->Exit();
};
taskTerminatedHandle = ^(NSString *output, NSString *errorOutput) {
context->Enter();
CefV8ValueList args;
args.push_back(CefV8Value::CreateInt([task terminationStatus]));
args.push_back(CefV8Value::CreateString([output UTF8String]));
args.push_back(CefV8Value::CreateString([errorOutput UTF8String]));
callback->ExecuteFunction(callback, args);
if (callback->HasException()) {
throwException(context->GetGlobal(), callback->GetException(), @"Error thrown in TaskTerminatedHandle");
}
context->Exit();
stdout.fileHandleForReading.writeabilityHandler = nil;
stderr.fileHandleForReading.writeabilityHandler = nil;
};
task.terminationHandler = ^(NSTask *) {
NSString *output = [[NSString alloc] initWithData:[[stdout fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
NSString *errorOutput = [[NSString alloc] initWithData:[[stderr fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
taskTerminatedHandle(output, errorOutput);
});
[output release];
[errorOutput release];
};
CefRefPtr<CefV8Value> stdoutFunction = options->GetValue("stdout");
if (stdoutFunction->IsFunction()) {
stdout.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stdoutFunction);
});
[contents release];
};
}
CefRefPtr<CefV8Value> stderrFunction = options->GetValue("stderr");
if (stderrFunction->IsFunction()) {
stderr.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stderrFunction);
});
[contents release];
};
}
[task launch];
return true;
}
else if (name == "getPlatform") {
retval = CefV8Value::CreateString("mac");
return true;
}
CefRefPtr<CefV8Value> onFile = arguments[1];
CefRefPtr<CefV8Value> onDir = arguments[2];
CefV8ValueList args;
FTSENT *entry;
while ((entry = fts_read(tree)) != NULL) {
if (entry->fts_level == 0) {
continue;
}
bool isFile = entry->fts_info == FTS_NSOK;
bool isDir = entry->fts_info == FTS_D;
if (!isFile && !isDir) {
continue;
}
int pathLength = entry->fts_pathlen - rootPathLength;
char relative[pathLength + 1];
relative[pathLength] = '\0';
strncpy(relative, entry->fts_path + rootPathLength, pathLength);
args.clear();
args.push_back(CefV8Value::CreateString(relative));
if (isFile) {
onFile->ExecuteFunction(onFile, args);
}
else {
CefRefPtr<CefV8Value> enterDir = onDir->ExecuteFunction(onDir, args);
if(enterDir != NULL && !enterDir->GetBoolValue()) {
fts_set(tree, entry, FTS_SKIP);
}
}
else if (name == "setWindowState") {
[windowStateLock lock];
windowState = arguments[0]->GetStringValue().ToString();
[windowStateLock unlock];
return true;
}
return true;
}
else if (name == "isDirectory") {
NSString *path = stringFromCefV8Value(arguments[0]);
BOOL isDir = false;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
retval = CefV8Value::CreateBool(exists && isDir);
return true;
}
else if (name == "isFile") {
NSString *path = stringFromCefV8Value(arguments[0]);
BOOL isDir = false;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
retval = CefV8Value::CreateBool(exists && !isDir);
return true;
}
else if (name == "remove") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
else if (name == "getWindowState") {
[windowStateLock lock];
retval = CefV8Value::CreateString(windowState);
[windowStateLock unlock];
return true;
}
return true;
}
else if (name == "writeToPasteboard") {
NSString *text = stringFromCefV8Value(arguments[0]);
return false;
};
NSPasteboard *pb = [NSPasteboard generalPasteboard];
[pb declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil];
[pb setString:text forType:NSStringPboardType];
return true;
}
else if (name == "readFromPasteboard") {
NSPasteboard *pb = [NSPasteboard generalPasteboard];
NSArray *results = [pb readObjectsForClasses:[NSArray arrayWithObjects:[NSString class], nil] options:nil];
if (results) {
retval = CefV8Value::CreateString([[results objectAtIndex:0] UTF8String]);
}
return true;
}
else if (name == "quit") {
[NSApp terminate:nil];
return true;
}
else if (name == "watchPath") {
NSString *path = stringFromCefV8Value(arguments[0]);
CefRefPtr<CefV8Value> function = arguments[1];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
WatchCallback callback = ^(NSString *eventType, NSString *path) {
context->Enter();
CefV8ValueList args;
args.push_back(CefV8Value::CreateString(std::string([eventType UTF8String], [eventType lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
args.push_back(CefV8Value::CreateString(std::string([path UTF8String], [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
function->ExecuteFunction(function, args);
context->Exit();
};
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
NSString *watchId = [pathWatcher watchPath:path callback:[[callback copy] autorelease]];
if (watchId) {
retval = CefV8Value::CreateString([watchId UTF8String]);
}
else {
exception = std::string("Failed to watch path '") + std::string([path UTF8String]) + std::string("' (it may not exist)");
}
return true;
}
else if (name == "unwatchPath") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSString *callbackId = stringFromCefV8Value(arguments[1]);
NSError *error = nil;
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
[pathWatcher unwatchPath:path callbackId:callbackId error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "getWatchedPaths") {
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
NSArray *paths = [pathWatcher watchedPaths];
CefRefPtr<CefV8Value> pathsArray = CefV8Value::CreateArray([paths count]);
for (int i = 0; i < [paths count]; i++) {
CefRefPtr<CefV8Value> path = CefV8Value::CreateString([[paths objectAtIndex:i] UTF8String]);
pathsArray->SetValue(i, path);
}
retval = pathsArray;
return true;
}
else if (name == "unwatchAllPaths") {
PathWatcher *pathWatcher = [PathWatcher pathWatcherForContext:CefV8Context::GetCurrentContext()];
[pathWatcher unwatchAllPaths];
return true;
}
else if (name == "makeDirectory") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
[fm createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "move") {
NSString *sourcePath = stringFromCefV8Value(arguments[0]);
NSString *targetPath = stringFromCefV8Value(arguments[1]);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
[fm moveItemAtPath:sourcePath toPath:targetPath error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
return true;
}
else if (name == "moveToTrash") {
NSString *sourcePath = stringFromCefV8Value(arguments[0]);
bool success = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
source:[sourcePath stringByDeletingLastPathComponent]
destination:@""
files:[NSArray arrayWithObject:[sourcePath lastPathComponent]]
tag:nil];
if (!success) {
std::string exception = "Can not move ";
exception += [sourcePath UTF8String];
exception += " to trash.";
}
return true;
}
else if (name == "reload") {
CefV8Context::GetCurrentContext()->GetBrowser()->ReloadIgnoreCache();
}
else if (name == "lastModified") {
NSString *path = stringFromCefV8Value(arguments[0]);
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
NSDictionary *attributes = [fm attributesOfItemAtPath:path error:&error];
if (error) {
exception = [[error localizedDescription] UTF8String];
}
NSDate *lastModified = [attributes objectForKey:NSFileModificationDate];
retval = CefV8Value::CreateDate(CefTime([lastModified timeIntervalSince1970]));
return true;
}
else if (name == "md5ForPath") {
NSString *path = stringFromCefV8Value(arguments[0]);
unsigned char outputData[CC_MD5_DIGEST_LENGTH];
NSData *inputData = [[NSData alloc] initWithContentsOfFile:path];
CC_MD5([inputData bytes], [inputData length], outputData);
[inputData release];
NSMutableString *hash = [[NSMutableString alloc] init];
for (NSUInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[hash appendFormat:@"%02x", outputData[i]];
}
retval = CefV8Value::CreateString([hash UTF8String]);
return true;
}
else if (name == "exec") {
NSString *command = stringFromCefV8Value(arguments[0]);
CefRefPtr<CefV8Value> options = arguments[1];
CefRefPtr<CefV8Value> callback = arguments[2];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
[task setStandardInput:[NSFileHandle fileHandleWithNullDevice]];
[task setArguments:[NSArray arrayWithObjects:@"-l", @"-c", command, nil]];
NSPipe *stdout = [NSPipe pipe];
NSPipe *stderr = [NSPipe pipe];
[task setStandardOutput:stdout];
[task setStandardError:stderr];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
void (^outputHandle)(NSString *contents, CefRefPtr<CefV8Value> function) = nil;
void (^taskTerminatedHandle)(NSString *output, NSString *errorOutput) = nil;
outputHandle = ^(NSString *contents, CefRefPtr<CefV8Value> function) {
context->Enter();
CefV8ValueList args;
args.push_back(CefV8Value::CreateString(std::string([contents UTF8String], [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
CefRefPtr<CefV8Value> retval = function->ExecuteFunction(function, args);
if (function->HasException()) {
throwException(context->GetGlobal(), function->GetException(), @"Error thrown in OutputHandle");
}
context->Exit();
};
taskTerminatedHandle = ^(NSString *output, NSString *errorOutput) {
context->Enter();
CefV8ValueList args;
args.push_back(CefV8Value::CreateInt([task terminationStatus]));
args.push_back(CefV8Value::CreateString([output UTF8String]));
args.push_back(CefV8Value::CreateString([errorOutput UTF8String]));
callback->ExecuteFunction(callback, args);
if (callback->HasException()) {
throwException(context->GetGlobal(), callback->GetException(), @"Error thrown in TaskTerminatedHandle");
}
context->Exit();
stdout.fileHandleForReading.writeabilityHandler = nil;
stderr.fileHandleForReading.writeabilityHandler = nil;
};
task.terminationHandler = ^(NSTask *) {
NSString *output = [[NSString alloc] initWithData:[[stdout fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
NSString *errorOutput = [[NSString alloc] initWithData:[[stderr fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
taskTerminatedHandle(output, errorOutput);
});
[output release];
[errorOutput release];
};
CefRefPtr<CefV8Value> stdoutFunction = options->GetValue("stdout");
if (stdoutFunction->IsFunction()) {
stdout.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stdoutFunction);
});
[contents release];
};
}
CefRefPtr<CefV8Value> stderrFunction = options->GetValue("stderr");
if (stderrFunction->IsFunction()) {
stderr.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stderrFunction);
});
[contents release];
};
}
[task launch];
return true;
}
else if (name == "getPlatform") {
retval = CefV8Value::CreateString("mac");
return true;
NSString *stringFromCefV8Value(const CefRefPtr<CefV8Value>& value) {
std::string cc_value = value->GetStringValue().ToString();
return [NSString stringWithUTF8String:cc_value.c_str()];
}
else if (name == "setWindowState") {
windowState = arguments[0]->GetStringValue().ToString();
return true;
}
void throwException(const CefRefPtr<CefV8Value>& global, CefRefPtr<CefV8Exception> exception, NSString *message) {
CefV8ValueList arguments;
else if (name == "getWindowState") {
retval = CefV8Value::CreateString(windowState);
return true;
}
message = [message stringByAppendingFormat:@"\n%s", exception->GetMessage().ToString().c_str()];
arguments.push_back(CefV8Value::CreateString(std::string([message UTF8String], [message lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
return false;
};
CefRefPtr<CefV8Value> console = global->GetValue("console");
console->GetValue("error")->ExecuteFunction(console, arguments);
}
} // namespace v8_extensions

View File

@ -3,18 +3,22 @@
namespace v8_extensions {
class OnigRegExp : public CefV8Handler {
public:
OnigRegExp();
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(OnigRegExp);
};
class OnigRegExp : public CefV8Handler {
public:
OnigRegExp();
void CreateContextBinding(CefRefPtr<CefV8Context> context);
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(OnigRegExp);
private:
OnigRegExp(OnigRegExp const&);
void operator=(OnigRegExp const&);
};
}

View File

@ -1,19 +0,0 @@
(function() {
native function buildOnigRegExp(source);
native function search(string, index);
native function test(string);
function OnigRegExp(source) {
var regexp = buildOnigRegExp(source);
regexp.constructor = OnigRegExp;
regexp.__proto__ = OnigRegExp.prototype;
regexp.source = source;
return regexp;
}
OnigRegExp.prototype.search = search;
OnigRegExp.prototype.test = test;
this.OnigRegExp = OnigRegExp;
})();

View File

@ -38,21 +38,33 @@ public:
return resultArray;
}
CefRefPtr<CefV8Value> Test(CefRefPtr<CefV8Value> string, CefRefPtr<CefV8Value> index) {
OnigResult *result = [m_regex search:stringFromCefV8Value(string) start:index->GetIntValue()];
return CefV8Value::CreateBool(result);
}
OnigRegexp *m_regex;
IMPLEMENT_REFCOUNTING(OnigRegexpUserData);
};
OnigRegExp::OnigRegExp() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/onig_reg_exp.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
CefRegisterExtension("v8/onig-reg-exp", [extensionCode UTF8String], this);
}
void OnigRegExp::CreateContextBinding(CefRefPtr<CefV8Context> context) {
const char* methodNames[] = { "search", "test", "buildOnigRegExp" };
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
int arrayLength = sizeof(methodNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = methodNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName, this);
nativeObject->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("$onigRegExp", nativeObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
bool OnigRegExp::Execute(const CefString& name,
@ -73,7 +85,7 @@ bool OnigRegExp::Execute(const CefString& name,
CefRefPtr<CefV8Value> index = arguments.size() > 1 ? arguments[1] : CefV8Value::CreateInt(0);
OnigRegExpUserData *userData = (OnigRegExpUserData *)object->GetUserData().get();
retval = userData->Test(string, index);
return true;
return true;
}
else if (name == "buildOnigRegExp") {
CefRefPtr<CefV8Value> pattern = arguments[0];

View File

@ -2,19 +2,23 @@
#include "include/cef_v8.h"
namespace v8_extensions {
class OnigScanner : public CefV8Handler {
public:
OnigScanner();
void CreateContextBinding(CefRefPtr<CefV8Context> context);
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(OnigRegExp);
private:
OnigScanner(OnigScanner const&);
void operator=(OnigScanner const&);
};
}

View File

@ -1,17 +0,0 @@
(function() {
native function buildScanner(sources);
native function findNextMatch(string, startPosition);
function OnigScanner(sources) {
var scanner = buildScanner(sources);
scanner.constructor = OnigScanner;
scanner.__proto__ = OnigScanner.prototype;
scanner.sources = sources;
return scanner;
}
OnigScanner.prototype.buildScanner = buildScanner;
OnigScanner.prototype.findNextMatch = findNextMatch;
this.OnigScanner = OnigScanner;
})();

View File

@ -130,11 +130,22 @@ class OnigScannerUserData : public CefBase {
};
OnigScanner::OnigScanner() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/onig_scanner.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
CefRegisterExtension("v8/onig-scanner", [extensionCode UTF8String], this);
}
void OnigScanner::CreateContextBinding(CefRefPtr<CefV8Context> context) {
const char* methodNames[] = { "findNextMatch", "buildScanner" };
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
int arrayLength = sizeof(methodNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = methodNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName, this);
nativeObject->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("$onigScanner", nativeObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
bool OnigScanner::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,

View File

@ -4,21 +4,23 @@
namespace v8_extensions {
class Tags : public CefV8Handler {
public:
Tags();
class Tags : public CefV8Handler {
public:
Tags();
void CreateContextBinding(CefRefPtr<CefV8Context> context);
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Tags);
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(Tags);
private:
CefRefPtr<CefV8Value> ParseEntry(tagEntry entry);
};
private:
Tags(Tags const&);
void operator=(Tags const&);
CefRefPtr<CefV8Value> ParseEntry(tagEntry entry);
};
}

View File

@ -1,10 +0,0 @@
var $tags = {};
(function() {
native function find(path, tag);
$tags.find = find;
native function getAllTagsAsync(path, callback);
$tags.getAllTagsAsync = getAllTagsAsync;
})();

View File

@ -3,100 +3,112 @@
namespace v8_extensions {
Tags::Tags() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/tags.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
CefRegisterExtension("v8/tags", [extensionCode UTF8String], this);
}
CefRefPtr<CefV8Value> Tags::ParseEntry(tagEntry entry) {
CefRefPtr<CefV8Value> tagEntry = CefV8Value::CreateObject(NULL);
tagEntry->SetValue("name", CefV8Value::CreateString(entry.name), V8_PROPERTY_ATTRIBUTE_NONE);
tagEntry->SetValue("file", CefV8Value::CreateString(entry.file), V8_PROPERTY_ATTRIBUTE_NONE);
if (entry.address.pattern) {
tagEntry->SetValue("pattern", CefV8Value::CreateString(entry.address.pattern), V8_PROPERTY_ATTRIBUTE_NONE);
Tags::Tags() : CefV8Handler() {
}
return tagEntry;
}
bool Tags::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
void Tags::CreateContextBinding(CefRefPtr<CefV8Context> context) {
const char* methodNames[] = { "find", "getAllTagsAsync" };
if (name == "find") {
std::string path = arguments[0]->GetStringValue().ToString();
std::string tag = arguments[1]->GetStringValue().ToString();
tagFileInfo info;
tagFile* tagFile;
tagFile = tagsOpen(path.c_str(), &info);
if (info.status.opened) {
tagEntry entry;
std::vector<CefRefPtr<CefV8Value>> entries;
if (tagsFind(tagFile, &entry, tag.c_str(), TAG_FULLMATCH | TAG_OBSERVECASE) == TagSuccess) {
entries.push_back(ParseEntry(entry));
while (tagsFindNext(tagFile, &entry) == TagSuccess) {
entries.push_back(ParseEntry(entry));
}
}
retval = CefV8Value::CreateArray(entries.size());
for (int i = 0; i < entries.size(); i++) {
retval->SetValue(i, entries[i]);
}
tagsClose(tagFile);
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
int arrayLength = sizeof(methodNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = methodNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName, this);
nativeObject->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
return true;
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("$tags", nativeObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
if (name == "getAllTagsAsync") {
std::string path = arguments[0]->GetStringValue().ToString();
CefRefPtr<CefV8Value> callback = arguments[1];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
CefRefPtr<CefV8Value> Tags::ParseEntry(tagEntry entry) {
CefRefPtr<CefV8Value> tagEntry = CefV8Value::CreateObject(NULL);
tagEntry->SetValue("name", CefV8Value::CreateString(entry.name), V8_PROPERTY_ATTRIBUTE_NONE);
tagEntry->SetValue("file", CefV8Value::CreateString(entry.file), V8_PROPERTY_ATTRIBUTE_NONE);
if (entry.address.pattern) {
tagEntry->SetValue("pattern", CefV8Value::CreateString(entry.address.pattern), V8_PROPERTY_ATTRIBUTE_NONE);
}
return tagEntry;
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
bool Tags::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "find") {
std::string path = arguments[0]->GetStringValue().ToString();
std::string tag = arguments[1]->GetStringValue().ToString();
tagFileInfo info;
tagFile* tagFile;
tagFile = tagsOpen(path.c_str(), &info);
std::vector<tagEntry> entries;
if (info.status.opened) {
tagEntry entry;
while (tagsNext(tagFile, &entry) == TagSuccess) {
entry.name = strdup(entry.name);
entry.file = strdup(entry.file);
if (entry.address.pattern) {
entry.address.pattern = strdup(entry.address.pattern);
std::vector<CefRefPtr<CefV8Value>> entries;
if (tagsFind(tagFile, &entry, tag.c_str(), TAG_FULLMATCH | TAG_OBSERVECASE) == TagSuccess) {
entries.push_back(ParseEntry(entry));
while (tagsFindNext(tagFile, &entry) == TagSuccess) {
entries.push_back(ParseEntry(entry));
}
entries.push_back(entry);
}
retval = CefV8Value::CreateArray(entries.size());
for (int i = 0; i < entries.size(); i++) {
retval->SetValue(i, entries[i]);
}
tagsClose(tagFile);
}
return true;
}
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
context->Enter();
CefRefPtr<CefV8Value> v8Tags = CefV8Value::CreateArray(entries.size());
for (int i = 0; i < entries.size(); i++) {
v8Tags->SetValue(i, ParseEntry(entries[i]));
free((void*)entries[i].name);
free((void*)entries[i].file);
if (entries[i].address.pattern) {
free((void*)entries[i].address.pattern);
if (name == "getAllTagsAsync") {
std::string path = arguments[0]->GetStringValue().ToString();
CefRefPtr<CefV8Value> callback = arguments[1];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
tagFileInfo info;
tagFile* tagFile;
tagFile = tagsOpen(path.c_str(), &info);
std::vector<tagEntry> entries;
if (info.status.opened) {
tagEntry entry;
while (tagsNext(tagFile, &entry) == TagSuccess) {
entry.name = strdup(entry.name);
entry.file = strdup(entry.file);
if (entry.address.pattern) {
entry.address.pattern = strdup(entry.address.pattern);
}
entries.push_back(entry);
}
tagsClose(tagFile);
}
CefV8ValueList callbackArgs;
callbackArgs.push_back(v8Tags);
callback->ExecuteFunction(callback, callbackArgs);
context->Exit();
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
context->Enter();
CefRefPtr<CefV8Value> v8Tags = CefV8Value::CreateArray(entries.size());
for (int i = 0; i < entries.size(); i++) {
v8Tags->SetValue(i, ParseEntry(entries[i]));
free((void*)entries[i].name);
free((void*)entries[i].file);
if (entries[i].address.pattern) {
free((void*)entries[i].address.pattern);
}
}
CefV8ValueList callbackArgs;
callbackArgs.push_back(v8Tags);
callback->ExecuteFunction(callback, callbackArgs);
context->Exit();
});
});
});
return true;
return true;
}
return false;
}
return false;
}
}

View File

@ -1,7 +1,7 @@
#!/bin/sh
usage() {
echo 'usage: `basename $0` 'chromium-dir' [cef-release-branch] 1>&2'
echo 'usage: update-cef chromium-dir [cef-release-branch] 1>&2'
echo
echo 'chromium-dir is the root of the chromium directory (i.e. ~/code/chromium)'
echo 'CEF release branches can be found at http://code.google.com/p/chromiumembedded/wiki/BranchesAndBuilding'
@ -14,9 +14,13 @@ fi
ATOM_ROOT=$(cd $(dirname $0); pwd -P)/..
CHROMIUM_DIR=$1
CEF_DIR=$CHROMIUM_DIR/src/cef/
CEF_DIR=$CHROMIUM_DIR/src/cef
CEF_RELEASE_BRANCH=${2:-1271}
CEF_BINARY_PATH=$(echo "$CEF_DIR"/binary_distrib/cef_binary_*_macosx/) # Expand the path
CEF_REVISION=$(svnversion $CEF_DIR)
CEF_BINARY_PATH=$(echo ${CEF_DIR}/binary_distrib/cef_binary_3.${CEF_RELEASE_BRANCH}.${CEF_REVISION}_macosx/) # Expand the path
echo "CEF_RELEASE_BRANCH=$CEF_RELEASE_BRANCH"
echo "CEF_BINARY_PATH=$CEF_BINARY_PATH"
# Update and compile CEF
CEF_AUTOMATE_SCRIPT_PATH=/tmp/cef-update

View File

@ -56,3 +56,22 @@ describe "the `atom` global", ->
expect(stylesheetElementForId(stylesheetPath).length).toBe 0
atom.loadPackage("package-with-module")
expect(stylesheetElementForId(stylesheetPath).length).toBe 1
describe ".loadPackages()", ->
beforeEach ->
window.rootView = new RootView
it "terminates the worker when all packages have been loaded", ->
spyOn(Worker.prototype, 'terminate').andCallThrough()
eventHandler = jasmine.createSpy('eventHandler')
rootView.on 'grammars-loaded', eventHandler
disabledPackages = config.get("core.disabledPackages")
disabledPackages.push('textmate-package.tmbundle')
config.set "core.disabledPackages", disabledPackages
atom.loadPackages()
waitsFor "all packages to load", 5000, -> eventHandler.callCount > 0
runs ->
expect(Worker.prototype.terminate).toHaveBeenCalled()
expect(Worker.prototype.terminate.calls.length).toBe 1

View File

@ -0,0 +1 @@
I am hidden so I shouldn't be loaded

View File

@ -0,0 +1 @@
I am not a valid plist but that shouldn't cause a crisis

View File

@ -1,3 +1,5 @@
OnigRegExp = require 'onig-reg-exp'
describe "OnigRegExp", ->
describe ".search(string, index)", ->
it "returns an array of the match and all capture groups", ->

View File

@ -3,6 +3,7 @@ _ = require 'underscore'
Package = require 'package'
TextMatePackage = require 'text-mate-package'
Theme = require 'theme'
LoadTextMatePackagesTask = require 'load-text-mate-packages-task'
messageIdCounter = 1
originalSendMessageToBrowserProcess = atom.sendMessageToBrowserProcess
@ -13,7 +14,15 @@ _.extend atom,
pendingBrowserProcessCallbacks: {}
loadPackages: ->
pack.load() for pack in @getPackages()
{packages, asyncTextMatePackages} = _.groupBy @getPackages(), (pack) ->
if pack instanceof TextMatePackage and pack.name isnt 'text.tmbundle'
'asyncTextMatePackages'
else
'packages'
pack.load() for pack in packages
if asyncTextMatePackages.length
new LoadTextMatePackagesTask(asyncTextMatePackages).start()
getPackages: ->
@getPackageNames().map((name) -> Package.build(name)).filter (pack) -> pack?

View File

@ -404,6 +404,11 @@ class Editor extends View
@gutter.widthChanged = (newWidth) =>
@scrollView.css('left', newWidth + 'px')
rootView?.on 'grammars-loaded', =>
@reloadGrammar()
for session in @editSessions
session.reloadGrammar() unless session is @activeEditSession
@scrollView.on 'scroll', =>
if @scrollView.scrollLeft() == 0
@gutter.removeClass('drop-shadow')

View File

@ -1,6 +1,7 @@
$ = require 'jquery'
_ = require 'underscore'
Subscriber = require 'subscriber'
GitRepository = require 'git-repository'
module.exports =
class Git

View File

@ -1,6 +1,7 @@
Range = require 'range'
_ = require 'underscore'
require 'underscore-extensions'
OnigRegExp = require 'onig-reg-exp'
module.exports =
class LanguageMode

View File

@ -0,0 +1,5 @@
TextMatePackage = require 'text-mate-package'
module.exports =
loadPackage: (name) ->
callTaskMethod('packageLoaded', new TextMatePackage(name).readGrammars())

View File

@ -0,0 +1,26 @@
Task = require 'Task'
module.exports =
class LoadTextMatePackagesTask extends Task
constructor: (@packages) ->
super('load-text-mate-packages-handler')
started: ->
@loadNextPackage()
loadNextPackage: ->
unless @packages.length
@terminate()
rootView.trigger 'grammars-loaded'
return
@package = @packages.shift()
@loadPackage(@package.name)
loadPackage: (name) ->
@callWorkerMethod('loadPackage', name)
packageLoaded: (grammars) ->
@package.loadGrammars(grammars)
@loadNextPackage()

View File

@ -2,16 +2,18 @@ _ = require 'underscore'
fs = require 'fs'
plist = require 'plist'
Token = require 'token'
OnigRegExp = require 'onig-reg-exp'
OnigScanner = require 'onig-scanner'
module.exports =
class TextMateGrammar
@loadFromPath: (path) ->
grammar = null
@readFromPath: (path) ->
grammarContent = null
plist.parseString fs.read(path), (e, data) ->
throw new Error(e) if e
grammar = new TextMateGrammar(data[0])
throw new Error("Failed to load grammar at path `#{path}`") unless grammar
grammar
grammarContent = data[0]
throw new Error("Failed to load grammar at path `#{path}`") unless grammarContent
grammarContent
name: null
fileTypes: null

View File

@ -22,28 +22,41 @@ class TextMatePackage extends Package
super
@preferencesPath = fs.join(@path, "Preferences")
@syntaxesPath = fs.join(@path, "Syntaxes")
@grammars = []
load: ->
try
for grammar in @getGrammars()
syntax.addGrammar(grammar)
for { selector, properties } in @getScopedProperties()
syntax.addProperties(selector, properties)
@loadGrammars()
catch e
console.warn "Failed to load package named '#{@name}'", e.stack
this
getGrammars: ->
return @grammars if @grammars
getGrammars: -> @grammars
readGrammars: ->
grammars = []
for grammarPath in fs.list(@syntaxesPath)
try
grammars.push(TextMateGrammar.readFromPath(grammarPath))
catch e
console.warn "Failed to load grammar at path '#{grammarPath}'", e.stack
grammars
addGrammar: (rawGrammar) ->
grammar = new TextMateGrammar(rawGrammar)
@grammars.push(grammar)
syntax.addGrammar(grammar)
loadGrammars: (rawGrammars) ->
rawGrammars = @readGrammars() unless rawGrammars?
@grammars = []
if fs.exists(@syntaxesPath)
for grammarPath in fs.list(@syntaxesPath)
try
@grammars.push TextMateGrammar.loadFromPath(grammarPath)
catch e
console.warn "Failed to load grammar at path '#{grammarPath}'", e.stack
@grammars
@addGrammar(rawGrammar) for rawGrammar in rawGrammars
@loadScopedProperties()
loadScopedProperties: ->
for { selector, properties } in @getScopedProperties()
syntax.addProperties(selector, properties)
getScopedProperties: ->
scopedProperties = []

View File

@ -1,17 +1,14 @@
AtomPackage = require 'atom-package'
fs = require 'fs'
PEG = require 'pegjs'
_ = require 'underscore'
SnippetExpansion = require './src/snippet-expansion'
Snippet = require './src/snippet'
require './src/package-extensions'
LoadSnippetsTask = require './src/load-snippets-task'
module.exports =
class Snippets extends AtomPackage
snippetsByExtension: {}
parser: PEG.buildParser(fs.read(require.resolve 'snippets/snippets.pegjs'), trackLineAndColumn: true)
userSnippetsDir: fs.join(config.configDirPath, 'snippets')
loaded: false
activate: (@rootView) ->
window.snippets = this
@ -19,10 +16,7 @@ class Snippets extends AtomPackage
@rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor)
loadAll: ->
for pack in atom.getPackages()
pack.loadSnippets()
@loadDirectory(@userSnippetsDir) if fs.exists(@userSnippetsDir)
new LoadSnippetsTask(this).start()
loadDirectory: (snippetsDirPath) ->
for snippetsPath in fs.list(snippetsDirPath) when fs.base(snippetsPath).indexOf('.') isnt 0
@ -39,12 +33,16 @@ class Snippets extends AtomPackage
for selector, snippetsByName of snippetsBySelector
snippetsByPrefix = {}
for name, attributes of snippetsByName
{ prefix, body } = attributes
bodyTree = @parser.parse(body)
{ prefix, body, bodyTree } = attributes
# if `add` isn't called by the loader task (in specs for example), we need to parse the body
bodyTree ?= @getBodyParser().parse(body)
snippet = new Snippet({name, prefix, bodyTree})
snippetsByPrefix[snippet.prefix] = snippet
syntax.addProperties(selector, snippets: snippetsByPrefix)
getBodyParser: ->
require 'snippets/src/snippet-body-parser'
enableSnippetsInEditor: (editor) ->
editor.command 'snippets:expand', (e) =>
editSession = editor.activeEditSession

View File

@ -1,19 +1,17 @@
Snippets = require 'snippets'
Snippet = require 'snippets/src/snippet'
LoadSnippetsTask = require 'snippets/src/load-snippets-task'
RootView = require 'root-view'
Buffer = require 'buffer'
Editor = require 'editor'
_ = require 'underscore'
fs = require 'fs'
AtomPackage = require 'atom-package'
TextMatePackage = require 'text-mate-package'
describe "Snippets extension", ->
[buffer, editor] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
spyOn(AtomPackage.prototype, 'loadSnippets')
spyOn(TextMatePackage.prototype, 'loadSnippets')
spyOn(LoadSnippetsTask.prototype, 'start')
atom.loadPackage("snippets")
editor = rootView.getActiveEditor()
buffer = editor.getBuffer()
@ -214,34 +212,64 @@ describe "Snippets extension", ->
expect(editor.getSelectedBufferRange()).toEqual [[1, 6], [1, 36]]
describe "snippet loading", ->
beforeEach ->
jasmine.unspy(LoadSnippetsTask.prototype, 'start')
spyOn(LoadSnippetsTask.prototype, 'loadAtomSnippets').andCallFake -> @snippetsLoaded({})
spyOn(LoadSnippetsTask.prototype, 'loadTextMateSnippets').andCallFake -> @snippetsLoaded({})
it "loads non-hidden snippet files from all atom packages with snippets directories, logging a warning if a file can't be parsed", ->
spyOn(console, 'warn').andCallThrough()
jasmine.unspy(AtomPackage.prototype, 'loadSnippets')
jasmine.unspy(LoadSnippetsTask.prototype, 'loadAtomSnippets')
spyOn(console, 'warn')
snippets.loaded = false
snippets.loadAll()
expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
waitsFor "all snippets to load", 5000, -> snippets.loaded
# warn about junk-file, but don't even try to parse a hidden file
expect(console.warn).toHaveBeenCalled()
expect(console.warn.calls.length).toBeGreaterThan 0
runs ->
expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
# warn about junk-file, but don't even try to parse a hidden file
expect(console.warn).toHaveBeenCalled()
expect(console.warn.calls.length).toBe 1
it "loads snippets from all TextMate packages with snippets", ->
jasmine.unspy(TextMatePackage.prototype, 'loadSnippets')
jasmine.unspy(LoadSnippetsTask.prototype, 'loadTextMateSnippets')
spyOn(console, 'warn')
snippets.loaded = false
snippets.loadAll()
snippet = syntax.getProperty(['.source.js'], 'snippets.fun')
expect(snippet.constructor).toBe Snippet
expect(snippet.prefix).toBe 'fun'
expect(snippet.name).toBe 'Function'
expect(snippet.body).toBe """
function function_name (argument) {
\t// body...
}
"""
waitsFor "all snippets to load", 5000, -> snippets.loaded
describe "Snippets parser", ->
runs ->
snippet = syntax.getProperty(['.source.js'], 'snippets.fun')
expect(snippet.constructor).toBe Snippet
expect(snippet.prefix).toBe 'fun'
expect(snippet.name).toBe 'Function'
expect(snippet.body).toBe """
function function_name (argument) {
\t// body...
}
"""
# warn about junk-file, but don't even try to parse a hidden file
expect(console.warn).toHaveBeenCalled()
expect(console.warn.calls.length).toBe 1
it "terminates the worker when loading completes", ->
jasmine.unspy(LoadSnippetsTask.prototype, 'loadAtomSnippets')
spyOn(Worker.prototype, 'terminate').andCallThrough()
snippets.loaded = false
snippets.loadAll()
waitsFor "all snippets to load", 5000, -> snippets.loaded
runs ->
expect(Worker.prototype.terminate).toHaveBeenCalled()
expect(Worker.prototype.terminate.calls.length).toBe 1
describe "Snippet body parser", ->
it "breaks a snippet body into lines, with each line containing tab stops at the appropriate position", ->
bodyTree = snippets.parser.parse """
bodyTree = snippets.getBodyParser().parse """
the quick brown $1fox ${2:jumped ${3:over}
}the ${4:lazy} dog
"""

View File

@ -0,0 +1,53 @@
fs = require 'fs'
TextMatePackage = require 'text-mate-package'
SnippetBodyParser = require './snippet-body-parser'
module.exports =
snippetsLoaded: (snippets) ->
for snippet in snippets
for selector, snippetsByName of snippet
for name, attributes of snippetsByName
attributes.bodyTree = SnippetBodyParser.parse(attributes.body)
callTaskMethod('snippetsLoaded', snippets)
loadTextMateSnippets: (path) ->
snippetsDirPath = fs.join(path, 'Snippets')
snippets = []
for snippetsPath in fs.list(snippetsDirPath)
logWarning = ->
console.warn "Error reading TextMate snippets file '#{snippetsPath}'"
continue if fs.base(snippetsPath).indexOf('.') is 0
try
if object = fs.readPlist(snippetsPath)
snippets.push(object) if object
else
logWarning()
catch e
logWarning()
@snippetsLoaded(@translateTextmateSnippets(snippets))
loadAtomSnippets: (path) ->
snippetsDirPath = fs.join(path, 'snippets')
snippets = []
for snippetsPath in fs.list(snippetsDirPath)
continue if fs.base(snippetsPath).indexOf('.') is 0
try
snippets.push(fs.readObject(snippetsPath))
catch e
console.warn "Error reading snippets file '#{snippetsPath}'"
@snippetsLoaded(snippets)
translateTextmateSnippets: (tmSnippets) ->
atomSnippets = {}
for { scope, name, content, tabTrigger } in tmSnippets
if scope
scope = TextMatePackage.cssSelectorFromScopeSelector(scope)
else
scope = '*'
snippetsForScope = (atomSnippets[scope] ?= {})
snippetsForScope[name] = { prefix: tabTrigger, body: content }
[atomSnippets]

View File

@ -0,0 +1,34 @@
Task = require 'Task'
TextMatePackage = require 'text-mate-package'
module.exports =
class LoadSnippetsTask extends Task
constructor: (@snippets) ->
super('snippets/src/load-snippets-handler')
@packages = atom.getPackages()
@packages.push(path: config.configDirPath)
started: ->
@loadNextPackageSnippets()
loadNextPackageSnippets: ->
unless @packages.length
@terminate()
@snippets.loaded = true
return
@packageBeingLoaded = @packages.shift()
if @packageBeingLoaded instanceof TextMatePackage
@loadTextMateSnippets(@packageBeingLoaded.path)
else
@loadAtomSnippets(@packageBeingLoaded.path)
loadAtomSnippets: (path) ->
@callWorkerMethod('loadAtomSnippets', path)
loadTextMateSnippets: (path) ->
@callWorkerMethod('loadTextMateSnippets', path)
snippetsLoaded: (snippets) ->
@snippets.add(snippet) for snippet in snippets
@loadNextPackageSnippets()

View File

@ -1,25 +0,0 @@
AtomPackage = require 'atom-package'
TextMatePackage = require 'text-mate-package'
fs = require 'fs'
AtomPackage.prototype.loadSnippets = ->
snippetsDirPath = fs.join(@path, 'snippets')
snippets.loadDirectory(snippetsDirPath) if fs.exists(snippetsDirPath)
TextMatePackage.prototype.loadSnippets = ->
snippetsDirPath = fs.join(@path, 'Snippets')
if fs.exists(snippetsDirPath)
tmSnippets = fs.list(snippetsDirPath).map (snippetPath) -> fs.readPlist(snippetPath)
snippets.add(@translateSnippets(tmSnippets))
TextMatePackage.prototype.translateSnippets = (tmSnippets) ->
atomSnippets = {}
for { scope, name, content, tabTrigger } in tmSnippets
if scope
scope = TextMatePackage.cssSelectorFromScopeSelector(scope)
else
scope = '*'
snippetsForScope = (atomSnippets[scope] ?= {})
snippetsForScope[name] = { prefix: tabTrigger, body: content }
atomSnippets

View File

@ -0,0 +1,4 @@
PEG = require 'pegjs'
fs = require 'fs'
grammarSrc = fs.read(require.resolve('./snippet-body.pegjs'))
module.exports = PEG.buildParser(grammarSrc, trackLineAndColumn: true)

View File

@ -2,7 +2,6 @@
# http://ringojs.org/api/v0.8/fs/
_ = require 'underscore'
$ = require 'jquery'
module.exports =
# Make the given path absolute by resolving it against the

View File

@ -0,0 +1,18 @@
module.exports =
class GitRepository
constructor: (path) ->
unless repo = $git.getRepository(path)
throw new Error("No Git repository found searching path: " + path)
repo.constructor = GitRepository
repo.__proto__ = GitRepository.prototype
return repo
getHead: $git.getHead
getPath: $git.getPath
getStatus: $git.getStatus
isIgnored: $git.isIgnored
checkoutHead: $git.checkoutHead
getDiffStats: $git.getDiffStats
isSubmodule: $git.isSubmodule
refreshIndex: $git.refreshIndex
destroy: $git.destroy

View File

@ -0,0 +1,11 @@
module.exports =
class OnigRegExp
constructor: (source) ->
regexp = $onigRegExp.buildOnigRegExp(source);
regexp.constructor = OnigRegExp
regexp.__proto__ = OnigRegExp.prototype
regexp.source = source
return regexp
search: $onigRegExp.search
test: $onigRegExp.test

View File

@ -0,0 +1,10 @@
module.exports =
class OnigScanner
constructor: (sources) ->
scanner = $onigScanner.buildScanner(sources)
scanner.constructor = OnigScanner
scanner.__proto__ = OnigScanner.prototype
scanner.sources = sources
return scanner
findNextMatch: $onigScanner.findNextMatch

View File

@ -25,17 +25,16 @@ require = (path, cb) ->
unless file = resolve(path)
throw new Error("Require can't find file at path '#{path}'")
parts = file.split '.'
ext = parts[parts.length-1]
ext = file.split('.').pop()
if __moduleExists file
if not __modules.loaded[file.toLowerCase()]?
console.warn "Circular require: #{__filename} required #{file}"
console.warn "Circular require: #{window.__filename} required #{file}"
return __modules[file]
else if __modules.loaded[file.toLowerCase()]
console.warn "Multiple requires (different cases) for #{file}"
[ previousFilename, window.__filename ] = [ __filename, file ]
[ previousFilename, window.__filename ] = [ window.__filename, file ]
__modules[file] = {} # Fix for circular references
__modules[file] = (exts[ext] or (file) -> __read file) file
window.__filename = previousFilename
@ -43,33 +42,67 @@ require = (path, cb) ->
define = (cb) ->
__defines.push ->
exports = __modules[__filename] or {}
exports = __modules[window.__filename] or {}
module = exports: exports
cb.call exports, require, exports, module
__modules.loaded[__filename.toLowerCase()] = true
__modules.loaded[window.__filename.toLowerCase()] = true
module.exports or exports
exts =
js: (file, code) ->
code or= __read file
eval("define(function(require, exports, module) { 'use strict';" + code + "})\n//@ sourceURL=" + file)
eval("define(function(require, exports, module) { 'use strict';#{code}})\n//@ sourceURL=#{file}")
__defines.pop()?.call()
coffee: (file) ->
exts.js(file, __coffeeCache(file))
coffee: (file, retry=true) ->
cacheFilePath = getCacheFilePath(file)
if __exists(cacheFilePath)
compiled = __read(cacheFilePath)
writeToCache = false
else
{CoffeeScript} = require 'coffee-script'
compiled = CoffeeScript.compile(__read(file), filename: file)
writeToCache = true
try
evaluated = exts.js(file, compiled)
$native.write(cacheFilePath, compiled) if writeToCache
evaluated
catch e
if retry
# Attempt a second compile to work around mysterious CEF/CoffeeScript
# timing issue where the CoffeeScript compiler generates invalid
# JavaScript such as [object Object].
console.warn "Error evaluating #{file}. Trying again...", e.stack
exts.coffee(file, false)
else
throw e
getPath = (path) ->
path = resolve(path)
return path unless path.split('.').pop() is 'coffee'
cacheFilePath = getCacheFilePath(path)
unless __exists(cacheFilePath)
{CoffeeScript} = require 'coffee-script'
compiled = CoffeeScript.compile(__read(path), filename: path)
$native.write(cacheFilePath, compiled)
cacheFilePath
getCacheFilePath = (path) ->
"/tmp/atom-compiled-scripts/#{$native.md5ForPath(path)}"
resolve = (name, {verifyExistence}={}) ->
verifyExistence ?= true
file = name
if /!/.test file
parts = file.split '!'
file = parts[parts.length-1]
file = file.split('!').pop()
if file[0..1] is './'
prefix = __filename.split('/')[0..-2].join '/'
prefix = window.__filename.split('/')[0..-2].join '/'
file = file.replace './', "#{prefix}/"
if file[0..2] is '../'
prefix = __filename.split('/')[0..-3].join '/'
prefix = window.__filename.split('/')[0..-3].join '/'
file = file.replace '../', "#{prefix}/"
if file[0] isnt '/'
@ -130,18 +163,6 @@ __exists = (path) ->
__isFile = (path) ->
$native.isFile path
__coffeeCache = (filePath) ->
{CoffeeScript} = require 'coffee-script'
tmpPath = "/tmp/atom-compiled-scripts"
cacheFilePath = [tmpPath, $native.md5ForPath(filePath)].join("/")
if __exists(cacheFilePath)
__read(cacheFilePath)
else
compiled = CoffeeScript.compile(__read(filePath), filename: filePath)
$native.write(cacheFilePath, compiled)
compiled
__read = (path) ->
try
$native.read(path)
@ -157,6 +178,7 @@ this.nakedLoad = nakedLoad
this.define = define
this.require.paths = paths
this.require.getPath = getPath
this.require.exts = exts
this.require.resolve = resolve

View File

@ -0,0 +1,29 @@
# This file is loaded within Task's worker thread. It will attempt to invoke
# any message with a 'method' and 'args' key on the global `handler` object. The
# initial `handler` object contains the `start` method, which is called by the
# task itself to relay information from the window thread and bootstrap the
# worker's environment. The `start` method then replaces the handler with an
# object required from the given `handlerPath`.
self.window = {}
self.attachEvent = ->
self.console =
warn: -> callTaskMethod 'warn', arguments...
log: -> callTaskMethod 'log', arguments...
error: -> callTaskMethod 'error', arguments...
# `callTaskMethod` can be used to invoke method's on the parent `Task` object
# back in the window thread.
self.callTaskMethod = (method, args...) ->
postMessage(method: method, args: args)
# The worker's initial handler replaces itself when `start` is invoked
self.handler =
start: ({resourcePath, requirePath, handlerPath}) ->
window.resourcePath = resourcePath
importScripts(requirePath)
self.handler = require(handlerPath)
callTaskMethod 'started'
self.addEventListener 'message', ({data}) ->
handler[data.method]?(data.args...) if data.method

35
src/stdlib/task.coffee Normal file
View File

@ -0,0 +1,35 @@
module.exports =
class Task
constructor: (@path) ->
start: ->
@worker = new Worker(require.getPath('task-shell'))
@worker.onmessage = ({data}) =>
if data.method and this[data.method]
this[data.method](data.args...)
else
@onMessage(data)
@startWorker()
log: -> console.log(arguments...)
warn: -> console.warn(arguments...)
error: -> console.error(arguments...)
startWorker: ->
@callWorkerMethod 'start'
resourcePath: window.resourcePath
requirePath: require.getPath('require')
handlerPath: @path
started: ->
onMessage: (message) ->
callWorkerMethod: (method, args...) ->
@postMessage({method, args})
postMessage: (data) ->
@worker.postMessage(data)
terminate: ->
@worker.terminate()

View File

@ -1,7 +1,9 @@
# Like sands through the hourglass, so are the days of our lives.
date = new Date().getTime()
require 'atom'
require 'window'
pathToOpen = atom.getWindowState('pathToOpen') ? window.location.params.pathToOpen
window.attachRootView(pathToOpen)
atom.show()
console.log "Load time: #{new Date().getTime() - date}"