From 96c79056f6d70aa613969bab0790166a9aa21a8a Mon Sep 17 00:00:00 2001 From: RaymondLim Date: Wed, 13 Jun 2012 08:59:11 -0700 Subject: [PATCH 1/5] File IO, file dialog and getElapsedMilliseconds implementation on Windows --- appshell/appshell_extensions_win.cpp | 63 +++++++++++++++++++++++++--- appshell/cefclient_win.cpp | 4 ++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/appshell/appshell_extensions_win.cpp b/appshell/appshell_extensions_win.cpp index c1f80a020..0cd307727 100644 --- a/appshell/appshell_extensions_win.cpp +++ b/appshell/appshell_extensions_win.cpp @@ -26,9 +26,16 @@ #include #include #include +#include +#include +#include + +// The global ClientHandler reference. +extern CefRefPtr g_handler; // Forward declarations for functions at the bottom of this file void FixFilename(ExtensionString& filename); +void EscapeJSONString(const std::wstring& str, std::wstring& finalResult); int ConvertErrnoCode(int errorCode, bool isReading = true); int ConvertWinErrorCode(int errorCode, bool isReading = true); @@ -49,6 +56,7 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, ExtensionString fileTypes, CefRefPtr& selectedFiles) { + std::wstring results = L"["; wchar_t szFile[MAX_PATH]; szFile[0] = 0; @@ -103,7 +111,7 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; - allowMulitpleSelection = false; // TODO: Raymond, please implement. + allowMulitpleSelection = false; // TODO (issue #65) - Use passed in file types. Note, when fileTypesStr is null, all files should be shown /* findAndReplaceString( fileTypesStr, std::string(" "), std::string(";*.")); @@ -119,7 +127,7 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, if (GetOpenFileName(&ofn)) { if (allowMulitpleSelection) { // Multiple selection encodes the files differently -/* + // If multiple files are selected, the first null terminator // signals end of directory that the files are all in std::wstring dir(szFile); @@ -163,7 +171,7 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, i += file.length() + 1; } } -*/ + } else { // If multiple files are not allowed, add the single file selectedFiles->SetString(0, szFile); @@ -306,13 +314,34 @@ int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString int32 SetPosixPermissions(ExtensionString filename, int32 mode) { - // TODO: Raymond, please implement + FixFilename(filename); + + DWORD dwAttr = GetFileAttributes(filename.c_str()); + + if (dwAttr == INVALID_FILE_ATTRIBUTES) + return ERR_NOT_FOUND; + + if ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0) + return NO_ERROR; + + bool write = (mode & 0200) != 0; + bool read = (mode & 0400) != 0; + int mask = (write ? _S_IWRITE : 0) | (read ? _S_IREAD : 0); + + if (_wchmod(filename.c_str(), mask) == -1) { + return ConvertErrnoCode(errno); + } + return NO_ERROR; } int32 DeleteFileOrDirectory(ExtensionString filename) { - // TODO: Raymond, please implement + FixFilename(filename); + + if (!DeleteFile(filename.c_str())) + return ConvertWinErrorCode(GetLastError()); + return NO_ERROR; } @@ -322,6 +351,30 @@ void FixFilename(ExtensionString& filename) replace(filename.begin(), filename.end(), '/', '\\'); } +// Escapes characters that have special meaning in JSON +void EscapeJSONString(const std::wstring& str, std::wstring& finalResult) { + std::wstring result; + + for(size_t pos = 0; pos != str.size(); ++pos) { + switch(str[pos]) { + case '\a': result.append(L"\\a"); break; + case '\b': result.append(L"\\b"); break; + case '\f': result.append(L"\\f"); break; + case '\n': result.append(L"\\n"); break; + case '\r': result.append(L"\\r"); break; + case '\t': result.append(L"\\t"); break; + case '\v': result.append(L"\\v"); break; + // Note: single quotes are OK for JSON + case '\"': result.append(L"\\\""); break; // double quote + case '\\': result.append(L"/"); break; // backslash + + default: result.append(1, str[pos]); break; + } + } + + finalResult = result; +} + // Maps errors from errno.h to the brackets error codes // found in brackets_extensions.js int ConvertErrnoCode(int errorCode, bool isReading) diff --git a/appshell/cefclient_win.cpp b/appshell/cefclient_win.cpp index 2a77217cb..1e363d2b2 100644 --- a/appshell/cefclient_win.cpp +++ b/appshell/cefclient_win.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "include/cef_app.h" @@ -28,6 +29,7 @@ #endif // SHOW_TOOLBAR_UI // Global Variables: +DWORD g_appStartupTime; HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name @@ -56,6 +58,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); + g_appStartupTime = timeGetTime(); + CefMainArgs main_args(hInstance); CefRefPtr app(new ClientApp); From af203886e5013b47fc31218bec0a0a161b7c3f98 Mon Sep 17 00:00:00 2001 From: RaymondLim Date: Wed, 13 Jun 2012 09:01:06 -0700 Subject: [PATCH 2/5] Adding the missed file in previous commit --- appshell/client_app_win.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/appshell/client_app_win.cpp b/appshell/client_app_win.cpp index 218f31cb4..c099089f4 100644 --- a/appshell/client_app_win.cpp +++ b/appshell/client_app_win.cpp @@ -24,8 +24,11 @@ #include "client_app.h" #include "resource.h" +#include #include +extern DWORD g_appStartupTime; + std::string ClientApp::GetExtensionJSSource() { extern HINSTANCE hInst; @@ -54,6 +57,6 @@ std::string ClientApp::GetExtensionJSSource() double ClientApp::GetElapsedMilliseconds() { - // TODO: Raymond, please implement - return 0; + return (timeGetTime() - g_appStartupTime); } + From 51aef81204b2aa8675a481f63a06c81cd066dff0 Mon Sep 17 00:00:00 2001 From: RaymondLim Date: Wed, 13 Jun 2012 14:17:22 -0700 Subject: [PATCH 3/5] Remove the code that use JSON string as suggested by Glenn. --- appshell/appshell_extensions_win.cpp | 51 ++-------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/appshell/appshell_extensions_win.cpp b/appshell/appshell_extensions_win.cpp index 0cd307727..849c82f0c 100644 --- a/appshell/appshell_extensions_win.cpp +++ b/appshell/appshell_extensions_win.cpp @@ -30,9 +30,6 @@ #include #include -// The global ClientHandler reference. -extern CefRefPtr g_handler; - // Forward declarations for functions at the bottom of this file void FixFilename(ExtensionString& filename); void EscapeJSONString(const std::wstring& str, std::wstring& finalResult); @@ -56,7 +53,6 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, ExtensionString fileTypes, CefRefPtr& selectedFiles) { - std::wstring results = L"["; wchar_t szFile[MAX_PATH]; szFile[0] = 0; @@ -111,8 +107,6 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; - allowMulitpleSelection = false; - // TODO (issue #65) - Use passed in file types. Note, when fileTypesStr is null, all files should be shown /* findAndReplaceString( fileTypesStr, std::string(" "), std::string(";*.")); LPCWSTR allFilesFilter = L"All Files\0*.*\0\0";*/ @@ -135,15 +129,11 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, // Check for two null terminators, which signal that only one file // was selected if (szFile[dir.length() + 1] == '\0') { - // Escape the single file path and add it to the JSON array - std::wstring escaped; - EscapeJSONString(dir, escaped); - results += L"\"" + escaped + L"\""; + selectedFiles->SetString(0, ExtensionString(dir)); } else { // Multiple files are selected wchar_t fullPath[MAX_PATH]; - bool firstFile = true; - for (int i = dir.length() + 1;;) { + for (int i = (dir.length() + 1), fileIndex = 0; ; fileIndex++) { // Get the next file name std::wstring file(&szFile[i]); @@ -154,18 +144,7 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, // The filename is relative to the directory that was specified as // the first string if (PathCombine(fullPath, dir.c_str(), file.c_str()) != NULL) - { - // Append a comma separator if it is not the first file in the list - if (firstFile) - firstFile = false; - else - results += L","; - - // Escape the path and add it to the list - std::wstring escaped; - EscapeJSONString(std::wstring(fullPath), escaped); - results += L"\"" + escaped + L"\""; - } + selectedFiles->SetString(fileIndex, ExtensionString(fullPath)); // Go to the start of the next file name i += file.length() + 1; @@ -351,30 +330,6 @@ void FixFilename(ExtensionString& filename) replace(filename.begin(), filename.end(), '/', '\\'); } -// Escapes characters that have special meaning in JSON -void EscapeJSONString(const std::wstring& str, std::wstring& finalResult) { - std::wstring result; - - for(size_t pos = 0; pos != str.size(); ++pos) { - switch(str[pos]) { - case '\a': result.append(L"\\a"); break; - case '\b': result.append(L"\\b"); break; - case '\f': result.append(L"\\f"); break; - case '\n': result.append(L"\\n"); break; - case '\r': result.append(L"\\r"); break; - case '\t': result.append(L"\\t"); break; - case '\v': result.append(L"\\v"); break; - // Note: single quotes are OK for JSON - case '\"': result.append(L"\\\""); break; // double quote - case '\\': result.append(L"/"); break; // backslash - - default: result.append(1, str[pos]); break; - } - } - - finalResult = result; -} - // Maps errors from errno.h to the brackets error codes // found in brackets_extensions.js int ConvertErrnoCode(int errorCode, bool isReading) From 0d77b3736e16cd7a2c650b853ad5630e2f3834cb Mon Sep 17 00:00:00 2001 From: RaymondLim Date: Wed, 13 Jun 2012 14:37:16 -0700 Subject: [PATCH 4/5] Remove the unneeded forward declaration. --- appshell/appshell_extensions_win.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/appshell/appshell_extensions_win.cpp b/appshell/appshell_extensions_win.cpp index 849c82f0c..2c7bbde48 100644 --- a/appshell/appshell_extensions_win.cpp +++ b/appshell/appshell_extensions_win.cpp @@ -32,7 +32,6 @@ // Forward declarations for functions at the bottom of this file void FixFilename(ExtensionString& filename); -void EscapeJSONString(const std::wstring& str, std::wstring& finalResult); int ConvertErrnoCode(int errorCode, bool isReading = true); int ConvertWinErrorCode(int errorCode, bool isReading = true); From 223ebd544fc5cbcdf60a49cc57cf88b889a884ba Mon Sep 17 00:00:00 2001 From: RaymondLim Date: Wed, 13 Jun 2012 19:22:56 -0700 Subject: [PATCH 5/5] Fix all file IO related issues. Rename FixFilename to ConvertToNativePath. Add a new function ConvertToUnixPath for converting all selected files to unix path before returning them to JS layer. Remove all calls to FixFilename since we're now using unix path and Windows file IO can handle unix path. Not sure we may still need it for Win XP. Append a '/' to the end of a directory before calling _wstat(). --- appshell/appshell_extensions_win.cpp | 44 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/appshell/appshell_extensions_win.cpp b/appshell/appshell_extensions_win.cpp index 2c7bbde48..3ddbe6c19 100644 --- a/appshell/appshell_extensions_win.cpp +++ b/appshell/appshell_extensions_win.cpp @@ -31,7 +31,8 @@ #include // Forward declarations for functions at the bottom of this file -void FixFilename(ExtensionString& filename); +void ConvertToNativePath(ExtensionString& filename); +void ConvertToUnixPath(ExtensionString& filename); int ConvertErrnoCode(int errorCode, bool isReading = true); int ConvertWinErrorCode(int errorCode, bool isReading = true); @@ -55,8 +56,6 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, wchar_t szFile[MAX_PATH]; szFile[0] = 0; - FixFilename(initialDirectory); - // TODO (issue #64) - This method should be using IFileDialog instead of the /* outdated SHGetPathFromIDList and GetOpenFileName. @@ -128,7 +127,9 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, // Check for two null terminators, which signal that only one file // was selected if (szFile[dir.length() + 1] == '\0') { - selectedFiles->SetString(0, ExtensionString(dir)); + ExtensionString filePath(dir); + ConvertToUnixPath(filePath); + selectedFiles->SetString(0, filePath); } else { // Multiple files are selected wchar_t fullPath[MAX_PATH]; @@ -142,8 +143,11 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, // The filename is relative to the directory that was specified as // the first string - if (PathCombine(fullPath, dir.c_str(), file.c_str()) != NULL) - selectedFiles->SetString(fileIndex, ExtensionString(fullPath)); + if (PathCombine(fullPath, dir.c_str(), file.c_str()) != NULL) { + ExtensionString filePath(fullPath); + ConvertToUnixPath(filePath); + selectedFiles->SetString(fileIndex, filePath); + } // Go to the start of the next file name i += file.length() + 1; @@ -162,9 +166,10 @@ int32 ShowOpenDialog(bool allowMulitpleSelection, int32 ReadDir(ExtensionString path, CefRefPtr& directoryContents) { - FixFilename(path); + if (path.length() && path[path.length() - 1] != '/') + path += '/'; - path += L"\\*"; + path += '*'; WIN32_FIND_DATA ffd; HANDLE hFind = FindFirstFile(path.c_str(), &ffd); @@ -205,8 +210,6 @@ int32 ReadDir(ExtensionString path, CefRefPtr& directoryContents) int32 GetFileModificationTime(ExtensionString filename, uint32& modtime, bool& isDir) { - FixFilename(filename); - DWORD dwAttr = GetFileAttributes(filename.c_str()); if (dwAttr == INVALID_FILE_ATTRIBUTES) { @@ -215,6 +218,11 @@ int32 GetFileModificationTime(ExtensionString filename, uint32& modtime, bool& i isDir = ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0); + // Remove trailing "/", if present. _wstat will fail with a "file not found" + // error if a directory has a trailing '/' in the name. + if (filename[filename.length() - 1] == '/') + filename[filename.length() - 1] = 0; + struct _stat buffer; if(_wstat(filename.c_str(), &buffer) == -1) { return ConvertErrnoCode(errno); @@ -227,8 +235,6 @@ int32 GetFileModificationTime(ExtensionString filename, uint32& modtime, bool& i int32 ReadFile(ExtensionString filename, ExtensionString encoding, std::string& contents) { - FixFilename(filename); - if (encoding != L"utf8") return ERR_UNSUPPORTED_ENCODING; @@ -268,8 +274,6 @@ int32 ReadFile(ExtensionString filename, ExtensionString encoding, std::string& int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding) { - FixFilename(filename); - if (encoding != L"utf8") return ERR_UNSUPPORTED_ENCODING; @@ -292,8 +296,6 @@ int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString int32 SetPosixPermissions(ExtensionString filename, int32 mode) { - FixFilename(filename); - DWORD dwAttr = GetFileAttributes(filename.c_str()); if (dwAttr == INVALID_FILE_ATTRIBUTES) @@ -315,20 +317,24 @@ int32 SetPosixPermissions(ExtensionString filename, int32 mode) int32 DeleteFileOrDirectory(ExtensionString filename) { - FixFilename(filename); - if (!DeleteFile(filename.c_str())) return ConvertWinErrorCode(GetLastError()); return NO_ERROR; } -void FixFilename(ExtensionString& filename) +void ConvertToNativePath(ExtensionString& filename) { // Convert '/' to '\' replace(filename.begin(), filename.end(), '/', '\\'); } +void ConvertToUnixPath(ExtensionString& filename) +{ + // Convert '\\' to '/' + replace(filename.begin(), filename.end(), '\\', '/'); +} + // Maps errors from errno.h to the brackets error codes // found in brackets_extensions.js int ConvertErrnoCode(int errorCode, bool isReading)