diff --git a/.circleci/config/base.yml b/.circleci/config/base.yml index 2a0ec8fe828..9e10618a189 100644 --- a/.circleci/config/base.yml +++ b/.circleci/config/base.yml @@ -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 diff --git a/docs/api/file-object.md b/docs/api/file-object.md index ea0ec4e4ecb..a1d5b36aa92 100644 --- a/docs/api/file-object.md +++ b/docs/api/file-object.md @@ -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 diff --git a/docs/api/web-utils.md b/docs/api/web-utils.md new file mode 100644 index 00000000000..4b0c7a05440 --- /dev/null +++ b/docs/api/web-utils.md @@ -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]) +``` diff --git a/docs/tutorial/sandbox.md b/docs/tutorial/sandbox.md index b9a08e3594d..cd76322d536 100644 --- a/docs/tutorial/sandbox.md +++ b/docs/tutorial/sandbox.md @@ -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) diff --git a/filenames.auto.gni b/filenames.auto.gni index 1d89fc05e32..8ceb1b366b0 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -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", diff --git a/filenames.gni b/filenames.gni index 1ac2ddb7d0c..0146238c36c 100644 --- a/filenames.gni +++ b/filenames.gni @@ -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", diff --git a/lib/renderer/api/module-list.ts b/lib/renderer/api/module-list.ts index 3f817525ea4..7ac9ea67a64 100644 --- a/lib/renderer/api/module-list.ts +++ b/lib/renderer/api/module-list.ts @@ -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') } ]; diff --git a/lib/renderer/api/web-utils.ts b/lib/renderer/api/web-utils.ts new file mode 100644 index 00000000000..d90480d0848 --- /dev/null +++ b/lib/renderer/api/web-utils.ts @@ -0,0 +1,3 @@ +const binding = process._linkedBinding('electron_renderer_web_utils'); + +export const getPathForFile = binding.getPathForFile; diff --git a/lib/sandboxed_renderer/api/module-list.ts b/lib/sandboxed_renderer/api/module-list.ts index 09772fd72e1..36454319e1f 100644 --- a/lib/sandboxed_renderer/api/module-list.ts +++ b/lib/sandboxed_renderer/api/module-list.ts @@ -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') } ]; diff --git a/patches/chromium/.patches b/patches/chromium/.patches index dc2532d6b3b..5a4f0eef060 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -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 diff --git a/patches/chromium/expose_webblob_path_to_allow_embedders_to_get_file_paths.patch b/patches/chromium/expose_webblob_path_to_allow_embedders_to_get_file_paths.patch new file mode 100644 index 00000000000..f9398fd9652 --- /dev/null +++ b/patches/chromium/expose_webblob_path_to_allow_embedders_to_get_file_paths.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +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(private_.Get())->GetPath().Utf8(); ++ return ""; ++} ++ + v8::Local WebBlob::ToV8Value(v8::Isolate* isolate) { + if (!private_.Get()) + return v8::Local(); diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 8ae2b2d7fe5..007f0bd6d05 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -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) \ diff --git a/shell/renderer/api/electron_api_web_utils.cc b/shell/renderer/api/electron_api_web_utils.cc new file mode 100644 index 00000000000..c1c399f89bf --- /dev/null +++ b/shell/renderer/api/electron_api_web_utils.cc @@ -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 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 exports, + v8::Local unused, + v8::Local 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) diff --git a/shell/renderer/api/electron_api_web_utils.h b/shell/renderer/api/electron_api_web_utils.h new file mode 100644 index 00000000000..288032733e7 --- /dev/null +++ b/shell/renderer/api/electron_api_web_utils.h @@ -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 file); + +} // namespace electron::api::web_utils + +#endif // ELECTRON_SHELL_RENDERER_API_ELECTRON_API_WEB_UTILS_H_ diff --git a/spec/api-web-utils-spec.ts b/spec/api-web-utils-spec.ts new file mode 100644 index 00000000000..a369d15146e --- /dev/null +++ b/spec/api-web-utils-spec.ts @@ -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(); + } + }); + }); +}); diff --git a/spec/fixtures/pages/file-input.html b/spec/fixtures/pages/file-input.html new file mode 100644 index 00000000000..db3ffef5bdc --- /dev/null +++ b/spec/fixtures/pages/file-input.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file