diff --git a/appshell/appshell_extensions.cpp b/appshell/appshell_extensions.cpp index 39e81caaa..d4e1a653e 100644 --- a/appshell/appshell_extensions.cpp +++ b/appshell/appshell_extensions.cpp @@ -302,6 +302,24 @@ class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate { // No additional response args for this function } + } else if (message_name == "MoveFileOrDirectoryToTrash") { + // Parameters: + // 0: int32 - callback id + // 1: string - path + if (argList->GetSize() != 2 || + argList->GetType(1) != VTYPE_STRING) { + error = ERR_INVALID_PARAMS; + } + + if (error == NO_ERROR) { + ExtensionString path = argList->GetString(1); + + MoveFileOrDirectoryToTrash(path, browser, response); + + // Skip standard callback handling. MoveFileOrDirectoryToTrash fires the + // callback asynchronously. + return true; + } } else if (message_name == "ShowDeveloperTools") { // Parameters - none diff --git a/appshell/appshell_extensions.js b/appshell/appshell_extensions.js index 464cf13f3..caa044399 100644 --- a/appshell/appshell_extensions.js +++ b/appshell/appshell_extensions.js @@ -19,7 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * - */ + */ // This is the JavaScript code for bridging to native functionality // See appshell_extentions_[platform] for implementation of native methods. @@ -40,7 +40,7 @@ if (!appshell.fs) { if (!appshell.app) { appshell.app = {}; } -(function () { +(function () { // Error values. These MUST be in sync with the error values // at the top of appshell_extensions_platform.h. @@ -352,7 +352,7 @@ if (!appshell.app) { }; /** - * Delete a file. + * Delete a file permanently. * * @param {string} path The path of the file to delete * @param {function(err)} callback Asynchronous callback function. The callback gets one argument (err). @@ -369,6 +369,25 @@ if (!appshell.app) { appshell.fs.unlink = function (path, callback) { DeleteFileOrDirectory(callback, path); }; + + /** + * Delete a file non-permanently (to trash). + * + * @param {string} path The path of the file or directory to delete + * @param {function(err)} callback Asynchronous callback function. The callback gets one argument (err). + * Possible error values: + * NO_ERROR + * ERR_UNKNOWN + * ERR_INVALID_PARAMS + * ERR_NOT_FOUND + * ERR_NOT_FILE + * + * @return None. This is an asynchronous call that sends all return information to the callback. + */ + native function MoveFileOrDirectoryToTrash(); + appshell.fs.moveToTrash = function (path, callback) { + MoveFileOrDirectoryToTrash(callback, path); + }; /** * Return the number of milliseconds that have elapsed since the application diff --git a/appshell/appshell_extensions_mac.mm b/appshell/appshell_extensions_mac.mm index 1516b9947..0636c5356 100644 --- a/appshell/appshell_extensions_mac.mm +++ b/appshell/appshell_extensions_mac.mm @@ -491,21 +491,51 @@ int32 DeleteFileOrDirectory(ExtensionString filename) NSString* path = [NSString stringWithUTF8String:filename.c_str()]; BOOL isDirectory; - // Contrary to the name of this function, we don't actually delete directories - if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) { + // Contrary to the name of this function, we don't actually delete directories + if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) { if (isDirectory) { - return ERR_NOT_FILE; - } + return ERR_NOT_FILE; + } } else { return ERR_NOT_FOUND; - } - + } + if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) - return NO_ERROR; + return NO_ERROR; return ConvertNSErrorCode(error, false); } +void MoveFileOrDirectoryToTrash(ExtensionString filename, CefRefPtr browser, CefRefPtr response) +{ + NSString* pathStr = [NSString stringWithUTF8String:filename.c_str()]; + NSURL* fileUrl = [NSURL fileURLWithPath: pathStr]; + + static CefRefPtr s_response; + static CefRefPtr s_browser; + + if (s_response) { + // Already a pending request. This will only happen if MoveFileOrDirectoryToTrash is called + // before the previous call has completed, which is not very likely. + response->GetArgumentList()->SetInt(1, ERR_UNKNOWN); + browser->SendProcessMessage(PID_RENDERER, response); + return; + } + + s_browser = browser; + s_response = response; + + [[NSWorkspace sharedWorkspace] recycleURLs:[NSArray arrayWithObject:fileUrl] completionHandler:^(NSDictionary *newURLs, NSError *error) { + // Invoke callback + s_response->GetArgumentList()->SetInt(1, ConvertNSErrorCode(error, false)); + s_browser->SendProcessMessage(PID_RENDERER, s_response); + + s_response = nil; + s_browser = nil; + }]; +} + + void NSArrayToCefList(NSArray* array, CefRefPtr& list) { for (NSUInteger i = 0; i < [array count]; i++) { diff --git a/appshell/appshell_extensions_platform.h b/appshell/appshell_extensions_platform.h index 6297d344b..9ff0f6252 100644 --- a/appshell/appshell_extensions_platform.h +++ b/appshell/appshell_extensions_platform.h @@ -92,6 +92,8 @@ int32 SetPosixPermissions(ExtensionString filename, int32 mode); int32 DeleteFileOrDirectory(ExtensionString filename); +void MoveFileOrDirectoryToTrash(ExtensionString filename, CefRefPtr browser, CefRefPtr response); + int32 GetNodeState(int32& state); void OnBeforeShutdown(); diff --git a/appshell/appshell_extensions_win.cpp b/appshell/appshell_extensions_win.cpp index c76b1a280..dbfe15701 100644 --- a/appshell/appshell_extensions_win.cpp +++ b/appshell/appshell_extensions_win.cpp @@ -716,6 +716,30 @@ int32 DeleteFileOrDirectory(ExtensionString filename) return NO_ERROR; } +void MoveFileOrDirectoryToTrash(ExtensionString filename, CefRefPtr browser, CefRefPtr response) +{ + DWORD dwAttr = GetFileAttributes(filename.c_str()); + int32 error = NO_ERROR; + + if (dwAttr == INVALID_FILE_ATTRIBUTES) + error = ERR_NOT_FOUND; + + if (error == NO_ERROR) { + WCHAR filepath[MAX_PATH+1] = {0}; + wcscpy(filepath, filename.c_str()); + SHFILEOPSTRUCT operation = {0}; + operation.wFunc = FO_DELETE; + operation.pFrom = filepath; + operation.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; + + if (SHFileOperation(&operation)) { + error = ERR_UNKNOWN; + } + } + + response->GetArgumentList()->SetInt(1, error); + browser->SendProcessMessage(PID_RENDERER, response); +} void OnBeforeShutdown() {