Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make shell.moveItemToTrash return false on Windows when move is unsuccessful #25170

Merged
merged 2 commits into from Aug 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions shell/common/platform_util_linux.cc
Expand Up @@ -12,6 +12,7 @@
#include "base/nix/xdg_util.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/threading/thread_restrictions.h"
#include "ui/gtk/gtk_util.h"
#include "url/gurl.h"

Expand Down Expand Up @@ -51,6 +52,8 @@ bool XDGUtil(const std::vector<std::string>& argv,
return false;

if (wait_for_exit) {
base::ScopedAllowBaseSyncPrimitivesForTesting
allow_sync; // required by WaitForExit
int exit_code = -1;
bool success = process.WaitForExit(&exit_code);
if (!callback.is_null())
Expand Down
6 changes: 5 additions & 1 deletion shell/common/platform_util_win.cc
Expand Up @@ -387,10 +387,14 @@ bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
if (!delete_sink)
return false;

BOOL pfAnyOperationsAborted;

// Processes the queued command DeleteItem. This will trigger
// the DeleteFileProgressSink to check for Recycle Bin.
return SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get())) &&
SUCCEEDED(pfo->PerformOperations());
SUCCEEDED(pfo->PerformOperations()) &&
SUCCEEDED(pfo->GetAnyOperationsAborted(&pfAnyOperationsAborted)) &&
!pfAnyOperationsAborted;
}

bool GetFolderPath(int key, base::FilePath* result) {
Expand Down
57 changes: 56 additions & 1 deletion spec-main/api-shell-spec.ts
@@ -1,9 +1,14 @@
import { BrowserWindow } from 'electron/main';
import { BrowserWindow, app } from 'electron/main';
import { shell } from 'electron/common';
import { closeAllWindows } from './window-helpers';
import { emittedOnce } from './events-helpers';
import * as http from 'http';
import * as fs from 'fs-extra';
import * as path from 'path';
import { AddressInfo } from 'net';
import { expect } from 'chai';
import { ifit } from './spec-helpers';
import { execSync } from 'child_process';

describe('shell module', () => {
describe('shell.openExternal()', () => {
Expand Down Expand Up @@ -57,4 +62,54 @@ describe('shell module', () => {
]);
});
});

describe('shell.moveItemToTrash()', () => {
it('moves an item to the trash', async () => {
const dir = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
const filename = path.join(dir, 'temp-to-be-deleted');
await fs.writeFile(filename, 'dummy-contents');
const result = shell.moveItemToTrash(filename);
expect(result).to.be.true();
expect(fs.existsSync(filename)).to.be.false();
});

it('returns false when called with a nonexistent path', () => {
const filename = path.join(app.getPath('temp'), 'does-not-exist');
const result = shell.moveItemToTrash(filename);
expect(result).to.be.false();
});

ifit(process.platform === 'darwin')('returns false when file has immutable flag', async () => {
const dir = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
const tempPath = path.join(dir, 'locked-file');
await fs.writeFile(tempPath, 'delete me if you can');

// https://ss64.com/osx/chflags.html
execSync(`chflags uchg ${tempPath}`);
expect(shell.moveItemToTrash(tempPath)).to.be.false();
expect(await fs.pathExists(tempPath)).to.be.true();

execSync(`chflags nouchg ${tempPath}`);
expect(shell.moveItemToTrash(tempPath)).to.be.true();
expect(await fs.pathExists(tempPath)).to.be.false();
});

ifit(process.platform === 'win32')('returns false when path is in use', async () => {
const tempPath = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
const cwd = process.cwd();
try {
// A process working directory is automatically locked on Windows.
// This is a workaround to avoid pulling in fs-extras flock method.
process.chdir(tempPath);

expect(shell.moveItemToTrash(tempPath)).to.be.false();
expect(await fs.pathExists(tempPath)).to.be.true();
} finally {
process.chdir(cwd);
}

expect(shell.moveItemToTrash(tempPath)).to.be.true();
expect(await fs.pathExists(tempPath)).to.be.false();
});
});
});