feat: add fileSystem
to ses.setPermissionCheckHandler
(#48327)
feat: add fileSystem to ses.setPermissionCheckHandler Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
parent
99feff3965
commit
cf9d0448be
7 changed files with 325 additions and 22 deletions
|
@ -939,14 +939,18 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
|
||||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||||
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
|
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
|
||||||
* `deprecated-sync-clipboard-read` _Deprecated_ - Request access to run `document.execCommand("paste")`
|
* `deprecated-sync-clipboard-read` _Deprecated_ - Request access to run `document.execCommand("paste")`
|
||||||
|
* `fileSystem` - Access to read, write, and file management capabilities using the [File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API).
|
||||||
* `requestingOrigin` string - The origin URL of the permission check
|
* `requestingOrigin` string - The origin URL of the permission check
|
||||||
* `details` Object - Some properties are only available on certain permission types.
|
* `details` Object - Some properties are only available on certain permission types.
|
||||||
* `embeddingOrigin` string (optional) - The origin of the frame embedding the frame that made the permission check. Only set for cross-origin sub frames making permission checks.
|
* `embeddingOrigin` string (optional) - The origin of the frame embedding the frame that made the permission check. Only set for cross-origin sub frames making permission checks.
|
||||||
* `securityOrigin` string (optional) - The security origin of the `media` check.
|
* `securityOrigin` string (optional) - The security origin of the `media` check.
|
||||||
* `mediaType` string (optional) - The type of media access being requested, can be `video`,
|
* `mediaType` string (optional) - The type of media access being requested, can be `video`,
|
||||||
`audio` or `unknown`
|
`audio` or `unknown`.
|
||||||
* `requestingUrl` string (optional) - The last URL the requesting frame loaded. This is not provided for cross-origin sub frames making permission checks.
|
* `requestingUrl` string (optional) - The last URL the requesting frame loaded. This is not provided for cross-origin sub frames making permission checks.
|
||||||
* `isMainFrame` boolean - Whether the frame making the request is the main frame
|
* `isMainFrame` boolean - Whether the frame making the request is the main frame.
|
||||||
|
* `filePath` string (optional) - The path of a `fileSystem` request.
|
||||||
|
* `isDirectory` boolean (optional) - Whether a `fileSystem` request is a directory.
|
||||||
|
* `fileAccessType` string (optional) - The access type of a `fileSystem` request. Can be `writable` or `readable`.
|
||||||
|
|
||||||
Sets the handler which can be used to respond to permission checks for the `session`.
|
Sets the handler which can be used to respond to permission checks for the `session`.
|
||||||
Returning `true` will allow the permission and `false` will reject it. Please note that
|
Returning `true` will allow the permission and `false` will reject it. Please note that
|
||||||
|
@ -968,6 +972,9 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> `isMainFrame` will always be `false` for a `fileSystem` request as a result of Chromium limitations.
|
||||||
|
|
||||||
#### `ses.setDisplayMediaRequestHandler(handler[, opts])`
|
#### `ses.setDisplayMediaRequestHandler(handler[, opts])`
|
||||||
|
|
||||||
* `handler` Function | null
|
* `handler` Function | null
|
||||||
|
|
|
@ -142,6 +142,14 @@ void ElectronPermissionManager::SetBluetoothPairingHandler(
|
||||||
bluetooth_pairing_handler_ = handler;
|
bluetooth_pairing_handler_ = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ElectronPermissionManager::HasPermissionRequestHandler() const {
|
||||||
|
return !request_handler_.is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ElectronPermissionManager::HasPermissionCheckHandler() const {
|
||||||
|
return !check_handler_.is_null();
|
||||||
|
}
|
||||||
|
|
||||||
void ElectronPermissionManager::RequestPermissionWithDetails(
|
void ElectronPermissionManager::RequestPermissionWithDetails(
|
||||||
blink::mojom::PermissionDescriptorPtr permission,
|
blink::mojom::PermissionDescriptorPtr permission,
|
||||||
content::RenderFrameHost* render_frame_host,
|
content::RenderFrameHost* render_frame_host,
|
||||||
|
|
|
@ -82,6 +82,9 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
|
||||||
void SetProtectedUSBHandler(const ProtectedUSBHandler& handler);
|
void SetProtectedUSBHandler(const ProtectedUSBHandler& handler);
|
||||||
void SetBluetoothPairingHandler(const BluetoothPairingHandler& handler);
|
void SetBluetoothPairingHandler(const BluetoothPairingHandler& handler);
|
||||||
|
|
||||||
|
bool HasPermissionRequestHandler() const;
|
||||||
|
bool HasPermissionCheckHandler() const;
|
||||||
|
|
||||||
void CheckBluetoothDevicePair(gin_helper::Dictionary details,
|
void CheckBluetoothDevicePair(gin_helper::Dictionary details,
|
||||||
PairCallback pair_callback) const;
|
PairCallback pair_callback) const;
|
||||||
|
|
||||||
|
|
|
@ -257,6 +257,28 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
|
||||||
// FileSystemAccessPermissionGrant:
|
// FileSystemAccessPermissionGrant:
|
||||||
PermissionStatus GetStatus() override {
|
PermissionStatus GetStatus() override {
|
||||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto* permission_manager =
|
||||||
|
static_cast<electron::ElectronPermissionManager*>(
|
||||||
|
context_->browser_context()->GetPermissionControllerDelegate());
|
||||||
|
if (permission_manager && permission_manager->HasPermissionCheckHandler()) {
|
||||||
|
base::Value::Dict details;
|
||||||
|
details.Set("filePath", base::FilePathToValue(path_info_.path));
|
||||||
|
details.Set("isDirectory", handle_type_ == HandleType::kDirectory);
|
||||||
|
details.Set("fileAccessType",
|
||||||
|
type_ == GrantType::kWrite ? "writable" : "readable");
|
||||||
|
|
||||||
|
bool granted = permission_manager->CheckPermissionWithDetails(
|
||||||
|
blink::PermissionType::FILE_SYSTEM, nullptr, origin_.GetURL(),
|
||||||
|
std::move(details));
|
||||||
|
return granted ? PermissionStatus::GRANTED : PermissionStatus::DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status_;
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionStatus GetActivePermissionStatus() {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
return status_;
|
return status_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,8 +301,8 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
|
||||||
// Check if a permission request has already been processed previously. This
|
// Check if a permission request has already been processed previously. This
|
||||||
// check is done first because we don't want to reset the status of a
|
// check is done first because we don't want to reset the status of a
|
||||||
// permission if it has already been granted.
|
// permission if it has already been granted.
|
||||||
if (GetStatus() != PermissionStatus::ASK || !context_) {
|
if (GetActivePermissionStatus() != PermissionStatus::ASK || !context_) {
|
||||||
if (GetStatus() == PermissionStatus::GRANTED) {
|
if (GetActivePermissionStatus() == PermissionStatus::GRANTED) {
|
||||||
SetStatus(PermissionStatus::GRANTED);
|
SetStatus(PermissionStatus::GRANTED);
|
||||||
}
|
}
|
||||||
std::move(callback).Run(PermissionRequestOutcome::kRequestAborted);
|
std::move(callback).Run(PermissionRequestOutcome::kRequestAborted);
|
||||||
|
@ -347,7 +369,7 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
|
||||||
permission_manager->RequestPermissionWithDetails(
|
permission_manager->RequestPermissionWithDetails(
|
||||||
content::PermissionDescriptorUtil::
|
content::PermissionDescriptorUtil::
|
||||||
CreatePermissionDescriptorForPermissionType(type),
|
CreatePermissionDescriptorForPermissionType(type),
|
||||||
rfh, origin, false, std::move(details),
|
rfh, origin, rfh->HasTransientUserActivation(), std::move(details),
|
||||||
base::BindOnce(&PermissionGrantImpl::OnPermissionRequestResult, this,
|
base::BindOnce(&PermissionGrantImpl::OnPermissionRequestResult, this,
|
||||||
std::move(callback)));
|
std::move(callback)));
|
||||||
}
|
}
|
||||||
|
@ -394,7 +416,8 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DCHECK_EQ(entry_it->second->GetStatus(), PermissionStatus::GRANTED);
|
DCHECK_EQ(entry_it->second->GetActivePermissionStatus(),
|
||||||
|
PermissionStatus::GRANTED);
|
||||||
|
|
||||||
auto* const grant_impl = entry_it->second;
|
auto* const grant_impl = entry_it->second;
|
||||||
grant_impl->SetPath(new_path);
|
grant_impl->SetPath(new_path);
|
||||||
|
@ -963,7 +986,8 @@ bool FileSystemAccessPermissionContext::OriginHasReadAccess(
|
||||||
auto it = active_permissions_map_.find(origin);
|
auto it = active_permissions_map_.find(origin);
|
||||||
if (it != active_permissions_map_.end()) {
|
if (it != active_permissions_map_.end()) {
|
||||||
return std::ranges::any_of(it->second.read_grants, [&](const auto& grant) {
|
return std::ranges::any_of(it->second.read_grants, [&](const auto& grant) {
|
||||||
return grant.second->GetStatus() == PermissionStatus::GRANTED;
|
return grant.second->GetActivePermissionStatus() ==
|
||||||
|
PermissionStatus::GRANTED;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,7 +1001,8 @@ bool FileSystemAccessPermissionContext::OriginHasWriteAccess(
|
||||||
auto it = active_permissions_map_.find(origin);
|
auto it = active_permissions_map_.find(origin);
|
||||||
if (it != active_permissions_map_.end()) {
|
if (it != active_permissions_map_.end()) {
|
||||||
return std::ranges::any_of(it->second.write_grants, [&](const auto& grant) {
|
return std::ranges::any_of(it->second.write_grants, [&](const auto& grant) {
|
||||||
return grant.second->GetStatus() == PermissionStatus::GRANTED;
|
return grant.second->GetActivePermissionStatus() ==
|
||||||
|
PermissionStatus::GRANTED;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1031,7 +1056,7 @@ bool FileSystemAccessPermissionContext::AncestorHasActivePermission(
|
||||||
parent = parent.DirName()) {
|
parent = parent.DirName()) {
|
||||||
auto i = relevant_grants.find(parent);
|
auto i = relevant_grants.find(parent);
|
||||||
if (i != relevant_grants.end() && i->second &&
|
if (i != relevant_grants.end() && i->second &&
|
||||||
i->second->GetStatus() == PermissionStatus::GRANTED) {
|
i->second->GetActivePermissionStatus() == PermissionStatus::GRANTED) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1054,7 +1079,7 @@ void FileSystemAccessPermissionContext::PermissionGrantDestroyed(
|
||||||
// be granted but won't be visible in any UI because the permission context
|
// be granted but won't be visible in any UI because the permission context
|
||||||
// isn't tracking them anymore.
|
// isn't tracking them anymore.
|
||||||
if (grant_it == grants.end()) {
|
if (grant_it == grants.end()) {
|
||||||
DCHECK_EQ(PermissionStatus::DENIED, grant->GetStatus());
|
DCHECK_EQ(PermissionStatus::DENIED, grant->GetActivePermissionStatus());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -901,13 +901,15 @@ describe('chromium features', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('File System API,', () => {
|
describe('File System API,', () => {
|
||||||
afterEach(closeAllWindows);
|
let w: BrowserWindow | null = null;
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
session.defaultSession.setPermissionRequestHandler(null);
|
session.defaultSession.setPermissionRequestHandler(null);
|
||||||
|
closeAllWindows();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows access by default to reading an OPFS file', async () => {
|
it('allows access by default to reading an OPFS file', async () => {
|
||||||
const w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
@ -929,7 +931,7 @@ describe('chromium features', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fileHandle.queryPermission by default has permission to read and write to OPFS files', async () => {
|
it('fileHandle.queryPermission by default has permission to read and write to OPFS files', async () => {
|
||||||
const w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
@ -951,7 +953,8 @@ describe('chromium features', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fileHandle.requestPermission automatically grants permission to read and write to OPFS files', async () => {
|
it('fileHandle.requestPermission automatically grants permission to read and write to OPFS files', async () => {
|
||||||
const w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
partition: 'file-system-spec',
|
partition: 'file-system-spec',
|
||||||
|
@ -971,8 +974,8 @@ describe('chromium features', () => {
|
||||||
expect(status).to.equal('granted');
|
expect(status).to.equal('granted');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('requests permission when trying to create a writable file handle', (done) => {
|
it('allows permission when trying to create a writable file handle', (done) => {
|
||||||
const writablePath = path.join(fixturesPath, 'file-system', 'test-writable.html');
|
const writablePath = path.join(fixturesPath, 'file-system', 'test-perms.html');
|
||||||
const testFile = path.join(fixturesPath, 'file-system', 'test.txt');
|
const testFile = path.join(fixturesPath, 'file-system', 'test.txt');
|
||||||
|
|
||||||
const w = new BrowserWindow({
|
const w = new BrowserWindow({
|
||||||
|
@ -1000,9 +1003,9 @@ describe('chromium features', () => {
|
||||||
|
|
||||||
ipcMain.once('did-create-file-handle', async () => {
|
ipcMain.once('did-create-file-handle', async () => {
|
||||||
const result = await w.webContents.executeJavaScript(`
|
const result = await w.webContents.executeJavaScript(`
|
||||||
new Promise((resolve, reject) => {
|
new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const writable = fileHandle.createWritable();
|
const writable = await handle.createWritable();
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} catch {
|
} catch {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
|
@ -1021,6 +1024,258 @@ describe('chromium features', () => {
|
||||||
w.webContents.paste();
|
w.webContents.paste();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('denies permission when trying to create a writable file handle', (done) => {
|
||||||
|
const writablePath = path.join(fixturesPath, 'file-system', 'test-perms.html');
|
||||||
|
const testFile = path.join(fixturesPath, 'file-system', 'test.txt');
|
||||||
|
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
w.webContents.session.setPermissionRequestHandler((wc, permission, callback, details) => {
|
||||||
|
expect(permission).to.equal('fileSystem');
|
||||||
|
|
||||||
|
const { href } = url.pathToFileURL(writablePath);
|
||||||
|
expect(details).to.deep.equal({
|
||||||
|
fileAccessType: 'writable',
|
||||||
|
isDirectory: false,
|
||||||
|
isMainFrame: true,
|
||||||
|
filePath: testFile,
|
||||||
|
requestingUrl: href
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.once('did-create-file-handle', async () => {
|
||||||
|
const result = await w.webContents.executeJavaScript(`
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
resolve(true);
|
||||||
|
} catch {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`, true);
|
||||||
|
expect(result).to.be.false();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
w.loadFile(writablePath);
|
||||||
|
|
||||||
|
w.webContents.once('did-finish-load', () => {
|
||||||
|
// @ts-expect-error Undocumented testing method.
|
||||||
|
clipboard._writeFilesForTesting([testFile]);
|
||||||
|
w.webContents.paste();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls twice when trying to query a read/write file handle permissions', (done) => {
|
||||||
|
const writablePath = path.join(fixturesPath, 'file-system', 'test-perms.html');
|
||||||
|
const testFile = path.join(fixturesPath, 'file-system', 'test.txt');
|
||||||
|
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
w.webContents.session.setPermissionCheckHandler((wc, permission, origin, details) => {
|
||||||
|
if (permission === 'fileSystem') {
|
||||||
|
const { fileAccessType, isDirectory, filePath } = details;
|
||||||
|
expect(['writable', 'readable']).to.contain(fileAccessType);
|
||||||
|
expect(isDirectory).to.be.false();
|
||||||
|
expect(filePath).to.equal(testFile);
|
||||||
|
calls++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.once('did-create-file-handle', async () => {
|
||||||
|
const permission = await w.webContents.executeJavaScript(`
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const permission = await handle.queryPermission({ mode: 'readwrite' });
|
||||||
|
resolve(permission);
|
||||||
|
} catch {
|
||||||
|
resolve('denied');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`, true);
|
||||||
|
expect(permission).to.equal('granted');
|
||||||
|
expect(calls).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
w.loadFile(writablePath);
|
||||||
|
|
||||||
|
w.webContents.once('did-finish-load', () => {
|
||||||
|
// @ts-expect-error Undocumented testing method.
|
||||||
|
clipboard._writeFilesForTesting([testFile]);
|
||||||
|
w.webContents.paste();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly denies permissions after creating a readable directory handle', (done) => {
|
||||||
|
const permPath = path.join(fixturesPath, 'file-system', 'test-perms.html');
|
||||||
|
const testDir = path.join(fixturesPath, 'file-system');
|
||||||
|
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
w.webContents.session.setPermissionCheckHandler((wc, permission, origin, details) => {
|
||||||
|
expect(permission).to.equal('fileSystem');
|
||||||
|
|
||||||
|
const { fileAccessType, isDirectory, filePath } = details;
|
||||||
|
expect(fileAccessType).to.equal('readable');
|
||||||
|
expect(isDirectory).to.be.true();
|
||||||
|
expect(filePath).to.equal(testDir);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.once('did-create-directory-handle', async () => {
|
||||||
|
const permission = await w.webContents.executeJavaScript(`
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const permission = await handle.queryPermission({ mode: 'read' });
|
||||||
|
resolve(permission);
|
||||||
|
} catch {
|
||||||
|
resolve('denied');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`, true);
|
||||||
|
expect(permission).to.equal('denied');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
w.loadFile(permPath);
|
||||||
|
|
||||||
|
w.webContents.once('did-finish-load', () => {
|
||||||
|
// @ts-expect-error Undocumented testing method.
|
||||||
|
clipboard._writeFilesForTesting([testDir]);
|
||||||
|
w.webContents.paste();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly allows permissions after creating a readable directory handle', (done) => {
|
||||||
|
const permPath = path.join(fixturesPath, 'file-system', 'test-perms.html');
|
||||||
|
const testDir = path.join(fixturesPath, 'file-system');
|
||||||
|
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
w.webContents.session.setPermissionCheckHandler((wc, permission, origin, details) => {
|
||||||
|
if (permission === 'fileSystem') {
|
||||||
|
const { fileAccessType, isDirectory, filePath } = details;
|
||||||
|
expect(fileAccessType).to.equal('readable');
|
||||||
|
expect(isDirectory).to.be.true();
|
||||||
|
expect(filePath).to.equal(testDir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.once('did-create-directory-handle', async () => {
|
||||||
|
const permission = await w.webContents.executeJavaScript(`
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const permission = await handle.queryPermission({ mode: 'read' });
|
||||||
|
resolve(permission);
|
||||||
|
} catch {
|
||||||
|
resolve('denied');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`, true);
|
||||||
|
expect(permission).to.equal('granted');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
w.loadFile(permPath);
|
||||||
|
|
||||||
|
w.webContents.once('did-finish-load', () => {
|
||||||
|
// @ts-expect-error Undocumented testing method.
|
||||||
|
clipboard._writeFilesForTesting([testDir]);
|
||||||
|
w.webContents.paste();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows in-session persistence of granted file permissions', (done) => {
|
||||||
|
const writablePath = path.join(fixturesPath, 'file-system', 'test-perms.html');
|
||||||
|
const testFile = path.join(fixturesPath, 'file-system', 'persist.txt');
|
||||||
|
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
w.webContents.session.setPermissionRequestHandler((_wc, _permission, callback) => {
|
||||||
|
callback(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
w.webContents.session.setPermissionCheckHandler((_wc, permission, _origin, details) => {
|
||||||
|
if (permission === 'fileSystem') {
|
||||||
|
const { fileAccessType, isDirectory, filePath } = details;
|
||||||
|
expect(fileAccessType).to.deep.equal('readable');
|
||||||
|
expect(isDirectory).to.be.false();
|
||||||
|
expect(filePath).to.equal(testFile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
let reload = true;
|
||||||
|
ipcMain.on('did-create-file-handle', async () => {
|
||||||
|
if (reload) {
|
||||||
|
w.webContents.reload();
|
||||||
|
reload = false;
|
||||||
|
} else {
|
||||||
|
const permission = await w.webContents.executeJavaScript(`
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const permission = await handle.queryPermission({ mode: 'read' });
|
||||||
|
resolve(permission);
|
||||||
|
} catch {
|
||||||
|
resolve('denied');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`, true);
|
||||||
|
expect(permission).to.equal('granted');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
w.loadFile(writablePath);
|
||||||
|
|
||||||
|
w.webContents.on('did-finish-load', () => {
|
||||||
|
// @ts-expect-error Undocumented testing method.
|
||||||
|
clipboard._writeFilesForTesting([testFile]);
|
||||||
|
w.webContents.paste();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('web workers', () => {
|
describe('web workers', () => {
|
||||||
|
|
1
spec/fixtures/file-system/persist.txt
vendored
Normal file
1
spec/fixtures/file-system/persist.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
hello persist
|
|
@ -10,13 +10,17 @@
|
||||||
<script>
|
<script>
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
let fileHandle = null;
|
let handle = null;
|
||||||
let sent = false;
|
let sent = false;
|
||||||
window.document.onpaste = async (event) => {
|
window.document.onpaste = async (event) => {
|
||||||
const fileItem = event.clipboardData.items[0];
|
const fileItem = event.clipboardData.items[0];
|
||||||
fileHandle = await fileItem.getAsFileSystemHandle();
|
handle = await fileItem.getAsFileSystemHandle();
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
|
if (handle.kind === 'file') {
|
||||||
ipcRenderer.send('did-create-file-handle');
|
ipcRenderer.send('did-create-file-handle');
|
||||||
|
} else {
|
||||||
|
ipcRenderer.send('did-create-directory-handle');
|
||||||
|
}
|
||||||
sent = true;
|
sent = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
Loading…
Add table
Add a link
Reference in a new issue