Compare commits

...

12 Commits

Author SHA1 Message Date
Electron Bot
24d6743d57 Bump v9.0.0-nightly.20191116 2019-11-16 07:32:27 -08:00
Robo
fbc3bb872b fix: incorrect size of windows on differently scaled monitors (#21100)
* Revert "fix: handle WM_GETMINMAXINFO instead of letting chromium do it (#19928)"

This reverts commit 27ce6a9cd3.

* fix: don't reset the width and height when correcting window placement
2019-11-15 09:28:11 -08:00
Electron Bot
46c12308cd Bump v9.0.0-nightly.20191115 2019-11-15 07:32:27 -08:00
Jeremy Apthorp
26ecf63ab4 test: remove a bunch of usage of the remote module (#21119) 2019-11-14 14:09:03 -08:00
Jeremy Apthorp
4f1536479e fix: implement 'login' event for net.ClientRequest (#21096) 2019-11-14 10:01:18 -08:00
Electron Bot
878ab916d2 Bump v9.0.0-nightly.20191114 2019-11-14 07:34:02 -08:00
Cheng Zhao
b02a20e4dc fix: webRequest should be able to modify CORS headers (#21099)
* fix: always use extraHeaders mode

* fix: clear pending callbacks

* fix: do not use "extraHeaders" for net module

* test: webRequest should be able to modify CROS headers

* chore: CROS => CORS

Co-Authored-By: Milan Burda <milan.burda@gmail.com>

* chore: CROS => CORS

Co-Authored-By: Milan Burda <milan.burda@gmail.com>
2019-11-14 14:51:24 +09:00
Shelley Vohr
af1e8a347e chore: remove unused promisify code (#21114) 2019-11-14 14:50:50 +09:00
Shelley Vohr
457b7bf24f chore: remove outdated v8 compat patch (#21115)
This was only intended to last the duration of Node.js v11, and we are not on v12 so this should no longer be necessary
2019-11-14 14:50:25 +09:00
Milan Burda
97e2569f02 feat: add 'screen' to systemPreferences.getMediaAccessStatus() (#20764)
* feat: add 'screen' to systemPreferences.getMediaAccessStatus()

* Update docs/api/system-preferences.md
2019-11-13 16:47:51 -05:00
Erick Zhao
ba85d4c3bb docs: document webkitdirectory breaking change (#20934)
* chore: empty folder structure

* chore: PR template

* docs: add `webkitdirectory` change in Electron 7

* linguist?

* link

* Delete hacktoberfest.md
2019-11-13 14:11:25 -05:00
Shelley Vohr
98844c20a7 build: missing include in windows release builds (#21045) 2019-11-13 14:10:12 -05:00
61 changed files with 792 additions and 728 deletions

View File

@@ -1 +1 @@
9.0.0-nightly.20191113
9.0.0-nightly.20191116

View File

@@ -32,6 +32,8 @@ static_library("chrome") {
"//chrome/browser/icon_loader_win.cc",
"//chrome/browser/icon_manager.cc",
"//chrome/browser/icon_manager.h",
"//chrome/browser/media/webrtc/system_media_capture_permissions_mac.h",
"//chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm",
"//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc",
"//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h",
"//chrome/browser/net/proxy_config_monitor.cc",

View File

@@ -323,8 +323,8 @@ Returns:
* `port` Integer
* `realm` String
* `callback` Function
* `username` String
* `password` String
* `username` String (optional)
* `password` String (optional)
Emitted when `webContents` wants to do basic auth.
@@ -341,6 +341,10 @@ app.on('login', (event, webContents, details, authInfo, callback) => {
})
```
If `callback` is called without a username or password, the authentication
request will be cancelled and the authentication error will be returned to the
page.
### Event: 'gpu-info-update'
Emitted whenever there is a GPU info update.

View File

@@ -158,7 +158,7 @@ const idleTime = getSystemIdleTime()
### webFrame Isolated World APIs
```js
// Removed in Elecron 7.0
// Removed in Electron 7.0
webFrame.setIsolatedWorldContentSecurityPolicy(worldId, csp)
webFrame.setIsolatedWorldHumanReadableName(worldId, name)
webFrame.setIsolatedWorldSecurityOrigin(worldId, securityOrigin)
@@ -176,6 +176,39 @@ webFrame.setIsolatedWorldInfo(
This property was removed in Chromium 77, and as such is no longer available.
### `webkitdirectory` attribute for `<input type="file"/>`
The `webkitdirectory` property on HTML file inputs allows them to select folders.
Previous versions of Electron had an incorrect implementation where the `event.target.files`
of the input returned a `FileList` that returned one `File` corresponding to the selected folder.
As of Electron 7, that `FileList` is now list of all files contained within
the folder, similarly to Chrome, Firefox, and Edge
([link to MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory)).
As an illustration, take a folder with this structure:
```console
folder
├── file1
├── file2
└── file3
```
In Electron <=6, this would return a `FileList` with a `File` object for:
```console
path/to/folder
```
In Electron 7, this now returns a `FileList` with a `File` object for:
```console
/path/to/folder/file3
/path/to/folder/file2
/path/to/folder/file1
```
Note that `webkitdirectory` no longer exposes the path to the selected folder.
If you require the path to the selected folder rather than the folder contents,
see the `dialog.showOpenDialog` API ([link](https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogbrowserwindow-options)).
## Planned Breaking API Changes (6.0)
### `win.setMenu(null)`

View File

@@ -70,8 +70,8 @@ Returns:
* `port` Integer
* `realm` String
* `callback` Function
* `username` String
* `password` String
* `username` String (optional)
* `password` String (optional)
Emitted when an authenticating proxy is asking for user credentials.

View File

@@ -91,7 +91,11 @@ The `desktopCapturer` module has the following methods:
Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured.
**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher,
which can detected by [`systemPreferences.getMediaAccessStatus`].
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-macos
## Caveats

View File

@@ -1,17 +1,6 @@
## Promisification
The Electron team is currently undergoing an initiative to convert callback-based functions in Electron to return Promises. During this transition period, both the callback and Promise-based versions of these functions will work correctly, and will both be documented.
To enable deprecation warnings for these updated functions, use the [`process.enablePromiseAPIs` runtime flag](../process.md#processenablepromiseapis).
When a majority of affected functions are migrated, this flag will be enabled by default and all developers will be able to see these deprecation warnings. At that time, the callback-based versions will also be removed from documentation. This document will be continuously updated as more functions are converted.
### Candidate Functions
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
- [contents.print([options], [callback])](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#print)
### Converted Functions
The Electron team recently underwent an initiative to convert callback-based APIs to Promise-based ones. See converted functions below:
- [app.getFileIcon(path[, options], callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#getFileIcon)
- [contents.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#capturePage)

View File

@@ -82,12 +82,6 @@ A `Boolean` that controls whether or not deprecation warnings are printed to `st
Setting this to `true` will silence deprecation warnings. This property is used
instead of the `--no-deprecation` command line flag.
### `process.enablePromiseAPIs`
A `Boolean` that controls whether or not deprecation warnings are printed to `stderr` when
formerly callback-based APIs converted to Promises are invoked using callbacks. Setting this to `true`
will enable deprecation warnings.
### `process.resourcesPath` _Readonly_
A `String` representing the path to the resources directory.

View File

@@ -434,11 +434,13 @@ Returns `Boolean` - `true` if the current process is a trusted accessibility cli
### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_
* `mediaType` String - `microphone` or `camera`.
* `mediaType` String - Can be `microphone`, `camera` or `screen`.
Returns `String` - Can be `not-determined`, `granted`, `denied`, `restricted` or `unknown`.
This user consent was not required until macOS 10.14 Mojave, so this method will always return `granted` if your system is running 10.13 High Sierra or lower.
This user consent was not required on macOS 10.13 High Sierra or lower so this method will always return `granted`.
macOS 10.14 Mojave or higher requires consent for `microphone` and `camera` access.
macOS 10.15 Catalina or higher requires consent for `screen` access.
### `systemPreferences.askForMediaAccess(mediaType)` _macOS_

View File

@@ -463,8 +463,8 @@ Returns:
* `port` Integer
* `realm` String
* `callback` Function
* `username` String
* `password` String
* `username` String (optional)
* `password` String (optional)
Emitted when `webContents` wants to do basic auth.

View File

@@ -247,22 +247,11 @@ class ClientRequest extends EventEmitter {
})
urlRequest.on('login', (event, authInfo, callback) => {
this.emit('login', authInfo, (username, password) => {
// If null or undefined username/password, force to empty string.
if (username === null || username === undefined) {
username = ''
}
if (typeof username !== 'string') {
throw new Error('username must be a string')
}
if (password === null || password === undefined) {
password = ''
}
if (typeof password !== 'string') {
throw new Error('password must be a string')
}
callback(username, password)
})
const handled = this.emit('login', authInfo, callback)
if (!handled) {
// If there were no listeners, cancel the authentication request.
callback()
}
})
if (callback) {

View File

@@ -116,64 +116,6 @@ const deprecate: ElectronInternal.DeprecationUtil = {
})
},
// deprecate a callback-based function in favor of one returning a Promise
promisify: <T extends (...args: any[]) => any>(fn: T): T => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
return function (this: any, ...params: any[]) {
let cb: Function | undefined
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
}
const promise = fn.apply(this, params)
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
return promise
.then((res: any) => {
process.nextTick(() => {
cb!.length === 2 ? cb!(null, res) : cb!(res)
})
return res
}, (err: Error) => {
process.nextTick(() => {
cb!.length === 2 ? cb!(err) : cb!()
})
throw err
})
} as T
},
// convertPromiseValue: Temporarily disabled until it's used
// deprecate a callback-based function in favor of one returning a Promise
promisifyMultiArg: <T extends (...args: any[]) => any>(fn: T /* convertPromiseValue: (v: any) => any */): T => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
return function (this: any, ...params) {
let cb: Function | undefined
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
}
const promise = fn.apply(this, params)
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
return promise
.then((res: any) => {
process.nextTick(() => {
// eslint-disable-next-line standard/no-callback-literal
cb!.length > 2 ? cb!(null, ...res) : cb!(...res)
})
}, (err: Error) => {
process.nextTick(() => cb!(err))
})
} as T
},
// change the name of a property
renameProperty: (o, oldName, newName) => {
const warn = warnOnce(oldName, newName)

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "9.0.0-nightly.20191113",
"version": "9.0.0-nightly.20191116",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -85,3 +85,5 @@ feat_unset_window_aspect_ratio_on_linux.patch
fix_ambiguous_reference_to_data.patch
backport_fix_msstl_compat_in_ui_events.patch
build_win_fix_msstl_compatibility_for_pdf.patch
fix_missing_algorithm_include.patch
add_trustedauthclient_to_urlloaderfactory.patch

View File

@@ -0,0 +1,158 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Tue, 12 Nov 2019 11:50:16 -0800
Subject: add TrustedAuthClient to URLLoaderFactory
This allows intercepting authentication requests for the 'net' module.
Without this, the 'login' event for electron.net.ClientRequest can't be
implemented, because the existing path checks for the presence of a
WebContents, and cancels the authentication if there's no WebContents
available, which there isn't in the case of the 'net' module.
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 6b14d8354375377526e141ee499a7583be3f22b0..eeb9e19c0ecdf4631e596e7c0927693f2239f293 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -181,6 +181,25 @@ interface TrustedURLLoaderHeaderClient {
pending_receiver<TrustedHeaderClient> header_client);
};
+interface TrustedAuthClient {
+ OnAuthRequired(
+ mojo_base.mojom.UnguessableToken? window_id,
+ uint32 process_id,
+ uint32 routing_id,
+ uint32 request_id,
+ url.mojom.Url url,
+ bool first_auth_attempt,
+ AuthChallengeInfo auth_info,
+ URLResponseHead? head,
+ pending_remote<AuthChallengeResponder> auth_challenge_responder);
+};
+interface TrustedURLLoaderAuthClient {
+ // When a new URLLoader is created, this will be called to pass a
+ // corresponding |auth_client|.
+ OnLoaderCreated(int32 request_id,
+ pending_receiver<TrustedAuthClient> auth_client);
+};
+
interface CertVerifierClient {
Verify(
int32 default_error,
@@ -559,6 +578,8 @@ struct URLLoaderFactoryParams {
// impact because of the extra process hops, so use should be minimized.
pending_remote<TrustedURLLoaderHeaderClient>? header_client;
+ pending_remote<TrustedURLLoaderAuthClient>? auth_client;
+
// If non-empty array is given, |factory_bound_allow_patterns| is used for
// CORS checks in addition to the per-context allow patterns that is managed
// via NetworkContext interface. This still respects the per-context block
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index d4e13ffaed76847b00cf98b248ba17ad70a9884c..33ab3ea9c60e097d8525f1066f3890a5bccd754a 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -335,6 +335,7 @@ URLLoader::URLLoader(
base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator,
mojom::TrustedURLLoaderHeaderClient* url_loader_header_client,
+ mojom::TrustedURLLoaderAuthClient* url_loader_auth_client,
mojom::OriginPolicyManager* origin_policy_manager)
: url_request_context_(url_request_context),
network_service_client_(network_service_client),
@@ -391,6 +392,11 @@ URLLoader::URLLoader(
header_client_.set_disconnect_handler(
base::BindOnce(&URLLoader::OnConnectionError, base::Unretained(this)));
}
+ if (url_loader_auth_client) {
+ url_loader_auth_client->OnLoaderCreated(request_id_, auth_client_.BindNewPipeAndPassReceiver());
+ auth_client_.set_disconnect_handler(
+ base::BindOnce(&URLLoader::OnConnectionError, base::Unretained(this)));
+ }
if (want_raw_headers_) {
options_ |= mojom::kURLLoadOptionSendSSLInfoWithResponse |
mojom::kURLLoadOptionSendSSLInfoForCertificateError;
@@ -818,7 +824,7 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request,
void URLLoader::OnAuthRequired(net::URLRequest* url_request,
const net::AuthChallengeInfo& auth_info) {
- if (!network_context_client_) {
+ if (!network_context_client_ && !auth_client_) {
OnAuthCredentials(base::nullopt);
return;
}
@@ -834,10 +840,18 @@ void URLLoader::OnAuthRequired(net::URLRequest* url_request,
if (url_request->response_headers())
head.headers = url_request->response_headers();
head.auth_challenge_info = auth_info;
- network_context_client_->OnAuthRequired(
- fetch_window_id_, factory_params_->process_id, render_frame_id_,
- request_id_, url_request_->url(), first_auth_attempt_, auth_info, head,
- auth_challenge_responder_receiver_.BindNewPipeAndPassRemote());
+
+ if (auth_client_) {
+ auth_client_->OnAuthRequired(
+ fetch_window_id_, factory_params_->process_id, render_frame_id_,
+ request_id_, url_request_->url(), first_auth_attempt_, auth_info, head,
+ auth_challenge_responder_receiver_.BindNewPipeAndPassRemote());
+ } else {
+ network_context_client_->OnAuthRequired(
+ fetch_window_id_, factory_params_->process_id, render_frame_id_,
+ request_id_, url_request_->url(), first_auth_attempt_, auth_info, head,
+ auth_challenge_responder_receiver_.BindNewPipeAndPassRemote());
+ }
auth_challenge_responder_receiver_.set_disconnect_handler(
base::BindOnce(&URLLoader::DeleteSelf, base::Unretained(this)));
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 0a47148a52a46f8a6f12f503731623f87e15b173..db8ca018c7e99a1a1acea156b4d49a755b93cc09 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -85,6 +85,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator,
mojom::TrustedURLLoaderHeaderClient* url_loader_header_client,
+ mojom::TrustedURLLoaderAuthClient* url_loader_auth_client,
mojom::OriginPolicyManager* origin_policy_manager);
~URLLoader() override;
@@ -362,6 +363,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
base::Optional<base::UnguessableToken> fetch_window_id_;
mojo::Remote<mojom::TrustedHeaderClient> header_client_;
+ mojo::Remote<mojom::TrustedAuthClient> auth_client_;
std::unique_ptr<FileOpenerForUpload> file_opener_for_upload_;
diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc
index 7145e0e96550d554bb1df85bd79818ec9a45f7b1..53225eb1b0b7f1aa2498cecc8222f9f897ac364f 100644
--- a/services/network/url_loader_factory.cc
+++ b/services/network/url_loader_factory.cc
@@ -65,6 +65,7 @@ URLLoaderFactory::URLLoaderFactory(
params_(std::move(params)),
resource_scheduler_client_(std::move(resource_scheduler_client)),
header_client_(std::move(params_->header_client)),
+ auth_client_(std::move(params_->auth_client)),
cors_url_loader_factory_(cors_url_loader_factory) {
DCHECK(context);
DCHECK_NE(mojom::kInvalidProcessId, params_->process_id);
@@ -209,6 +210,7 @@ void URLLoaderFactory::CreateLoaderAndStart(
resource_scheduler_client_, std::move(keepalive_statistics_recorder),
std::move(network_usage_accumulator),
header_client_.is_bound() ? header_client_.get() : nullptr,
+ auth_client_.is_bound() ? auth_client_.get() : nullptr,
context_->origin_policy_manager());
cors_url_loader_factory_->OnLoaderCreated(std::move(loader));
}
diff --git a/services/network/url_loader_factory.h b/services/network/url_loader_factory.h
index 7b143aa49be833ddf05b7b99bea19ee0b674b79c..6d1fbca87e3827c953fdac2cfb96806114d8aea9 100644
--- a/services/network/url_loader_factory.h
+++ b/services/network/url_loader_factory.h
@@ -71,6 +71,7 @@ class URLLoaderFactory : public mojom::URLLoaderFactory {
mojom::URLLoaderFactoryParamsPtr params_;
scoped_refptr<ResourceSchedulerClient> resource_scheduler_client_;
mojo::Remote<mojom::TrustedURLLoaderHeaderClient> header_client_;
+ mojo::Remote<mojom::TrustedURLLoaderAuthClient> auth_client_;
// |cors_url_loader_factory_| owns this.
cors::CorsURLLoaderFactory* cors_url_loader_factory_;

View File

@@ -7,7 +7,7 @@ Electron has exposed methods to allow setting the aspect ratio
for a given window, and as a part of that needed to allow users to
reset the aspect ratio. On macOS, we can do this with existing APIs,
but on Linux this was not possible using currently exposed APIs without
this patch.
this patch.
Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/1895789.

View File

@@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Thu, 7 Nov 2019 16:49:03 -0800
Subject: fix: missing algorithm include
This file had an include-what-you-use issue leading to release build
failures, as it was unable to find max in the std namespace.
Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/1904823.
diff --git a/media/base/byte_queue.cc b/media/base/byte_queue.cc
index 245fafa668568fd308e5a2806dafc1c5f0bf3dbd..f9cd0c214ac6210d4332bad47b56ef8130be67a2 100644
--- a/media/base/byte_queue.cc
+++ b/media/base/byte_queue.cc
@@ -4,6 +4,8 @@
#include "media/base/byte_queue.h"
+#include <algorithm>
+
#include "base/logging.h"
#include "base/numerics/checked_math.h"
diff --git a/third_party/blink/public/platform/web_time_range.h b/third_party/blink/public/platform/web_time_range.h
index 2c17f7c8ce58f1255994e9e6fd74f70fa1a22b1c..4b0a59bf3308b49d2c4ffd86120759417094cb21 100644
--- a/third_party/blink/public/platform/web_time_range.h
+++ b/third_party/blink/public/platform/web_time_range.h
@@ -31,6 +31,8 @@
#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_TIME_RANGE_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_TIME_RANGE_H_
+#include <algorithm>
+
#include "third_party/blink/public/platform/web_vector.h"
namespace blink {
diff --git a/third_party/blink/renderer/core/layout/min_max_size.h b/third_party/blink/renderer/core/layout/min_max_size.h
index c3fdbde6e4bf585351b3fc8d0ce2bd2673a82b5e..4233e6c79a1d67dd6c1ca33929d0beedbf9a1116 100644
--- a/third_party/blink/renderer/core/layout/min_max_size.h
+++ b/third_party/blink/renderer/core/layout/min_max_size.h
@@ -5,6 +5,8 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZE_H_
+#include <algorithm>
+
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h b/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h
index 50f699428acf8251f14411dc30caa750cfac88bf..8236afa14ee99b5f61e6c785721abf62299cbaed 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h
@@ -5,6 +5,8 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GEOMETRY_NG_MARGIN_STRUT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GEOMETRY_NG_MARGIN_STRUT_H_
+#include <algorithm>
+
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
diff --git a/third_party/blink/renderer/core/style/style_difference.h b/third_party/blink/renderer/core/style/style_difference.h
index 91292961f1f02e53c58653f72297ea05d78fc07b..517fbb8f5a7aa549373e3806aaec4b3d1f0ec13f 100644
--- a/third_party/blink/renderer/core/style/style_difference.h
+++ b/third_party/blink/renderer/core/style/style_difference.h
@@ -5,7 +5,9 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_STYLE_DIFFERENCE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_STYLE_DIFFERENCE_H_
+#include <algorithm>
#include <iosfwd>
+
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"

View File

@@ -1,7 +1,6 @@
add_realloc.patch
build_gn.patch
expose_mksnapshot.patch
deps_provide_more_v8_backwards_compatibility.patch
dcheck.patch
export_symbols_needed_for_windows_build.patch
workaround_an_undefined_symbol_error.patch

View File

@@ -1,347 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Anna Henningsen <anna@addaleax.net>
Date: Fri, 28 Sep 2018 22:43:38 -0400
Subject: deps: provide more V8 backwards compatibility
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add back a number deprecated APIs, using shims that
should work well enough at least for the duration of Node 11
and do not come with significant maintenance overhead.
Refs: https://github.com/nodejs/node/issues/23122
PR-URL: https://github.com/nodejs/node/pull/23158
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
diff --git a/include/v8.h b/include/v8.h
index fe86e1a17f2cad844ef92f45a8da6e7fee4eba49..3450677471b7276b631ea12a4cd39514c8105b46 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1330,6 +1330,10 @@ class V8_EXPORT PrimitiveArray {
public:
static Local<PrimitiveArray> New(Isolate* isolate, int length);
int Length() const;
+ V8_DEPRECATED("Use Isolate* version")
+ void Set(int index, Local<Primitive> item);
+ V8_DEPRECATED("Use Isolate* version")
+ Local<Primitive> Get(int index);
void Set(Isolate* isolate, int index, Local<Primitive> item);
Local<Primitive> Get(Isolate* isolate, int index);
};
@@ -2078,6 +2082,8 @@ class V8_EXPORT StackTrace {
/**
* Returns a StackFrame at a particular index.
*/
+ V8_DEPRECATED("Use Isolate version")
+ Local<StackFrame> GetFrame(uint32_t index) const;
Local<StackFrame> GetFrame(Isolate* isolate, uint32_t index) const;
/**
@@ -2774,6 +2780,15 @@ class V8_EXPORT Value : public Data {
Local<Boolean> ToBoolean(Isolate* isolate) const;
+ V8_DEPRECATED("Use maybe version")
+ inline Local<Boolean> ToBoolean() const;
+ V8_DEPRECATED("Use maybe version")
+ inline Local<String> ToString() const;
+ V8_DEPRECATED("Use maybe version")
+ inline Local<Object> ToObject() const;
+ V8_DEPRECATED("Use maybe version")
+ inline Local<Integer> ToInteger() const;
+
/**
* Attempts to convert a string to an array index.
* Returns an empty handle if the conversion fails.
@@ -2790,7 +2805,14 @@ class V8_EXPORT Value : public Data {
Local<Context> context) const;
V8_WARN_UNUSED_RESULT Maybe<int32_t> Int32Value(Local<Context> context) const;
+ V8_DEPRECATED("Use maybe version") bool BooleanValue() const;
+ V8_DEPRECATED("Use maybe version") double NumberValue() const;
+ V8_DEPRECATED("Use maybe version") int64_t IntegerValue() const;
+ V8_DEPRECATED("Use maybe version") uint32_t Uint32Value() const;
+ V8_DEPRECATED("Use maybe version") int32_t Int32Value() const;
+
/** JS == */
+ V8_DEPRECATED("Use maybe version") bool Equals(Local<Value> that) const;
V8_WARN_UNUSED_RESULT Maybe<bool> Equals(Local<Context> context,
Local<Value> that) const;
bool StrictEquals(Local<Value> that) const;
@@ -2897,6 +2919,8 @@ class V8_EXPORT String : public Name {
* Returns the number of bytes in the UTF-8 encoded
* representation of this string.
*/
+ V8_DEPRECATED("Use Isolate version instead") int Utf8Length() const;
+
int Utf8Length(Isolate* isolate) const;
/**
@@ -2953,12 +2977,22 @@ class V8_EXPORT String : public Name {
// 16-bit character codes.
int Write(Isolate* isolate, uint16_t* buffer, int start = 0, int length = -1,
int options = NO_OPTIONS) const;
+ V8_DEPRECATED("Use Isolate* version")
+ int Write(uint16_t* buffer, int start = 0, int length = -1,
+ int options = NO_OPTIONS) const;
// One byte characters.
int WriteOneByte(Isolate* isolate, uint8_t* buffer, int start = 0,
int length = -1, int options = NO_OPTIONS) const;
+ V8_DEPRECATED("Use Isolate* version")
+ int WriteOneByte(uint8_t* buffer, int start = 0,
+ int length = -1, int options = NO_OPTIONS) const;
// UTF-8 encoded characters.
int WriteUtf8(Isolate* isolate, char* buffer, int length = -1,
int* nchars_ref = nullptr, int options = NO_OPTIONS) const;
+ V8_DEPRECATED("Use Isolate* version")
+ int WriteUtf8(char* buffer, int length = -1,
+ int* nchars_ref = nullptr,
+ int options = NO_OPTIONS) const;
/**
* A zero length string.
@@ -3126,6 +3160,8 @@ class V8_EXPORT String : public Name {
*/
static Local<String> Concat(Isolate* isolate, Local<String> left,
Local<String> right);
+ V8_DEPRECATED("Use Isolate* version")
+ static Local<String> Concat(Local<String> left, Local<String> right);
/**
* Creates a new external string using the data defined in the given
@@ -3190,6 +3226,8 @@ class V8_EXPORT String : public Name {
*/
class V8_EXPORT Utf8Value {
public:
+ V8_DEPRECATED("Use Isolate version")
+ explicit Utf8Value(Local<v8::Value> obj);
Utf8Value(Isolate* isolate, Local<v8::Value> obj);
~Utf8Value();
char* operator*() { return str_; }
@@ -3213,6 +3251,8 @@ class V8_EXPORT String : public Name {
*/
class V8_EXPORT Value {
public:
+ V8_DEPRECATED("Use Isolate version")
+ explicit Value(Local<v8::Value> obj);
Value(Isolate* isolate, Local<v8::Value> obj);
~Value();
uint16_t* operator*() { return str_; }
@@ -5689,6 +5729,8 @@ class V8_EXPORT BooleanObject : public Object {
class V8_EXPORT StringObject : public Object {
public:
static Local<Value> New(Isolate* isolate, Local<String> value);
+ V8_DEPRECATED("Use Isolate* version")
+ static Local<Value> New(Local<String> value);
Local<String> ValueOf() const;
@@ -11131,6 +11173,29 @@ template <class T> Value* Value::Cast(T* value) {
}
+Local<Boolean> Value::ToBoolean() const {
+ return ToBoolean(Isolate::GetCurrent());
+}
+
+
+Local<String> Value::ToString() const {
+ return ToString(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(Local<String>());
+}
+
+
+Local<Object> Value::ToObject() const {
+ return ToObject(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(Local<Object>());
+}
+
+
+Local<Integer> Value::ToInteger() const {
+ return ToInteger(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(Local<Integer>());
+}
+
+
Boolean* Boolean::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
diff --git a/src/api/api.cc b/src/api/api.cc
index f6f2ba988d95660637f6733dafdd12db7acaa992..d753abd5d2d7e788b7dd96a200d5be0d85f87fe3 100644
--- a/src/api/api.cc
+++ b/src/api/api.cc
@@ -2199,6 +2199,10 @@ int PrimitiveArray::Length() const {
return array->length();
}
+void PrimitiveArray::Set(int index, Local<Primitive> item) {
+ return Set(Isolate::GetCurrent(), index, item);
+}
+
void PrimitiveArray::Set(Isolate* v8_isolate, int index,
Local<Primitive> item) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
@@ -2212,6 +2216,10 @@ void PrimitiveArray::Set(Isolate* v8_isolate, int index,
array->set(index, *i_item);
}
+Local<Primitive> PrimitiveArray::Get(int index) {
+ return Get(Isolate::GetCurrent(), index);
+}
+
Local<Primitive> PrimitiveArray::Get(Isolate* v8_isolate, int index) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::Handle<i::FixedArray> array = Utils::OpenHandle(this);
@@ -2975,6 +2983,10 @@ void Message::PrintCurrentStackTrace(Isolate* isolate, FILE* out) {
// --- S t a c k T r a c e ---
+Local<StackFrame> StackTrace::GetFrame(uint32_t index) const {
+ return GetFrame(Isolate::GetCurrent(), index);
+}
+
Local<StackFrame> StackTrace::GetFrame(Isolate* v8_isolate,
uint32_t index) const {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
@@ -3562,6 +3574,34 @@ MaybeLocal<BigInt> Value::ToBigInt(Local<Context> context) const {
RETURN_ESCAPED(result);
}
+bool Value::BooleanValue() const {
+ return BooleanValue(Isolate::GetCurrent());
+}
+
+
+double Value::NumberValue() const {
+ return NumberValue(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(std::numeric_limits<double>::quiet_NaN());
+}
+
+
+int64_t Value::IntegerValue() const {
+ return NumberValue(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(0);
+}
+
+
+uint32_t Value::Uint32Value() const {
+ return Uint32Value(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(0);
+}
+
+
+int32_t Value::Int32Value() const {
+ return Int32Value(Isolate::GetCurrent()->GetCurrentContext())
+ .FromMaybe(0);
+}
+
bool Value::BooleanValue(Isolate* v8_isolate) const {
return Utils::OpenHandle(this)->BooleanValue(
reinterpret_cast<i::Isolate*>(v8_isolate));
@@ -3951,6 +3991,11 @@ MaybeLocal<Uint32> Value::ToArrayIndex(Local<Context> context) const {
return Local<Uint32>();
}
+bool Value::Equals(Local<Value> that) const {
+ return Equals(Isolate::GetCurrent()->GetCurrentContext(), that)
+ .FromMaybe(false);
+}
+
Maybe<bool> Value::Equals(Local<Context> context, Local<Value> that) const {
i::Isolate* isolate = Utils::OpenHandle(*context)->GetIsolate();
auto self = Utils::OpenHandle(this);
@@ -5168,6 +5213,10 @@ bool String::ContainsOnlyOneByte() const {
return helper.Check(*str);
}
+int String::Utf8Length() const {
+ return Utf8Length(Isolate::GetCurrent());
+}
+
int String::Utf8Length(Isolate* isolate) const {
i::Handle<i::String> str = Utils::OpenHandle(this);
str = i::String::Flatten(reinterpret_cast<i::Isolate*>(isolate), str);
@@ -5320,6 +5369,14 @@ static int WriteUtf8Impl(i::Vector<const Char> string, char* write_start,
}
} // anonymous namespace
+
+int String::WriteUtf8(char* buffer, int capacity,
+ int* nchars_ref, int options) const {
+ return WriteUtf8(Isolate::GetCurrent(),
+ buffer, capacity, nchars_ref, options);
+}
+
+
int String::WriteUtf8(Isolate* v8_isolate, char* buffer, int capacity,
int* nchars_ref, int options) const {
i::Handle<i::String> str = Utils::OpenHandle(this);
@@ -5358,6 +5415,17 @@ static inline int WriteHelper(i::Isolate* isolate, const String* string,
return end - start;
}
+int String::WriteOneByte(uint8_t* buffer, int start,
+ int length, int options) const {
+ return WriteOneByte(Isolate::GetCurrent(), buffer, start, length, options);
+}
+
+
+int String::Write(uint16_t* buffer, int start, int length,
+ int options) const {
+ return Write(Isolate::GetCurrent(), buffer, start, length, options);
+}
+
int String::WriteOneByte(Isolate* isolate, uint8_t* buffer, int start,
int length, int options) const {
return WriteHelper(reinterpret_cast<i::Isolate*>(isolate), this, buffer,
@@ -6269,6 +6337,11 @@ MaybeLocal<String> String::NewFromTwoByte(Isolate* isolate,
return result;
}
+Local<String> v8::String::Concat(Local<String> left,
+ Local<String> right) {
+ return Concat(Isolate::GetCurrent(), left, right);
+}
+
Local<String> v8::String::Concat(Isolate* v8_isolate, Local<String> left,
Local<String> right) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
@@ -6539,6 +6612,10 @@ bool v8::BooleanObject::ValueOf() const {
return js_primitive_wrapper->value().IsTrue(isolate);
}
+Local<v8::Value> v8::StringObject::New(Local<String> value) {
+ return New(Isolate::GetCurrent(), value);
+}
+
Local<v8::Value> v8::StringObject::New(Isolate* v8_isolate,
Local<String> value) {
i::Handle<i::String> string = Utils::OpenHandle(*value);
@@ -9078,6 +9155,9 @@ bool MicrotasksScope::IsRunningMicrotasks(Isolate* v8_isolate) {
return microtask_queue->IsRunningMicrotasks();
}
+String::Utf8Value::Utf8Value(v8::Local<v8::Value> obj)
+ : Utf8Value(Isolate::GetCurrent(), obj) {}
+
String::Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> obj)
: str_(nullptr), length_(0) {
if (obj.IsEmpty()) return;
@@ -9095,6 +9175,9 @@ String::Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> obj)
String::Utf8Value::~Utf8Value() { i::DeleteArray(str_); }
+String::Value::Value(v8::Local<v8::Value> obj)
+ : Value(Isolate::GetCurrent(), obj) {}
+
String::Value::Value(v8::Isolate* isolate, v8::Local<v8::Value> obj)
: str_(nullptr), length_(0) {
if (obj.IsEmpty()) return;

View File

@@ -21,6 +21,7 @@
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
#include "net/base/mac/url_conversions.h"
#include "shell/browser/mac/atom_application.h"
#include "shell/browser/mac/dict_util.h"
@@ -103,6 +104,23 @@ AVMediaType ParseMediaType(const std::string& media_type) {
}
}
std::string ConvertSystemPermission(
system_media_permissions::SystemPermission value) {
using SystemPermission = system_media_permissions::SystemPermission;
switch (value) {
case SystemPermission::kNotDetermined:
return "not-determined";
case SystemPermission::kRestricted:
return "restricted";
case SystemPermission::kDenied:
return "denied";
case SystemPermission::kAllowed:
return "granted";
default:
return "unknown";
}
}
} // namespace
void SystemPreferences::PostNotification(const std::string& name,
@@ -574,24 +592,15 @@ std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
std::string SystemPreferences::GetMediaAccessStatus(
const std::string& media_type,
gin_helper::Arguments* args) {
if (auto type = ParseMediaType(media_type)) {
if (@available(macOS 10.14, *)) {
switch ([AVCaptureDevice authorizationStatusForMediaType:type]) {
case AVAuthorizationStatusNotDetermined:
return "not-determined";
case AVAuthorizationStatusRestricted:
return "restricted";
case AVAuthorizationStatusDenied:
return "denied";
case AVAuthorizationStatusAuthorized:
return "granted";
default:
return "unknown";
}
} else {
// access always allowed pre-10.14 Mojave
return "granted";
}
if (media_type == "camera") {
return ConvertSystemPermission(
system_media_permissions::CheckSystemVideoCapturePermission());
} else if (media_type == "microphone") {
return ConvertSystemPermission(
system_media_permissions::CheckSystemAudioCapturePermission());
} else if (media_type == "screen") {
return ConvertSystemPermission(
system_media_permissions::CheckSystemScreenCapturePermission());
} else {
args->ThrowError("Invalid media type");
return std::string();

View File

@@ -6,6 +6,7 @@
#include <utility>
#include "base/containers/id_map.h"
#include "gin/handle.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/system/string_data_source.h"
@@ -13,6 +14,7 @@
#include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
#include "shell/browser/api/atom_api_session.h"
#include "shell/browser/atom_browser_context.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_helper/dictionary.h"
@@ -51,6 +53,11 @@ namespace api {
namespace {
base::IDMap<URLRequest*>& GetAllRequests() {
static base::NoDestructor<base::IDMap<URLRequest*>> s_all_requests;
return *s_all_requests;
}
// Network state for request and response.
enum State {
STATE_STARTED = 1 << 0,
@@ -166,7 +173,9 @@ class ChunkedDataPipeGetter : public UploadDataPipeGetter,
mojo::ReceiverSet<network::mojom::ChunkedDataPipeGetter> receiver_set_;
};
URLRequest::URLRequest(gin::Arguments* args) : weak_factory_(this) {
URLRequest::URLRequest(gin::Arguments* args)
: id_(GetAllRequests().Add(this)), weak_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
request_ = std::make_unique<network::ResourceRequest>();
gin_helper::Dictionary dict;
if (args->GetNext(&dict)) {
@@ -176,6 +185,8 @@ URLRequest::URLRequest(gin::Arguments* args) : weak_factory_(this) {
request_->redirect_mode = redirect_mode_;
}
request_->render_frame_id = id_;
std::string partition;
gin::Handle<api::Session> session;
if (!dict.Get("session", &session)) {
@@ -192,6 +203,38 @@ URLRequest::URLRequest(gin::Arguments* args) : weak_factory_(this) {
URLRequest::~URLRequest() = default;
URLRequest* URLRequest::FromID(uint32_t id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return GetAllRequests().Lookup(id);
}
void URLRequest::OnAuthRequired(
const GURL& url,
bool first_auth_attempt,
net::AuthChallengeInfo auth_info,
network::mojom::URLResponseHeadPtr head,
mojo::PendingRemote<network::mojom::AuthChallengeResponder>
auth_challenge_responder) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojo::Remote<network::mojom::AuthChallengeResponder> auth_responder(
std::move(auth_challenge_responder));
auth_responder.set_disconnect_handler(
base::BindOnce(&URLRequest::Cancel, weak_factory_.GetWeakPtr()));
auto cb = base::BindOnce(
[](mojo::Remote<network::mojom::AuthChallengeResponder> auth_responder,
gin::Arguments* args) {
base::string16 username_str, password_str;
if (!args->GetNext(&username_str) || !args->GetNext(&password_str)) {
auth_responder->OnAuthCredentials(base::nullopt);
return;
}
auth_responder->OnAuthCredentials(
net::AuthCredentials(username_str, password_str));
},
std::move(auth_responder));
Emit("login", auth_info, base::AdaptCallbackForRepeating(std::move(cb)));
}
bool URLRequest::NotStarted() const {
return request_state_ == 0;
}
@@ -512,11 +555,12 @@ void URLRequest::EmitError(EventType type, base::StringPiece message) {
}
template <typename... Args>
void URLRequest::EmitEvent(EventType type, Args... args) {
void URLRequest::EmitEvent(EventType type, Args&&... args) {
const char* method =
type == EventType::kRequest ? "_emitRequestEvent" : "_emitResponseEvent";
v8::HandleScope handle_scope(isolate());
gin_helper::CustomEmit(isolate(), GetWrapper(), method, args...);
gin_helper::CustomEmit(isolate(), GetWrapper(), method,
std::forward<Args>(args)...);
}
// static

View File

@@ -17,6 +17,7 @@
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/mojom/data_pipe_getter.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "shell/common/gin_helper/event_emitter.h"
namespace electron {
@@ -32,6 +33,15 @@ class URLRequest : public gin_helper::EventEmitter<URLRequest>,
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
static URLRequest* FromID(uint32_t id);
void OnAuthRequired(
const GURL& url,
bool first_auth_attempt,
net::AuthChallengeInfo auth_info,
network::mojom::URLResponseHeadPtr head,
mojo::PendingRemote<network::mojom::AuthChallengeResponder>
auth_challenge_responder);
protected:
explicit URLRequest(gin::Arguments* args);
@@ -89,7 +99,7 @@ class URLRequest : public gin_helper::EventEmitter<URLRequest>,
};
void EmitError(EventType type, base::StringPiece error);
template <typename... Args>
void EmitEvent(EventType type, Args... args);
void EmitEvent(EventType type, Args&&... args);
std::unique_ptr<network::ResourceRequest> request_;
std::unique_ptr<network::SimpleURLLoader> loader_;
@@ -132,6 +142,8 @@ class URLRequest : public gin_helper::EventEmitter<URLRequest>,
// Used by pin/unpin to manage lifetime.
v8::Global<v8::Object> wrapper_;
uint32_t id_;
base::WeakPtrFactory<URLRequest> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(URLRequest);

View File

@@ -332,6 +332,10 @@ void WebRequest::OnCompleted(extensions::WebRequestInfo* info,
HandleSimpleEvent(kOnCompleted, info, request, net_error);
}
void WebRequest::OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) {
callbacks_.erase(info->id);
}
template <WebRequest::SimpleEvent event>
void WebRequest::SetSimpleListener(gin::Arguments* args) {
SetListener<SimpleListener>(event, &simple_listeners_, args);

View File

@@ -84,6 +84,7 @@ class WebRequest : public gin::Wrappable<WebRequest>, public WebRequestAPI {
void OnCompleted(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
int net_error) override;
void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) override;
enum SimpleEvent {
kOnSendHeaders,

View File

@@ -28,10 +28,12 @@
#include "content/browser/blob_storage/chrome_blob_storage_context.h" // nogncheck
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/escape.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "shell/browser/api/atom_api_url_request.h"
#include "shell/browser/atom_browser_client.h"
#include "shell/browser/atom_browser_main_parts.h"
#include "shell/browser/atom_download_manager_delegate.h"
@@ -325,6 +327,7 @@ AtomBrowserContext::GetURLLoaderFactory() {
network::mojom::URLLoaderFactoryParamsPtr params =
network::mojom::URLLoaderFactoryParams::New();
params->header_client = std::move(header_client);
params->auth_client = auth_client_.BindNewPipeAndPassRemote();
params->process_id = network::mojom::kBrowserProcessId;
params->is_trusted = true;
params->is_corb_enabled = false;
@@ -342,6 +345,39 @@ AtomBrowserContext::GetURLLoaderFactory() {
return url_loader_factory_;
}
class AuthResponder : public network::mojom::TrustedAuthClient {
public:
AuthResponder() {}
~AuthResponder() override = default;
private:
void OnAuthRequired(
const base::Optional<::base::UnguessableToken>& window_id,
uint32_t process_id,
uint32_t routing_id,
uint32_t request_id,
const ::GURL& url,
bool first_auth_attempt,
const ::net::AuthChallengeInfo& auth_info,
::network::mojom::URLResponseHeadPtr head,
mojo::PendingRemote<network::mojom::AuthChallengeResponder>
auth_challenge_responder) override {
api::URLRequest* url_request = api::URLRequest::FromID(routing_id);
if (url_request) {
url_request->OnAuthRequired(url, first_auth_attempt, auth_info,
std::move(head),
std::move(auth_challenge_responder));
}
}
};
void AtomBrowserContext::OnLoaderCreated(
int32_t request_id,
mojo::PendingReceiver<network::mojom::TrustedAuthClient> auth_client) {
mojo::MakeSelfOwnedReceiver(std::make_unique<AuthResponder>(),
std::move(auth_client));
}
content::PushMessagingService* AtomBrowserContext::GetPushMessagingService() {
return nullptr;
}

View File

@@ -17,6 +17,7 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/resource_context.h"
#include "electron/buildflags/buildflags.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "shell/browser/media/media_device_id_salt.h"
@@ -50,7 +51,8 @@ class WebViewManager;
class AtomBrowserContext
: public base::RefCountedDeleteOnSequence<AtomBrowserContext>,
public content::BrowserContext {
public content::BrowserContext,
public network::mojom::TrustedURLLoaderAuthClient {
public:
// partition_id => browser_context
struct PartitionKey {
@@ -149,6 +151,10 @@ class AtomBrowserContext
friend class base::RefCountedDeleteOnSequence<AtomBrowserContext>;
friend class base::DeleteHelper<AtomBrowserContext>;
void OnLoaderCreated(int32_t request_id,
mojo::PendingReceiver<network::mojom::TrustedAuthClient>
header_client) override;
// Initialize pref registry.
void InitPrefs();
@@ -185,6 +191,7 @@ class AtomBrowserContext
// Shared URLLoaderFactory.
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
mojo::Receiver<network::mojom::TrustedURLLoaderAuthClient> auth_client_{this};
base::WeakPtrFactory<AtomBrowserContext> weak_factory_;

View File

@@ -10,9 +10,12 @@
#include <vector>
#include "base/callback.h"
#include "base/strings/string16.h"
#include "gin/arguments.h"
#include "gin/dictionary.h"
#include "shell/browser/api/atom_api_web_contents.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/value_converter.h"
@@ -57,7 +60,7 @@ void LoginHandler::EmitEvent(
v8::HandleScope scope(isolate);
auto details = gin::Dictionary::CreateEmpty(isolate);
details.Set("url", url.spec());
details.Set("url", url);
// These parameters aren't documented, and I'm not sure that they're useful,
// but we might as well stick 'em on the details object. If it turns out they
@@ -70,17 +73,23 @@ void LoginHandler::EmitEvent(
api_web_contents->Emit("login", std::move(details), auth_info,
base::BindOnce(&LoginHandler::CallbackFromJS,
weak_factory_.GetWeakPtr()));
if (!default_prevented) {
if (!default_prevented && auth_required_callback_) {
std::move(auth_required_callback_).Run(base::nullopt);
}
}
LoginHandler::~LoginHandler() = default;
void LoginHandler::CallbackFromJS(base::string16 username,
base::string16 password) {
std::move(auth_required_callback_)
.Run(net::AuthCredentials(username, password));
void LoginHandler::CallbackFromJS(gin::Arguments* args) {
if (auth_required_callback_) {
base::string16 username, password;
if (!args->GetNext(&username) || !args->GetNext(&password)) {
std::move(auth_required_callback_).Run(base::nullopt);
return;
}
std::move(auth_required_callback_)
.Run(net::AuthCredentials(username, password));
}
}
} // namespace electron

View File

@@ -5,7 +5,6 @@
#ifndef SHELL_BROWSER_LOGIN_HANDLER_H_
#define SHELL_BROWSER_LOGIN_HANDLER_H_
#include "base/strings/string16.h"
#include "base/values.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/login_delegate.h"
@@ -15,6 +14,10 @@ namespace content {
class WebContents;
}
namespace gin {
class Arguments;
}
namespace electron {
// Handles HTTP basic auth.
@@ -36,7 +39,7 @@ class LoginHandler : public content::LoginDelegate,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers,
bool first_auth_attempt);
void CallbackFromJS(base::string16 username, base::string16 password);
void CallbackFromJS(gin::Arguments* args);
LoginAuthRequiredCallback auth_required_callback_;

View File

@@ -345,39 +345,23 @@ bool NativeWindowViews::PreHandleMSG(UINT message,
return false;
}
case WM_GETMINMAXINFO: {
// We need to handle GETMINMAXINFO ourselves because chromium tries to
// get the scale factor of the window during it's version of this handler
// based on the window position, which is invalid at this point. The
// previous method of calling SetWindowPlacement fixed the window
// position for the scale factor calculation but broke other things.
MINMAXINFO* info = reinterpret_cast<MINMAXINFO*>(l_param);
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestPoint(
last_normal_placement_bounds_.origin());
// We do this to work around a Windows bug, where the minimized Window
// would report that the closest display to it is not the one that it was
// previously on (but the leftmost one instead). We restore the position
// of the window during the restore operation, this way chromium can
// use the proper display to calculate the scale factor to use.
if (!last_normal_placement_bounds_.IsEmpty() &&
GetWindowPlacement(GetAcceleratedWidget(), &wp)) {
wp.rcNormalPosition = last_normal_placement_bounds_.ToRECT();
SetWindowPlacement(GetAcceleratedWidget(), &wp);
const gfx::Size widget_min_size = widget()->GetMinimumSize();
const gfx::Size widget_max_size = widget()->GetMaximumSize();
gfx::Size min_size = gfx::ScaleToCeiledSize(
ContentBoundsToWindowBounds(gfx::Rect(widget_min_size)).size(),
display.device_scale_factor());
gfx::Size max_size = gfx::ScaleToCeiledSize(
ContentBoundsToWindowBounds(gfx::Rect(widget_max_size)).size(),
display.device_scale_factor());
info->ptMinTrackSize.x = min_size.width();
info->ptMinTrackSize.y = min_size.height();
if (widget_max_size.width() || widget_max_size.height()) {
if (!max_size.width())
max_size.set_width(GetSystemMetrics(SM_CXMAXTRACK));
if (!max_size.height())
max_size.set_height(GetSystemMetrics(SM_CYMAXTRACK));
info->ptMaxTrackSize.x = max_size.width();
info->ptMaxTrackSize.y = max_size.height();
last_normal_placement_bounds_ = gfx::Rect();
}
*result = 1;
return true;
return false;
}
case WM_NCCALCSIZE: {
if (!has_frame() && w_param == TRUE) {

View File

@@ -13,7 +13,6 @@
#include "base/task/post_task.h"
#include "content/public/browser/file_url_loader.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/data_pipe_producer.h"
#include "mojo/public/cpp/system/file_data_source.h"
#include "net/base/filename_util.h"

View File

@@ -49,9 +49,10 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
target_client_(std::move(client)),
current_response_(network::mojom::URLResponseHead::New()),
proxied_client_binding_(this),
// TODO(zcbenz): We should always use "extraHeaders" mode to be compatible
// with old APIs.
has_any_extra_headers_listeners_(false) {
// Always use "extraHeaders" mode to be compatible with old APIs, except
// when the |request_id_| is zero, which is not supported in Chromium and
// only happens in Electron when the request is started from net module.
has_any_extra_headers_listeners_(network_service_request_id != 0) {
// If there is a client error, clean up the request.
target_client_.set_connection_error_handler(base::BindOnce(
&ProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
@@ -60,7 +61,19 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
}
ProxyingURLLoaderFactory::InProgressRequest::~InProgressRequest() {
// TODO(zcbenz): Do cleanup here.
// This is important to ensure that no outstanding blocking requests continue
// to reference state owned by this object.
if (info_) {
factory_->web_request_api()->OnRequestWillBeDestroyed(&info_.value());
}
if (on_before_send_headers_callback_) {
std::move(on_before_send_headers_callback_)
.Run(net::ERR_ABORTED, base::nullopt);
}
if (on_headers_received_callback_) {
std::move(on_headers_received_callback_)
.Run(net::ERR_ABORTED, base::nullopt, base::nullopt);
}
}
void ProxyingURLLoaderFactory::InProgressRequest::Restart() {
@@ -84,8 +97,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::UpdateRequestInfo() {
current_request_uses_header_client_ =
factory_->url_loader_header_client_receiver_.is_bound() &&
network_service_request_id_ != 0 &&
false /* TODO(zcbenz): HasExtraHeadersListenerForRequest */;
network_service_request_id_ != 0 && has_any_extra_headers_listeners_;
}
void ProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
@@ -722,6 +734,10 @@ void ProxyingURLLoaderFactory::CreateLoaderAndStart(
// don't use it for identity here.
const uint64_t web_request_id = ++g_request_id;
// Notes: Chromium assumes that requests with zero-ID would never use the
// "extraHeaders" code path, however in Electron requests started from
// the net module would have zero-ID because they do not have renderer process
// associated.
if (request_id)
network_request_id_to_web_request_id_.emplace(request_id, web_request_id);

View File

@@ -67,6 +67,7 @@ class WebRequestAPI {
virtual void OnCompleted(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
int net_error) = 0;
virtual void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) = 0;
};
// This class is responsible for following tasks when NetworkService is enabled:

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 9,0,0,20191113
PRODUCTVERSION 9,0,0,20191113
FILEVERSION 9,0,0,20191116
PRODUCTVERSION 9,0,0,20191116
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -80,7 +80,12 @@ describe('BrowserWindow module', () => {
})
it('should emit beforeunload handler', async () => {
await w.loadFile(path.join(fixtures, 'api', 'beforeunload-false.html'))
const beforeunload = emittedOnce(w, 'onbeforeunload')
const beforeunload = new Promise(resolve => {
ipcMain.once('onbeforeunload', (e) => {
e.returnValue = null
resolve()
})
})
w.close()
await beforeunload
})
@@ -164,8 +169,9 @@ describe('BrowserWindow module', () => {
expect(content).to.equal('close')
})
it('should emit beforeunload event', async () => {
w.loadFile(path.join(fixtures, 'api', 'close-beforeunload-false.html'))
await emittedOnce(w, 'onbeforeunload')
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html'))
const [e] = await emittedOnce(ipcMain, 'onbeforeunload')
e.returnValue = null
})
})
@@ -1552,7 +1558,7 @@ describe('BrowserWindow module', () => {
expect(test).to.eql('preload')
})
it('can successfully delete the Buffer global', async () => {
const preload = path.join(fixtures, 'module', 'delete-buffer.js')
const preload = path.join(__dirname, 'fixtures', 'module', 'delete-buffer.js')
const w = new BrowserWindow({
show: false,
webPreferences: {
@@ -1676,7 +1682,7 @@ describe('BrowserWindow module', () => {
describe('"enableRemoteModule" option', () => {
const generateSpecs = (description: string, sandbox: boolean) => {
describe(description, () => {
const preload = path.join(fixtures, 'module', 'preload-remote.js')
const preload = path.join(__dirname, 'fixtures', 'module', 'preload-remote.js')
it('enables the remote module by default', async () => {
const w = new BrowserWindow({
@@ -1794,7 +1800,7 @@ describe('BrowserWindow module', () => {
preload
}
})
const htmlPath = path.join(fixtures, 'api', 'sandbox.html?exit-event')
const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?exit-event')
const pageUrl = 'file://' + htmlPath
w.loadURL(pageUrl)
const [, url] = await emittedOnce(ipcMain, 'answer')
@@ -1815,7 +1821,7 @@ describe('BrowserWindow module', () => {
w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
options.webPreferences!.preload = preload
})
const htmlPath = path.join(fixtures, 'api', 'sandbox.html?window-open')
const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?window-open')
const pageUrl = 'file://' + htmlPath
const answer = emittedOnce(ipcMain, 'answer')
w.loadURL(pageUrl)
@@ -1844,7 +1850,7 @@ describe('BrowserWindow module', () => {
options.webPreferences!.preload = preload
})
w.loadFile(
path.join(fixtures, 'api', 'sandbox.html'),
path.join(__dirname, 'fixtures', 'api', 'sandbox.html'),
{ search: 'window-open-external' }
)
@@ -1921,7 +1927,11 @@ describe('BrowserWindow module', () => {
prefs.foo = 'bar'
})
w.loadFile(path.join(fixtures, 'api', 'new-window.html'))
const [, , webPreferences] = await emittedOnce(ipcMain, 'answer')
const [[, childWebContents]] = await Promise.all([
emittedOnce(app, 'web-contents-created'),
emittedOnce(ipcMain, 'answer')
])
const webPreferences = (childWebContents as any).getLastWebPreferences()
expect(webPreferences.foo).to.equal('bar')
})
@@ -1952,7 +1962,7 @@ describe('BrowserWindow module', () => {
'parent-answer',
'child-answer'
], done)
w.loadFile(path.join(fixtures, 'api', 'sandbox.html'), { search: 'verify-ipc-sender' })
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'verify-ipc-sender' })
})
describe('event handling', () => {
@@ -1988,7 +1998,7 @@ describe('BrowserWindow module', () => {
'did-frame-finish-load',
'dom-ready'
], done)
w.loadFile(path.join(fixtures, 'api', 'sandbox.html'), { search: 'webcontents-events' })
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'webcontents-events' })
})
})
@@ -2021,7 +2031,7 @@ describe('BrowserWindow module', () => {
sandbox: true
}
})
w.loadFile(path.join(fixtures, 'api', 'sandbox.html'), { search: 'reload-remote' })
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote' })
ipcMain.on('get-remote-module-path', (event) => {
event.returnValue = path.join(fixtures, 'module', 'hello.js')
@@ -2057,7 +2067,7 @@ describe('BrowserWindow module', () => {
options.webPreferences!.preload = preload
})
w.loadFile(path.join(fixtures, 'api', 'sandbox.html'), { search: 'reload-remote-child' })
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote-child' })
ipcMain.on('get-remote-module-path', (event) => {
event.returnValue = path.join(fixtures, 'module', 'hello-child.js')
@@ -2233,7 +2243,11 @@ describe('BrowserWindow module', () => {
prefs.foo = 'bar'
})
w.loadFile(path.join(fixtures, 'api', 'new-window.html'))
const [, , webPreferences] = await emittedOnce(ipcMain, 'answer')
const [[, childWebContents]] = await Promise.all([
emittedOnce(app, 'web-contents-created'),
emittedOnce(ipcMain, 'answer')
])
const webPreferences = (childWebContents as any).getLastWebPreferences()
expect(webPreferences.foo).to.equal('bar')
})
it('should have nodeIntegration disabled in child windows', async () => {
@@ -2359,22 +2373,27 @@ describe('BrowserWindow module', () => {
beforeEach(() => {
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } })
})
afterEach(() => {
ipcMain.removeAllListeners('onbeforeunload')
})
afterEach(closeAllWindows)
it('returning undefined would not prevent close', (done) => {
w.once('closed', () => { done() })
w.loadFile(path.join(fixtures, 'api', 'close-beforeunload-undefined.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-undefined.html'))
})
it('returning false would prevent close', (done) => {
w.once('onbeforeunload' as any, () => { done() })
w.loadFile(path.join(fixtures, 'api', 'close-beforeunload-false.html'))
it('returning false would prevent close', async () => {
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html'))
const [e] = await emittedOnce(ipcMain, 'onbeforeunload')
e.returnValue = null
})
it('returning empty string would prevent close', (done) => {
w.once('onbeforeunload' as any, () => { done() })
w.loadFile(path.join(fixtures, 'api', 'close-beforeunload-empty-string.html'))
ipcMain.once('onbeforeunload', (e) => { e.returnValue = null; done() })
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-empty-string.html'))
})
it('emits for each close attempt', (done) => {
let beforeUnloadCount = 0
w.on('onbeforeunload' as any, () => {
ipcMain.on('onbeforeunload', (e) => {
e.returnValue = null
beforeUnloadCount += 1
if (beforeUnloadCount < 3) {
w.close()
@@ -2382,12 +2401,13 @@ describe('BrowserWindow module', () => {
done()
}
})
w.webContents.once('did-finish-load', () => { w.close() })
w.loadFile(path.join(fixtures, 'api', 'beforeunload-false-prevent3.html'))
w.webContents.once('did-finish-load', () => { w.webContents.executeJavaScript('window.close()', true) })
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false-prevent3.html'))
})
it('emits for each reload attempt', (done) => {
let beforeUnloadCount = 0
w.on('onbeforeunload' as any, () => {
ipcMain.on('onbeforeunload', (e) => {
e.returnValue = null
beforeUnloadCount += 1
if (beforeUnloadCount < 3) {
w.reload()
@@ -2401,11 +2421,12 @@ describe('BrowserWindow module', () => {
})
w.reload()
})
w.loadFile(path.join(fixtures, 'api', 'beforeunload-false-prevent3.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false-prevent3.html'))
})
it('emits for each navigation attempt', (done) => {
let beforeUnloadCount = 0
w.on('onbeforeunload' as any, () => {
ipcMain.on('onbeforeunload', (e) => {
e.returnValue = null
beforeUnloadCount += 1
if (beforeUnloadCount < 3) {
w.loadURL('about:blank')
@@ -2419,7 +2440,7 @@ describe('BrowserWindow module', () => {
})
w.loadURL('about:blank')
})
w.loadFile(path.join(fixtures, 'api', 'beforeunload-false-prevent3.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false-prevent3.html'))
})
})

View File

@@ -192,79 +192,4 @@ describe('deprecate', () => {
expect(warnings[0]).to.equal('\'old\' is deprecated and will be removed. Please use \'new\' instead.')
})
})
describe('promisify', () => {
const expected = 'Hello, world!'
let promiseFunc: (param: any) => Promise<any>
let warnings: string[]
const enableCallbackWarnings = () => {
warnings = []
deprecate.setHandler(warning => warnings.push(warning))
process.enablePromiseAPIs = true
}
beforeEach(() => {
deprecate.setHandler(null)
process.throwDeprecation = true
promiseFunc = param => new Promise((resolve) => resolve(param))
})
it('acts as a pass-through for promise-based invocations', async () => {
enableCallbackWarnings()
promiseFunc = deprecate.promisify(promiseFunc)
const actual = await promiseFunc(expected)
expect(actual).to.equal(expected)
expect(warnings).to.have.lengthOf(0)
})
it('only calls back an error if the callback is called with (err, data)', async () => {
enableCallbackWarnings()
const erringPromiseFunc = deprecate.promisify(
() => new Promise((resolve, reject) => {
reject(new Error('fail'))
})
)
{
const [err, data] = await new Promise(resolve => {
(erringPromiseFunc as any)((err: Error | undefined, data: any) => {
resolve([err, data])
}).catch(() => { /* silence deprecation warning */ })
})
expect(data).to.be.undefined()
expect(err).to.be.an.instanceOf(Error).with.property('message', 'fail')
}
{
const data = await new Promise(resolve => {
(erringPromiseFunc as any)((data: any) => { resolve(data) })
.catch(() => { /* silence deprecation warning */ })
})
expect(data).to.be.undefined()
}
})
it('warns exactly once for callback-based invocations', (done) => {
enableCallbackWarnings()
promiseFunc = deprecate.promisify(promiseFunc)
let callbackCount = 0
const invocationCount = 3
const callback = (actual: number) => {
expect(actual).to.equal(expected)
expect(warnings).to.have.lengthOf(1)
expect(warnings[0]).to.include('promiseFunc')
callbackCount += 1
if (callbackCount === invocationCount) {
done()
}
}
for (let i = 0; i < invocationCount; i += 1) {
(promiseFunc as any)(expected, callback)
}
})
})
})

View File

@@ -45,24 +45,6 @@ describe('ipc main module', () => {
})
})
describe('remote objects registry', () => {
it('does not dereference until the render view is deleted (regression)', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true
}
})
ipcMain.once('error-message', (event, message) => {
expect(message).to.match(/^Cannot call method 'getURL' on missing remote object/)
done()
})
w.loadFile(path.join(fixtures, 'api', 'render-view-deleted.html'))
})
})
describe('ipcMain.on', () => {
it('is not used for internals', async () => {
const appPath = path.join(fixtures, 'api', 'ipc-main-listeners')

View File

@@ -1,5 +1,5 @@
import { expect } from 'chai'
import { net as originalNet, session, ClientRequest } from 'electron'
import { net as originalNet, session, ClientRequest, BrowserWindow } from 'electron'
import * as http from 'http'
import * as url from 'url'
import { AddressInfo } from 'net'
@@ -206,6 +206,135 @@ describe('net module', () => {
urlRequest.end()
})
})
it('should emit the login event when 401', async () => {
const [user, pass] = ['user', 'pass']
const serverUrl = await respondOnce.toSingleURL((request, response) => {
if (!request.headers.authorization) {
return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end()
}
response.writeHead(200).end('ok')
})
let loginAuthInfo: any
await new Promise((resolve, reject) => {
const request = net.request({ method: 'GET', url: serverUrl })
request.on('response', (response) => {
response.on('error', reject)
response.on('data', () => {})
response.on('end', () => resolve())
})
request.on('login', (authInfo, cb) => {
loginAuthInfo = authInfo
cb(user, pass)
})
request.on('error', reject)
request.end()
})
expect(loginAuthInfo.realm).to.equal('Foo')
expect(loginAuthInfo.scheme).to.equal('basic')
})
it('should produce an error on the response object when cancelling authentication', async () => {
const serverUrl = await respondOnce.toSingleURL((request, response) => {
if (!request.headers.authorization) {
return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end()
}
response.writeHead(200).end('ok')
})
await expect(new Promise((resolve, reject) => {
const request = net.request({ method: 'GET', url: serverUrl })
request.on('response', (response) => {
response.on('error', reject)
response.on('data', () => {})
response.on('end', () => resolve())
})
request.on('login', (authInfo, cb) => {
cb()
})
request.on('error', reject)
request.end()
})).to.eventually.be.rejectedWith('net::ERR_HTTP_RESPONSE_CODE_FAILURE')
})
it('should share credentials with WebContents', async () => {
const [user, pass] = ['user', 'pass']
const serverUrl = await new Promise<string>((resolve) => {
const server = http.createServer((request, response) => {
if (!request.headers.authorization) {
return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end()
}
return response.writeHead(200).end('ok')
})
server.listen(0, '127.0.0.1', () => {
resolve(`http://127.0.0.1:${(server.address() as AddressInfo).port}`)
})
after(() => { server.close() })
})
const bw = new BrowserWindow({ show: false })
const loaded = bw.loadURL(serverUrl)
bw.webContents.on('login', (event, details, authInfo, cb) => {
event.preventDefault()
cb(user, pass)
})
await loaded
bw.close()
await new Promise((resolve, reject) => {
const request = net.request({ method: 'GET', url: serverUrl })
request.on('response', (response) => {
response.on('error', reject)
response.on('data', () => {})
response.on('end', () => resolve())
})
request.on('login', () => {
// we shouldn't receive a login event, because the credentials should
// be cached.
reject(new Error('unexpected login event'))
})
request.on('error', reject)
request.end()
})
})
it('should share proxy credentials with WebContents', async () => {
const [user, pass] = ['user', 'pass']
const proxyPort = await new Promise<number>((resolve) => {
const server = http.createServer((request, response) => {
if (!request.headers['proxy-authorization']) {
return response.writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }).end()
}
return response.writeHead(200).end('ok')
})
server.listen(0, '127.0.0.1', () => {
resolve((server.address() as AddressInfo).port)
})
after(() => { server.close() })
})
const customSession = session.fromPartition(`net-proxy-test-${Math.random()}`)
await customSession.setProxy({ proxyRules: `127.0.0.1:${proxyPort}`, proxyBypassRules: '<-loopback>' })
const bw = new BrowserWindow({ show: false, webPreferences: { session: customSession } })
const loaded = bw.loadURL('http://127.0.0.1:9999')
bw.webContents.on('login', (event, details, authInfo, cb) => {
event.preventDefault()
cb(user, pass)
})
await loaded
bw.close()
await new Promise((resolve, reject) => {
const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession })
request.on('response', (response) => {
response.on('error', reject)
response.on('data', () => {})
response.on('end', () => resolve())
})
request.on('login', () => {
// we shouldn't receive a login event, because the credentials should
// be cached.
reject(new Error('unexpected login event'))
})
request.on('error', reject)
request.end()
})
})
})
describe('ClientRequest API', () => {

View File

@@ -158,6 +158,24 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
})
})
describe('remote objects registry', () => {
it('does not dereference until the render view is deleted (regression)', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true
}
})
ipcMain.once('error-message', (event, message) => {
expect(message).to.match(/^Cannot call method 'getURL' on missing remote object/)
done()
})
w.loadFile(path.join(fixtures, 'api', 'render-view-deleted.html'))
})
})
describe('remote listeners', () => {
afterEach(closeAllWindows)
@@ -168,7 +186,7 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
nodeIntegration: true
}
})
await w.loadFile(path.join(__dirname, '..', 'spec', 'fixtures', 'api', 'remote-event-handler.html'))
await w.loadFile(path.join(fixtures, 'api', 'remote-event-handler.html'))
w.webContents.reload()
await emittedOnce(w.webContents, 'did-finish-load')

