feat: implement allowFileAccess loadExtension option (#25198)
Co-authored-by: Samuel Maddock <samuel.maddock@gmail.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org>
This commit is contained in:
parent
a75cd89d2a
commit
a5e9af330f
8 changed files with 38 additions and 11 deletions
|
@ -15,7 +15,7 @@ extension capabilities.
|
|||
|
||||
Electron only supports loading unpacked extensions (i.e., `.crx` files do not
|
||||
work). Extensions are installed per-`session`. To load an extension, call
|
||||
[`ses.loadExtension`](session.md#sesloadextensionpath):
|
||||
[`ses.loadExtension`](session.md#sesloadextensionpath-options):
|
||||
|
||||
```js
|
||||
const { session } = require('electron')
|
||||
|
|
|
@ -750,9 +750,13 @@ will not work on non-persistent (in-memory) sessions.
|
|||
|
||||
**Note:** On macOS and Windows 10 this word will be removed from the OS custom dictionary as well
|
||||
|
||||
#### `ses.loadExtension(path)`
|
||||
#### `ses.loadExtension(path[, options])`
|
||||
|
||||
* `path` String - Path to a directory containing an unpacked Chrome extension
|
||||
* `options` Object (optional)
|
||||
* `allowFileAccess` Boolean - Whether to allow the extension to read local files over `file://`
|
||||
protocol and inject content scripts into `file://` pages. This is required e.g. for loading
|
||||
devtools extensions on `file://` URLs. Defaults to false.
|
||||
|
||||
Returns `Promise<Extension>` - resolves when the extension is loaded.
|
||||
|
||||
|
@ -775,7 +779,11 @@ const { app, session } = require('electron')
|
|||
const path = require('path')
|
||||
|
||||
app.on('ready', async () => {
|
||||
await session.defaultSession.loadExtension(path.join(__dirname, 'react-devtools'))
|
||||
await session.defaultSession.loadExtension(
|
||||
path.join(__dirname, 'react-devtools'),
|
||||
// allowFileAccess is required to load the devtools extension on file:// URLs.
|
||||
{ allowFileAccess: true }
|
||||
)
|
||||
// Note that in order to use the React DevTools extension, you'll need to
|
||||
// download and unzip a copy of the extension.
|
||||
})
|
||||
|
|
|
@ -795,7 +795,8 @@ std::vector<base::FilePath> Session::GetPreloads() const {
|
|||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
v8::Local<v8::Promise> Session::LoadExtension(
|
||||
const base::FilePath& extension_path) {
|
||||
const base::FilePath& extension_path,
|
||||
gin::Arguments* args) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<const extensions::Extension*> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
@ -812,10 +813,19 @@ v8::Local<v8::Promise> Session::LoadExtension(
|
|||
return handle;
|
||||
}
|
||||
|
||||
int load_flags = extensions::Extension::FOLLOW_SYMLINKS_ANYWHERE;
|
||||
gin_helper::Dictionary options;
|
||||
if (args->GetNext(&options)) {
|
||||
bool allowFileAccess = false;
|
||||
options.Get("allowFileAccess", &allowFileAccess);
|
||||
if (allowFileAccess)
|
||||
load_flags |= extensions::Extension::ALLOW_FILE_ACCESS;
|
||||
}
|
||||
|
||||
auto* extension_system = static_cast<extensions::ElectronExtensionSystem*>(
|
||||
extensions::ExtensionSystem::Get(browser_context()));
|
||||
extension_system->LoadExtension(
|
||||
extension_path,
|
||||
extension_path, load_flags,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<const extensions::Extension*> promise,
|
||||
const extensions::Extension* extension,
|
||||
|
|
|
@ -136,7 +136,8 @@ class Session : public gin::Wrappable<Session>,
|
|||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
v8::Local<v8::Promise> LoadExtension(const base::FilePath& extension_path);
|
||||
v8::Local<v8::Promise> LoadExtension(const base::FilePath& extension_path,
|
||||
gin::Arguments* args);
|
||||
void RemoveExtension(const std::string& extension_id);
|
||||
v8::Local<v8::Value> GetExtension(const std::string& extension_id);
|
||||
v8::Local<v8::Value> GetAllExtensions();
|
||||
|
|
|
@ -30,7 +30,8 @@ using LoadErrorBehavior = ExtensionRegistrar::LoadErrorBehavior;
|
|||
namespace {
|
||||
|
||||
std::pair<scoped_refptr<const Extension>, std::string> LoadUnpacked(
|
||||
const base::FilePath& extension_dir) {
|
||||
const base::FilePath& extension_dir,
|
||||
int load_flags) {
|
||||
// app_shell only supports unpacked extensions.
|
||||
// NOTE: If you add packed extension support consider removing the flag
|
||||
// FOLLOW_SYMLINKS_ANYWHERE below. Packed extensions should not have symlinks.
|
||||
|
@ -40,7 +41,6 @@ std::pair<scoped_refptr<const Extension>, std::string> LoadUnpacked(
|
|||
return std::make_pair(nullptr, err);
|
||||
}
|
||||
|
||||
int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE;
|
||||
std::string load_error;
|
||||
scoped_refptr<Extension> extension = file_util::LoadExtension(
|
||||
extension_dir, Manifest::COMMAND_LINE, load_flags, &load_error);
|
||||
|
@ -75,10 +75,11 @@ ElectronExtensionLoader::~ElectronExtensionLoader() = default;
|
|||
|
||||
void ElectronExtensionLoader::LoadExtension(
|
||||
const base::FilePath& extension_dir,
|
||||
int load_flags,
|
||||
base::OnceCallback<void(const Extension*, const std::string&)> cb) {
|
||||
base::PostTaskAndReplyWithResult(
|
||||
GetExtensionFileTaskRunner().get(), FROM_HERE,
|
||||
base::BindOnce(&LoadUnpacked, extension_dir),
|
||||
base::BindOnce(&LoadUnpacked, extension_dir, load_flags),
|
||||
base::BindOnce(&ElectronExtensionLoader::FinishExtensionLoad,
|
||||
weak_factory_.GetWeakPtr(), std::move(cb)));
|
||||
}
|
||||
|
@ -174,9 +175,13 @@ void ElectronExtensionLoader::LoadExtensionForReload(
|
|||
LoadErrorBehavior load_error_behavior) {
|
||||
CHECK(!path.empty());
|
||||
|
||||
// TODO(nornagon): we should save whether file access was granted
|
||||
// when loading this extension and retain it here. As is, reloading an
|
||||
// extension will cause the file access permission to be dropped.
|
||||
int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE;
|
||||
base::PostTaskAndReplyWithResult(
|
||||
GetExtensionFileTaskRunner().get(), FROM_HERE,
|
||||
base::BindOnce(&LoadUnpacked, path),
|
||||
base::BindOnce(&LoadUnpacked, path, load_flags),
|
||||
base::BindOnce(&ElectronExtensionLoader::FinishExtensionReload,
|
||||
weak_factory_.GetWeakPtr(), extension_id));
|
||||
did_schedule_reload_ = true;
|
||||
|
|
|
@ -37,6 +37,7 @@ class ElectronExtensionLoader : public ExtensionRegistrar::Delegate {
|
|||
// Loads an unpacked extension from a directory synchronously. Returns the
|
||||
// extension on success, or nullptr otherwise.
|
||||
void LoadExtension(const base::FilePath& extension_dir,
|
||||
int load_flags,
|
||||
base::OnceCallback<void(const Extension* extension,
|
||||
const std::string&)> cb);
|
||||
|
||||
|
|
|
@ -55,8 +55,9 @@ ElectronExtensionSystem::~ElectronExtensionSystem() = default;
|
|||
|
||||
void ElectronExtensionSystem::LoadExtension(
|
||||
const base::FilePath& extension_dir,
|
||||
int load_flags,
|
||||
base::OnceCallback<void(const Extension*, const std::string&)> cb) {
|
||||
extension_loader_->LoadExtension(extension_dir, std::move(cb));
|
||||
extension_loader_->LoadExtension(extension_dir, load_flags, std::move(cb));
|
||||
}
|
||||
|
||||
void ElectronExtensionSystem::FinishInitialization() {
|
||||
|
|
|
@ -41,6 +41,7 @@ class ElectronExtensionSystem : public ExtensionSystem {
|
|||
// success, or nullptr otherwise.
|
||||
void LoadExtension(
|
||||
const base::FilePath& extension_dir,
|
||||
int load_flags,
|
||||
base::OnceCallback<void(const Extension*, const std::string&)> cb);
|
||||
|
||||
// Finish initialization for the shell extension system.
|
||||
|
|
Loading…
Reference in a new issue