diff --git a/appshell/appshell_extensions.cpp b/appshell/appshell_extensions.cpp index b721e89a4..d07eb0d2c 100644 --- a/appshell/appshell_extensions.cpp +++ b/appshell/appshell_extensions.cpp @@ -304,12 +304,14 @@ class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate { ExtensionString filename = argList->GetString(1); ExtensionString encoding = argList->GetString(2); std::string contents = ""; + bool preserveBOM = false; - error = ReadFile(filename, encoding, contents); + error = ReadFile(filename, encoding, contents, preserveBOM); // Set response args for this function responseArgs->SetString(2, contents); responseArgs->SetString(3, encoding); + responseArgs->SetBool(4, preserveBOM); } } else if (message_name == "WriteFile") { // Parameters: @@ -317,10 +319,11 @@ class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate { // 1: string - filename // 2: string - data // 3: string - encoding - if (argList->GetSize() != 4 || + if (argList->GetSize() != 5 || argList->GetType(1) != VTYPE_STRING || argList->GetType(2) != VTYPE_STRING || - argList->GetType(3) != VTYPE_STRING) { + argList->GetType(3) != VTYPE_STRING || + argList->GetType(4) != VTYPE_BOOL) { error = ERR_INVALID_PARAMS; } @@ -328,8 +331,9 @@ class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate { ExtensionString filename = argList->GetString(1); std::string contents = argList->GetString(2); ExtensionString encoding = argList->GetString(3); + bool preserveBOM = argList->GetBool(4); - error = WriteFile(filename, contents, encoding); + error = WriteFile(filename, contents, encoding, preserveBOM); // No additional response args for this function } } else if (message_name == "SetPosixPermissions") { diff --git a/appshell/appshell_extensions.js b/appshell/appshell_extensions.js index 018c69292..e67dbf8c6 100644 --- a/appshell/appshell_extensions.js +++ b/appshell/appshell_extensions.js @@ -70,6 +70,21 @@ if (!appshell.app) { * @constant An unsupported encoding value was specified. */ appshell.fs.ERR_UNSUPPORTED_ENCODING = 5; + + /** + * @constant File could not be encoded. + */ + appshell.fs.ERR_ENCODE_FILE_FAILED = 18; + + /** + * @constant File could not be decoded. + */ + appshell.fs.ERR_DECODE_FILE_FAILED = 19; + + /** + * @constant File was encoded with utf-16 + */ + appshell.fs.ERR_UNSUPPORTED_UTF16_ENCODING = 20; /** * @constant File could not be written. @@ -394,8 +409,8 @@ if (!appshell.app) { * @return None. This is an asynchronous call that sends all return information to the callback. */ native function WriteFile(); - appshell.fs.writeFile = function (path, data, encoding, callback) { - WriteFile(callback || _dummyCallback, path, data, encoding); + appshell.fs.writeFile = function (path, data, encoding, preserveBOM, callback) { + WriteFile(callback || _dummyCallback, path, data, encoding, preserveBOM); }; /** diff --git a/appshell/appshell_extensions_mac.mm b/appshell/appshell_extensions_mac.mm index d5fa3c499..7edf42f3c 100644 --- a/appshell/appshell_extensions_mac.mm +++ b/appshell/appshell_extensions_mac.mm @@ -56,6 +56,8 @@ #include #include +#define UTF8_BOM "\xEF\xBB\xBF" + NSMutableArray* pendingOpenFiles; @interface ChromeWindowsTerminatedObserver : NSObject @@ -647,62 +649,53 @@ int32 GetFileInfo(ExtensionString filename, uint32& modtime, bool& isDir, double return ConvertNSErrorCode(error, true); } -int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& contents) +int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& contents, bool& preserveBOM) { if (encoding == "utf8") { encoding = "UTF-8"; } - NSString* path = [NSString stringWithUTF8String:filename.c_str()]; - - NSStringEncoding enc; int32 error = NO_ERROR; - - NSString* fileContents = nil; - if (encoding == "UTF-8") { - enc = NSUTF8StringEncoding; - NSError* NSerror = nil; - fileContents = [NSString stringWithContentsOfFile:path encoding:enc error:&NSerror]; - } - - if (fileContents) - { - contents = [fileContents UTF8String]; - return NO_ERROR; - } else { + + try { + std::ifstream file(filename.c_str()); + std::stringstream ss; + ss << file.rdbuf(); + contents = ss.str(); + std::string detectedCharSet; try { - std::ifstream file(filename.c_str()); - std::stringstream ss; - ss << file.rdbuf(); - contents = ss.str(); - std::string detectedCharSet; - try { - if (encoding == "UTF-8") { - CharSetDetect ICUDetector; - ICUDetector(contents.c_str(), contents.size(), detectedCharSet); - } - else { - detectedCharSet = encoding; - } - if (!detectedCharSet.empty()) { + if (encoding == "UTF-8") { + CharSetDetect ICUDetector; + ICUDetector(contents.c_str(), contents.size(), detectedCharSet); + } + else { + detectedCharSet = encoding; + } + if (detectedCharSet == "UTF-16LE" || detectedCharSet == "UTF-16BE") { + return ERR_UNSUPPORTED_UTF16_ENCODING; + } + if (detectedCharSet != "UTF-8") { + try { std::transform(detectedCharSet.begin(), detectedCharSet.end(), detectedCharSet.begin(), ::toupper); DecodeContents(contents, detectedCharSet); encoding = detectedCharSet; + } catch (...) { + error = ERR_DECODE_FILE_FAILED; } - else { - error = ERR_UNSUPPORTED_ENCODING; - } - } catch (...) { - error = ERR_UNSUPPORTED_ENCODING; + } + else { + CheckAndRemoveUTF8BOM(contents, preserveBOM); } } catch (...) { - error = ERR_CANT_READ; + error = ERR_UNSUPPORTED_ENCODING; } + } catch (...) { + error = ERR_CANT_READ; } return error; } -int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding) +int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding, bool preserveBOM) { const char *filenameStr = filename.c_str(); int32 error = NO_ERROR; @@ -715,8 +708,10 @@ int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString CharSetEncode ICUEncoder(encoding); ICUEncoder(contents); } catch (...) { - error = ERR_CANT_READ; + error = ERR_ENCODE_FILE_FAILED; } + } else if (encoding == "UTF-8" && preserveBOM) { + contents = UTF8_BOM + contents; } try { diff --git a/appshell/appshell_extensions_platform.cpp b/appshell/appshell_extensions_platform.cpp index 7c4474c98..1e4f233ff 100644 --- a/appshell/appshell_extensions_platform.cpp +++ b/appshell/appshell_extensions_platform.cpp @@ -2,6 +2,8 @@ #include #include +#define UTF8_BOM "\xEF\xBB\xBF" + CharSetDetect::CharSetDetect() { m_charsetDetector_ = NULL; m_icuError = U_ZERO_ERROR; @@ -88,3 +90,11 @@ void DecodeContents(std::string &contents, const std::string& encoding) { } } #endif + +void CheckAndRemoveUTF8BOM(std::string& contents, bool& preserveBOM) { + if (contents.length() >= 3 && contents.substr(0,3) == UTF8_BOM) { + contents.erase(0,3); + preserveBOM = true; + } +} + diff --git a/appshell/appshell_extensions_platform.h b/appshell/appshell_extensions_platform.h index a13187cb1..107e52484 100644 --- a/appshell/appshell_extensions_platform.h +++ b/appshell/appshell_extensions_platform.h @@ -38,27 +38,30 @@ // Extension error codes. These MUST be in sync with the error // codes in appshell_extensions.js #if !defined(OS_WIN) // NO_ERROR is defined on windows -static const int NO_ERROR = 0; +static const int NO_ERROR = 0; #endif -static const int ERR_UNKNOWN = 1; -static const int ERR_INVALID_PARAMS = 2; -static const int ERR_NOT_FOUND = 3; -static const int ERR_CANT_READ = 4; -static const int ERR_UNSUPPORTED_ENCODING = 5; -static const int ERR_CANT_WRITE = 6; -static const int ERR_OUT_OF_SPACE = 7; -static const int ERR_NOT_FILE = 8; -static const int ERR_NOT_DIRECTORY = 9; -static const int ERR_FILE_EXISTS = 10; -static const int ERR_BROWSER_NOT_INSTALLED = 11; -static const int ERR_CL_TOOLS_CANCELLED = 12; -static const int ERR_CL_TOOLS_RMFAILED = 13; -static const int ERR_CL_TOOLS_MKDIRFAILED = 14; +static const int ERR_UNKNOWN = 1; +static const int ERR_INVALID_PARAMS = 2; +static const int ERR_NOT_FOUND = 3; +static const int ERR_CANT_READ = 4; +static const int ERR_UNSUPPORTED_ENCODING = 5; +static const int ERR_CANT_WRITE = 6; +static const int ERR_OUT_OF_SPACE = 7; +static const int ERR_NOT_FILE = 8; +static const int ERR_NOT_DIRECTORY = 9; +static const int ERR_FILE_EXISTS = 10; +static const int ERR_BROWSER_NOT_INSTALLED = 11; +static const int ERR_CL_TOOLS_CANCELLED = 12; +static const int ERR_CL_TOOLS_RMFAILED = 13; +static const int ERR_CL_TOOLS_MKDIRFAILED = 14; static const int ERR_CL_TOOLS_SYMLINKFAILED = 15; -static const int ERR_CL_TOOLS_SERVFAILED = 16; -static const int ERR_CL_TOOLS_NOTSUPPORTED = 17; +static const int ERR_CL_TOOLS_SERVFAILED = 16; +static const int ERR_CL_TOOLS_NOTSUPPORTED = 17; +static const int ERR_ENCODE_FILE_FAILED = 18; +static const int ERR_DECODE_FILE_FAILED = 19; +static const int ERR_UNSUPPORTED_UTF16_ENCODING = 20; -static const int ERR_PID_NOT_FOUND = -9999; // negative int to avoid confusion with real PIDs +static const int ERR_PID_NOT_FOUND = -9999; // negative int to avoid confusion with real PIDs typedef uint8_t u8; typedef uint16_t u16; @@ -112,6 +115,8 @@ class CharSetEncode void DecodeContents(std::string &contents, const std::string& encoding); #endif +void CheckAndRemoveUTF8BOM(std::string& contents, bool& preserveBOM); + // Native extension code. These are implemented in appshell_extensions_mac.mm // and appshell_extensions_win.cpp int32 OpenLiveBrowser(ExtensionString argURL, bool enableRemoteDebugging); @@ -158,9 +163,9 @@ int32 Rename(ExtensionString oldName, ExtensionString newName); int32 GetFileInfo(ExtensionString filename, uint32& modtime, bool& isDir, double& size, ExtensionString& realPath); -int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& contents); +int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& contents, bool& hasBOM); -int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding); +int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding, bool preserveBOM); int32 SetPosixPermissions(ExtensionString filename, int32 mode); @@ -207,4 +212,4 @@ int32 GetMenuPosition(CefRefPtr browser, const ExtensionString& comm void DragWindow(CefRefPtr browser); -std::string GetSystemUniqueID(); \ No newline at end of file +std::string GetSystemUniqueID(); diff --git a/appshell/appshell_extensions_win.cpp b/appshell/appshell_extensions_win.cpp index 0efc1be5b..cdd184d7b 100644 --- a/appshell/appshell_extensions_win.cpp +++ b/appshell/appshell_extensions_win.cpp @@ -46,6 +46,8 @@ #define UNICODE_LEFT_ARROW 0x2190 #define UNICODE_DOWN_ARROW 0x2193 +#define UTF8_BOM "\xEF\xBB\xBF" + // Forward declarations for functions at the bottom of this file void ConvertToNativePath(ExtensionString& filename); void ConvertToUnixPath(ExtensionString& filename); @@ -858,8 +860,6 @@ CharSetMap charSetMap = { "SHIFT_JIS", 932 }, { "ISO-8859-3", 28593 }, { "ISO-8859-4", 28594 }, - { "UTF-16LE", 1200 }, - { "UTF-16BE", 1201}, { "WINDOWS-1257", 1257 }, { "WINDOWS-1258", 1258 }, { "GB2312", 936 }, @@ -933,7 +933,7 @@ class ReadFileHandle { -int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& contents) +int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& contents, bool& preserveBOM) { if (encoding == L"utf8") { encoding = L"UTF-8"; @@ -985,6 +985,9 @@ int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& std::wstring_convert> conv; detectedCharSet = conv.to_bytes(encoding); } + if (detectedCharSet == "UTF-16LE" || detectedCharSet == "UTF-16LE") { + return ERR_UNSUPPORTED_UTF16_ENCODING; + } std::transform(detectedCharSet.begin(), detectedCharSet.end(), detectedCharSet.begin(), ::toupper); CharSetMap::iterator iter = charSetMap.find(detectedCharSet); @@ -1001,13 +1004,16 @@ int32 ReadFile(ExtensionString filename, ExtensionString& encoding, std::string& error = NO_ERROR; } catch (...) { - error = ERR_UNSUPPORTED_ENCODING; + error = ERR_DECODE_FILE_FAILED; } } } catch (...) { error = ERR_UNSUPPORTED_ENCODING; } } + if (encoding == L"UTF-8") { + CheckAndRemoveUTF8BOM(contents, preserveBOM); + } } else { error = ConvertWinErrorCode(GetLastError(), false); @@ -1057,7 +1063,7 @@ static void WideToCharSet(const std::wstring &aUTF16string, long codePage, std:: -int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding) +int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString encoding, bool preserveBOM) { if (encoding == L"utf8") { encoding = L"UTF-8"; @@ -1071,9 +1077,12 @@ int32 WriteFile(ExtensionString filename, std::string contents, ExtensionString WideToCharSet(content, iter->second, contents); } else { - error = ERR_UNSUPPORTED_ENCODING; + error = ERR_ENCODE_FILE_FAILED; } } + if (encoding == L"UTF-8" && preserveBOM) { + contents = UTF8_BOM + contents; + } HANDLE hFile = CreateFile(filename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);