diff --git a/.atom/atom.coffee b/.atom/atom.coffee new file mode 100644 index 000000000..462e010ec --- /dev/null +++ b/.atom/atom.coffee @@ -0,0 +1,8 @@ +requireExtension 'autocomplete' +requireExtension 'strip-trailing-whitespace' +requireExtension 'fuzzy-finder' +requireExtension 'tree-view' +requireExtension 'command-panel' +requireExtension 'keybindings-view' +requireExtension 'snippets' +requireExtension 'status-bar' diff --git a/.atom/snippets/coffee.snippets b/.atom/snippets/coffee.snippets new file mode 100644 index 000000000..57b3e3980 --- /dev/null +++ b/.atom/snippets/coffee.snippets @@ -0,0 +1,34 @@ +snippet de "Describe block" +describe "${1:description}", -> + ${2:body} +endsnippet + +snippet i "It block" +it "$1", -> + $2 +endsnippet + +snippet be "Before each" +beforeEach -> + $1 +endsnippet + +snippet ex "Expectation" +expect($1).to$2 +endsnippet + +snippet log "Console log" +console.log $1 +endsnippet + +snippet ra "Range array" +[[$1, $2], [$3, $4]] +endsnippet + +snippet pt "Point array" +[$1, $2] +endsnippet + +snippet spy "Jasmine spy" +jasmine.createSpy("${1:description}")$2 +endsnippet diff --git a/Atom-Linux/.gitignore b/Atom-Linux/.gitignore new file mode 100644 index 000000000..3e2d8a8e3 --- /dev/null +++ b/Atom-Linux/.gitignore @@ -0,0 +1,2 @@ +/atom +*.o diff --git a/Atom-Linux/Makefile b/Atom-Linux/Makefile new file mode 100644 index 000000000..77a042faa --- /dev/null +++ b/Atom-Linux/Makefile @@ -0,0 +1,93 @@ +INSTALLDIR=/usr/share/atom + +CXXFLAGS := -Werror \ + -pthread \ + -fno-exceptions \ + -fno-strict-aliasing \ + -Wall \ + -Wno-unused-parameter \ + -Wno-missing-field-initializers \ + -fvisibility=hidden \ + -pipe \ + -fPIC \ + -pthread \ + -D_REENTRANT \ + -I/usr/include/gtk-2.0 \ + -I/usr/lib/gtk-2.0/include \ + -I/usr/include/atk-1.0 \ + -I/usr/include/cairo \ + -I/usr/include/pango-1.0 \ + -I/usr/include/gio-unix-2.0/ \ + -I/usr/include/glib-2.0 \ + -I/usr/include/openssl \ + -I/usr/lib/glib-2.0/include \ + -I../cef \ + -O2 \ + -fno-ident \ + -fdata-sections \ + -ffunction-sections \ + -fno-rtti \ + -fno-threadsafe-statics \ + -fvisibility-inlines-hidden \ + -Wsign-compare + +LDFLAGS := -pthread \ + -Wl,-z,noexecstack \ + -fPIC \ + -Llib \ + -Wl,-O1 \ + -Wl,--as-needed \ + -Wl,--gc-sections \ + -Wl,-rpath=$(INSTALLDIR) + +LIBS := -lX11 \ + -lgtk-x11-2.0 \ + -lgdk-x11-2.0 \ + -latk-1.0 \ + -lgio-2.0 \ + -lpangoft2-1.0 \ + -lgdk_pixbuf-2.0 \ + -lm \ + -lpangocairo-1.0 \ + -lcairo \ + -lpango-1.0 \ + -lgobject-2.0 \ + -lgmodule-2.0 \ + -lgthread-2.0 \ + -lrt \ + -lglib-2.0 \ + -lssl \ + -lcrypto \ + -lcef \ + -lcef_dll_wrapper \ + -lonig + +SOURCES=atom.cpp native_handler.cpp client_handler.cpp onig_regexp_extension.cpp io_utils.cpp +OBJECTS=$(SOURCES:.cpp=.o) + +all: + g++ $(CXXFLAGS) -c $(SOURCES) + g++ -o atom $(OBJECTS) $(LDFLAGS) $(LIBS) + +clean: + rm -rf atom *.o + +install: + mkdir -p $(INSTALLDIR) + cp atom $(INSTALLDIR) + cp chrome.pak $(INSTALLDIR) + cp -R locales $(INSTALLDIR) + cp atom.png $(INSTALLDIR) + cp lib/libcef.so $(INSTALLDIR) + cp lib/libcef_dll_wrapper.a $(INSTALLDIR) + cp -R ../src $(INSTALLDIR) + cp -R ../static $(INSTALLDIR) + cp -R ../vendor $(INSTALLDIR) + cp -R ../bundles $(INSTALLDIR) + cp -R ../themes $(INSTALLDIR) + cp ../index.html $(INSTALLDIR) + coffee -c -o $(INSTALLDIR)/src/stdlib ../src/stdlib/require.coffee + ln -sf $(INSTALLDIR)/atom /usr/local/bin/atom + +spec-install: install + cp -R ../spec $(INSTALLDIR) diff --git a/Atom-Linux/atom.cpp b/Atom-Linux/atom.cpp new file mode 100644 index 000000000..3457166fc --- /dev/null +++ b/Atom-Linux/atom.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include +#include +#include +#include "atom.h" +#include "include/cef_app.h" +#include "include/cef_browser.h" +#include "include/cef_frame.h" +#include "include/cef_runnable.h" +#include "client_handler.h" +#include "onig_regexp_extension.h" +#include "io_utils.h" + +char* szWorkingDir; // The current working directory + +const char* szPath; // The folder the application is in + +const char* szPathToOpen; // The file to open + +CefRefPtr g_handler; + +void AppGetSettings(CefSettings& settings, CefRefPtr& app) { + CefString(&settings.cache_path) = ""; + CefString(&settings.user_agent) = ""; + CefString(&settings.product_version) = ""; + CefString(&settings.locale) = ""; + CefString(&settings.log_file) = ""; + CefString(&settings.javascript_flags) = ""; + + settings.log_severity = LOGSEVERITY_ERROR; + settings.local_storage_quota = 0; + settings.session_storage_quota = 0; +} + +void destroy(void) { + CefQuitMessageLoop(); +} + +void TerminationSignalHandler(int signatl) { + destroy(); +} + +// WebViewDelegate::TakeFocus in the test webview delegate. +static gboolean HandleFocus(GtkWidget* widget, GdkEventFocus* focus) { + if (g_handler.get() && g_handler->GetBrowserHwnd()) { + // Give focus to the browser window. + g_handler->GetBrowser()->SetFocus(true); + } + + return TRUE; +} + +int main(int argc, char *argv[]) { + szWorkingDir = get_current_dir_name(); + if (szWorkingDir == NULL) + return -1; + + std::string appDir = io_util_app_directory(); + if (appDir.empty()) + return -1; + + szPath = appDir.c_str(); + + std::string pathToOpen; + if (argc >= 2) { + if (argv[1][0] != '/') { + pathToOpen.append(szWorkingDir); + pathToOpen.append("/"); + pathToOpen.append(argv[1]); + } else + pathToOpen.append(argv[1]); + } else + pathToOpen.append(szWorkingDir); + szPathToOpen = pathToOpen.c_str(); + + GtkWidget* window; + + gtk_init(&argc, &argv); + + CefSettings settings; + CefRefPtr app; + + AppGetSettings(settings, app); + CefInitialize(settings, app); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "atom"); + gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); + gtk_window_maximize(GTK_WINDOW(window)); + + g_signal_connect(window, "focus", G_CALLBACK(&HandleFocus), NULL); + + GtkWidget* vbox = gtk_vbox_new(FALSE, 0); + + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(gtk_widget_destroyed), &window); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); + + // Create the handler. + g_handler = new ClientHandler(); + g_handler->SetMainHwnd(vbox); + g_handler->SetWindow(window); + + new OnigRegexpExtension(); + + // Create the browser view. + CefWindowInfo window_info; + CefBrowserSettings browserSettings; + + window_info.SetAsChild(vbox); + + std::string path = io_utils_real_app_path("/index.html"); + if (path.empty()) + return -1; + + std::string resolved("file://"); + resolved.append(path); + + CefBrowser::CreateBrowserSync(window_info, + static_cast >(g_handler), resolved, browserSettings); + + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_widget_show_all(GTK_WIDGET(window)); + + GdkPixbuf *pixbuf; + GError *error = NULL; + std::string iconPath; + iconPath.append(szPath); + iconPath.append("/atom.png"); + pixbuf = gdk_pixbuf_new_from_file(iconPath.c_str(), &error); + if (pixbuf) + gtk_window_set_icon(GTK_WINDOW(window), pixbuf); + + // Install an signal handler so we clean up after ourselves. + signal(SIGINT, TerminationSignalHandler); + signal(SIGTERM, TerminationSignalHandler); + + CefRunMessageLoop(); + + CefShutdown(); + + return 0; +} + +// Global functions + +std::string AppGetWorkingDirectory() { + return szWorkingDir; +} + +std::string AppPath() { + return szPath; +} + +std::string PathToOpen() { + return szPathToOpen; +} diff --git a/Atom-Linux/atom.h b/Atom-Linux/atom.h new file mode 100644 index 000000000..7172fabd3 --- /dev/null +++ b/Atom-Linux/atom.h @@ -0,0 +1,30 @@ +#ifndef ATOM_H_ +#define ATOM_H_ +#pragma once + +#include +#include "include/cef_base.h" + +class CefApp; +class CefBrowser; +class CefCommandLine; + +// Returns the main browser window instance. +CefRefPtr AppGetBrowser(); + +// Returns the main application window handle. +CefWindowHandle AppGetMainHwnd(); + +// Returns the application working directory. +std::string AppGetWorkingDirectory(); + +// Returns the application's path. +std::string AppPath(); + +// Returns the initial path to open. +std::string PathToOpen(); + +// Returns the application settings +void AppGetSettings(CefSettings& settings, CefRefPtr& app); + +#endif diff --git a/Atom-Linux/atom.png b/Atom-Linux/atom.png new file mode 100644 index 000000000..b77e76777 Binary files /dev/null and b/Atom-Linux/atom.png differ diff --git a/Atom-Linux/chrome.pak b/Atom-Linux/chrome.pak new file mode 100644 index 000000000..51266259d Binary files /dev/null and b/Atom-Linux/chrome.pak differ diff --git a/Atom-Linux/client_handler.cpp b/Atom-Linux/client_handler.cpp new file mode 100644 index 000000000..7794d6ad2 --- /dev/null +++ b/Atom-Linux/client_handler.cpp @@ -0,0 +1,263 @@ +// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "client_handler.h" +#include +#include +#include +#include +#include "include/cef_browser.h" +#include "include/cef_frame.h" +#include "atom.h" +#include "native_handler.h" +#include "atom.h" +#include +#include + +ClientHandler::ClientHandler() : + m_MainHwnd(NULL), m_BrowserHwnd(NULL) { + m_nativeHandler = new NativeHandler(); +} + +ClientHandler::~ClientHandler() { +} + +void ClientHandler::OnAfterCreated(CefRefPtr browser) { + REQUIRE_UI_THREAD(); + + AutoLock lock_scope(this); + if (!m_Browser.get()) { + // We need to keep the main child window, but not popup windows + m_Browser = browser; + m_BrowserHwnd = browser->GetWindowHandle(); + } +} + +bool ClientHandler::DoClose(CefRefPtr browser) { + REQUIRE_UI_THREAD(); + + if (m_BrowserHwnd == browser->GetWindowHandle()) { + // Since the main window contains the browser window, we need to close + // the parent window instead of the browser window. + CloseMainWindow(); + + // Return true here so that we can skip closing the browser window + // in this pass. (It will be destroyed due to the call to close + // the parent above.) + return true; + } + + // A popup browser window is not contained in another window, so we can let + // these windows close by themselves. + return false; +} + +void ClientHandler::OnBeforeClose(CefRefPtr browser) { + REQUIRE_UI_THREAD(); + + if (m_BrowserHwnd == browser->GetWindowHandle()) { + // Free the browser pointer so that the browser can be destroyed + m_Browser = NULL; + } +} + +void ClientHandler::OnLoadStart(CefRefPtr browser, + CefRefPtr frame) { + REQUIRE_UI_THREAD(); + + if (m_BrowserHwnd == browser->GetWindowHandle() && frame->IsMain()) { + CefRefPtr context = frame->GetV8Context(); + CefRefPtr global = context->GetGlobal(); + context->Enter(); + + CefRefPtr windowNumber = CefV8Value::CreateInt(0); + global->SetValue("$windowNumber", windowNumber, V8_PROPERTY_ATTRIBUTE_NONE); + + std::string path; + if (m_nativeHandler && !m_nativeHandler->path.empty()) + path = m_nativeHandler->path; + else + path.append(PathToOpen()); + + CefRefPtr pathToOpen = CefV8Value::CreateString(path); + global->SetValue("$pathToOpen", pathToOpen, V8_PROPERTY_ATTRIBUTE_NONE); + + m_nativeHandler->window = window; + m_nativeHandler->path = path; + + CefRefPtr atom = CefV8Value::CreateObject(NULL, NULL); + global->SetValue("atom", atom, V8_PROPERTY_ATTRIBUTE_NONE); + + CefRefPtr loadPath = CefV8Value::CreateString(AppPath()); + atom->SetValue("loadPath", loadPath, V8_PROPERTY_ATTRIBUTE_NONE); + + CefRefPtr bootstrapScript = CefV8Value::CreateString( + "single-window-bootstrap"); + global->SetValue("$bootstrapScript", bootstrapScript, + V8_PROPERTY_ATTRIBUTE_NONE); + + context->Exit(); + } +} + +void ClientHandler::OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, int httpStatusCode) { + REQUIRE_UI_THREAD(); + +} + +bool ClientHandler::OnLoadError(CefRefPtr browser, + CefRefPtr frame, ErrorCode errorCode, const CefString& failedUrl, + CefString& errorText) { + REQUIRE_UI_THREAD(); + + if (errorCode == ERR_CACHE_MISS) { + // Usually caused by navigating to a page with POST data via back or + // forward buttons. + errorText = "Expired Form Data" + "

