зеркало из https://github.com/electron/electron.git
feat: add webUtils module with getPathForFile method (#38776)
* feat: add blinkUtils module with getPathForFile method This is designed to replace the File.path augmentation we currently have in place to allow apps to get the filesystem path for a file that blink has a representation of. File.path is non-standard and messes with certain websites, using a method like this is effectively 0-cost and removes one of the final deviations we have with web standards. * add error * refactor: update per PR feedback * chore: update patches * oops * chore: update patches * chore: update patches * feat: add blinkUtils module with getPathForFile method This is designed to replace the File.path augmentation we currently have in place to allow apps to get the filesystem path for a file that blink has a representation of. File.path is non-standard and messes with certain websites, using a method like this is effectively 0-cost and removes one of the final deviations we have with web standards. * add error * refactor: update per PR feedback * chore: update patches * oops * chore: update patches * chore: update patches * chore: update patches * fix: provide isolate to WebBlob::FromV8Value * chore: add tests * build: fix depshash mismatch on arm64 macOS --------- Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
This commit is contained in:
Родитель
2c03b8fd6b
Коммит
d6bb9b40b0
|
@ -2165,7 +2165,7 @@ jobs:
|
||||||
<<: *env-ninja-status
|
<<: *env-ninja-status
|
||||||
<<: *env-macos-build
|
<<: *env-macos-build
|
||||||
<<: *env-apple-silicon
|
<<: *env-apple-silicon
|
||||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac --custom-var=host_cpu=arm64'
|
||||||
steps:
|
steps:
|
||||||
- electron-build:
|
- electron-build:
|
||||||
persist: true
|
persist: true
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
> Use the HTML5 `File` API to work natively with files on the filesystem.
|
> Use the HTML5 `File` API to work natively with files on the filesystem.
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> The `path` property that Electron adds to the `File` interface is deprecated
|
||||||
|
> and **will** be removed in a future Electron release. We recommend you
|
||||||
|
> use `webUtils.getPathForFile` instead.
|
||||||
|
|
||||||
The DOM's File interface provides abstraction around native files in order to
|
The DOM's File interface provides abstraction around native files in order to
|
||||||
let users work on native files directly with the HTML5 file API. Electron has
|
let users work on native files directly with the HTML5 file API. Electron has
|
||||||
added a `path` attribute to the `File` interface which exposes the file's real
|
added a `path` attribute to the `File` interface which exposes the file's real
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# webUtils
|
||||||
|
|
||||||
|
> A utility layer to interact with Web API objects (Files, Blobs, etc.)
|
||||||
|
|
||||||
|
Process: [Renderer](../glossary.md#renderer-process)
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
The `webUtils` module has the following methods:
|
||||||
|
|
||||||
|
### `webUtils.getPathForFile(file)`
|
||||||
|
|
||||||
|
* `file` File - A web [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object.
|
||||||
|
|
||||||
|
Returns `string` - The file system path that this `File` object points to. In the case where the object passed in is not a `File` object an exception is thrown. In the case where the File object passed in was constructed in JS and is not backed by a file on disk an empty string is returned.
|
||||||
|
|
||||||
|
This method superceded the previous augmentation to the `File` object with the `path` property. An example is included below.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Before
|
||||||
|
const oldPath = document.querySelector('input').files[0].path
|
||||||
|
|
||||||
|
// After
|
||||||
|
const { webUtils } = require('electron')
|
||||||
|
const newPath = webUtils.getPathForFile(document.querySelector('input').files[0])
|
||||||
|
```
|
|
@ -46,7 +46,7 @@ scripts attached to sandboxed renderers will still have a polyfilled subset of N
|
||||||
APIs available. A `require` function similar to Node's `require` module is exposed,
|
APIs available. A `require` function similar to Node's `require` module is exposed,
|
||||||
but can only import a subset of Electron and Node's built-in modules:
|
but can only import a subset of Electron and Node's built-in modules:
|
||||||
|
|
||||||
* `electron` (following renderer process modules: `contextBridge`, `crashReporter`, `ipcRenderer`, `nativeImage`, `webFrame`)
|
* `electron` (following renderer process modules: `contextBridge`, `crashReporter`, `ipcRenderer`, `nativeImage`, `webFrame`, `webUtils`)
|
||||||
* [`events`](https://nodejs.org/api/events.html)
|
* [`events`](https://nodejs.org/api/events.html)
|
||||||
* [`timers`](https://nodejs.org/api/timers.html)
|
* [`timers`](https://nodejs.org/api/timers.html)
|
||||||
* [`url`](https://nodejs.org/api/url.html)
|
* [`url`](https://nodejs.org/api/url.html)
|
||||||
|
|
|
@ -67,6 +67,7 @@ auto_filenames = {
|
||||||
"docs/api/web-frame-main.md",
|
"docs/api/web-frame-main.md",
|
||||||
"docs/api/web-frame.md",
|
"docs/api/web-frame.md",
|
||||||
"docs/api/web-request.md",
|
"docs/api/web-request.md",
|
||||||
|
"docs/api/web-utils.md",
|
||||||
"docs/api/webview-tag.md",
|
"docs/api/webview-tag.md",
|
||||||
"docs/api/window-open.md",
|
"docs/api/window-open.md",
|
||||||
"docs/api/structures/bluetooth-device.md",
|
"docs/api/structures/bluetooth-device.md",
|
||||||
|
@ -151,6 +152,7 @@ auto_filenames = {
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
|
"lib/renderer/api/web-utils.ts",
|
||||||
"lib/renderer/common-init.ts",
|
"lib/renderer/common-init.ts",
|
||||||
"lib/renderer/inspector.ts",
|
"lib/renderer/inspector.ts",
|
||||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||||
|
@ -281,6 +283,7 @@ auto_filenames = {
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/module-list.ts",
|
"lib/renderer/api/module-list.ts",
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
|
"lib/renderer/api/web-utils.ts",
|
||||||
"lib/renderer/common-init.ts",
|
"lib/renderer/common-init.ts",
|
||||||
"lib/renderer/init.ts",
|
"lib/renderer/init.ts",
|
||||||
"lib/renderer/inspector.ts",
|
"lib/renderer/inspector.ts",
|
||||||
|
@ -318,6 +321,7 @@ auto_filenames = {
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/module-list.ts",
|
"lib/renderer/api/module-list.ts",
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
|
"lib/renderer/api/web-utils.ts",
|
||||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||||
"lib/renderer/ipc-renderer-internal.ts",
|
"lib/renderer/ipc-renderer-internal.ts",
|
||||||
"lib/worker/init.ts",
|
"lib/worker/init.ts",
|
||||||
|
|
|
@ -682,6 +682,8 @@ filenames = {
|
||||||
"shell/renderer/api/electron_api_spell_check_client.cc",
|
"shell/renderer/api/electron_api_spell_check_client.cc",
|
||||||
"shell/renderer/api/electron_api_spell_check_client.h",
|
"shell/renderer/api/electron_api_spell_check_client.h",
|
||||||
"shell/renderer/api/electron_api_web_frame.cc",
|
"shell/renderer/api/electron_api_web_frame.cc",
|
||||||
|
"shell/renderer/api/electron_api_web_utils.cc",
|
||||||
|
"shell/renderer/api/electron_api_web_utils.h",
|
||||||
"shell/renderer/browser_exposed_renderer_interfaces.cc",
|
"shell/renderer/browser_exposed_renderer_interfaces.cc",
|
||||||
"shell/renderer/browser_exposed_renderer_interfaces.h",
|
"shell/renderer/browser_exposed_renderer_interfaces.h",
|
||||||
"shell/renderer/content_settings_observer.cc",
|
"shell/renderer/content_settings_observer.cc",
|
||||||
|
|
|
@ -4,5 +4,6 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
||||||
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
||||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
{ name: 'webFrame', loader: () => require('./web-frame') },
|
||||||
|
{ name: 'webUtils', loader: () => require('./web-utils') }
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
const binding = process._linkedBinding('electron_renderer_web_utils');
|
||||||
|
|
||||||
|
export const getPathForFile = binding.getPathForFile;
|
|
@ -18,5 +18,9 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{
|
{
|
||||||
name: 'webFrame',
|
name: 'webFrame',
|
||||||
loader: () => require('@electron/internal/renderer/api/web-frame')
|
loader: () => require('@electron/internal/renderer/api/web-frame')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'webUtils',
|
||||||
|
loader: () => require('@electron/internal/renderer/api/web-utils')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -130,8 +130,9 @@ fix_harden_blink_scriptstate_maybefrom.patch
|
||||||
chore_add_buildflag_guard_around_new_include.patch
|
chore_add_buildflag_guard_around_new_include.patch
|
||||||
fix_use_delegated_generic_capturer_when_available.patch
|
fix_use_delegated_generic_capturer_when_available.patch
|
||||||
build_remove_ent_content_analysis_assert.patch
|
build_remove_ent_content_analysis_assert.patch
|
||||||
fix_activate_background_material_on_windows.patch
|
expose_webblob_path_to_allow_embedders_to_get_file_paths.patch
|
||||||
fix_move_autopipsettingshelper_behind_branding_buildflag.patch
|
fix_move_autopipsettingshelper_behind_branding_buildflag.patch
|
||||||
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
||||||
|
fix_activate_background_material_on_windows.patch
|
||||||
feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch
|
feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch
|
||||||
chore_remove_check_is_test_on_script_injection_tracker.patch
|
chore_remove_check_is_test_on_script_injection_tracker.patch
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Samuel Attard <marshallofsound@electronjs.org>
|
||||||
|
Date: Tue, 13 Jun 2023 15:36:04 -0700
|
||||||
|
Subject: expose WebBlob::Path to allow embedders to get file paths
|
||||||
|
|
||||||
|
Used to replace the File.path augmentation Electron currently implements. This is safer / more web-standard technique.
|
||||||
|
|
||||||
|
diff --git a/third_party/blink/public/web/web_blob.h b/third_party/blink/public/web/web_blob.h
|
||||||
|
index 384a59138db11ea38028f844dd67e328ebffbe7b..f153997c2afccef1fa1b64ee5f162c28a2d07e5d 100644
|
||||||
|
--- a/third_party/blink/public/web/web_blob.h
|
||||||
|
+++ b/third_party/blink/public/web/web_blob.h
|
||||||
|
@@ -67,6 +67,7 @@ class BLINK_EXPORT WebBlob {
|
||||||
|
void Reset();
|
||||||
|
void Assign(const WebBlob&);
|
||||||
|
WebString Uuid();
|
||||||
|
+ std::string Path();
|
||||||
|
|
||||||
|
bool IsNull() const { return private_.IsNull(); }
|
||||||
|
|
||||||
|
diff --git a/third_party/blink/renderer/core/exported/web_blob.cc b/third_party/blink/renderer/core/exported/web_blob.cc
|
||||||
|
index ce7b5e229789d606df5e74461f09e2e1db59fc95..b1bf2affa5b7f10d9b45d062a2ce0479f5a3b80a 100644
|
||||||
|
--- a/third_party/blink/renderer/core/exported/web_blob.cc
|
||||||
|
+++ b/third_party/blink/renderer/core/exported/web_blob.cc
|
||||||
|
@@ -40,6 +40,7 @@
|
||||||
|
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
|
||||||
|
#include "third_party/blink/renderer/core/fileapi/blob.h"
|
||||||
|
#include "third_party/blink/renderer/core/fileapi/file_backed_blob_factory_dispatcher.h"
|
||||||
|
+#include "third_party/blink/renderer/core/fileapi/file.h"
|
||||||
|
#include "third_party/blink/renderer/platform/blob/blob_data.h"
|
||||||
|
#include "third_party/blink/renderer/platform/file_metadata.h"
|
||||||
|
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
|
||||||
|
@@ -83,6 +84,14 @@ WebString WebBlob::Uuid() {
|
||||||
|
return private_->Uuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
+std::string WebBlob::Path() {
|
||||||
|
+ if (!private_.Get())
|
||||||
|
+ return "";
|
||||||
|
+ if (private_->IsFile() && private_->HasBackingFile())
|
||||||
|
+ return To<File>(private_.Get())->GetPath().Utf8();
|
||||||
|
+ return "";
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
v8::Local<v8::Value> WebBlob::ToV8Value(v8::Isolate* isolate) {
|
||||||
|
if (!private_.Get())
|
||||||
|
return v8::Local<v8::Value>();
|
|
@ -90,6 +90,7 @@
|
||||||
V(electron_common_v8_util)
|
V(electron_common_v8_util)
|
||||||
|
|
||||||
#define ELECTRON_RENDERER_BINDINGS(V) \
|
#define ELECTRON_RENDERER_BINDINGS(V) \
|
||||||
|
V(electron_renderer_web_utils) \
|
||||||
V(electron_renderer_context_bridge) \
|
V(electron_renderer_context_bridge) \
|
||||||
V(electron_renderer_crash_reporter) \
|
V(electron_renderer_crash_reporter) \
|
||||||
V(electron_renderer_ipc) \
|
V(electron_renderer_ipc) \
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) 2023 Salesforce, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "shell/renderer/api/electron_api_web_utils.h"
|
||||||
|
|
||||||
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
|
#include "shell/common/gin_helper/error_thrower.h"
|
||||||
|
#include "shell/common/node_includes.h"
|
||||||
|
#include "third_party/blink/public/web/web_blob.h"
|
||||||
|
|
||||||
|
namespace electron::api::web_utils {
|
||||||
|
|
||||||
|
std::string GetPathForFile(v8::Isolate* isolate, v8::Local<v8::Value> file) {
|
||||||
|
blink::WebBlob blob = blink::WebBlob::FromV8Value(isolate, file);
|
||||||
|
if (blob.IsNull()) {
|
||||||
|
gin_helper::ErrorThrower(isolate).ThrowTypeError(
|
||||||
|
"getPathForFile expected to receive a File object but one was not "
|
||||||
|
"provided");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return blob.Path();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace electron::api::web_utils
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void Initialize(v8::Local<v8::Object> exports,
|
||||||
|
v8::Local<v8::Value> unused,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
void* priv) {
|
||||||
|
v8::Isolate* isolate = context->GetIsolate();
|
||||||
|
gin_helper::Dictionary dict(isolate, exports);
|
||||||
|
dict.SetMethod("getPathForFile", &electron::api::web_utils::GetPathForFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_renderer_web_utils, Initialize)
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2023 Salesforce, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ELECTRON_SHELL_RENDERER_API_ELECTRON_API_WEB_UTILS_H_
|
||||||
|
#define ELECTRON_SHELL_RENDERER_API_ELECTRON_API_WEB_UTILS_H_
|
||||||
|
|
||||||
|
#include "v8/include/v8.h"
|
||||||
|
|
||||||
|
namespace electron::api::web_utils {
|
||||||
|
|
||||||
|
std::string GetPathForFile(v8::Isolate* isolate, v8::Local<v8::Value> file);
|
||||||
|
|
||||||
|
} // namespace electron::api::web_utils
|
||||||
|
|
||||||
|
#endif // ELECTRON_SHELL_RENDERER_API_ELECTRON_API_WEB_UTILS_H_
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as path from 'node:path';
|
||||||
|
import { BrowserWindow } from 'electron/main';
|
||||||
|
import { defer } from './lib/spec-helpers';
|
||||||
|
// import { once } from 'node:events';
|
||||||
|
|
||||||
|
describe('webUtils module', () => {
|
||||||
|
const fixtures = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
|
describe('getPathForFile', () => {
|
||||||
|
it('returns nothing for a Blob', async () => {
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: false,
|
||||||
|
nodeIntegration: true,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
defer(() => w.close());
|
||||||
|
await w.loadFile(path.resolve(fixtures, 'pages', 'file-input.html'));
|
||||||
|
const pathFromWebUtils = await w.webContents.executeJavaScript('require("electron").webUtils.getPathForFile(new Blob([1, 2, 3]))');
|
||||||
|
expect(pathFromWebUtils).to.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports the correct path for a File object', async () => {
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: false,
|
||||||
|
nodeIntegration: true,
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
defer(() => w.close());
|
||||||
|
await w.loadFile(path.resolve(fixtures, 'pages', 'file-input.html'));
|
||||||
|
const { debugger: debug } = w.webContents;
|
||||||
|
debug.attach();
|
||||||
|
try {
|
||||||
|
const { root: { nodeId } } = await debug.sendCommand('DOM.getDocument');
|
||||||
|
const { nodeId: inputNodeId } = await debug.sendCommand('DOM.querySelector', { nodeId, selector: 'input' });
|
||||||
|
await debug.sendCommand('DOM.setFileInputFiles', {
|
||||||
|
files: [__filename],
|
||||||
|
nodeId: inputNodeId
|
||||||
|
});
|
||||||
|
const pathFromWebUtils = await w.webContents.executeJavaScript('require("electron").webUtils.getPathForFile(document.querySelector("input").files[0])');
|
||||||
|
expect(pathFromWebUtils).to.equal(__filename);
|
||||||
|
} finally {
|
||||||
|
debug.detach();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<input type="file" id="file" />
|
||||||
|
</body>
|
||||||
|
</html>
|
Загрузка…
Ссылка в новой задаче