Compare commits

..

22 Commits

Author SHA1 Message Date
Electron Bot
29c1248e96 Bump v12.0.0-nightly.20200904 2020-09-04 07:31:11 -07:00
Samuel Attard
a6b9f9d8e5 fix: only focus a webContents if the window was not initially hidden (#25292) 2020-09-04 15:57:29 +09:00
Jeremy Rose
d305fe7d30 docs: breaking changes for moveItemToTrash (#25286) 2020-09-03 12:44:16 -07:00
Jeremy Rose
4dc09ea9dc feat: [crashReporter] enable compression by default (#25288) 2020-09-03 12:43:58 -07:00
Charles Kerr
4484e95fc8 chore: graceful handling of notes with sub-lists (#25279)
* chore: graceful handling of notes with sub-lists

Handle multine release notes that contain their own bullet points.

Also, if a release note begins with a bullet point, remove it because it
will confuse the markdown parser to have two bullet points.
2020-09-03 14:42:48 -05:00
Robo
03e60cce8b fix: avoid creating client_id file for empty DIR_CRASH_DUMPS (#25296) 2020-09-03 11:52:43 -07:00
Electron Bot
733d56e908 Bump v12.0.0-nightly.20200903 2020-09-03 07:32:01 -07:00
Cheng Zhao
43485b8705 fix: multiple dock icons when calling dock.show/hide (#25269)
* fix: mulitple dock icons when calling dock.show/hide

* test: run dock.show tests after dock.hide tests
2020-09-03 20:46:24 +09:00
Jeremy Rose
bda6378685 chore: remove "no breaking changes" warning in PR template (#25285) 2020-09-02 11:56:10 -07:00
Jeremy Rose
1b6534b326 feat: add shell.trashItem() to replace shell.moveItemToTrash() (#25114) 2020-09-02 10:32:33 -07:00
Shelley Vohr
e9e7eee25e fix: provide asynchronous cleanup hooks in n-api (#25135) 2020-09-02 10:22:05 -07:00
Electron Bot
7e698df8f3 Bump v12.0.0-nightly.20200902 2020-09-02 07:32:22 -07:00
morgan-sam
e0611d0946 docs: setZoomLevel chromium zoom policy (#25105) 2020-09-02 10:06:58 +09:00
Samuel Attard
184e72fafd revert: "fix: reply notifs sometimes destroyed too early" (#25247)
* Revert "fix: reply notifs sometimes destroyed too early (#25086)"

This reverts commit bea6c9e4e1.

* nothing
2020-09-02 10:02:47 +09:00
Eryk Rakowski
3745b76da8 feat(extensions): add support for some chrome.management APIs (#25098)
* fix: initialize management policy

* fix(extensions): crash when using chrome.management

* test: add tests

* docs: add a note about chrome.management

* fix: lint errors

* fix: lint errors

* fix: remove favicon_service include

* fix: add missing management permission

* docs: more supported apis

* fix: extensions.md line endings
2020-09-02 09:59:56 +09:00
Jeremy Rose
bf7dbff858 docs: note remote deprecation in docs/api/remote (#24941) 2020-08-31 11:27:00 -07:00
John Kleinschmidt
7c10f86c6e ci: cleanup up test app directories (#25145)
* ci: cleanup up test app directories

* ci: use electron prefix for a testing apps so that the can be cleaned up

* Revert "ci: cleanup up test app directories"

This reverts commit a47daba812.

* fixup test due to app name change
2020-08-31 14:07:12 -04:00
Electron Bot
9803e4d526 Bump v12.0.0-nightly.20200831 2020-08-31 07:32:25 -07:00
Robo
068b464e13 fix: client area inset calculation when maximized for framless windows (#25052)
* adopt per monitor scale factor

* fix: client area inset calculation when maximized

* address review feedback

* pass correct glass insets to GetDwmFrameInsetsInPixels

* remove unused code

* Windows 8 and 10 use the same DWM frame calculation

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 16:55:50 +09:00
Milan Burda
a09694ae85 chore: replace CRLF with LF (#25161)
* chore: replace CRLF with LF

* chore: replace CRLF with LF in docs and tests

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 16:52:11 +09:00
Cheng Zhao
e06a1c2746 docs: fix supported platforms of powerMonitor (#25149) 2020-08-31 16:46:10 +09:00
Shelley Vohr
c5320b3951 fix: save dialog extensions should be deterministic (#25164) 2020-08-28 09:40:47 -07:00
78 changed files with 1169 additions and 91 deletions

View File

@@ -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

View File

@@ -1 +1 @@
12.0.0-nightly.20200828
12.0.0-nightly.20200904

View File

@@ -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

View File

@@ -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`

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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",

View File

@@ -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) => {

View File

@@ -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');

View File

@@ -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;

View File

@@ -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": {

View File

@@ -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

View 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_

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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() {

View File

@@ -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_;

View File

@@ -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 {

View File

@@ -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_;

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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();

View File

@@ -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";

View File

@@ -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

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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"
]
}
]
}

View File

@@ -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);

View 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

View File

@@ -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)

View 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_

View File

@@ -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");

View File

@@ -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();
}

View File

@@ -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];

View File

@@ -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', () => {

View File

@@ -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]);

View File

@@ -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();
});
});
});

View File

@@ -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 } });

View File

@@ -1,4 +1,4 @@
{
"name": "ipc-main-listeners",
"name": "electron-test-ipc-main-listeners",
"main": "main.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "net-log",
"name": "electron-test-net-log",
"main": "main.js"
}

View File

@@ -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,

View File

@@ -2,6 +2,7 @@ if (process.platform === 'linux') {
process.crashReporter.start({
submitURL: process.argv[2],
productName: 'Zombies',
compress: false,
globalExtra: {
_version: process.argv[3]
}

View File

@@ -1,4 +1,4 @@
{
"name": "crash",
"name": "electron-test-crash",
"main": "main.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "remote-control",
"name": "electron-test-remote-control",
"main": "main.js"
}

View File

@@ -1,5 +1,5 @@
{
"name": "initial-app",
"name": "electron-test-initial-app",
"version": "1.0.0",
"main": "./index.js"
}

View File

@@ -1,5 +1,5 @@
{
"name": "initial-app",
"name": "electron-test-initial-app",
"version": "1.0.0",
"main": "./index.js"
}

View File

@@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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', () => {

View File

@@ -1,4 +1,4 @@
{
"name": "app-path",
"name": "electron-test-app-path",
"main": "lib/index.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "command-line",
"name": "electron-test-command-line",
"main": "main.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "default-menu",
"name": "electron-test-default-menu",
"main": "main.js"
}

View File

@@ -1,5 +1,5 @@
{
"name": "locale-check",
"name": "electron-test-locale-check",
"main": "main.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "site-instance-overrides",
"name": "electron-test-site-instance-overrides",
"main": "main.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "window-all-closed",
"name": "electron-test-window-all-closed",
"main": "main.js"
}

View File

@@ -1,4 +1,4 @@
{
"name": "snapshot-items-available",
"name": "electron-test-snapshot-items-available",
"main": "main.js"
}

View File

@@ -56,6 +56,7 @@ declare namespace Electron {
getLastWebPreferences(): Electron.WebPreferences;
_getPreloadPaths(): string[];
equal(other: WebContents): boolean;
_initiallyShown: boolean;
}
interface WebPreferences {