Expired Form Data

" + "

Your form request has expired. " + "Click reload to re-submit the form data.

" + ""; + } else { + // All other messages. + std::stringstream ss; + ss << "Load Failed" + "

Load Failed

" + "

Load of URL " << std::string(failedUrl) + << " failed with error code " << static_cast(errorCode) + << ".

" + ""; + errorText = ss.str(); + } + + return false; +} + +void ClientHandler::OnNavStateChange(CefRefPtr browser, + bool canGoBack, bool canGoForward) { + //Intentionally left blank +} + +bool ClientHandler::OnConsoleMessage(CefRefPtr browser, + const CefString& message, const CefString& source, int line) { + REQUIRE_UI_THREAD(); + std::cout << std::string(message) << std::endl; + return false; +} + +void ClientHandler::OnFocusedNodeChanged(CefRefPtr browser, + CefRefPtr frame, CefRefPtr node) { + REQUIRE_UI_THREAD(); +} + +bool ClientHandler::OnKeyEvent(CefRefPtr browser, KeyEventType type, + int code, int modifiers, bool isSystemKey, bool isAfterJavaScript) { + REQUIRE_UI_THREAD(); + + return false; +} + +bool ClientHandler::GetPrintHeaderFooter(CefRefPtr browser, + CefRefPtr frame, const CefPrintInfo& printInfo, + const CefString& url, const CefString& title, int currentPage, int maxPages, + CefString& topLeft, CefString& topCenter, CefString& topRight, + CefString& bottomLeft, CefString& bottomCenter, CefString& bottomRight) { + REQUIRE_UI_THREAD(); + + // Place the page title at top left + topLeft = title; + // Place the page URL at top right + topRight = url; + + // Place "Page X of Y" at bottom center + std::stringstream strstream; + strstream << "Page " << currentPage << " of " << maxPages; + bottomCenter = strstream.str(); + + return false; +} + +void ClientHandler::OnContextCreated(CefRefPtr browser, + CefRefPtr frame, CefRefPtr context) { + REQUIRE_UI_THREAD(); +} + +bool ClientHandler::OnDragStart(CefRefPtr browser, + CefRefPtr dragData, DragOperationsMask mask) { + REQUIRE_UI_THREAD(); + + return false; +} + +bool ClientHandler::OnDragEnter(CefRefPtr browser, + CefRefPtr dragData, DragOperationsMask mask) { + REQUIRE_UI_THREAD(); + + return false; +} + +void ClientHandler::SetWindow(GtkWidget* widget) { + window = widget; +} + +void ClientHandler::SetMainHwnd(CefWindowHandle hwnd) { + AutoLock lock_scope(this); + m_MainHwnd = hwnd; +} + +// ClientHandler::ClientLifeSpanHandler implementation +bool ClientHandler::OnBeforePopup(CefRefPtr parentBrowser, + const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, + const CefString& url, CefRefPtr& client, + CefBrowserSettings& settings) { + REQUIRE_UI_THREAD(); + + return false; +} + +void ClientHandler::OnAddressChange(CefRefPtr browser, + CefRefPtr frame, const CefString& url) { + //Intentionally left blank +} + +void ClientHandler::OnTitleChange(CefRefPtr browser, + const CefString& title) { + REQUIRE_UI_THREAD(); + + std::string titleStr(title); + + size_t inHomeDir; + std::string home = getenv("HOME"); + inHomeDir = titleStr.find(home); + if (inHomeDir == 0) { + titleStr = titleStr.substr(home.length()); + titleStr.insert(0, "~"); + } + + size_t lastSlash; + lastSlash = titleStr.rfind("/"); + + std::string formatted; + if (lastSlash != std::string::npos && lastSlash + 1 < titleStr.length()) { + formatted.append(titleStr, lastSlash + 1, titleStr.length() - lastSlash); + formatted.append(" ("); + formatted.append(titleStr, 0, lastSlash); + formatted.append(")"); + } else + formatted.append(titleStr); + formatted.append(" - atom"); + + GtkWidget* window = gtk_widget_get_ancestor( + GTK_WIDGET(browser->GetWindowHandle()), GTK_TYPE_WINDOW); + gtk_window_set_title(GTK_WINDOW(window), formatted.c_str()); +} + +void ClientHandler::SendNotification(NotificationType type) { + // TODO(port): Implement this method. +} + +void ClientHandler::CloseMainWindow() { + // TODO(port): Close main window. +} diff --git a/Atom-Linux/client_handler.h b/Atom-Linux/client_handler.h new file mode 100644 index 000000000..c45dba015 --- /dev/null +++ b/Atom-Linux/client_handler.h @@ -0,0 +1,155 @@ +#ifndef CLIENT_HANDLER_H_ +#define CLIENT_HANDLER_H_ +#pragma once + +#include +#include +#include "include/cef_client.h" +#include "util.h" +#include "native_handler.h" + +// ClientHandler implementation. +class ClientHandler: public CefClient, + public CefLifeSpanHandler, + public CefLoadHandler, + public CefDisplayHandler, + public CefFocusHandler, + public CefKeyboardHandler, + public CefPrintHandler, + public CefV8ContextHandler, + public CefDragHandler { +public: + ClientHandler(); + virtual ~ClientHandler(); + + // CefClient methods + virtual CefRefPtr GetLifeSpanHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetLoadHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetDisplayHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetFocusHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetKeyboardHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetPrintHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetV8ContextHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetDragHandler() OVERRIDE { + return this; + } + + // CefLifeSpanHandler methods + virtual bool OnBeforePopup(CefRefPtr parentBrowser, + const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, + const CefString& url, CefRefPtr& client, + CefBrowserSettings& settings) OVERRIDE; + virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE; + virtual bool DoClose(CefRefPtr browser) OVERRIDE; + virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE; + + // CefLoadHandler methods + virtual void OnLoadStart(CefRefPtr browser, + CefRefPtr frame) OVERRIDE; + virtual void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, int httpStatusCode) OVERRIDE; + virtual bool OnLoadError(CefRefPtr browser, + CefRefPtr frame, ErrorCode errorCode, + const CefString& failedUrl, CefString& errorText) OVERRIDE; + + // CefDisplayHandler methods + virtual void OnNavStateChange(CefRefPtr browser, bool canGoBack, + bool canGoForward) OVERRIDE; + virtual void OnAddressChange(CefRefPtr browser, + CefRefPtr frame, const CefString& url) OVERRIDE; + virtual void OnTitleChange(CefRefPtr browser, + const CefString& title) OVERRIDE; + virtual bool OnConsoleMessage(CefRefPtr browser, + const CefString& message, const CefString& source, int line) OVERRIDE; + + // CefFocusHandler methods. + virtual void OnFocusedNodeChanged(CefRefPtr browser, + CefRefPtr frame, CefRefPtr node) OVERRIDE; + + // CefKeyboardHandler methods. + virtual bool OnKeyEvent(CefRefPtr browser, KeyEventType type, + int code, int modifiers, bool isSystemKey, bool isAfterJavaScript) + OVERRIDE; + + // CefPrintHandler methods. + virtual bool GetPrintHeaderFooter(CefRefPtr browser, + CefRefPtr frame, const CefPrintInfo& printInfo, + const CefString& url, const CefString& title, int currentPage, + int maxPages, CefString& topLeft, CefString& topCenter, + CefString& topRight, CefString& bottomLeft, CefString& bottomCenter, + CefString& bottomRight) OVERRIDE; + + // CefV8ContextHandler methods + virtual void OnContextCreated(CefRefPtr browser, + CefRefPtr frame, CefRefPtr context) OVERRIDE; + + // CefDragHandler methods. + virtual bool OnDragStart(CefRefPtr browser, + CefRefPtr dragData, DragOperationsMask mask) OVERRIDE; + virtual bool OnDragEnter(CefRefPtr browser, + CefRefPtr dragData, DragOperationsMask mask) OVERRIDE; + + void SetWindow(GtkWidget* window); + void SetMainHwnd(CefWindowHandle hwnd); + CefWindowHandle GetMainHwnd() { + return m_MainHwnd; + } + void SetEditHwnd(CefWindowHandle hwnd); + + CefRefPtr GetBrowser() { + return m_Browser; + } + CefWindowHandle GetBrowserHwnd() { + return m_BrowserHwnd; + } + + enum NotificationType { + NOTIFY_CONSOLE_MESSAGE + }; + void SendNotification(NotificationType type); + void CloseMainWindow(); + +protected: + + GtkWidget* window; + + CefRefPtr m_nativeHandler; + + // The child browser window + CefRefPtr m_Browser; + + // The main frame window handle + CefWindowHandle m_MainHwnd; + + // The child browser window handle + CefWindowHandle m_BrowserHwnd; + + // The edit window handle + CefWindowHandle m_EditHwnd; + + // Support for logging. + std::string m_LogFile; + + // Include the default reference counting implementation. +IMPLEMENT_REFCOUNTING(ClientHandler) + ; + // Include the default locking implementation. +IMPLEMENT_LOCKING(ClientHandler) + ; +}; + +#endif diff --git a/Atom-Linux/io_utils.cpp b/Atom-Linux/io_utils.cpp new file mode 100644 index 000000000..494c00fff --- /dev/null +++ b/Atom-Linux/io_utils.cpp @@ -0,0 +1,49 @@ +#include "io_utils.h" +#include "atom.h" +#include +#include +#include + +#define BUFFER_SIZE 8192 + +using namespace std; + +int io_utils_read(string path, string* output) { + int fd = open(path.c_str(), O_RDONLY); + if (fd <= 0) + return 0; + + char buffer[BUFFER_SIZE]; + unsigned int bytesRead = 0; + unsigned int totalRead = 0; + while ((bytesRead = read(fd, buffer, BUFFER_SIZE)) > 0) { + output->append(buffer, 0, bytesRead); + totalRead += bytesRead; + } + close(fd); + return totalRead; +} + +string io_utils_real_app_path(string relativePath) { + string path = AppPath() + relativePath; + char* realPath = realpath(path.c_str(), NULL); + if (realPath != NULL) { + string realAppPath(realPath); + free(realPath); + return realAppPath; + } else + return ""; +} + +string io_util_app_directory() { + char path[BUFFER_SIZE]; + if (readlink("/proc/self/exe", path, BUFFER_SIZE) < 2) + return ""; + + string appPath(path); + unsigned int lastSlash = appPath.rfind("/"); + if (lastSlash != string::npos) + return appPath.substr(0, lastSlash); + else + return appPath; +} \ No newline at end of file diff --git a/Atom-Linux/io_utils.h b/Atom-Linux/io_utils.h new file mode 100644 index 000000000..f01a25e16 --- /dev/null +++ b/Atom-Linux/io_utils.h @@ -0,0 +1,22 @@ +#ifndef IO_UTILS_H_ +#define IO_UTILS_H_ +#pragma once + +#include + +/** + * Read file at path and append to output string + */ +int io_utils_read(std::string path, std::string* output); + +/** + * Get realpath for given path that is relative to the app path + */ +std::string io_utils_real_app_path(std::string relativePath); + +/** + * Get path to directory where atom app resides + */ +std::string io_util_app_directory(); + +#endif diff --git a/Atom-Linux/lib/libcef.so b/Atom-Linux/lib/libcef.so new file mode 100755 index 000000000..f6e77b31f Binary files /dev/null and b/Atom-Linux/lib/libcef.so differ diff --git a/Atom-Linux/lib/libcef_dll_wrapper.a b/Atom-Linux/lib/libcef_dll_wrapper.a new file mode 100644 index 000000000..c0c1e53dc Binary files /dev/null and b/Atom-Linux/lib/libcef_dll_wrapper.a differ diff --git a/Atom-Linux/locales/en-US.pak b/Atom-Linux/locales/en-US.pak new file mode 100644 index 000000000..00fc0cba9 Binary files /dev/null and b/Atom-Linux/locales/en-US.pak differ diff --git a/Atom-Linux/native_handler.cpp b/Atom-Linux/native_handler.cpp new file mode 100644 index 000000000..4ed2dd25a --- /dev/null +++ b/Atom-Linux/native_handler.cpp @@ -0,0 +1,527 @@ +#include "native_handler.h" +#include "include/cef_base.h" +#include "include/cef_runnable.h" +#include "client_handler.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; + +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(function, args, retval, e, true); + + context->Exit(); + } +} + +NativeHandler::NativeHandler() : + CefV8Handler() { + string nativePath = io_utils_real_app_path("/src/stdlib/native-handler.js"); + if (!nativePath.empty()) { + string extensionCode; + if (io_utils_read(nativePath, &extensionCode) > 0) + CefRegisterExtension("v8/native-handler", extensionCode, this); + } + + notifyFd = inotify_init(); + if (notifyFd != -1) + g_thread_create_full(NotifyWatchersCallback, this, 0, true, false, + G_THREAD_PRIORITY_NORMAL, NULL); +} + +void NativeHandler::NotifyWatchers() { + char buffer[BUFFER_SIZE]; + ssize_t bufferRead; + size_t eventSize; + ssize_t bufferIndex; + struct inotify_event *event; + bufferRead = read(notifyFd, buffer, BUFFER_SIZE); + while (bufferRead > 0) { + bufferIndex = 0; + while (bufferIndex < bufferRead) { + event = (struct inotify_event *) &buffer[bufferIndex]; + eventSize = offsetof (struct inotify_event, name) + event->len; + + NotifyContext context; + context.descriptor = event->wd; + context.callbacks = pathCallbacks; + CefPostTask(TID_UI, + NewCefRunnableFunction(&ExecuteWatchCallback, context)); + + bufferIndex += eventSize; + } + bufferRead = read(notifyFd, buffer, BUFFER_SIZE); + } +} + +void NativeHandler::Exists(const CefString& name, CefRefPtr 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(); + 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(); + 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(arguments[2], args, retval, e, true); + 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(); + 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(callback, args, retval, e, true); + 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, 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 == "showDevTools") + CefV8Context::GetCurrentContext()->GetBrowser()->ShowDevTools(); + else if (name == "openDialog") + OpenDialog(name, object, arguments, retval, exception); + else if (name == "open") + Open(name, object, arguments, retval, exception); + else if (name == "write") + Write(name, object, arguments, retval, exception); + else if (name == "writeToPasteboard") + WriteToPasteboard(name, object, arguments, retval, exception); + else if (name == "readFromPasteboard") + ReadFromPasteboard(name, object, arguments, retval, exception); + else if (name == "asyncList") + AsyncList(name, object, arguments, retval, exception); + else if (name == "makeDirectory") + MakeDirectory(name, object, arguments, retval, exception); + else if (name == "move") + Move(name, object, arguments, retval, exception); + else if (name == "remove") + Remove(name, object, arguments, retval, exception); + else if (name == "alert") + Alert(name, object, arguments, retval, exception); + else if (name == "watchPath") + WatchPath(name, object, arguments, retval, exception); + else if (name == "unwatchPath") + UnwatchPath(name, object, arguments, retval, exception); + else if (name == "md5ForPath") + Digest(name, object, arguments, retval, exception); + else if (name == "getPlatform") + retval = CefV8Value::CreateString("linux"); + else if (name == "lastModified") + LastModified(name, object, arguments, retval, exception); + else + cout << "Unhandled -> " + name.ToString() << " : " + << arguments[0]->GetStringValue().ToString() << endl; + return true; +} diff --git a/Atom-Linux/native_handler.h b/Atom-Linux/native_handler.h new file mode 100644 index 000000000..7af1826bc --- /dev/null +++ b/Atom-Linux/native_handler.h @@ -0,0 +1,128 @@ +#ifndef NATIVE_HANDLER_H_ +#define NATIVE_HANDLER_H_ + +#include "include/cef_base.h" +#include "include/cef_v8.h" +#include +#include + +struct CallbackContext { + CefRefPtr context; + CefRefPtr function; + CefRefPtr eventTypes; +}; + +struct NotifyContext { + int descriptor; + std::map > callbacks; +}; + +class NativeHandler: public CefV8Handler { +public: + NativeHandler(); + + GtkWidget* window; + + std::string path; + + virtual bool Execute(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void NotifyWatchers(); + +IMPLEMENT_REFCOUNTING(NativeHandler) + ; + +private: + + int notifyFd; + + unsigned long int idCounter; + + std::map > pathCallbacks; + + std::map pathDescriptors; + + void Exists(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Read(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Absolute(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void List(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void AsyncList(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void IsFile(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void IsDirectory(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void OpenDialog(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Open(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Write(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void WriteToPasteboard(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void ReadFromPasteboard(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void MakeDirectory(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Move(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Remove(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Alert(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void WatchPath(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void UnwatchPath(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void Digest(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + + void LastModified(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); +}; + +#endif diff --git a/Atom-Linux/onig_regexp_extension.cpp b/Atom-Linux/onig_regexp_extension.cpp new file mode 100644 index 000000000..686fe9f2f --- /dev/null +++ b/Atom-Linux/onig_regexp_extension.cpp @@ -0,0 +1,185 @@ +#include "onig_regexp_extension.h" +#include "include/cef_base.h" +#include "include/cef_runnable.h" +#include +#include +#include +#include "io_utils.h" + +using namespace std; + +class OnigRegexpUserData: public CefBase { +public: + OnigRegexpUserData(CefRefPtr source) { + OnigErrorInfo error; + string input = source->GetStringValue().ToString(); + int length = input.length(); + UChar* pattern = (UChar*) input.c_str(); + int code = onig_new(®ex, pattern, pattern + length, + ONIG_OPTION_SINGLELINE, ONIG_ENCODING_UTF8, ONIG_SYNTAX_DEFAULT, + &error); + if (code != ONIG_NORMAL) { + char errorText[ONIG_MAX_ERROR_MESSAGE_LEN]; + onig_error_code_to_str((OnigUChar*) errorText, code, &error); + cout << errorText << " for pattern: " << input << endl; + } + } + + ~OnigRegexpUserData() { + onig_free(regex); + } + + OnigRegion* SearchRegion(string input, int index) { + if (!regex) + return NULL; + + OnigRegion* region = onig_region_new(); + UChar* search = (UChar*) input.c_str(); + unsigned char* start = search + index; + unsigned char* end = search + input.length(); + int code = onig_search(regex, search, end, start, end, region, + ONIG_OPTION_NONE); + if (code >= 0) + return region; + else { + onig_region_free(region, 1); + return NULL; + } + } + + CefRefPtr Search(CefRefPtr argument, + CefRefPtr index) { + string input = argument->GetStringValue().ToString(); + OnigRegion* region = SearchRegion(input, index->GetIntValue()); + if (!region) + return CefV8Value::CreateNull(); + + CefRefPtr indices; + CefRefPtr resultArray = CefV8Value::CreateArray(); + CefRefPtr indicesArray = CefV8Value::CreateArray(); + for (int i = 0; i < region->num_regs; i++) { + int begin = region->beg[i]; + int end = region->end[i]; + resultArray->SetValue(i, + CefV8Value::CreateString(input.substr(begin, end - begin))); + indicesArray->SetValue(i, CefV8Value::CreateInt(begin)); + } + resultArray->SetValue("index", CefV8Value::CreateInt(region->beg[0]), + V8_PROPERTY_ATTRIBUTE_NONE); + resultArray->SetValue("indices", indicesArray, V8_PROPERTY_ATTRIBUTE_NONE); + onig_region_free(region, 1); + return resultArray; + } + + CefRefPtr Test(CefRefPtr argument, + CefRefPtr index) { + OnigRegion* region = SearchRegion(argument->GetStringValue().ToString(), + index->GetIntValue()); + CefRefPtr text = CefV8Value::CreateBool(region != NULL); + if (region) + onig_region_free(region, 1); + return text; + } + + CefRefPtr GetCaptureIndices(CefRefPtr argument, + CefRefPtr index) { + OnigRegion* region = SearchRegion(argument->GetStringValue().ToString(), + index->GetIntValue()); + CefRefPtr indices; + if (region) { + indices = BuildCaptureIndices(region); + onig_region_free(region, 1); + } else + indices = CefV8Value::CreateNull(); + return indices; + } + + CefRefPtr BuildCaptureIndices(OnigRegion *region) { + CefRefPtr array = CefV8Value::CreateArray(); + int i = 0; + for (int index = 0; index < region->num_regs; index++) { + int begin = region->beg[index]; + int end = region->end[index]; + if (end - begin <= 0) + continue; + array->SetValue(i++, CefV8Value::CreateInt(index)); + array->SetValue(i++, CefV8Value::CreateInt(begin)); + array->SetValue(i++, CefV8Value::CreateInt(end)); + } + + return array; + } + + CefRefPtr CaptureCount() { + if (regex) + return CefV8Value::CreateInt(onig_number_of_captures(regex)); + else + return CefV8Value::CreateInt(0); + } + + regex_t* regex; + +IMPLEMENT_REFCOUNTING(OnigRegexpUserData) + ; +} +; + +OnigRegexpExtension::OnigRegexpExtension() : + CefV8Handler() { + string realFilePath = io_utils_real_app_path( + "/src/stdlib/onig-reg-exp-extension.js"); + if (!realFilePath.empty()) { + string extensionCode; + if (io_utils_read(realFilePath, &extensionCode) > 0) + CefRegisterExtension("v8/oniguruma", extensionCode, this); + } +} + +bool OnigRegexpExtension::Execute(const CefString& name, + CefRefPtr object, const CefV8ValueList& arguments, + CefRefPtr& retval, CefString& exception) { + if (name == "getCaptureIndices") { + CefRefPtr string = arguments[0]; + CefRefPtr index = + arguments.size() > 1 ? arguments[1] : CefV8Value::CreateInt(0); + OnigRegexpUserData *userData = + (OnigRegexpUserData *) object->GetUserData().get(); + retval = userData->GetCaptureIndices(string, index); + return true; + } + + if (name == "search") { + CefRefPtr string = arguments[0]; + CefRefPtr index = + arguments.size() > 1 ? arguments[1] : CefV8Value::CreateInt(0); + OnigRegexpUserData *userData = + (OnigRegexpUserData *) object->GetUserData().get(); + retval = userData->Search(string, index); + return true; + } + + if (name == "test") { + CefRefPtr string = arguments[0]; + CefRefPtr index = + arguments.size() > 1 ? arguments[1] : CefV8Value::CreateInt(0); + OnigRegexpUserData *userData = + (OnigRegexpUserData *) object->GetUserData().get(); + retval = userData->Test(string, index); + return true; + } + + if (name == "buildOnigRegExp") { + CefRefPtr userData = new OnigRegexpUserData(arguments[0]); + retval = CefV8Value::CreateObject(userData, NULL); + return true; + } + + if (name == "getCaptureCount") { + OnigRegexpUserData *userData = + (OnigRegexpUserData *) object->GetUserData().get(); + retval = userData->CaptureCount(); + return true; + } + + return false; +} diff --git a/Atom-Linux/onig_regexp_extension.h b/Atom-Linux/onig_regexp_extension.h new file mode 100644 index 000000000..0d30ac157 --- /dev/null +++ b/Atom-Linux/onig_regexp_extension.h @@ -0,0 +1,21 @@ +#ifndef ONIG_REGEXP_EXTENSION_H_ +#define ONIG_REGEXP_EXTENSION_H_ + +#include "include/cef_base.h" +#include "include/cef_v8.h" + +class OnigRegexpExtension: public CefV8Handler { + +public: + OnigRegexpExtension(); + + virtual bool Execute(const CefString& name, CefRefPtr object, + const CefV8ValueList& arguments, CefRefPtr& retval, + CefString& exception); + +IMPLEMENT_REFCOUNTING(OnigRegexpExtension) + ; + +}; + +#endif diff --git a/Atom-Linux/util.h b/Atom-Linux/util.h new file mode 100644 index 000000000..51f371d82 --- /dev/null +++ b/Atom-Linux/util.h @@ -0,0 +1,22 @@ +// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef UTIL_H_ +#define UTIL_H_ +#pragma once + +#include "include/cef_task.h" + +#include // NOLINT(build/include_order) +#ifndef NDEBUG +#define ASSERT(condition) if (!(condition)) { assert(false); } +#else +#define ASSERT(condition) ((void)0) +#endif + +#define REQUIRE_UI_THREAD() ASSERT(CefCurrentlyOn(TID_UI)); +#define REQUIRE_IO_THREAD() ASSERT(CefCurrentlyOn(TID_IO)); +#define REQUIRE_FILE_THREAD() ASSERT(CefCurrentlyOn(TID_FILE)); + +#endif diff --git a/README.md b/README.md new file mode 100644 index 000000000..d5ef7e210 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Atom — Futuristic Text Editing +## Be forwarned: Atom is pre-alpha software! + + +## Building from source + +1. Get [xcode 4.2 or above](http://itunes.apple.com/us/app/xcode/id448457090?mt=12) + +2. Install CoffeeScript http://coffeescript.org/ +``` +brew install nodejs +curl https://npmjs.org/install.sh | sh +npm i -g coffee-script +``` + +3. `git clone git@github.com:github/atom.git` + +4. `cd atom` + +5. `rake run` + + +## Setup Your ~/.atom Directory +An example .atom directory can be found at the root of the source tree + +## Basic Keyboard shortcuts +Atom doesn't have much in the way of menus yet. Use these keyboard shortcuts to +explore features. + +`cmd-o` : open file/directory + +`cmd-n` : new window + +`cmd-t` : open fuzzy file finder + +`cmd-:` : open command prompt + +`cmd-f` : open command prompt with / + +`cmd-g` : repeat the last search + +`cmd-r` : reload the current window + +`cmd-alt-ctrl-s` : run specs + +`cmd-alt-arrows` : split screen in direction of arrow + +`cmd-alt-w` : toggle word wrap + +`cmd-alt-f` : fold selected lines + +Most default OS X keybindings also work. + +## Init Script + +Atom will require `~/.atom/atom.coffee` whenever a window is opened or reloaded if it is present in your +home directory. This is a rudimentary jumping off point for your own customizations. + +## Command Panel + +A partial implementation of the [Sam command language](http://man.cat-v.org/plan_9/1/sam) + +*Examples* + +`,` selects entire file + +`1,4` selects lines 1-4 + +`/pattern` selects the first match after the cursor/selection + +`s/pattern/replacement` replace first text matching pattern in current selection + +`s/pattern/replacement/g` replace all text matching pattern in current selection + +`,s/pattern/replacement/g` replace all text matching pattern in file + +`1,4s/pattern/replacement` replace all text matching pattern in lines 1-4 + +`x/pattern` selects all matches in the current selections + +`,x/pattern` selects all matches in the file + +`,x/pattern1/ x/pattern2` "structural regex" - selects all matches of pattern2 inside matches of pattern1 + +## Key Bindings + +Atom has a CSS based key binding scheme. We will add a nicer loading mechanism, but for now you can bind +keys by calling `window.keymap.bindKeys` with a CSS selector and a hash of key-pattern -> event mappings. + +```coffeescript +window.keymap.bindKeys '.editor' + 'ctrl-p': 'party-time' + 'ctrl-q': 'open-dialog-q' +``` + +When a keypress matches a pattern on an element that matches the selector, it will be translated to the +named event, which will bubble up the DOM from the site of the keypress. Extension code can listen for +the named event and react to it. + + diff --git a/docs/file-modification b/docs/file-modification new file mode 100644 index 000000000..a3df1c0be --- /dev/null +++ b/docs/file-modification @@ -0,0 +1,18 @@ +Buffers + modify + - if dirty alert user (immediately if focused, or on next focus) + - if clean update contents + + remove + - mark file as unsaved (but maintains previous path) + - only unsubscribe from KQueue + - attempt to resubscribe after timeout (because of git weirdness) + + move file + - update path + + move ancestor directory + - update path on focus and save + + recreated after remove (at same path) + - resubscribe on focus or save \ No newline at end of file diff --git a/docs/flexbox-test-2.html b/docs/flexbox-test-2.html new file mode 100644 index 000000000..482f2e6e9 --- /dev/null +++ b/docs/flexbox-test-2.html @@ -0,0 +1,64 @@ + + + + + + +
+
+
+ My cross size (height) should be equal to the height of my flexbox container. I should overflow, because my height should be too short to contain all my content. +
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
+
+
+ My cross size (height) should also be equal to the height of my flexbox container, but I don't overflow. +
+
+ + + + diff --git a/docs/flexbox-test-old.html b/docs/flexbox-test-old.html new file mode 100644 index 000000000..683c44842 --- /dev/null +++ b/docs/flexbox-test-old.html @@ -0,0 +1,84 @@ + + + + wtf + + + + +
+
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
+
+
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
+
+
+ I am a child of a flexbox item. My height should be exactly the height of my container. +
+
+
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
+
+
Keffiyeh mustache pickled post-ironic, lomo vegan food truck helvetica direct trade nostrud. Assumenda odio brunch, DIY non anim delectus sunt aliqua organic VHS nihil pork belly accusamus. Bushwick vice high life tumblr mumblecore ullamco. High life pop-up lomo, pariatur exercitation odio helvetica food truck ex williamsburg stumptown hoodie ea polaroid jean shorts. Keytar sed sapiente, mumblecore fixie +1 cred occaecat accusamus. Fanny pack authentic dolor, id four loko dolore ex +1 pop-up. Thundercats cliche aliqua, fugiat irony marfa chambray banksy kogi organic selvage VHS DIY. Id mumblecore nisi, brunch narwhal nostrud vegan squid before they sold out. Sriracha laborum nesciunt, in salvia you probably haven't heard of them mustache VHS commodo squid proident williamsburg. Magna vero gentrify labore, non american apparel occaecat put a bird on it mlkshk DIY occupy eu pinterest aute cliche. Nihil delectus commodo voluptate nostrud. Tattooed tempor skateboard, sed tumblr nostrud chambray put a bird on it non salvia helvetica consectetur mcsweeney's incididunt. Est semiotics ut yr, fanny pack leggings voluptate carles. Ea odd future hoodie cred. Ex small batch wayfarers sartorial. Delectus mumblecore skateboard, kogi esse keytar vinyl sriracha before they sold out typewriter marfa odd future viral mollit. Polaroid biodiesel street art viral cupidatat art party, post-ironic minim. Irony qui reprehenderit, put a bird on it eiusmod iphone labore skateboard. Craft beer keffiyeh echo park, 3 wolf moon thundercats gentrify dolor beard VHS ullamco cillum post-ironic qui chambray. Sartorial cred ex, aliqua trust fund est consectetur put a bird on it in nisi cupidatat sapiente art party freegan. Mlkshk authentic velit laborum. Est tattooed hella pickled qui flexitarian. Mustache wes anderson food truck, cardigan selvage organic wayfarers VHS irure typewriter irony. Kale chips pitchfork four loko before they sold out. Quis blog proident jean shorts voluptate, photo booth high life post-ironic odio hella whatever. Vinyl 3 wolf moon qui, officia non artisan et helvetica cosby sweater velit street art proident quinoa reprehenderit. Odd future fap put a bird on it laboris, kale chips tempor duis velit. Voluptate mlkshk brooklyn nihil. Hoodie sustainable excepteur next level. Anim qui aliqua officia keffiyeh semiotics. Est brooklyn pop-up photo booth, jean shorts banksy mumblecore. Consequat typewriter ennui put a bird on it odio. Reprehenderit narwhal master cleanse, messenger bag sed wayfarers vinyl adipisicing ex nesciunt. Mollit carles ethnic craft beer shoreditch, incididunt veniam laboris small batch authentic dreamcatcher proident you probably haven't heard of them seitan. Id art party narwhal assumenda farm-to-table brooklyn. Seitan artisan adipisicing put a bird on it aute lomo. Next level letterpress pitchfork, master cleanse Austin small batch scenester mlkshk trust fund hella accusamus laboris iphone lo-fi. Minim consectetur fanny pack occupy, lo-fi twee cupidatat nostrud laborum sint. Master cleanse consectetur excepteur enim food truck banksy. Qui nisi truffaut helvetica excepteur. Quinoa banksy non four loko tattooed keffiyeh pickled, ex semiotics quis odd future consectetur flexitarian. Quis readymade 8-bit nisi.
+
+ + + diff --git a/docs/flexbox-test.html b/docs/flexbox-test.html new file mode 100644 index 000000000..c301a8ab7 --- /dev/null +++ b/docs/flexbox-test.html @@ -0,0 +1,53 @@ + + + + + + +
+
+
+ I should be 100% of the height of my container, right? +
+
+
+ I don't have a flex property, so I should have an auto height +
+
+ + diff --git a/script/atom.githubapp.com b/script/atom.githubapp.com new file mode 100644 index 000000000..ba0363c02 --- /dev/null +++ b/script/atom.githubapp.com @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA5+pXspHed0hoB1Jel8mWmwGUzjSQsrzTmOA6rrlr1hbr9w9d +FGq8raUR3XAvALjpySYtLqzSVD5uFXTC0L/rdunH/w3PTdm5tVjvtOGi9TXF0F/L +fItXCHyTzGFGjomQQvO1GCKRcqhtF9rZHXJB3Ad4YRyN0AIpbCXzSWT9lUneVjvY +l1KIpzO1/ji2d7L3IbdnWi4vRyOUwvTVT4TFPcisWC3MnjB+EcnoYMYVsFJ/qrlP +32q+YRzNBezXawC15sOqX1sSCq79pyJC/x4X8CzxYAWtTx8e01TNlAbu6Btubfc4 +a+J7EG4aCywZRvEFJZHuJM2tJ76c5PXVgH+7DQIDAQABAoIBAGkA8K4YX5qwfPF7 +uPqOupL6N07Z+/4Bgui/9bHwoEOx4yrcmO9KmJ0ilSEmzxFLqAZ8M47jjuO31I4A +bMipbMpNdVElBoygHPZgC0BhvS+7JhJ/nRmJ2WEtTpoGHTUiUeAOihsgY6x/V82q +OA2eEvW1qCqIDCl3IRwnXq0Hfc1eZRL31zE9QaTi7Jbept7SvGWudBFGwREiS9VI +CzjnvQxrdKcXaa5q6cdGmkJhgoPDkU1H7FVPWZ/n+iGsK2wFPfCkbykBt++mqEW5 +TLJvd+gGbhCBJRwNSnfsZ6CMs8Ly+LTJ8RjBIkfTyOOl6tECUSuU4CpNRfv3HzSo +5wy97eECgYEA9wfDcnvLM/KcQ5N8GdGDIlI2WBxwQPY93rcvTinKO2M6pMDDognX +USSD/HBFs/cWnRihDOtpl8HOl8j9BXKfSJKXiWKj++u6rmG9eJ9UOfQ7sZrkic6N +AatHRpuE0J0fmgQUE1a+VG3VheLtOTunpT9PM6UDYL4XcxsdHUlbH9kCgYEA8FYU +lCsbFZGwYD2oG10o11ofFKTtRC9pTgDm9GM5k7I54uLKn08tdXs962D6gMnS2teE +bRzz5lidwrioykyCTDWVyjwySShCFgim7tXyldrlin4FVgOH4s7E7oi6rTv1BpV3 +mNaxvjfozGmd6z44skQOmqdt4S5Y9ph0NF/+aFUCgYBn8WRT77L5EMSfypcVZgr/ +jgg/2veA3hv6cqn9AaiOhJpQaKvoqqtnAS5oMfWiW/2v7IUXopazvGDpjTCp8ikD +qGsK6+6EOhQ6+/V1o9UU7aeQe9ReCANdCGzdUOL5xKNfcwffe0hqzixiQbH/uxI3 +fgQdfvmE43eKf4YopI0rQQKBgQCXTeoYueaMNrMuc8Kgr46lvTPyAD7rfVmzZPgB +hXJbHIo634KmtucsnhEGXYB3khfv9FyHgQG/06xGSZDiAzbImbeaoTkbO4AXdQz7 +MbBOTYfTH7FmTETBMmFitqunoiaJJZIso4U09ufVwOxNjFePSy9Sh/OyH0acwK/v +xTtaHQKBgDoJ2fVbPTASjHx8W5lTZrAySvjC+AmwH710mpgDzWd6XYmQxZMzh7I0 +zykIellBiqKqrNmXBv2DUwgpunXm2CLwavxlsFgg1wfTY9qVBf1ztSFXtOsdMe/2 +j2n/f3jqMeOkPzSXeDqZL67fh4KcegEevpwsYoIHij0FAVlXEuoq +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/script/cibuild b/script/cibuild new file mode 100755 index 000000000..f28e89e4c --- /dev/null +++ b/script/cibuild @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +sha = `git rev-parse HEAD`.chop +key = File.expand_path('../atom.githubapp.com', __FILE__) +system 'chmod', '600', key + +system 'ssh', + '-l', 'github', + '-i', key, + '-o', 'StrictHostKeyChecking=no', + '-p', '6000', + 'atom.githubapp.com', + "cd /Users/github/code/atom && git fetch -q origin && git reset -q --hard #{sha} && rake test && rake benchmark" + +exit $?.exitstatus \ No newline at end of file