View File

@@ -520,6 +520,7 @@ describe('session module', () => {
})
response.on('error', (error: any) => { reject(new Error(error)) })
})
request.on('error', (error: any) => { reject(new Error(error)) })
request.end()
})
// the first time should throw due to unauthenticated

View File

@@ -246,6 +246,11 @@ describe('systemPreferences module', () => {
const microphoneStatus = systemPreferences.getMediaAccessStatus('microphone')
expect(statuses).to.include(microphoneStatus)
})
it('returns an access status for a screen access request', () => {
const screenStatus = systemPreferences.getMediaAccessStatus('screen')
expect(statuses).to.include(screenStatus)
})
})
describe('systemPreferences.getAnimationSettings()', () => {

View File

@@ -47,20 +47,20 @@ describe('webContents module', () => {
w.webContents.once('will-prevent-unload', () => {
expect.fail('should not have fired')
})
w.loadFile(path.join(fixturesPath, 'api', 'close-beforeunload-undefined.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-undefined.html'))
})
it('emits if beforeunload returns false', (done) => {
it('emits if beforeunload returns false', async () => {
const w = new BrowserWindow({ show: false })
w.webContents.once('will-prevent-unload', () => done())
w.loadFile(path.join(fixturesPath, 'api', 'close-beforeunload-false.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html'))
await emittedOnce(w.webContents, 'will-prevent-unload')
})
it('supports calling preventDefault on will-prevent-unload events', (done) => {
it('supports calling preventDefault on will-prevent-unload events', async () => {
const w = new BrowserWindow({ show: false })
w.webContents.once('will-prevent-unload', event => event.preventDefault())
w.once('closed', () => done())
w.loadFile(path.join(fixturesPath, 'api', 'close-beforeunload-false.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html'))
await emittedOnce(w, 'closed')
})
})
@@ -695,15 +695,19 @@ describe('webContents module', () => {
describe('focus()', () => {
describe('when the web contents is hidden', () => {
afterEach(closeAllWindows)
it('does not blur the focused window', (done) => {
it('does not blur the focused window', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } })
ipcMain.once('answer', (event, parentFocused, childFocused) => {
expect(parentFocused).to.be.true()
expect(childFocused).to.be.false()
done()
})
w.show()
w.loadFile(path.join(fixturesPath, 'pages', 'focus-web-contents.html'))
await w.loadURL('about:blank')
w.focus()
const child = new BrowserWindow({ show: false })
child.loadURL('about:blank')
child.webContents.focus()
const currentFocused = w.isFocused()
const childFocused = child.isFocused()
child.close()
expect(currentFocused).to.be.true()
expect(childFocused).to.be.false()
})
})
})
@@ -1534,7 +1538,7 @@ describe('webContents module', () => {
}
response
.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' })
.end()
.end('401')
}).listen(0, '127.0.0.1', () => {
serverPort = (server.address() as AddressInfo).port
serverUrl = `http://127.0.0.1:${serverPort}`
@@ -1557,6 +1561,10 @@ describe('webContents module', () => {
})
})
afterEach(async () => {
await session.defaultSession.clearAuthCache({ type: 'password' })
})
after(() => {
server.close()
proxyServer.close()
@@ -1607,5 +1615,16 @@ describe('webContents module', () => {
expect(eventAuthInfo.port).to.equal(proxyServerPort)
expect(eventAuthInfo.realm).to.equal('Foo')
})
it('cancels authentication when callback is called with no arguments', async () => {
const w = new BrowserWindow({ show: false })
w.webContents.on('login', (event, request, authInfo, cb) => {
event.preventDefault()
cb()
})
await w.loadURL(serverUrl)
const body = await w.webContents.executeJavaScript(`document.documentElement.textContent`)
expect(body).to.equal('401')
})
})
})

View File

@@ -20,6 +20,9 @@ describe('webRequest module', () => {
if (req.headers.accept === '*/*;test/header') {
content += 'header/received'
}
if (req.headers.origin === 'http://new-origin') {
content += 'new/origin'
}
res.end(content)
}
})
@@ -145,6 +148,16 @@ describe('webRequest module', () => {
expect(data).to.equal('/header/received')
})
it('can change CORS headers', async () => {
ses.webRequest.onBeforeSendHeaders((details, callback) => {
const requestHeaders = details.requestHeaders
requestHeaders.Origin = 'http://new-origin'
callback({ requestHeaders: requestHeaders })
})
const { data } = await ajax(defaultURL)
expect(data).to.equal('/new/origin')
})
it('resets the whole headers', async () => {
const requestHeaders = {
Test: 'header'
@@ -199,6 +212,16 @@ describe('webRequest module', () => {
expect(headers).to.match(/^custom: Changed$/m)
})
it('can change CORS headers', async () => {
ses.webRequest.onHeadersReceived((details, callback) => {
const responseHeaders = details.responseHeaders!
responseHeaders['access-control-allow-origin'] = ['http://new-origin'] as any
callback({ responseHeaders: responseHeaders })
})
const { headers } = await ajax(defaultURL)
expect(headers).to.match(/^access-control-allow-origin: http:\/\/new-origin$/m)
})
it('does not change header by default', async () => {
ses.webRequest.onHeadersReceived((details, callback) => {
callback({})

View File

@@ -5,7 +5,7 @@
var unloadPreventedCount = 0;
window.onbeforeunload = function() {
setTimeout(function() {
require('electron').remote.getCurrentWindow().emit('onbeforeunload');
require('electron').ipcRenderer.sendSync('onbeforeunload')
}, 0);
if (unloadPreventedCount < 3) {
unloadPreventedCount++;

View File

@@ -5,7 +5,7 @@
var unloadPrevented = false;
window.onbeforeunload = function() {
setTimeout(function() {
require('electron').remote.getCurrentWindow().emit('onbeforeunload');
require('electron').ipcRenderer.sendSync('onbeforeunload');
}, 0);
if (!unloadPrevented) {

View File

@@ -5,7 +5,7 @@
var unloadPrevented = false;
window.onbeforeunload = function() {
setTimeout(function() {
require('electron').remote.getCurrentWindow().emit('onbeforeunload');
require('electron').ipcRenderer.sendSync('onbeforeunload');
}, 0);
if (!unloadPrevented) {
unloadPrevented = true;

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<webview nodeintegration src="./a.html"></webview>
<script>
var wv = document.querySelector('webview')
wv.addEventListener('dom-ready', () => {
wv.openDevTools()
})
</script>
</script>
</body>
</html>

View File

@@ -164,7 +164,21 @@ describe('<webview> tag', function () {
const extensionPath = path.join(fixtures, 'devtools-extensions', 'foo')
BrowserWindow.addDevToolsExtension(extensionPath)
w.loadFile(path.join(fixtures, 'pages', 'webview-devtools.html'))
w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'webview-devtools.html'))
app.once('web-contents-created', (e, webContents) => {
webContents.on('devtools-opened', function () {
const showPanelIntervalId = setInterval(function () {
if (!webContents.isDestroyed() && webContents.devToolsWebContents) {
webContents.devToolsWebContents.executeJavaScript('(' + function () {
const lastPanelId: any = (window as any).UI.inspectorView._tabbedPane._tabs.peekLast().id;
(window as any).UI.inspectorView.showPanel(lastPanelId)
}.toString() + ')()')
} else {
clearInterval(showPanelIntervalId)
}
}, 100)
})
})
const [, { runtimeId, tabId }] = await emittedOnce(ipcMain, 'answer')
expect(runtimeId).to.equal('foo')

View File

@@ -233,7 +233,7 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
})
describe('remote value in browser', () => {
const print = path.join(fixtures, 'module', 'print_name.js')
const print = path.join(__dirname, '..', 'spec-main', 'fixtures', 'module', 'print_name.js')
const printName = remote.require(print)
it('preserves NaN', () => {

View File

@@ -5,7 +5,7 @@
var unloadPrevented = false;
window.onbeforeunload = function() {
setTimeout(function() {
require('electron').remote.getCurrentWindow().emit('onbeforeunload');
require('electron').ipcRenderer.sendSync('onbeforeunload');
}, 0);
if (!unloadPrevented) {
unloadPrevented = true;

View File

@@ -1,4 +1,4 @@
const { ipcRenderer, remote } = require('electron')
const { ipcRenderer } = require('electron')
ipcRenderer.send('answer', process.argv, remote.getCurrentWindow().webContents.getWebPreferences())
ipcRenderer.send('answer', process.argv)
window.close()

View File

@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
const {ipcRenderer, remote} = require('electron')
remote.getCurrentWindow().focus()
const child = new remote.BrowserWindow({show: false})
child.loadURL('about:blank')
child.webContents.focus()
const currentFocused = remote.getCurrentWindow().isFocused()
const childFocused = child.isFocused()
child.close()
ipcRenderer.send('answer', currentFocused, childFocused)
</script>
</head>
<body>
</body>
</html>

View File

@@ -1,31 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<webview nodeintegration src="./a.html"></webview>
<script>
var wv = document.querySelector('webview')
wv.addEventListener('dom-ready', () => {
const { remote } = require('electron')
const webContents = remote.webContents.fromId(wv.getWebContentsId())
webContents.on('devtools-opened', function () {
var showPanelIntevalId = setInterval(function () {
if (webContents.devToolsWebContents) {
webContents.devToolsWebContents.executeJavaScript('(' + (function () {
var lastPanelId = UI.inspectorView._tabbedPane._tabs.peekLast().id
UI.inspectorView.showPanel(lastPanelId)
}).toString() + ')()')
} else {
clearInterval(showPanelIntevalId)
}
}, 100)
})
wv.openDevTools()
})
</script>
</script>
</body>
</html>

View File

@@ -1,8 +1,7 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
var size = require('electron').remote.getCurrentWindow().getSize();
window.opener.postMessage('size: ' + size[0] + ' ' + size[1], '*')
window.opener.postMessage('size: ' + outerWidth + ' ' + outerHeight, '*')
</script>
</body>
</html>

View File

@@ -90,11 +90,6 @@ declare namespace ElectronInternal {
removeProperty<T, K extends (keyof T & string)>(object: T, propertyName: K): T;
renameProperty<T, K extends (keyof T & string)>(object: T, oldName: string, newName: K): T;
moveAPI(fn: Function, oldUsage: string, newUsage: string): Function;
promisify<T extends (...args: any[]) => any>(fn: T): T;
// convertPromiseValue: Temporarily disabled until it's used
promisifyMultiArg<T extends (...args: any[]) => any>(fn: T, /*convertPromiseValue: (v: any) => any*/): T;
}
interface DesktopCapturer {