зеркало из 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-macos-build
|
||||
<<: *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:
|
||||
- electron-build:
|
||||
persist: true
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
> 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
|
||||
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
|
||||
|
|
|
@ -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,
|
||||
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)
|
||||
* [`timers`](https://nodejs.org/api/timers.html)
|
||||
* [`url`](https://nodejs.org/api/url.html)
|
||||
|
|
|
@ -67,6 +67,7 @@ auto_filenames = {
|
|||
"docs/api/web-frame-main.md",
|
||||
"docs/api/web-frame.md",
|
||||
"docs/api/web-request.md",
|
||||
"docs/api/web-utils.md",
|
||||
"docs/api/webview-tag.md",
|
||||
"docs/api/window-open.md",
|
||||
"docs/api/structures/bluetooth-device.md",
|
||||
|
@ -151,6 +152,7 @@ auto_filenames = {
|
|||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
|
@ -281,6 +283,7 @@ auto_filenames = {
|
|||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
|
@ -318,6 +321,7 @@ auto_filenames = {
|
|||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.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.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.h",
|
||||
"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.h",
|
||||
"shell/renderer/content_settings_observer.cc",
|
||||
|
|
|
@ -4,5 +4,6 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
|||
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
||||
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
||||
{ 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',
|
||||
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
|
||||
fix_use_delegated_generic_capturer_when_available.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
|
||||
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
||||
fix_activate_background_material_on_windows.patch
|
||||
feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.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)
|
||||
|
||||
#define ELECTRON_RENDERER_BINDINGS(V) \
|
||||
V(electron_renderer_web_utils) \
|
||||
V(electron_renderer_context_bridge) \
|
||||
V(electron_renderer_crash_reporter) \
|
||||
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>
|
Загрузка…
Ссылка в новой задаче