mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
22 Commits
v12.0.0-ni
...
v12.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29c1248e96 | ||
|
|
a6b9f9d8e5 | ||
|
|
d305fe7d30 | ||
|
|
4dc09ea9dc | ||
|
|
4484e95fc8 | ||
|
|
03e60cce8b | ||
|
|
733d56e908 | ||
|
|
43485b8705 | ||
|
|
bda6378685 | ||
|
|
1b6534b326 | ||
|
|
e9e7eee25e | ||
|
|
7e698df8f3 | ||
|
|
e0611d0946 | ||
|
|
184e72fafd | ||
|
|
3745b76da8 | ||
|
|
bf7dbff858 | ||
|
|
7c10f86c6e | ||
|
|
9803e4d526 | ||
|
|
068b464e13 | ||
|
|
a09694ae85 | ||
|
|
e06a1c2746 | ||
|
|
c5320b3951 |
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -15,7 +15,6 @@ Contributors guide: https://github.com/electron/electron/blob/master/CONTRIBUTIN
|
||||
- [ ] relevant documentation is changed or added
|
||||
- [ ] PR title follows semantic [commit guidelines](https://github.com/electron/electron/blob/master/docs/development/pull-requests.md#commit-message-guidelines)
|
||||
- [ ] [PR release notes](https://github.com/electron/clerk/blob/master/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/master/README.md#examples).
|
||||
- [ ] This is **NOT A BREAKING CHANGE**. Breaking changes may not be merged to master until 11-x-y is branched.
|
||||
|
||||
#### Release Notes
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
12.0.0-nightly.20200828
|
||||
12.0.0-nightly.20200904
|
||||
@@ -59,7 +59,7 @@ The `crashReporter` module has the following methods:
|
||||
* `rateLimit` Boolean (optional) _macOS_ _Windows_ - If true, limit the
|
||||
number of crashes uploaded to 1/hour. Default is `false`.
|
||||
* `compress` Boolean (optional) - If true, crash reports will be compressed
|
||||
and uploaded with `Content-Encoding: gzip`. Default is `false`.
|
||||
and uploaded with `Content-Encoding: gzip`. Default is `true`.
|
||||
* `extra` Record<String, String> (optional) - Extra string key/value
|
||||
annotations that will be sent along with crash reports that are generated
|
||||
in the main process. Only string values are supported. Crashes generated in
|
||||
|
||||
@@ -102,3 +102,15 @@ The following methods of `chrome.tabs` are supported:
|
||||
> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
> supported and will raise an error.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
|
||||
@@ -8,19 +8,19 @@ Process: [Main](../glossary.md#main-process)
|
||||
|
||||
The `powerMonitor` module emits the following events:
|
||||
|
||||
### Event: 'suspend'
|
||||
### Event: 'suspend' _macOS_ _Windows_
|
||||
|
||||
Emitted when the system is suspending.
|
||||
|
||||
### Event: 'resume'
|
||||
### Event: 'resume' _macOS_ _Windows_
|
||||
|
||||
Emitted when system is resuming.
|
||||
|
||||
### Event: 'on-ac' _Windows_
|
||||
### Event: 'on-ac' _macOS_ _Windows_
|
||||
|
||||
Emitted when the system changes to AC power.
|
||||
|
||||
### Event: 'on-battery' _Windows_
|
||||
### Event: 'on-battery' _macOS_ _Windows_
|
||||
|
||||
Emitted when system changes to battery power.
|
||||
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
Process: [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
> ⚠️ WARNING ⚠️
|
||||
> The `remote` module is [deprecated](https://github.com/electron/electron/issues/21408).
|
||||
> Instead of `remote`, use [`ipcRenderer`](ipc-renderer.md) and
|
||||
> [`ipcMain`](ipc-main.md).
|
||||
>
|
||||
> Read more about why the `remote` module is deprecated [here](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31).
|
||||
>
|
||||
> If you still want to use `remote` despite the performance and security
|
||||
> concerns, see [@electron/remote](https://github.com/electron/remote).
|
||||
|
||||
The `remote` module provides a simple way to do inter-process communication
|
||||
(IPC) between the renderer process (web page) and the main process.
|
||||
|
||||
|
||||
@@ -45,15 +45,27 @@ Returns `Promise<void>`
|
||||
|
||||
Open the given external protocol URL in the desktop's default manner. (For example, mailto: URLs in the user's default mail agent).
|
||||
|
||||
### `shell.moveItemToTrash(fullPath[, deleteOnFail])`
|
||||
### `shell.moveItemToTrash(fullPath[, deleteOnFail])` _Deprecated_
|
||||
|
||||
* `fullPath` String
|
||||
* `deleteOnFail` Boolean (optional) - Whether or not to unilaterally remove the item if the Trash is disabled or unsupported on the volume. _macOS_
|
||||
|
||||
Returns `Boolean` - Whether the item was successfully moved to the trash or otherwise deleted.
|
||||
|
||||
> NOTE: This method is deprecated. Use `shell.trashItem` instead.
|
||||
|
||||
Move the given file to trash and returns a boolean status for the operation.
|
||||
|
||||
### `shell.trashItem(path)`
|
||||
|
||||
* `path` String - path to the item to be moved to the trash.
|
||||
|
||||
Returns `Promise<void>` - Resolves when the operation has been completed.
|
||||
Rejects if there was an error while deleting the requested item.
|
||||
|
||||
This moves a path to the OS-specific trash location (Trash on macOS, Recycle
|
||||
Bin on Windows, and a desktop-environment-specific location on Linux).
|
||||
|
||||
### `shell.beep()`
|
||||
|
||||
Play the beep sound.
|
||||
|
||||
@@ -1117,6 +1117,10 @@ increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively. The formula for this is
|
||||
`scale := 1.2 ^ level`.
|
||||
|
||||
> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the
|
||||
> zoom level for a specific domain propagates across all instances of windows with
|
||||
> the same domain. Differentiating the window URLs will make zoom work per-window.
|
||||
|
||||
#### `contents.getZoomLevel()`
|
||||
|
||||
Returns `Number` - the current zoom level.
|
||||
|
||||
@@ -41,6 +41,10 @@ Changes the zoom level to the specified level. The original size is 0 and each
|
||||
increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively.
|
||||
|
||||
> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the
|
||||
> zoom level for a specific domain propagates across all instances of windows with
|
||||
> the same domain. Differentiating the window URLs will make zoom work per-window.
|
||||
|
||||
### `webFrame.getZoomLevel()`
|
||||
|
||||
Returns `Number` - The current zoom level.
|
||||
|
||||
@@ -648,6 +648,10 @@ increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively. The formula for this is
|
||||
`scale := 1.2 ^ level`.
|
||||
|
||||
> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the
|
||||
> zoom level for a specific domain propagates across all instances of windows with
|
||||
> the same domain. Differentiating the window URLs will make zoom work per-window.
|
||||
|
||||
### `<webview>.getZoomFactor()`
|
||||
|
||||
Returns `Number` - the current zoom factor.
|
||||
|
||||
@@ -12,6 +12,20 @@ This document uses the following convention to categorize breaking changes:
|
||||
- **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
|
||||
- **Removed:** An API or feature was removed, and is no longer supported by Electron.
|
||||
|
||||
## Planned Breaking API Changes (13.0)
|
||||
|
||||
### Removed: `shell.moveItemToTrash()`
|
||||
|
||||
The deprecated synchronous `shell.moveItemToTrash()` API has been removed. Use
|
||||
the asynchronous `shell.trashItem()` instead.
|
||||
|
||||
```js
|
||||
// Removed in Electron 13
|
||||
shell.moveItemToTrash(path)
|
||||
// Replace with
|
||||
shell.trashItem(path).then(/* ... */)
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (12.0)
|
||||
|
||||
### Default Changed: `contextIsolation` defaults to `true`
|
||||
@@ -50,6 +64,18 @@ If your crash ingestion server does not support compressed payloads, you can
|
||||
turn off compression by specifying `{ compress: false }` in the crash reporter
|
||||
options.
|
||||
|
||||
### Deprecated: `shell.moveItemToTrash()`
|
||||
|
||||
The synchronous `shell.moveItemToTrash()` has been replaced by the new,
|
||||
asynchronous `shell.trashItem()`.
|
||||
|
||||
```js
|
||||
// Deprecated in Electron 12
|
||||
shell.moveItemToTrash(path)
|
||||
// Replace with
|
||||
shell.trashItem(path).then(/* ... */)
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (11.0)
|
||||
|
||||
There are no breaking changes planned for 11.0.
|
||||
|
||||
@@ -565,7 +565,9 @@ filenames = {
|
||||
"shell/common/node_util.h",
|
||||
"shell/common/options_switches.cc",
|
||||
"shell/common/options_switches.h",
|
||||
"shell/common/platform_util.cc",
|
||||
"shell/common/platform_util.h",
|
||||
"shell/common/platform_util_internal.h",
|
||||
"shell/common/platform_util_linux.cc",
|
||||
"shell/common/platform_util_mac.mm",
|
||||
"shell/common/platform_util_win.cc",
|
||||
@@ -625,6 +627,8 @@ filenames = {
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.cc",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.h",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.cc",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.h",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.cc",
|
||||
|
||||
@@ -28,9 +28,11 @@ Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);
|
||||
// Though this hack is only needed on macOS when the app is launched from
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
this.webContents.once('load-url' as any, function (this: WebContents) {
|
||||
this.focus();
|
||||
});
|
||||
if (this.webContents._initiallyShown) {
|
||||
this.webContents.once('load-url' as any, function (this: WebContents) {
|
||||
this.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event: Event) => {
|
||||
|
||||
@@ -13,7 +13,7 @@ class CrashReporter {
|
||||
submitURL,
|
||||
uploadToServer = true,
|
||||
rateLimit = false,
|
||||
compress = false
|
||||
compress = true
|
||||
} = options || {};
|
||||
|
||||
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
export default process._linkedBinding('electron_common_shell');
|
||||
import { deprecate } from 'electron/main';
|
||||
|
||||
const shell = process._linkedBinding('electron_common_shell');
|
||||
|
||||
shell.moveItemToTrash = deprecate.renameFunction(shell.moveItemToTrash, 'shell.trashItem');
|
||||
|
||||
export default shell;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "12.0.0-nightly.20200828",
|
||||
"version": "12.0.0-nightly.20200904",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -43,3 +43,4 @@ fix_allow_preventing_setpromiserejectcallback.patch
|
||||
lib_use_non-symbols_in_isurlinstance_check.patch
|
||||
feat_add_implementation_of_v8_platform_postjob.patch
|
||||
fix_enable_tls_renegotiation.patch
|
||||
n-api_src_provide_asynchronous_cleanup_hooks.patch
|
||||
|
||||
250
patches/node/n-api_src_provide_asynchronous_cleanup_hooks.patch
Normal file
250
patches/node/n-api_src_provide_asynchronous_cleanup_hooks.patch
Normal file
@@ -0,0 +1,250 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Tue, 25 Aug 2020 19:34:12 -0700
|
||||
Subject: n-api,src: provide asynchronous cleanup hooks
|
||||
|
||||
Backports https://github.com/nodejs/node/pull/34572 and
|
||||
https://github.com/nodejs/node/pull/34819.
|
||||
|
||||
Sometimes addons need to perform cleanup actions, for example
|
||||
closing libuv handles or waiting for requests to finish, that
|
||||
cannot be performed synchronously.
|
||||
|
||||
Add C++ API and N-API functions that allow providing such
|
||||
asynchronous cleanup hooks.
|
||||
|
||||
This patch can be removed when Electron upgrades to a version of Node.js
|
||||
which contains the above referenced commit(s).
|
||||
|
||||
diff --git a/src/api/hooks.cc b/src/api/hooks.cc
|
||||
index 037bdda6f41c825dd112b0cc9fca0ebde47c6163..3b16c0350d8a8494202144407664af41d338fe04 100644
|
||||
--- a/src/api/hooks.cc
|
||||
+++ b/src/api/hooks.cc
|
||||
@@ -73,8 +73,35 @@ int EmitExit(Environment* env) {
|
||||
.ToChecked();
|
||||
}
|
||||
|
||||
+typedef void (*CleanupHook)(void* arg);
|
||||
+typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*);
|
||||
+
|
||||
+struct AsyncCleanupHookInfo final {
|
||||
+ Environment* env;
|
||||
+ AsyncCleanupHook fun;
|
||||
+ void* arg;
|
||||
+ bool started = false;
|
||||
+ // Use a self-reference to make sure the storage is kept alive while the
|
||||
+ // cleanup hook is registered but not yet finished.
|
||||
+ std::shared_ptr<AsyncCleanupHookInfo> self;
|
||||
+};
|
||||
+
|
||||
+// Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
|
||||
+// (but not publicly so for easier ABI/API changes). In particular,
|
||||
+// std::shared_ptr does not generally maintain a consistent ABI even on a
|
||||
+// specific platform.
|
||||
+struct ACHHandle final {
|
||||
+ std::shared_ptr<AsyncCleanupHookInfo> info;
|
||||
+};
|
||||
+// This is implemented as an operator on a struct because otherwise you can't
|
||||
+// default-initialize AsyncCleanupHookHandle, because in C++ for a
|
||||
+// std::unique_ptr to be default-initializable the deleter type also needs
|
||||
+// to be default-initializable; in particular, function types don't satisfy
|
||||
+// this.
|
||||
+void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; }
|
||||
+
|
||||
void AddEnvironmentCleanupHook(Isolate* isolate,
|
||||
- void (*fun)(void* arg),
|
||||
+ CleanupHook fun,
|
||||
void* arg) {
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
CHECK_NOT_NULL(env);
|
||||
@@ -82,13 +109,50 @@ void AddEnvironmentCleanupHook(Isolate* isolate,
|
||||
}
|
||||
|
||||
void RemoveEnvironmentCleanupHook(Isolate* isolate,
|
||||
- void (*fun)(void* arg),
|
||||
+ CleanupHook fun,
|
||||
void* arg) {
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
CHECK_NOT_NULL(env);
|
||||
env->RemoveCleanupHook(fun, arg);
|
||||
}
|
||||
|
||||
+static void FinishAsyncCleanupHook(void* arg) {
|
||||
+ AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
|
||||
+ std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
|
||||
+
|
||||
+ info->env->DecreaseWaitingRequestCounter();
|
||||
+ info->self.reset();
|
||||
+}
|
||||
+
|
||||
+static void RunAsyncCleanupHook(void* arg) {
|
||||
+ AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
|
||||
+ info->env->IncreaseWaitingRequestCounter();
|
||||
+ info->started = true;
|
||||
+ info->fun(info->arg, FinishAsyncCleanupHook, info);
|
||||
+}
|
||||
+
|
||||
+AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||
+ Isolate* isolate,
|
||||
+ AsyncCleanupHook fun,
|
||||
+ void* arg) {
|
||||
+ Environment* env = Environment::GetCurrent(isolate);
|
||||
+ CHECK_NOT_NULL(env);
|
||||
+ auto info = std::make_shared<AsyncCleanupHookInfo>();
|
||||
+ info->env = env;
|
||||
+ info->fun = fun;
|
||||
+ info->arg = arg;
|
||||
+ info->self = info;
|
||||
+ env->AddCleanupHook(RunAsyncCleanupHook, info.get());
|
||||
+ return AsyncCleanupHookHandle(new ACHHandle { info });
|
||||
+}
|
||||
+
|
||||
+void RemoveEnvironmentCleanupHook(
|
||||
+ AsyncCleanupHookHandle handle) {
|
||||
+ if (handle->info->started) return;
|
||||
+ handle->info->self.reset();
|
||||
+ handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
|
||||
+}
|
||||
+
|
||||
async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
if (env == nullptr) return -1;
|
||||
diff --git a/src/node.h b/src/node.h
|
||||
index 60ecc3bd3499c23b297bc11e7f052aede20520c2..4c4e55e338d7b42c36818a45f6b41170c495adde 100644
|
||||
--- a/src/node.h
|
||||
+++ b/src/node.h
|
||||
@@ -739,6 +739,20 @@ NODE_EXTERN void RemoveEnvironmentCleanupHook(v8::Isolate* isolate,
|
||||
void (*fun)(void* arg),
|
||||
void* arg);
|
||||
|
||||
+/* These are async equivalents of the above. After the cleanup hook is invoked,
|
||||
+ * `cb(cbarg)` *must* be called, and attempting to remove the cleanup hook will
|
||||
+ * have no effect. */
|
||||
+struct ACHHandle;
|
||||
+struct NODE_EXTERN DeleteACHHandle { void operator()(ACHHandle*) const; };
|
||||
+typedef std::unique_ptr<ACHHandle, DeleteACHHandle> AsyncCleanupHookHandle;
|
||||
+
|
||||
+NODE_EXTERN AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||
+ v8::Isolate* isolate,
|
||||
+ void (*fun)(void* arg, void (*cb)(void*), void* cbarg),
|
||||
+ void* arg);
|
||||
+
|
||||
+NODE_EXTERN void RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle holder);
|
||||
+
|
||||
/* Returns the id of the current execution context. If the return value is
|
||||
* zero then no execution has been set. This will happen if the user handles
|
||||
* I/O from native code. */
|
||||
diff --git a/src/node_api.cc b/src/node_api.cc
|
||||
index fe24eca1b8e2d81fbafd0a1e2da38d957fbaa1c1..66168bd2c28ce6481516e63734616f487e3ec3e1 100644
|
||||
--- a/src/node_api.cc
|
||||
+++ b/src/node_api.cc
|
||||
@@ -507,6 +507,70 @@ napi_status napi_remove_env_cleanup_hook(napi_env env,
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
+struct napi_async_cleanup_hook_handle__ {
|
||||
+ napi_async_cleanup_hook_handle__(napi_env env,
|
||||
+ napi_async_cleanup_hook user_hook,
|
||||
+ void* user_data):
|
||||
+ env_(env),
|
||||
+ user_hook_(user_hook),
|
||||
+ user_data_(user_data) {
|
||||
+ handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
|
||||
+ env->Ref();
|
||||
+ }
|
||||
+
|
||||
+ ~napi_async_cleanup_hook_handle__() {
|
||||
+ node::RemoveEnvironmentCleanupHook(std::move(handle_));
|
||||
+ if (done_cb_ != nullptr)
|
||||
+ done_cb_(done_data_);
|
||||
+
|
||||
+ // Release the `env` handle asynchronously since it would be surprising if
|
||||
+ // a call to a N-API function would destroy `env` synchronously.
|
||||
+ static_cast<node_napi_env>(env_)->node_env()
|
||||
+ ->SetImmediate([env = env_](node::Environment*) { env->Unref(); });
|
||||
+ }
|
||||
+
|
||||
+ static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
|
||||
+ auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
|
||||
+ handle->done_cb_ = done_cb;
|
||||
+ handle->done_data_ = done_data;
|
||||
+ handle->user_hook_(handle, handle->user_data_);
|
||||
+ }
|
||||
+
|
||||
+ node::AsyncCleanupHookHandle handle_;
|
||||
+ napi_env env_ = nullptr;
|
||||
+ napi_async_cleanup_hook user_hook_ = nullptr;
|
||||
+ void* user_data_ = nullptr;
|
||||
+ void (*done_cb_)(void*) = nullptr;
|
||||
+ void* done_data_ = nullptr;
|
||||
+};
|
||||
+
|
||||
+napi_status napi_add_async_cleanup_hook(
|
||||
+ napi_env env,
|
||||
+ napi_async_cleanup_hook hook,
|
||||
+ void* arg,
|
||||
+ napi_async_cleanup_hook_handle* remove_handle) {
|
||||
+ CHECK_ENV(env);
|
||||
+ CHECK_ARG(env, hook);
|
||||
+
|
||||
+ napi_async_cleanup_hook_handle__* handle =
|
||||
+ new napi_async_cleanup_hook_handle__(env, hook, arg);
|
||||
+
|
||||
+ if (remove_handle != nullptr)
|
||||
+ *remove_handle = handle;
|
||||
+
|
||||
+ return napi_clear_last_error(env);
|
||||
+}
|
||||
+
|
||||
+napi_status napi_remove_async_cleanup_hook(
|
||||
+ napi_async_cleanup_hook_handle remove_handle) {
|
||||
+
|
||||
+ if (remove_handle == nullptr)
|
||||
+ return napi_invalid_arg;
|
||||
+
|
||||
+ delete remove_handle;
|
||||
+ return napi_ok;
|
||||
+}
|
||||
+
|
||||
napi_status napi_fatal_exception(napi_env env, napi_value err) {
|
||||
NAPI_PREAMBLE(env);
|
||||
CHECK_ARG(env, err);
|
||||
diff --git a/src/node_api.h b/src/node_api.h
|
||||
index 2f1b45572d8130f27492eb6188c1aa611f2e01a3..577a1dcd94987202819e7a36a2d9674f13d13614 100644
|
||||
--- a/src/node_api.h
|
||||
+++ b/src/node_api.h
|
||||
@@ -250,6 +250,19 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
|
||||
|
||||
#endif // NAPI_VERSION >= 4
|
||||
|
||||
+#ifdef NAPI_EXPERIMENTAL
|
||||
+
|
||||
+NAPI_EXTERN napi_status napi_add_async_cleanup_hook(
|
||||
+ napi_env env,
|
||||
+ napi_async_cleanup_hook hook,
|
||||
+ void* arg,
|
||||
+ napi_async_cleanup_hook_handle* remove_handle);
|
||||
+
|
||||
+NAPI_EXTERN napi_status napi_remove_async_cleanup_hook(
|
||||
+ napi_async_cleanup_hook_handle remove_handle);
|
||||
+
|
||||
+#endif // NAPI_EXPERIMENTAL
|
||||
+
|
||||
EXTERN_C_END
|
||||
|
||||
#endif // SRC_NODE_API_H_
|
||||
diff --git a/src/node_api_types.h b/src/node_api_types.h
|
||||
index 1c9a2b8aa21889c0d29fb02b234ae9698d122c2c..0e400e9676df5ba09d350fe7a2a70a1dc9e4d3d6 100644
|
||||
--- a/src/node_api_types.h
|
||||
+++ b/src/node_api_types.h
|
||||
@@ -41,4 +41,10 @@ typedef struct {
|
||||
const char* release;
|
||||
} napi_node_version;
|
||||
|
||||
+#ifdef NAPI_EXPERIMENTAL
|
||||
+typedef struct napi_async_cleanup_hook_handle__* napi_async_cleanup_hook_handle;
|
||||
+typedef void (*napi_async_cleanup_hook)(napi_async_cleanup_hook_handle handle,
|
||||
+ void* data);
|
||||
+#endif // NAPI_EXPERIMENTAL
|
||||
+
|
||||
#endif // SRC_NODE_API_TYPES_H_
|
||||
@@ -6,6 +6,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { GitProcess } = require('dugite');
|
||||
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.ELECTRON_GITHUB_TOKEN
|
||||
@@ -109,13 +110,21 @@ const getNoteFromClerk = async (ghKey) => {
|
||||
return NO_NOTES;
|
||||
}
|
||||
if (comment.body.startsWith(PERSIST_LEAD)) {
|
||||
return comment.body
|
||||
let lines = comment.body
|
||||
.slice(PERSIST_LEAD.length).trim() // remove PERSIST_LEAD
|
||||
.split(/\r?\n/) // split into lines
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted
|
||||
.map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines
|
||||
.join(' ') // join the note lines
|
||||
.map(line => line.slice(QUOTE_LEAD.length)); // unquote the lines
|
||||
|
||||
const firstLine = lines.shift();
|
||||
// indent anything after the first line to ensure that
|
||||
// multiline notes with their own sub-lists don't get
|
||||
// parsed in the markdown as part of the top-level list
|
||||
// (example: https://github.com/electron/electron/pull/25216)
|
||||
lines = lines.map(line => ' ' + line);
|
||||
return [firstLine, ...lines]
|
||||
.join('\n') // join the lines
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
@@ -532,6 +541,15 @@ function renderTrops (commit, excludeBranch) {
|
||||
function renderDescription (commit) {
|
||||
let note = commit.note || commit.subject || '';
|
||||
note = note.trim();
|
||||
|
||||
// release notes bullet point every change, so if the note author
|
||||
// manually started the content with a bullet point, that will confuse
|
||||
// the markdown renderer -- remove the redundant bullet point
|
||||
// (example: https://github.com/electron/electron/pull/25216)
|
||||
if (note.startsWith('*')) {
|
||||
note = note.slice(1).trim();
|
||||
}
|
||||
|
||||
if (note.length !== 0) {
|
||||
note = note[0].toUpperCase() + note.substr(1);
|
||||
|
||||
|
||||
@@ -86,18 +86,22 @@ const std::map<std::string, std::string>& GetGlobalCrashKeys() {
|
||||
return GetGlobalCrashKeysMutable();
|
||||
}
|
||||
|
||||
base::FilePath GetClientIdPath() {
|
||||
base::FilePath path;
|
||||
base::PathService::Get(electron::DIR_CRASH_DUMPS, &path);
|
||||
return path.Append("client_id");
|
||||
bool GetClientIdPath(base::FilePath* path) {
|
||||
if (base::PathService::Get(electron::DIR_CRASH_DUMPS, path)) {
|
||||
*path = path->Append("client_id");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ReadClientId() {
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
std::string client_id;
|
||||
// "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".length == 36
|
||||
if (!base::ReadFileToStringWithMaxSize(GetClientIdPath(), &client_id, 36) ||
|
||||
client_id.size() != 36)
|
||||
base::FilePath client_id_path;
|
||||
if (GetClientIdPath(&client_id_path) &&
|
||||
(!base::ReadFileToStringWithMaxSize(client_id_path, &client_id, 36) ||
|
||||
client_id.size() != 36))
|
||||
return std::string();
|
||||
return client_id;
|
||||
}
|
||||
@@ -105,7 +109,9 @@ std::string ReadClientId() {
|
||||
void WriteClientId(const std::string& client_id) {
|
||||
DCHECK_EQ(client_id.size(), 36u);
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
base::WriteFile(GetClientIdPath(), client_id);
|
||||
base::FilePath client_id_path;
|
||||
if (GetClientIdPath(&client_id_path))
|
||||
base::WriteFile(client_id_path, client_id);
|
||||
}
|
||||
|
||||
std::string GetClientId() {
|
||||
|
||||
@@ -474,8 +474,8 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
// BrowserViews are not attached to a window initially so they should start
|
||||
// off as hidden. This is also important for compositor recycling. See:
|
||||
// https://github.com/electron/electron/pull/21372
|
||||
bool initially_shown = type_ != Type::BROWSER_VIEW;
|
||||
options.Get(options::kShow, &initially_shown);
|
||||
initially_shown_ = type_ != Type::BROWSER_VIEW;
|
||||
options.Get(options::kShow, &initially_shown_);
|
||||
|
||||
// Obtain the session.
|
||||
std::string partition;
|
||||
@@ -531,7 +531,7 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
#endif
|
||||
} else {
|
||||
content::WebContents::CreateParams params(session->browser_context());
|
||||
params.initially_hidden = !initially_shown;
|
||||
params.initially_hidden = !initially_shown_;
|
||||
web_contents = content::WebContents::Create(params);
|
||||
}
|
||||
|
||||
@@ -2732,6 +2732,10 @@ v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
|
||||
return v8::Local<v8::Value>::New(isolate, debugger_);
|
||||
}
|
||||
|
||||
bool WebContents::WasInitiallyShown() {
|
||||
return initially_shown_;
|
||||
}
|
||||
|
||||
void WebContents::GrantOriginAccess(const GURL& url) {
|
||||
content::ChildProcessSecurityPolicy::GetInstance()->GrantCommitOrigin(
|
||||
web_contents()->GetMainFrame()->GetProcess()->GetID(),
|
||||
@@ -2925,6 +2929,7 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
|
||||
.SetProperty("hostWebContents", &WebContents::HostWebContents)
|
||||
.SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
|
||||
.SetProperty("debugger", &WebContents::Debugger)
|
||||
.SetProperty("_initiallyShown", &WebContents::WasInitiallyShown)
|
||||
.Build();
|
||||
}
|
||||
|
||||
|
||||
@@ -393,6 +393,7 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||
content::WebContents* HostWebContents() const;
|
||||
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
|
||||
bool WasInitiallyShown();
|
||||
|
||||
WebContentsZoomController* GetZoomController() { return zoom_controller_; }
|
||||
|
||||
@@ -683,6 +684,8 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||
// Observers of this WebContents.
|
||||
base::ObserverList<ExtendedWebContentsObserver> observers_;
|
||||
|
||||
bool initially_shown_ = true;
|
||||
|
||||
// The ID of the process of the currently committed RenderViewHost.
|
||||
// -1 means no speculative RVH has been committed yet.
|
||||
int currently_committed_process_id_ = -1;
|
||||
|
||||
@@ -355,6 +355,7 @@ class Browser : public WindowListObserver {
|
||||
|
||||
#if defined(OS_MAC)
|
||||
std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_;
|
||||
base::Time last_dock_show_;
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_WIN)
|
||||
|
||||
@@ -383,6 +383,20 @@ std::string Browser::DockGetBadgeText() {
|
||||
}
|
||||
|
||||
void Browser::DockHide() {
|
||||
// Transforming application state from UIElement to Foreground is an
|
||||
// asyncronous operation, and unfortunately there is currently no way to know
|
||||
// when it is finished.
|
||||
// So if we call DockHide => DockShow => DockHide => DockShow in a very short
|
||||
// time, we would triger a bug of macOS that, there would be multiple dock
|
||||
// icons of the app left in system.
|
||||
// To work around this, we make sure DockHide does nothing if it is called
|
||||
// immediately after DockShow. After some experiments, 1 second seems to be
|
||||
// a proper interval.
|
||||
if (!last_dock_show_.is_null() &&
|
||||
base::Time::Now() - last_dock_show_ < base::TimeDelta::FromSeconds(1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto* const& window : WindowList::GetWindows())
|
||||
[window->GetNativeWindow().GetNativeNSWindow() setCanHide:NO];
|
||||
|
||||
@@ -398,6 +412,7 @@ bool Browser::DockIsVisible() {
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) {
|
||||
last_dock_show_ = base::Time::Now();
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// TODO(sentialx): emit relevant events in Electron's session?
|
||||
#include "shell/browser/extensions/api/management/electron_management_api_delegate.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "chrome/common/extensions/extension_metrics.h"
|
||||
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
|
||||
#include "chrome/common/web_application_info.h"
|
||||
#include "chrome/common/webui_url_constants.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "extensions/browser/api/management/management_api.h"
|
||||
#include "extensions/browser/api/management/management_api_constants.h"
|
||||
#include "extensions/browser/disable_reason.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/extension_system.h"
|
||||
#include "extensions/common/api/management.h"
|
||||
#include "extensions/common/extension.h"
|
||||
#include "services/data_decoder/public/cpp/data_decoder.h"
|
||||
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
|
||||
|
||||
namespace {
|
||||
class ManagementSetEnabledFunctionInstallPromptDelegate
|
||||
: public extensions::InstallPromptDelegate {
|
||||
public:
|
||||
ManagementSetEnabledFunctionInstallPromptDelegate(
|
||||
content::WebContents* web_contents,
|
||||
content::BrowserContext* browser_context,
|
||||
const extensions::Extension* extension,
|
||||
const base::Callback<void(bool)>& callback) {
|
||||
// TODO(sentialx): emit event
|
||||
}
|
||||
~ManagementSetEnabledFunctionInstallPromptDelegate() override {}
|
||||
|
||||
private:
|
||||
base::WeakPtrFactory<ManagementSetEnabledFunctionInstallPromptDelegate>
|
||||
weak_factory_{this};
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ManagementSetEnabledFunctionInstallPromptDelegate);
|
||||
};
|
||||
|
||||
class ManagementUninstallFunctionUninstallDialogDelegate
|
||||
: public extensions::UninstallDialogDelegate {
|
||||
public:
|
||||
ManagementUninstallFunctionUninstallDialogDelegate(
|
||||
extensions::ManagementUninstallFunctionBase* function,
|
||||
const extensions::Extension* target_extension,
|
||||
bool show_programmatic_uninstall_ui) {
|
||||
// TODO(sentialx): emit event
|
||||
}
|
||||
|
||||
~ManagementUninstallFunctionUninstallDialogDelegate() override {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ManagementUninstallFunctionUninstallDialogDelegate);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronManagementAPIDelegate::ElectronManagementAPIDelegate() {}
|
||||
|
||||
ElectronManagementAPIDelegate::~ElectronManagementAPIDelegate() {}
|
||||
|
||||
void ElectronManagementAPIDelegate::LaunchAppFunctionDelegate(
|
||||
const extensions::Extension* extension,
|
||||
content::BrowserContext* context) const {
|
||||
// TODO(sentialx): emit event
|
||||
extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_EXTENSION_API,
|
||||
extension->GetType());
|
||||
}
|
||||
|
||||
GURL ElectronManagementAPIDelegate::GetFullLaunchURL(
|
||||
const extensions::Extension* extension) const {
|
||||
return extensions::AppLaunchInfo::GetFullLaunchURL(extension);
|
||||
}
|
||||
|
||||
extensions::LaunchType ElectronManagementAPIDelegate::GetLaunchType(
|
||||
const extensions::ExtensionPrefs* prefs,
|
||||
const extensions::Extension* extension) const {
|
||||
// TODO(sentialx)
|
||||
return extensions::LAUNCH_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::
|
||||
GetPermissionWarningsByManifestFunctionDelegate(
|
||||
extensions::ManagementGetPermissionWarningsByManifestFunction* function,
|
||||
const std::string& manifest_str) const {
|
||||
data_decoder::DataDecoder::ParseJsonIsolated(
|
||||
manifest_str,
|
||||
base::BindOnce(
|
||||
&extensions::ManagementGetPermissionWarningsByManifestFunction::
|
||||
OnParse,
|
||||
function));
|
||||
}
|
||||
|
||||
std::unique_ptr<extensions::InstallPromptDelegate>
|
||||
ElectronManagementAPIDelegate::SetEnabledFunctionDelegate(
|
||||
content::WebContents* web_contents,
|
||||
content::BrowserContext* browser_context,
|
||||
const extensions::Extension* extension,
|
||||
const base::Callback<void(bool)>& callback) const {
|
||||
return std::unique_ptr<ManagementSetEnabledFunctionInstallPromptDelegate>(
|
||||
new ManagementSetEnabledFunctionInstallPromptDelegate(
|
||||
web_contents, browser_context, extension, callback));
|
||||
}
|
||||
|
||||
std::unique_ptr<extensions::UninstallDialogDelegate>
|
||||
ElectronManagementAPIDelegate::UninstallFunctionDelegate(
|
||||
extensions::ManagementUninstallFunctionBase* function,
|
||||
const extensions::Extension* target_extension,
|
||||
bool show_programmatic_uninstall_ui) const {
|
||||
return std::unique_ptr<extensions::UninstallDialogDelegate>(
|
||||
new ManagementUninstallFunctionUninstallDialogDelegate(
|
||||
function, target_extension, show_programmatic_uninstall_ui));
|
||||
}
|
||||
|
||||
bool ElectronManagementAPIDelegate::CreateAppShortcutFunctionDelegate(
|
||||
extensions::ManagementCreateAppShortcutFunction* function,
|
||||
const extensions::Extension* extension,
|
||||
std::string* error) const {
|
||||
return false; // TODO(sentialx): route event and return true
|
||||
}
|
||||
|
||||
std::unique_ptr<extensions::AppForLinkDelegate>
|
||||
ElectronManagementAPIDelegate::GenerateAppForLinkFunctionDelegate(
|
||||
extensions::ManagementGenerateAppForLinkFunction* function,
|
||||
content::BrowserContext* context,
|
||||
const std::string& title,
|
||||
const GURL& launch_url) const {
|
||||
// TODO(sentialx)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ElectronManagementAPIDelegate::CanContextInstallWebApps(
|
||||
content::BrowserContext* context) const {
|
||||
// TODO(sentialx)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::InstallOrLaunchReplacementWebApp(
|
||||
content::BrowserContext* context,
|
||||
const GURL& web_app_url,
|
||||
InstallOrLaunchWebAppCallback callback) const {
|
||||
// TODO(sentialx)
|
||||
}
|
||||
|
||||
bool ElectronManagementAPIDelegate::CanContextInstallAndroidApps(
|
||||
content::BrowserContext* context) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::CheckAndroidAppInstallStatus(
|
||||
const std::string& package_name,
|
||||
AndroidAppInstallStatusCallback callback) const {
|
||||
std::move(callback).Run(false);
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::InstallReplacementAndroidApp(
|
||||
const std::string& package_name,
|
||||
InstallAndroidAppCallback callback) const {
|
||||
std::move(callback).Run(false);
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::EnableExtension(
|
||||
content::BrowserContext* context,
|
||||
const std::string& extension_id) const {
|
||||
// const extensions::Extension* extension =
|
||||
// extensions::ExtensionRegistry::Get(context)->GetExtensionById(
|
||||
// extension_id, extensions::ExtensionRegistry::EVERYTHING);
|
||||
|
||||
// TODO(sentialx): we don't have ExtensionService
|
||||
// If the extension was disabled for a permissions increase, the Management
|
||||
// API will have displayed a re-enable prompt to the user, so we know it's
|
||||
// safe to grant permissions here.
|
||||
// extensions::ExtensionSystem::Get(context)
|
||||
// ->extension_service()
|
||||
// ->GrantPermissionsAndEnableExtension(extension);
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::DisableExtension(
|
||||
content::BrowserContext* context,
|
||||
const extensions::Extension* source_extension,
|
||||
const std::string& extension_id,
|
||||
extensions::disable_reason::DisableReason disable_reason) const {
|
||||
// TODO(sentialx): we don't have ExtensionService
|
||||
// extensions::ExtensionSystem::Get(context)
|
||||
// ->extension_service()
|
||||
// ->DisableExtensionWithSource(source_extension, extension_id,
|
||||
// disable_reason);
|
||||
}
|
||||
|
||||
bool ElectronManagementAPIDelegate::UninstallExtension(
|
||||
content::BrowserContext* context,
|
||||
const std::string& transient_extension_id,
|
||||
extensions::UninstallReason reason,
|
||||
base::string16* error) const {
|
||||
// TODO(sentialx): we don't have ExtensionService
|
||||
// return extensions::ExtensionSystem::Get(context)
|
||||
// ->extension_service()
|
||||
// ->UninstallExtension(transient_extension_id, reason, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ElectronManagementAPIDelegate::SetLaunchType(
|
||||
content::BrowserContext* context,
|
||||
const std::string& extension_id,
|
||||
extensions::LaunchType launch_type) const {
|
||||
// TODO(sentialx)
|
||||
// extensions::SetLaunchType(context, extension_id, launch_type);
|
||||
}
|
||||
|
||||
GURL ElectronManagementAPIDelegate::GetIconURL(
|
||||
const extensions::Extension* extension,
|
||||
int icon_size,
|
||||
ExtensionIconSet::MatchType match,
|
||||
bool grayscale) const {
|
||||
GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
|
||||
chrome::kChromeUIExtensionIconURL,
|
||||
extension->id().c_str(), icon_size, match,
|
||||
grayscale ? "?grayscale=true" : ""));
|
||||
CHECK(icon_url.is_valid());
|
||||
return icon_url;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_API_MANAGEMENT_ELECTRON_MANAGEMENT_API_DELEGATE_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_API_MANAGEMENT_ELECTRON_MANAGEMENT_API_DELEGATE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/task/cancelable_task_tracker.h"
|
||||
#include "extensions/browser/api/management/management_api_delegate.h"
|
||||
|
||||
class ElectronManagementAPIDelegate : public extensions::ManagementAPIDelegate {
|
||||
public:
|
||||
ElectronManagementAPIDelegate();
|
||||
~ElectronManagementAPIDelegate() override;
|
||||
|
||||
// ManagementAPIDelegate.
|
||||
void LaunchAppFunctionDelegate(
|
||||
const extensions::Extension* extension,
|
||||
content::BrowserContext* context) const override;
|
||||
GURL GetFullLaunchURL(const extensions::Extension* extension) const override;
|
||||
extensions::LaunchType GetLaunchType(
|
||||
const extensions::ExtensionPrefs* prefs,
|
||||
const extensions::Extension* extension) const override;
|
||||
void GetPermissionWarningsByManifestFunctionDelegate(
|
||||
extensions::ManagementGetPermissionWarningsByManifestFunction* function,
|
||||
const std::string& manifest_str) const override;
|
||||
std::unique_ptr<extensions::InstallPromptDelegate> SetEnabledFunctionDelegate(
|
||||
content::WebContents* web_contents,
|
||||
content::BrowserContext* browser_context,
|
||||
const extensions::Extension* extension,
|
||||
const base::Callback<void(bool)>& callback) const override;
|
||||
std::unique_ptr<extensions::UninstallDialogDelegate>
|
||||
UninstallFunctionDelegate(
|
||||
extensions::ManagementUninstallFunctionBase* function,
|
||||
const extensions::Extension* target_extension,
|
||||
bool show_programmatic_uninstall_ui) const override;
|
||||
bool CreateAppShortcutFunctionDelegate(
|
||||
extensions::ManagementCreateAppShortcutFunction* function,
|
||||
const extensions::Extension* extension,
|
||||
std::string* error) const override;
|
||||
std::unique_ptr<extensions::AppForLinkDelegate>
|
||||
GenerateAppForLinkFunctionDelegate(
|
||||
extensions::ManagementGenerateAppForLinkFunction* function,
|
||||
content::BrowserContext* context,
|
||||
const std::string& title,
|
||||
const GURL& launch_url) const override;
|
||||
bool CanContextInstallWebApps(
|
||||
content::BrowserContext* context) const override;
|
||||
void InstallOrLaunchReplacementWebApp(
|
||||
content::BrowserContext* context,
|
||||
const GURL& web_app_url,
|
||||
ManagementAPIDelegate::InstallOrLaunchWebAppCallback callback)
|
||||
const override;
|
||||
bool CanContextInstallAndroidApps(
|
||||
content::BrowserContext* context) const override;
|
||||
void CheckAndroidAppInstallStatus(
|
||||
const std::string& package_name,
|
||||
ManagementAPIDelegate::AndroidAppInstallStatusCallback callback)
|
||||
const override;
|
||||
void InstallReplacementAndroidApp(
|
||||
const std::string& package_name,
|
||||
ManagementAPIDelegate::InstallAndroidAppCallback callback) const override;
|
||||
void EnableExtension(content::BrowserContext* context,
|
||||
const std::string& extension_id) const override;
|
||||
void DisableExtension(
|
||||
content::BrowserContext* context,
|
||||
const extensions::Extension* source_extension,
|
||||
const std::string& extension_id,
|
||||
extensions::disable_reason::DisableReason disable_reason) const override;
|
||||
bool UninstallExtension(content::BrowserContext* context,
|
||||
const std::string& transient_extension_id,
|
||||
extensions::UninstallReason reason,
|
||||
base::string16* error) const override;
|
||||
void SetLaunchType(content::BrowserContext* context,
|
||||
const std::string& extension_id,
|
||||
extensions::LaunchType launch_type) const override;
|
||||
GURL GetIconURL(const extensions::Extension* extension,
|
||||
int icon_size,
|
||||
ExtensionIconSet::MatchType match,
|
||||
bool grayscale) const override;
|
||||
};
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_MANAGEMENT_ELECTRON_MANAGEMENT_API_DELEGATE_H_
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "extensions/browser/api/app_runtime/app_runtime_api.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/info_map.h"
|
||||
#include "extensions/browser/management_policy.h"
|
||||
#include "extensions/browser/notification_types.h"
|
||||
#include "extensions/browser/null_app_sorting.h"
|
||||
#include "extensions/browser/quota_service.h"
|
||||
@@ -91,6 +92,8 @@ void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
|
||||
|
||||
if (!browser_context_->IsOffTheRecord())
|
||||
LoadComponentExtensions();
|
||||
|
||||
management_policy_.reset(new ManagementPolicy);
|
||||
}
|
||||
|
||||
std::unique_ptr<base::DictionaryValue> ParseManifest(
|
||||
@@ -130,7 +133,7 @@ RuntimeData* ElectronExtensionSystem::runtime_data() {
|
||||
}
|
||||
|
||||
ManagementPolicy* ElectronExtensionSystem::management_policy() {
|
||||
return nullptr;
|
||||
return management_policy_.get();
|
||||
}
|
||||
|
||||
ServiceWorkerManager* ElectronExtensionSystem::service_worker_manager() {
|
||||
|
||||
@@ -104,6 +104,7 @@ class ElectronExtensionSystem : public ExtensionSystem {
|
||||
std::unique_ptr<QuotaService> quota_service_;
|
||||
std::unique_ptr<SharedUserScriptManager> shared_user_script_manager_;
|
||||
std::unique_ptr<AppSorting> app_sorting_;
|
||||
std::unique_ptr<ManagementPolicy> management_policy_;
|
||||
|
||||
std::unique_ptr<ElectronExtensionLoader> extension_loader_;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "shell/browser/extensions/api/management/electron_management_api_delegate.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#include "shell/browser/extensions/electron_messaging_delegate.h"
|
||||
|
||||
@@ -59,6 +60,11 @@ void ElectronExtensionsAPIClient::AttachWebContentsHelpers(
|
||||
#endif
|
||||
}
|
||||
|
||||
ManagementAPIDelegate*
|
||||
ElectronExtensionsAPIClient::CreateManagementAPIDelegate() const {
|
||||
return new ElectronManagementAPIDelegate;
|
||||
}
|
||||
|
||||
std::unique_ptr<MimeHandlerViewGuestDelegate>
|
||||
ElectronExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate(
|
||||
MimeHandlerViewGuest* guest) const {
|
||||
|
||||
@@ -25,6 +25,7 @@ class ElectronExtensionsAPIClient : public ExtensionsAPIClient {
|
||||
std::unique_ptr<MimeHandlerViewGuestDelegate>
|
||||
CreateMimeHandlerViewGuestDelegate(
|
||||
MimeHandlerViewGuest* guest) const override;
|
||||
ManagementAPIDelegate* CreateManagementAPIDelegate() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ElectronMessagingDelegate> messaging_delegate_;
|
||||
|
||||
@@ -43,10 +43,7 @@
|
||||
// https://developer.apple.com/documentation/foundation/nsusernotificationactivationtype?language=objc
|
||||
if (notif.activationType ==
|
||||
NSUserNotificationActivationTypeContentsClicked) {
|
||||
// If a notification with a reply button is clicked and the user has not
|
||||
// yet replied, we do not want to destroy the notification.
|
||||
bool should_destroy = ![notif hasReplyButton];
|
||||
notification->NotificationClicked(should_destroy);
|
||||
notification->NotificationClicked();
|
||||
} else if (notif.activationType ==
|
||||
NSUserNotificationActivationTypeActionButtonClicked) {
|
||||
notification->NotificationActivated();
|
||||
|
||||
@@ -21,12 +21,10 @@ Notification::~Notification() {
|
||||
delegate()->NotificationDestroyed();
|
||||
}
|
||||
|
||||
void Notification::NotificationClicked(bool should_destroy) {
|
||||
void Notification::NotificationClicked() {
|
||||
if (delegate())
|
||||
delegate()->NotificationClick();
|
||||
|
||||
if (should_destroy)
|
||||
Destroy();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Notification::NotificationDismissed() {
|
||||
|
||||
@@ -54,7 +54,7 @@ class Notification {
|
||||
virtual void Dismiss() = 0;
|
||||
|
||||
// Should be called by derived classes.
|
||||
void NotificationClicked(bool should_destroy = true);
|
||||
void NotificationClicked();
|
||||
void NotificationDismissed();
|
||||
void NotificationFailed();
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
IInspectable* args) {
|
||||
base::PostTask(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&Notification::NotificationClicked, notification_, true));
|
||||
base::BindOnce(&Notification::NotificationClicked, notification_));
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << "Notification clicked";
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 12,0,0,20200828
|
||||
PRODUCTVERSION 12,0,0,20200828
|
||||
FILEVERSION 12,0,0,20200904
|
||||
PRODUCTVERSION 12,0,0,20200904
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -90,7 +90,8 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
|
||||
// Create array to keep file types and their name.
|
||||
for (const Filter& filter : filters) {
|
||||
NSMutableSet* file_type_set = [NSMutableSet set];
|
||||
NSMutableOrderedSet* file_type_set =
|
||||
[NSMutableOrderedSet orderedSetWithCapacity:filters.size()];
|
||||
[filter_names addObject:@(filter.first.c_str())];
|
||||
for (std::string ext : filter.second) {
|
||||
// macOS is incapable of understanding multiple file extensions,
|
||||
@@ -104,7 +105,7 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
|
||||
[file_type_set addObject:@(ext.c_str())];
|
||||
}
|
||||
[file_types_list addObject:[file_type_set allObjects]];
|
||||
[file_types_list addObject:[file_type_set array]];
|
||||
}
|
||||
|
||||
// Passing empty array to setAllowedFileTypes will cause exception.
|
||||
|
||||
@@ -4,12 +4,31 @@
|
||||
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
|
||||
#include "base/win/windows_version.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/win/hwnd_util.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
gfx::Insets GetGlassInsets() {
|
||||
int frame_height =
|
||||
display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSIZEFRAME) +
|
||||
display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXPADDEDBORDER);
|
||||
|
||||
int frame_size =
|
||||
base::win::GetVersion() < base::win::Version::WIN8
|
||||
? display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME)
|
||||
: 0;
|
||||
|
||||
return gfx::Insets(frame_height, frame_size, frame_size, frame_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const char WinFrameView::kViewClassName[] = "WinFrameView";
|
||||
|
||||
WinFrameView::WinFrameView() {}
|
||||
@@ -23,6 +42,17 @@ gfx::Rect WinFrameView::GetWindowBoundsForClientBounds(
|
||||
client_bounds);
|
||||
}
|
||||
|
||||
gfx::Rect WinFrameView::GetBoundsForClientView() const {
|
||||
if (window_->IsMaximized() && !window_->has_frame()) {
|
||||
gfx::Insets insets = GetGlassInsets();
|
||||
gfx::Rect result(width(), height());
|
||||
result.Inset(insets);
|
||||
return result;
|
||||
} else {
|
||||
return bounds();
|
||||
}
|
||||
}
|
||||
|
||||
int WinFrameView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (window_->has_frame())
|
||||
return frame_->client_view()->NonClientHitTest(point);
|
||||
|
||||
@@ -16,6 +16,7 @@ class WinFrameView : public FramelessView {
|
||||
~WinFrameView() override;
|
||||
|
||||
// views::NonClientFrameView:
|
||||
gfx::Rect GetBoundsForClientView() const override;
|
||||
gfx::Rect GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const override;
|
||||
int NonClientHitTest(const gfx::Point& point) override;
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h"
|
||||
|
||||
#include "base/win/windows_version.h"
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "ui/base/win/hwnd_metrics.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/views/win/hwnd_util.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -36,18 +41,49 @@ bool ElectronDesktopWindowTreeHostWin::HasNativeFrame() const {
|
||||
// Since we never use chromium's titlebar implementation, we can just say
|
||||
// that we use a native titlebar. This will disable the repaint locking when
|
||||
// DWM composition is disabled.
|
||||
return true;
|
||||
return !ui::win::IsAeroGlassEnabled();
|
||||
}
|
||||
|
||||
bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels(
|
||||
gfx::Insets* insets) const {
|
||||
if (IsMaximized() && !native_window_view_->has_frame()) {
|
||||
HMONITOR monitor = ::MonitorFromWindow(
|
||||
native_window_view_->GetAcceleratedWidget(), MONITOR_DEFAULTTONEAREST);
|
||||
int frame_height = display::win::ScreenWin::GetSystemMetricsForMonitor(
|
||||
monitor, SM_CYSIZEFRAME) +
|
||||
display::win::ScreenWin::GetSystemMetricsForMonitor(
|
||||
monitor, SM_CXPADDEDBORDER);
|
||||
int frame_size = base::win::GetVersion() < base::win::Version::WIN8
|
||||
? display::win::ScreenWin::GetSystemMetricsForMonitor(
|
||||
monitor, SM_CXSIZEFRAME)
|
||||
: 0;
|
||||
insets->Set(frame_height, frame_size, frame_size, frame_size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets(
|
||||
gfx::Insets* insets,
|
||||
HMONITOR monitor) const {
|
||||
if (IsMaximized() && !native_window_view_->has_frame()) {
|
||||
// Windows automatically adds a standard width border to all sides when a
|
||||
// window is maximized.
|
||||
int frame_thickness = ui::GetFrameThickness(monitor) - 1;
|
||||
*insets = gfx::Insets(frame_thickness, frame_thickness, frame_thickness,
|
||||
frame_thickness);
|
||||
if (base::win::GetVersion() < base::win::Version::WIN8) {
|
||||
// This tells Windows that most of the window is a client area, meaning
|
||||
// Chrome will draw it. Windows still fills in the glass bits because of
|
||||
// the // DwmExtendFrameIntoClientArea call in |UpdateDWMFrame|.
|
||||
// Without this 1 pixel offset on the right and bottom:
|
||||
// * windows paint in a more standard way, and
|
||||
// * we get weird black bars at the top when maximized in multiple
|
||||
// monitor configurations.
|
||||
int border_thickness = 1;
|
||||
insets->Set(0, 0, border_thickness, border_thickness);
|
||||
} else {
|
||||
const int frame_thickness = ui::GetFrameThickness(monitor);
|
||||
// Reduce the Windows non-client border size because we extend the border
|
||||
// into our client area in UpdateDWMFrame(). The top inset must be 0 or
|
||||
// else Windows will draw a full native titlebar outside the client area.
|
||||
insets->Set(0, frame_thickness, frame_thickness, frame_thickness);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -27,6 +27,7 @@ class ElectronDesktopWindowTreeHostWin
|
||||
LRESULT* result) override;
|
||||
bool ShouldPaintAsActive() const override;
|
||||
bool HasNativeFrame() const override;
|
||||
bool GetDwmFrameInsetsInPixels(gfx::Insets* insets) const override;
|
||||
bool GetClientAreaInsets(gfx::Insets* insets,
|
||||
HMONITOR monitor) const override;
|
||||
|
||||
|
||||
@@ -94,6 +94,25 @@ bool MoveItemToTrash(gin::Arguments* args) {
|
||||
return platform_util::MoveItemToTrash(full_path, delete_on_fail);
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> TrashItem(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
platform_util::TrashItem(
|
||||
path, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, bool success,
|
||||
const std::string& error) {
|
||||
if (success) {
|
||||
promise.Resolve();
|
||||
} else {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
}
|
||||
},
|
||||
std::move(promise)));
|
||||
return handle;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool WriteShortcutLink(const base::FilePath& shortcut_path,
|
||||
gin_helper::Arguments* args) {
|
||||
@@ -158,6 +177,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
dict.SetMethod("openPath", &OpenPath);
|
||||
dict.SetMethod("openExternal", &OpenExternal);
|
||||
dict.SetMethod("moveItemToTrash", &MoveItemToTrash);
|
||||
dict.SetMethod("trashItem", &TrashItem);
|
||||
dict.SetMethod("beep", &platform_util::Beep);
|
||||
#if defined(OS_WIN)
|
||||
dict.SetMethod("writeShortcutLink", &WriteShortcutLink);
|
||||
|
||||
@@ -5,5 +5,65 @@
|
||||
"extension"
|
||||
],
|
||||
"location": "component"
|
||||
}
|
||||
}
|
||||
},
|
||||
"management": [
|
||||
{
|
||||
"channel": "stable",
|
||||
"extension_types": [
|
||||
"extension",
|
||||
"legacy_packaged_app"
|
||||
]
|
||||
},
|
||||
{
|
||||
"channel": "stable",
|
||||
"extension_types": [
|
||||
"platform_app"
|
||||
],
|
||||
"whitelist": [
|
||||
"AE27D69DBE571F4B1694F05C89B710C646792231", // Published ADT
|
||||
// TODO(grv): clean up once Apps developer tool is published.
|
||||
"5107DE9024C329EEA9C9A72D94C16723790C6422", // Apps Developer Tool.
|
||||
"8C0B1873FFFB65E4D0F4D772879F7304CEF125C2", // Apps Editor old.
|
||||
"FA0501B579070BB9CBD4FCAEC8CB0EDF22BA2F04", // Apps Editor published.
|
||||
"EE17C698905F7F2E6DDC87C9C30F11E164C829F4", // Watchdog (Activity Log)
|
||||
"90113DA9516526D24DAF156C629CC41C049E8882", // Watchdog Test Version
|
||||
"4A4EA121622FCA3D78ED2AB534197F43D7189EE0", // Spark nightly build.
|
||||
"9FDE6E7F06FCFA11D9A05041C7FF6D8AE662F5D1", // Spark release.
|
||||
"50B4A905D522C06E27CA6D099E3E54BDA1F152C5", // Spark Beta channel.
|
||||
"BA0C8BB92084C9741312D90D3EA882526853455F", // Spark dev channel.
|
||||
"5F57A9AE8DFF5D6BB09DF8606270402612E871E5", // http://crbug.com/422624
|
||||
"46578A13607D38F1DC8E280C4F499FB0A2F9565C", // http://crbug.com/819404
|
||||
"898FB5A39687D210766B8998BA4530B99C9E6586", // http://crbug.com/819404
|
||||
"82F30B65397BC3E4ADE627BBD857AB8A58210648", // http://crbug.com/819404
|
||||
"C74B2AF138F9EDECD04D0965AB36CA66C8290466" // http://crbug.com/957772
|
||||
]
|
||||
},
|
||||
{
|
||||
"channel": "stable",
|
||||
"extension_types": [
|
||||
"hosted_app"
|
||||
],
|
||||
"whitelist": [
|
||||
"B44D08FD98F1523ED5837D78D0A606EA9D6206E5" // Web Store
|
||||
]
|
||||
},
|
||||
{
|
||||
"channel": "stable",
|
||||
"extension_types": [
|
||||
"platform_app"
|
||||
],
|
||||
"session_types": [
|
||||
"kiosk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"channel": "stable",
|
||||
"dependencies": [
|
||||
"behavior:imprivata_login_screen_extension"
|
||||
],
|
||||
"extension_types": [
|
||||
"login_screen_extension"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -38,6 +38,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
|
||||
APIPermissionInfo::kFlagInternal},
|
||||
{APIPermission::kResourcesPrivate, "resourcesPrivate",
|
||||
APIPermissionInfo::kFlagCannotBeOptional},
|
||||
{APIPermission::kManagement, "management"},
|
||||
};
|
||||
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
|
||||
return base::make_span(permissions_to_register);
|
||||
|
||||
37
shell/common/platform_util.cc
Normal file
37
shell/common/platform_util.cc
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2020 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/common/platform_util.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "shell/common/platform_util_internal.h"
|
||||
|
||||
namespace platform_util {
|
||||
|
||||
void TrashItemOnBlockingThread(
|
||||
const base::FilePath& full_path,
|
||||
base::OnceCallback<void(bool, const std::string&)> callback) {
|
||||
std::string error;
|
||||
bool success = internal::PlatformTrashItem(full_path, &error);
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(std::move(callback), success, error));
|
||||
}
|
||||
|
||||
void TrashItem(const base::FilePath& full_path,
|
||||
base::OnceCallback<void(bool, const std::string&)> callback) {
|
||||
// XXX: is continue_on_shutdown right?
|
||||
base::ThreadPool::PostTask(FROM_HERE,
|
||||
{base::MayBlock(), base::WithBaseSyncPrimitives(),
|
||||
base::TaskPriority::USER_BLOCKING,
|
||||
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
|
||||
base::BindOnce(&TrashItemOnBlockingThread,
|
||||
full_path, std::move(callback)));
|
||||
}
|
||||
|
||||
} // namespace platform_util
|
||||
@@ -40,9 +40,13 @@ void OpenExternal(const GURL& url,
|
||||
const OpenExternalOptions& options,
|
||||
OpenCallback callback);
|
||||
|
||||
// Move a file to trash.
|
||||
// Move a file to trash. (Deprecated.)
|
||||
bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail);
|
||||
|
||||
// Move a file to trash, asynchronously.
|
||||
void TrashItem(const base::FilePath& full_path,
|
||||
base::OnceCallback<void(bool, const std::string&)> callback);
|
||||
|
||||
void Beep();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
26
shell/common/platform_util_internal.h
Normal file
26
shell/common/platform_util_internal.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2020 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_COMMON_PLATFORM_UTIL_INTERNAL_H_
|
||||
#define SHELL_COMMON_PLATFORM_UTIL_INTERNAL_H_
|
||||
|
||||
#include "shell/common/platform_util.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace platform_util {
|
||||
namespace internal {
|
||||
|
||||
// Called by platform_util.cc on to invoke platform specific logic to move
|
||||
// |path| to trash using a suitable handler.
|
||||
bool PlatformTrashItem(const base::FilePath& path, std::string* error);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace platform_util
|
||||
|
||||
#endif // SHELL_COMMON_PLATFORM_UTIL_INTERNAL_H_
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "dbus/bus.h"
|
||||
#include "dbus/message.h"
|
||||
#include "dbus/object_proxy.h"
|
||||
#include "shell/common/platform_util_internal.h"
|
||||
#include "ui/gtk/gtk_util.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
@@ -220,6 +221,19 @@ bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
|
||||
return XDGUtil(argv, base::FilePath(), true, platform_util::OpenCallback());
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
if (!MoveItemToTrash(full_path, false)) {
|
||||
// TODO(nornagon): at least include the exit code?
|
||||
*error = "Failed to move item to trash";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void Beep() {
|
||||
// echo '\a' > /dev/console
|
||||
FILE* fp = fopen("/dev/console", "a");
|
||||
|
||||
@@ -117,10 +117,13 @@ void OpenExternal(const GURL& url,
|
||||
});
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
|
||||
bool MoveItemToTrashWithError(const base::FilePath& full_path,
|
||||
bool delete_on_fail,
|
||||
std::string* error) {
|
||||
NSString* path_string = base::SysUTF8ToNSString(full_path.value());
|
||||
if (!path_string) {
|
||||
LOG(WARNING) << "Invalid file path " << full_path.value();
|
||||
*error = "Invalid file path: " + full_path.value();
|
||||
LOG(WARNING) << *error;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,14 +145,27 @@ bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
|
||||
}
|
||||
|
||||
if (!did_trash) {
|
||||
*error = base::SysNSStringToUTF8([err localizedDescription]);
|
||||
LOG(WARNING) << "NSWorkspace failed to move file " << full_path.value()
|
||||
<< " to trash: "
|
||||
<< base::SysNSStringToUTF8([err localizedDescription]);
|
||||
<< " to trash: " << *error;
|
||||
}
|
||||
|
||||
return did_trash;
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
std::string error; // ignored
|
||||
return MoveItemToTrashWithError(path, delete_on_fail, &error);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
return MoveItemToTrashWithError(full_path, false, error);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void Beep() {
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
@@ -347,15 +347,15 @@ void OpenExternal(const GURL& url,
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
base::win::ScopedCOMInitializer com_initializer;
|
||||
if (!com_initializer.Succeeded())
|
||||
return false;
|
||||
|
||||
bool MoveItemToTrashWithError(const base::FilePath& path,
|
||||
bool delete_on_fail,
|
||||
std::string* error) {
|
||||
Microsoft::WRL::ComPtr<IFileOperation> pfo;
|
||||
if (FAILED(::CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL,
|
||||
IID_PPV_ARGS(&pfo))))
|
||||
IID_PPV_ARGS(&pfo)))) {
|
||||
*error = "Failed to create FileOperation instance";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Elevation prompt enabled for UAC protected files. This overrides the
|
||||
// SILENT, NO_UI and NOERRORUI flags.
|
||||
@@ -365,38 +365,77 @@ bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
// ALLOWUNDO in favor of ADDUNDORECORD.
|
||||
if (FAILED(pfo->SetOperationFlags(
|
||||
FOF_NO_UI | FOFX_ADDUNDORECORD | FOF_NOERRORUI | FOF_SILENT |
|
||||
FOFX_SHOWELEVATIONPROMPT | FOFX_RECYCLEONDELETE)))
|
||||
FOFX_SHOWELEVATIONPROMPT | FOFX_RECYCLEONDELETE))) {
|
||||
*error = "Failed to set operation flags";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// For Windows 7 and Vista, RecycleOnDelete is the default behavior.
|
||||
if (FAILED(pfo->SetOperationFlags(FOF_NO_UI | FOF_ALLOWUNDO |
|
||||
FOF_NOERRORUI | FOF_SILENT |
|
||||
FOFX_SHOWELEVATIONPROMPT)))
|
||||
FOFX_SHOWELEVATIONPROMPT))) {
|
||||
*error = "Failed to set operation flags";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an IShellItem from the supplied source path.
|
||||
Microsoft::WRL::ComPtr<IShellItem> delete_item;
|
||||
if (FAILED(SHCreateItemFromParsingName(
|
||||
path.value().c_str(), NULL,
|
||||
IID_PPV_ARGS(delete_item.GetAddressOf()))))
|
||||
IID_PPV_ARGS(delete_item.GetAddressOf())))) {
|
||||
*error = "Failed to parse path";
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IFileOperationProgressSink> delete_sink(
|
||||
new DeleteFileProgressSink);
|
||||
if (!delete_sink)
|
||||
if (!delete_sink) {
|
||||
*error = "Failed to create delete sink";
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL pfAnyOperationsAborted;
|
||||
|
||||
// Processes the queued command DeleteItem. This will trigger
|
||||
// the DeleteFileProgressSink to check for Recycle Bin.
|
||||
return SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get())) &&
|
||||
SUCCEEDED(pfo->PerformOperations()) &&
|
||||
SUCCEEDED(pfo->GetAnyOperationsAborted(&pfAnyOperationsAborted)) &&
|
||||
!pfAnyOperationsAborted;
|
||||
if (!SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get()))) {
|
||||
*error = "Failed to enqueue DeleteItem command";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(pfo->PerformOperations())) {
|
||||
*error = "Failed to perform delete operation";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(pfo->GetAnyOperationsAborted(&pfAnyOperationsAborted))) {
|
||||
*error = "Failed to check operation status";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pfAnyOperationsAborted) {
|
||||
*error = "Operation was aborted";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
std::string error; // ignored
|
||||
base::win::ScopedCOMInitializer com_initializer;
|
||||
return MoveItemToTrashWithError(path, delete_on_fail, &error);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
return MoveItemToTrashWithError(full_path, false, error);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
bool GetFolderPath(int key, base::FilePath* result) {
|
||||
wchar_t system_buffer[MAX_PATH];
|
||||
|
||||
|
||||
@@ -1437,6 +1437,16 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('dock.hide', () => {
|
||||
it('should not throw', () => {
|
||||
app.dock.hide();
|
||||
expect(app.dock.isVisible()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
// Note that dock.show tests should run after dock.hide tests, to work
|
||||
// around a bug of macOS.
|
||||
// See https://github.com/electron/electron/pull/25269 for more.
|
||||
describe('dock.show', () => {
|
||||
it('should not throw', () => {
|
||||
return app.dock.show().then(() => {
|
||||
@@ -1452,13 +1462,6 @@ describe('app module', () => {
|
||||
await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dock.hide', () => {
|
||||
it('should not throw', () => {
|
||||
app.dock.hide();
|
||||
expect(app.dock.isVisible()).to.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('whenReady', () => {
|
||||
|
||||
@@ -232,6 +232,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
@@ -245,7 +246,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.prod).to.equal('Electron');
|
||||
expect(crash._productName).to.equal('remote-control');
|
||||
expect(crash._productName).to.equal('electron-test-remote-control');
|
||||
expect(crash.process_type).to.equal('renderer');
|
||||
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
|
||||
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
|
||||
@@ -271,6 +272,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: { longParam: 'a'.repeat(100000) }
|
||||
});
|
||||
@@ -287,6 +289,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
remotely((port: number, kKeyLengthMax: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: {
|
||||
['a'.repeat(kKeyLengthMax + 10)]: 'value',
|
||||
@@ -399,6 +402,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
|
||||
@@ -112,4 +112,19 @@ describe('shell module', () => {
|
||||
expect(await fs.pathExists(tempPath)).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('shell.trashItem()', () => {
|
||||
it('moves an item to the trash', async () => {
|
||||
const dir = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
|
||||
const filename = path.join(dir, 'temp-to-be-deleted');
|
||||
await fs.writeFile(filename, 'dummy-contents');
|
||||
await shell.trashItem(filename);
|
||||
expect(fs.existsSync(filename)).to.be.false();
|
||||
});
|
||||
|
||||
it('throws when called with a nonexistent path', async () => {
|
||||
const filename = path.join(app.getPath('temp'), 'does-not-exist');
|
||||
await expect(shell.trashItem(filename)).to.eventually.be.rejected();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,26 @@ describe('chrome extensions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not crash when using chrome.management', async () => {
|
||||
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
|
||||
w.loadURL('about:blank');
|
||||
|
||||
await emittedOnce(w.webContents, 'dom-ready');
|
||||
await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
|
||||
const args: any = await emittedOnce(app, 'web-contents-created');
|
||||
const wc: Electron.WebContents = args[1];
|
||||
await expect(wc.executeJavaScript(`
|
||||
(() => {
|
||||
return new Promise((resolve) => {
|
||||
chrome.management.getSelf((info) => {
|
||||
resolve(info);
|
||||
});
|
||||
})
|
||||
})();
|
||||
`)).to.eventually.have.property('id');
|
||||
});
|
||||
|
||||
it('can open WebSQLDatabase in a background page', async () => {
|
||||
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "ipc-main-listeners",
|
||||
"name": "electron-test-ipc-main-listeners",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "net-log",
|
||||
"name": "electron-test-net-log",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ const addGlobalParam = app.commandLine.getSwitchValue('add-global-param')?.split
|
||||
crashReporter.start({
|
||||
productName: 'Zombies',
|
||||
companyName: 'Umbrella Corporation',
|
||||
compress: false,
|
||||
uploadToServer,
|
||||
submitURL: url,
|
||||
ignoreSystemCrashHandler: true,
|
||||
|
||||
@@ -2,6 +2,7 @@ if (process.platform === 'linux') {
|
||||
process.crashReporter.start({
|
||||
submitURL: process.argv[2],
|
||||
productName: 'Zombies',
|
||||
compress: false,
|
||||
globalExtra: {
|
||||
_version: process.argv[3]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "crash",
|
||||
"name": "electron-test-crash",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "remote-control",
|
||||
"name": "electron-test-remote-control",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "initial-app",
|
||||
"name": "electron-test-initial-app",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "initial-app",
|
||||
"name": "electron-test-initial-app",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "initial-app",
|
||||
"name": "electron-test-initial-app",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-21497-comments
vendored
Normal file
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-21497-comments
vendored
Normal file
File diff suppressed because one or more lines are too long
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-22770-comments
vendored
Normal file
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-22770-comments
vendored
Normal file
File diff suppressed because one or more lines are too long
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-25052-comments
vendored
Normal file
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-25052-comments
vendored
Normal file
File diff suppressed because one or more lines are too long
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-25216-comments
vendored
Normal file
1
spec-main/fixtures/release-notes/cache/electron-electron-issue-25216-comments
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"status":200,"url":"https://api.github.com/repos/electron/electron/issues/25216/comments?per_page=100","headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset","cache-control":"private, max-age=60, s-maxage=60","connection":"close","content-encoding":"gzip","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Wed, 02 Sep 2020 15:55:20 GMT","etag":"W/\"dc98adeb828ec1f60e0be31a73a31f30\"","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"GitHub.com","status":"200 OK","strict-transport-security":"max-age=31536000; includeSubdomains; preload","transfer-encoding":"chunked","vary":"Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With","x-accepted-oauth-scopes":"","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-media-type":"github.v3; format=json","x-github-request-id":"E876:6741:2819ABF:5CAD2C6:5F4FC068","x-oauth-scopes":"repo","x-ratelimit-limit":"5000","x-ratelimit-remaining":"4943","x-ratelimit-reset":"1599063118","x-xss-protection":"1; mode=block"},"data":[{"url":"https://api.github.com/repos/electron/electron/issues/comments/684017257","html_url":"https://github.com/electron/electron/pull/25216#issuecomment-684017257","issue_url":"https://api.github.com/repos/electron/electron/issues/25216","id":684017257,"node_id":"MDEyOklzc3VlQ29tbWVudDY4NDAxNzI1Nw==","user":{"login":"release-clerk[bot]","id":42386326,"node_id":"MDM6Qm90NDIzODYzMjY=","avatar_url":"https://avatars0.githubusercontent.com/in/16104?v=4","gravatar_id":"","url":"https://api.github.com/users/release-clerk%5Bbot%5D","html_url":"https://github.com/apps/release-clerk","followers_url":"https://api.github.com/users/release-clerk%5Bbot%5D/followers","following_url":"https://api.github.com/users/release-clerk%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/release-clerk%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/release-clerk%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/release-clerk%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/release-clerk%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/release-clerk%5Bbot%5D/repos","events_url":"https://api.github.com/users/release-clerk%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/release-clerk%5Bbot%5D/received_events","type":"Bot","site_admin":false},"created_at":"2020-08-31T20:22:50Z","updated_at":"2020-08-31T20:22:50Z","author_association":"NONE","body":"**Release Notes Persisted**\n\n> * Fixes the following issues for frameless when maximized on Windows:\r\n> * fix unreachable task bar when auto hidden with position top\r\n> * fix 1px extending to secondary monitor\r\n> * fix 1px overflowing into taskbar at certain resolutions\r\n> * fix white line on top of window under 4k resolutions","performed_via_github_app":null}]}
|
||||
1
spec-main/fixtures/release-notes/cache/electron-electron-pull-25052
vendored
Normal file
1
spec-main/fixtures/release-notes/cache/electron-electron-pull-25052
vendored
Normal file
File diff suppressed because one or more lines are too long
1
spec-main/fixtures/release-notes/cache/electron-electron-pull-25216
vendored
Normal file
1
spec-main/fixtures/release-notes/cache/electron-electron-pull-25216
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -106,6 +106,9 @@ describe('release notes', () => {
|
||||
const newTropFix = new Commit('a6ff42c190cb5caf8f3e217748e49183a951491b', 'fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22750)');
|
||||
const oldTropFix = new Commit('8751f485c5a6c8c78990bfd55a4350700f81f8cd', 'fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22749)');
|
||||
|
||||
// a PR that has unusual note formatting
|
||||
const sublist = new Commit('61dc1c88fd34a3e8fff80c80ed79d0455970e610', 'fix: client area inset calculation when maximized for framless windows (#25052) (#25216)');
|
||||
|
||||
before(() => {
|
||||
// location of relase-notes' octokit reply cache
|
||||
const fixtureDir = path.resolve(__dirname, 'fixtures', 'release-notes');
|
||||
@@ -151,6 +154,34 @@ describe('release notes', () => {
|
||||
expect(results.feat[0].note).to.equal(realText);
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
it('removes redundant bullet points', async function () {
|
||||
const testCommit = sublist;
|
||||
const version = 'v10.1.1';
|
||||
|
||||
gitFake.setBranch(newBranch, [...sharedHistory, testCommit]);
|
||||
const results: any = await notes.get(oldBranch, newBranch, version);
|
||||
const rendered: any = await notes.render(results);
|
||||
|
||||
expect(rendered).to.not.include('* *');
|
||||
});
|
||||
|
||||
it('indents sublists', async function () {
|
||||
const testCommit = sublist;
|
||||
const version = 'v10.1.1';
|
||||
|
||||
gitFake.setBranch(newBranch, [...sharedHistory, testCommit]);
|
||||
const results: any = await notes.get(oldBranch, newBranch, version);
|
||||
const rendered: any = await notes.render(results);
|
||||
|
||||
expect(rendered).to.include([
|
||||
'* Fixed the following issues for frameless when maximized on Windows:',
|
||||
' * fix unreachable task bar when auto hidden with position top',
|
||||
' * fix 1px extending to secondary monitor',
|
||||
' * fix 1px overflowing into taskbar at certain resolutions',
|
||||
' * fix white line on top of window under 4k resolutions. [#25216]'].join('\n'));
|
||||
});
|
||||
});
|
||||
// test that when you feed in different semantic commit types,
|
||||
// the parser returns them in the results' correct category
|
||||
describe('semantic commit', () => {
|
||||
|
||||
2
spec/fixtures/api/app-path/package.json
vendored
2
spec/fixtures/api/app-path/package.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "app-path",
|
||||
"name": "electron-test-app-path",
|
||||
"main": "lib/index.js"
|
||||
}
|
||||
|
||||
2
spec/fixtures/api/command-line/package.json
vendored
2
spec/fixtures/api/command-line/package.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "command-line",
|
||||
"name": "electron-test-command-line",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
2
spec/fixtures/api/default-menu/package.json
vendored
2
spec/fixtures/api/default-menu/package.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "default-menu",
|
||||
"name": "electron-test-default-menu",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
2
spec/fixtures/api/locale-check/package.json
vendored
2
spec/fixtures/api/locale-check/package.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "locale-check",
|
||||
"name": "electron-test-locale-check",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "site-instance-overrides",
|
||||
"name": "electron-test-site-instance-overrides",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "window-all-closed",
|
||||
"name": "electron-test-window-all-closed",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "snapshot-items-available",
|
||||
"name": "electron-test-snapshot-items-available",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
1
typings/internal-electron.d.ts
vendored
1
typings/internal-electron.d.ts
vendored
@@ -56,6 +56,7 @@ declare namespace Electron {
|
||||
getLastWebPreferences(): Electron.WebPreferences;
|
||||
_getPreloadPaths(): string[];
|
||||
equal(other: WebContents): boolean;
|
||||
_initiallyShown: boolean;
|
||||
}
|
||||
|
||||
interface WebPreferences {
|
||||
|
||||
Reference in New Issue
Block a user