Compare commits

..

14 Commits

Author SHA1 Message Date
Electron Bot
276d17a063 Bump v10.0.0-beta.20 2020-08-10 09:47:49 -07:00
Shelley Vohr
f6ddfd70b3 chore: revert adding Trop annotations to release notes (#24922)
This reverts commit e3cf1ab030.
2020-08-10 09:40:27 -07:00
trop[bot]
04f652a53b build: ensure symbol files are named lowercase on disk so that boto can find them (#24857)
* build: ensure symbol files are named lowercase on disk so that boto can find them

* build: only do the lower case symbol copy on case sensitive file systems (#24876)

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
2020-08-07 13:51:44 -07:00
trop[bot]
04c5f4acac fix: use non-symbols in isURLInstance check (#24860)
* fix: use non-symbols in isURLInstance check

* update patches

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-08-06 20:48:09 -07:00
Shelley Vohr
ca15aa70d6 chore: add V8 crash information to crashReporter (#24866) 2020-08-06 20:46:37 -07:00
trop[bot]
96bc80ace7 fix: do not render inactive titlebar as active on Windows (#24874)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-06 10:46:09 -07:00
Charles Kerr
e3cf1ab030 chore: add Trop annotations to release notes. (#24666) 2020-08-06 10:41:47 -07:00
trop[bot]
04e3a95b85 fix: loading dedicated/shared worker scripts over custom protocol (#24749)
* fix: add patch to avoid crash in worker with nodeintegration enabled

[worker_global_scope.cc(111)] Check failed: url_.IsValid().

* fix: loading dedicated/shared worker over custom protocols

Backports https://chromium-review.googlesource.com/c/chromium/src/+/1798250
that distinguishes loading the main script resource of dedicated/shared
worker, this allows us to register a custom URLLoaderFactory.

* spec: add crash test for worker with nodeIntegrationInWorker

* update patches

* Remove extra patchlist patches

* Fixup patch

* update patches

Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-08-06 09:59:24 -07:00
trop[bot]
58045d85c8 fix: duplicate suspend/resume events (#24843)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-06 08:30:48 -07:00
Jeremy Rose
08fc9dfc40 fix: increase max crash key value length (#24853) 2020-08-06 08:30:25 -07:00
Electron Bot
5fb3e0359d Bump v10.0.0-beta.19 2020-08-06 08:03:00 -07:00
Electron Bot
325444e1c8 Bump v10.0.0-beta.18 2020-08-05 12:10:22 -07:00
trop[bot]
bc4884788c feat: add worldSafe flag for executeJS results (#24711)
* feat: add worldSafe flag for executeJS results

* chore: do not log warning for webContents.executeJS

* Apply suggestions from code review

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

* chore: apply PR feedback

* chore: split logic a bit

* chore: allow primitives through the world safe checl

* chore: clean up per PR feedback

* chore: flip boolean logic

* chore: update per PR feedback

* chore: fix typo

* chore: fix spec

* Update web-frame.ts

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-08-05 10:24:51 -07:00
trop[bot]
0a766acc73 fix(extensions): bypass cors in requests made from background pages (#24822)
* fix: cors

* test: add tests

* Update spec-main/extensions-spec.ts

* fix: tests

* fix: add all_urls permission

Co-authored-by: sentialx <sentialx@gmail.com>
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
2020-08-04 11:21:24 -04:00
37 changed files with 508 additions and 45 deletions

View File

@@ -1 +1 @@
10.0.0-beta.17
10.0.0-beta.20

View File

@@ -348,6 +348,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
You can access this context in the dev tools by selecting the
'Electron Isolated Context' entry in the combo box at the top of the
Console tab.
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
can't unsafely cross between worlds when using `contextIsolation`. The default
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
* `nativeWindowOpen` Boolean (optional) - Whether to use native
`window.open()`. Defaults to `false`. Child windows will always have node
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently

View File

@@ -156,10 +156,16 @@ parameters in a renderer process will not result in those parameters being sent
with crashes that occur in other renderer processes or in the main process.
**Note:** Parameters have limits on the length of the keys and values. Key
names must be no longer than 39 bytes, and values must be no longer than 127
names must be no longer than 39 bytes, and values must be no longer than 20320
bytes. Keys with names longer than the maximum will be silently ignored. Key
values longer than the maximum length will be truncated.
**Note:** On linux values that are longer than 127 bytes will be chunked into
multiple keys, each 127 bytes in length. E.g. `addExtraParameter('foo', 'a'.repeat(130))`
will result in two chunked keys `foo__1` and `foo__2`, the first will contain
the first 127 bytes and the second will contain the remaining 3 bytes. On
your crash reporting backend you should stitch together keys in this format.
### `crashReporter.removeExtraParameter(key)`
* `key` String - Parameter key, must be no longer than 39 bytes.

View File

@@ -1,4 +1,5 @@
import { EventEmitter } from 'events';
import deprecate from '@electron/internal/common/api/deprecate';
const binding = process.electronBinding('web_frame');
@@ -47,14 +48,26 @@ class WebFrame extends EventEmitter {
}
}
const { hasSwitch } = process.electronBinding('command_line');
const worldSafeJS = hasSwitch('world-safe-execute-javascript') && hasSwitch('context-isolation');
// Populate the methods.
for (const name in binding) {
if (!name.startsWith('_')) { // some methods are manually populated above
// TODO(felixrieseberg): Once we can type web_frame natives, we could
// use a neat `keyof` here
(WebFrame as any).prototype[name] = function (...args: Array<any>) {
if (!worldSafeJS && name.startsWith('executeJavaScript')) {
deprecate.log(`Security Warning: webFrame.${name} was called without worldSafeExecuteJavaScript enabled. This is considered unsafe. worldSafeExecuteJavaScript will be enabled by default in Electron 12.`);
}
return binding[name](this.context, ...args);
};
// TODO(MarshallOfSound): Remove once the above deprecation is removed
if (name.startsWith('executeJavaScript')) {
(WebFrame as any).prototype[`_${name}`] = function (...args: Array<any>) {
return binding[name](this.context, ...args);
};
}
}
}

View File

@@ -12,6 +12,11 @@ export const webFrameInit = () => {
ipcRendererUtils.handle('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (
event, method: keyof WebFrameMethod, ...args: any[]
) => {
// TODO(MarshallOfSound): Remove once the world-safe-execute-javascript deprecation warning is removed
if (method.startsWith('executeJavaScript')) {
return (webFrame as any)[`_${method}`](...args);
}
// The TypeScript compiler cannot handle the sheer number of
// call signatures here and simply gives up. Incorrect invocations
// will be caught by "keyof WebFrameMethod" though.

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "10.0.0-beta.17",
"version": "10.0.0-beta.20",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -97,3 +97,4 @@ disable_unnecessary_ischromefirstrun_check.patch
use_electron_resources_in_icon_reader_service.patch
fix_include_missing_header_file.patch
cherrypick_future_chrome_commit_to_remove_superfluous_dcheck.patch
worker_feat_add_hook_to_notify_script_ready.patch

View File

@@ -0,0 +1,92 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Thu, 17 Oct 2019 18:00:32 -0700
Subject: feat: add hook to notify script ready from WorkerScriptController
In Off-the-main-thread fetch, the WorkerGlobalScope will be in a half
initialized state until the script is finished downloading.
Doc: https://docs.google.com/document/d/1JCv8TD2nPLNC2iRCp_D1OM4I3uTS0HoEobuTymaMqgw/edit
During this stage if the global object is transformed for ex: copying properties
in DidInitializeWorkerContextOnWorkerThread hook then an access to property like
location will result in a crash WorkerGlobalScope::Url() because the script has
not been set with response URL yet.
This issue cannot happen in chromium with existing usage, but can surface when an
embedder tries to integrate Node.js in the worker. Hence, this new hook is proposed
that clearly establishes the worker script is ready for evaluation with the scope
initialized.
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index e56aae188d7d8f46a4438b8e99f7e9b714c5875a..56b1155e36ed29a1a406e3824b6903346ce40793 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -404,6 +404,11 @@ class CONTENT_EXPORT ContentRendererClient {
virtual void DidInitializeWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) {}
+ // Notifies that a worker script has been downloaded, scope initialized and
+ // ready for evaluation. This function is called from the worker thread.
+ virtual void WorkerScriptReadyForEvaluationOnWorkerThread(
+ v8::Local<v8::Context> context) {}
+
// Notifies that a worker context will be destroyed. This function is called
// from the worker thread.
virtual void WillDestroyWorkerContextOnWorkerThread(
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index d5a84147b0479e18e53fc94b28c41393803c8af3..c140cc17d12b91bd8e2026751cc183a2880e79b4 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -872,6 +872,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated(
worker);
}
+void RendererBlinkPlatformImpl::WorkerScriptReadyForEvaluation(
+ const v8::Local<v8::Context>& worker) {
+ GetContentClient()->renderer()->WorkerScriptReadyForEvaluationOnWorkerThread(
+ worker);
+}
+
bool RendererBlinkPlatformImpl::IsExcludedHeaderForServiceWorkerFetchEvent(
const blink::WebString& header_name) {
return GetContentClient()
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 6c8ef4c7c37e6b951f228500b178a53d91ebdfb9..4165e0cf3d3c5b0c289f2a5d83d72d698b71e5a3 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -181,6 +181,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl {
void DidStartWorkerThread() override;
void WillStopWorkerThread() override;
void WorkerContextCreated(const v8::Local<v8::Context>& worker) override;
+ void WorkerScriptReadyForEvaluation(
+ const v8::Local<v8::Context>& worker) override;
void WorkerContextWillDestroy(const v8::Local<v8::Context>& worker) override;
bool IsExcludedHeaderForServiceWorkerFetchEvent(
const blink::WebString& header_name) override;
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 01930661416c1987790bb9b3fdd88a04e3662c7a..10d5a00c69f939b5871dea5ea9d6f059066c458d 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -619,6 +619,8 @@ class BLINK_PLATFORM_EXPORT Platform {
virtual void DidStartWorkerThread() {}
virtual void WillStopWorkerThread() {}
virtual void WorkerContextCreated(const v8::Local<v8::Context>& worker) {}
+ virtual void WorkerScriptReadyForEvaluation(
+ const v8::Local<v8::Context>& worker) {}
virtual void WorkerContextWillDestroy(const v8::Local<v8::Context>& worker) {}
virtual bool AllowScriptExtensionForServiceWorker(
const WebSecurityOrigin& script_origin) {
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index d5e60500c217a71762030688ea13c93c3a6e717c..8be5fc85456be85acbca78542106bdf6a98965a7 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -323,6 +323,8 @@ void WorkerOrWorkletScriptController::PrepareForEvaluation() {
wrapper_type_info->InstallConditionalFeatures(
context, *world_, global_object, v8::Local<v8::Object>(),
v8::Local<v8::Function>(), global_interface_template);
+
+ Platform::Current()->WorkerScriptReadyForEvaluation(context);
}
void WorkerOrWorkletScriptController::DisableEvalInternal(

View File

@@ -45,3 +45,4 @@ crypto_update_root_certificates.patch
lib_src_switch_buffer_kmaxlength_to_size_t.patch
update_tests_after_increasing_typed_array_size.patch
darwin_work_around_clock_jumping_back_in_time.patch
lib_use_non-symbols_in_isurlinstance_check.patch

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Tue, 4 Aug 2020 09:17:06 -0700
Subject: lib: use non-symbols in isURLInstance check
This slightly changes the conditional used to determine whether or
not something is a URL instance. Since Node.js adds symbols to the URL
not specified by the WHATWG, those symbols are not present in other
implementations (like Blink's) and therefore can cause false negatives.
This fixes that by slightly changing the check to properties present
in all URL instances as specified in the WHATWG spec.
Upstreamed at: https://github.com/nodejs/node/pull/34622.
diff --git a/lib/internal/url.js b/lib/internal/url.js
index 09fa9f2cf47d50d369fa3eae378f81767486e83e..e6a37dcd22c51b37838a577be44be198649eabad 100644
--- a/lib/internal/url.js
+++ b/lib/internal/url.js
@@ -1348,8 +1348,7 @@ function getPathFromURLPosix(url) {
function fileURLToPath(path) {
if (typeof path === 'string')
path = new URL(path);
- else if (path == null || !path[searchParams] ||
- !path[searchParams][searchParams])
+ else if (path == null || !path['origin'] || !path['href'])
throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
if (path.protocol !== 'file:')
throw new ERR_INVALID_URL_SCHEME('file');
@@ -1397,8 +1396,7 @@ function pathToFileURL(filepath) {
}
function toPathIfFileURL(fileURLOrPath) {
- if (fileURLOrPath == null || !fileURLOrPath[searchParams] ||
- !fileURLOrPath[searchParams][searchParams])
+ if (fileURLOrPath == null || !fileURLOrPath['origin'] || !fileURLOrPath['href'])
return fileURLOrPath;
return fileURLToPath(fileURLOrPath);
}

View File

@@ -2,8 +2,14 @@
import glob
import os
import shutil
import subprocess
import sys
import tempfile
def is_fs_case_sensitive():
with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
return(not os.path.exists(tmp_file.name.lower()))
sys.path.append(
os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "/../.."))
@@ -49,7 +55,19 @@ def main():
# The symbol server needs lowercase paths, it will fail otherwise
# So lowercase all the file paths here
if is_fs_case_sensitive():
for f in files:
lower_f = f.lower()
if lower_f != f:
if os.path.exists(lower_f):
shutil.rmtree(lower_f)
lower_dir = os.path.dirname(lower_f)
if not os.path.exists(lower_dir):
os.makedirs(lower_dir)
shutil.copy2(f, lower_f)
files = [f.lower() for f in files]
for f in files:
assert os.path.exists(f)
bucket, access_key, secret_key = s3_config()
upload_symbols(bucket, access_key, secret_key, files)

View File

@@ -24,29 +24,27 @@
if ((self = [super init])) {
NSDistributedNotificationCenter* distCenter =
[NSDistributedNotificationCenter defaultCenter];
// A notification that the screen was locked.
[distCenter addObserver:self
selector:@selector(onScreenLocked:)
name:@"com.apple.screenIsLocked"
object:nil];
// A notification that the screen was unlocked by the user.
[distCenter addObserver:self
selector:@selector(onScreenUnlocked:)
name:@"com.apple.screenIsUnlocked"
object:nil];
// A notification that the workspace posts before the machine goes to sleep.
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(isSuspending:)
name:NSWorkspaceWillSleepNotification
object:nil];
[distCenter addObserver:self
selector:@selector(isSuspending:)
name:NSWorkspaceWillSleepNotification
object:nil];
// A notification that the workspace posts when the machine wakes from
// sleep.
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(isResuming:)
name:NSWorkspaceDidWakeNotification
object:nil];
[distCenter addObserver:self
selector:@selector(isResuming:)
name:NSWorkspaceDidWakeNotification
object:nil];
}
return self;
}

View File

@@ -49,10 +49,6 @@
#include "content/public/common/web_preferences.h"
#include "electron/buildflags/buildflags.h"
#include "electron/grit/electron_resources.h"
#include "extensions/browser/extension_navigation_ui_data.h"
#include "extensions/browser/extension_protocols.h"
#include "extensions/common/constants.h"
#include "extensions/common/switches.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "net/base/escape.h"
#include "net/ssl/ssl_cert_request_info.h"
@@ -141,9 +137,12 @@
#include "content/public/browser/file_url_loader.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_message_filter.h"
#include "extensions/browser/extension_navigation_throttle.h"
#include "extensions/browser/extension_navigation_ui_data.h"
#include "extensions/browser/extension_protocols.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
@@ -151,8 +150,11 @@
#include "extensions/browser/info_map.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_map.h"
#include "extensions/browser/url_loader_factory_manager.h"
#include "extensions/common/api/mime_handler.mojom.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/switches.h"
#include "shell/browser/extensions/electron_extension_message_filter.h"
#include "shell/browser/extensions/electron_extension_system.h"
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
@@ -1277,6 +1279,16 @@ void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
URLLoaderFactoryType::kNavigation, factories);
}
void ElectronBrowserClient::
RegisterNonNetworkWorkerMainResourceURLLoaderFactories(
content::BrowserContext* browser_context,
NonNetworkURLLoaderFactoryMap* factories) {
auto* protocol_registry =
ProtocolRegistry::FromBrowserContext(browser_context);
protocol_registry->RegisterURLLoaderFactories(
URLLoaderFactoryType::kWorkerMainResource, factories);
}
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
namespace {
@@ -1483,6 +1495,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory(
if (bypass_redirect_checks)
*bypass_redirect_checks = true;
return true;
}
@@ -1501,6 +1514,9 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
factory_params->is_corb_enabled = false;
}
}
extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
browser_context, origin, is_for_isolated_world, factory_params);
}
#if defined(OS_WIN)

View File

@@ -178,6 +178,9 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
void RegisterNonNetworkNavigationURLLoaderFactories(
int frame_tree_node_id,
NonNetworkURLLoaderFactoryMap* factories) override;
void RegisterNonNetworkWorkerMainResourceURLLoaderFactories(
content::BrowserContext* browser_context,
NonNetworkURLLoaderFactoryMap* factories) override;
void RegisterNonNetworkSubresourceURLLoaderFactories(
int render_process_id,
int render_frame_id,

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 10,0,0,17
PRODUCTVERSION 10,0,0,17
FILEVERSION 10,0,0,20
PRODUCTVERSION 10,0,0,20
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -24,6 +24,14 @@ bool ElectronDesktopWindowTreeHostWin::PreHandleMSG(UINT message,
return native_window_view_->PreHandleMSG(message, w_param, l_param, result);
}
bool ElectronDesktopWindowTreeHostWin::ShouldPaintAsActive() const {
// Tell Chromium to use system default behavior when rendering inactive
// titlebar, otherwise it would render inactive titlebar as active under
// some cases.
// See also https://github.com/electron/electron/issues/24647.
return false;
}
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

View File

@@ -25,6 +25,7 @@ class ElectronDesktopWindowTreeHostWin
WPARAM w_param,
LPARAM l_param,
LRESULT* result) override;
bool ShouldPaintAsActive() const override;
bool HasNativeFrame() const override;
bool GetClientAreaInsets(gfx::Insets* insets,
HMONITOR monitor) const override;

View File

@@ -127,6 +127,7 @@ WebContentsPreferences::WebContentsPreferences(
SetDefaultBoolIfUndefined(options::kSandbox, false);
SetDefaultBoolIfUndefined(options::kNativeWindowOpen, false);
SetDefaultBoolIfUndefined(options::kContextIsolation, false);
SetDefaultBoolIfUndefined(options::kWorldSafeExecuteJavaScript, false);
SetDefaultBoolIfUndefined(options::kJavaScript, true);
SetDefaultBoolIfUndefined(options::kImages, true);
SetDefaultBoolIfUndefined(options::kTextAreasAreResizable, true);
@@ -337,6 +338,9 @@ void WebContentsPreferences::AppendCommandLineSwitches(
if (IsEnabled(options::kContextIsolation))
command_line->AppendSwitch(switches::kContextIsolation);
if (IsEnabled(options::kWorldSafeExecuteJavaScript))
command_line->AppendSwitch(switches::kWorldSafeExecuteJavaScript);
// --background-color.
std::string s;
if (GetAsString(&preference_, options::kBackgroundColor, &s)) {

View File

@@ -133,6 +133,16 @@ std::vector<v8::Local<v8::Value>> GetWeaklyTrackedValues(v8::Isolate* isolate) {
}
return locals;
}
// This causes a fatal error by creating a circular extension dependency.
void TriggerFatalErrorForTesting(v8::Isolate* isolate) {
static const char* aDeps[] = {"B"};
v8::RegisterExtension(std::make_unique<v8::Extension>("A", "", 1, aDeps));
static const char* bDeps[] = {"A"};
v8::RegisterExtension(std::make_unique<v8::Extension>("B", "", 1, bDeps));
v8::ExtensionConfiguration config(1, bDeps);
v8::Context::New(isolate, &config);
}
#endif
void Initialize(v8::Local<v8::Object> exports,
@@ -160,6 +170,7 @@ void Initialize(v8::Local<v8::Object> exports,
&RequestGarbageCollectionForTesting);
dict.SetMethod("isSameOrigin", &IsSameOrigin);
#ifdef DCHECK_IS_ON
dict.SetMethod("triggerFatalErrorForTesting", &TriggerFatalErrorForTesting);
dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues);
dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues);
dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue);

View File

@@ -31,15 +31,26 @@
#include "shell/common/node_includes.h"
#include "third_party/blink/renderer/platform/heap/process_heap.h" // nogncheck
#if !defined(MAS_BUILD)
#include "shell/common/crash_keys.h"
#endif
namespace electron {
namespace {
// Called when there is a fatal error in V8, we just crash the process here so
// we can get the stack trace.
void FatalErrorCallback(const char* location, const char* message) {
void V8FatalErrorCallback(const char* location, const char* message) {
LOG(ERROR) << "Fatal error in V8: " << location << " " << message;
ElectronBindings::Crash();
#if !defined(MAS_BUILD)
crash_keys::SetCrashKey("electron.v8-fatal.message", message);
crash_keys::SetCrashKey("electron.v8-fatal.location", location);
#endif
volatile int* zero = nullptr;
*zero = 0;
}
} // namespace
@@ -86,7 +97,7 @@ void ElectronBindings::BindProcess(v8::Isolate* isolate,
void ElectronBindings::BindTo(v8::Isolate* isolate,
v8::Local<v8::Object> process) {
isolate->SetFatalErrorHandler(FatalErrorCallback);
isolate->SetFatalErrorHandler(V8FatalErrorCallback);
gin_helper::Dictionary dict(isolate, process);
BindProcess(isolate, &dict, metrics_.get());

View File

@@ -24,7 +24,19 @@ namespace crash_keys {
namespace {
using ExtraCrashKeys = std::deque<crash_reporter::CrashKeyString<127>>;
#if defined(OS_LINUX)
// Breakpad has a flawed system of calculating the number of chunks
// we add 127 bytes to force an extra chunk
constexpr size_t kMaxCrashKeyValueSize = 20479;
#else
constexpr size_t kMaxCrashKeyValueSize = 20320;
#endif
static_assert(kMaxCrashKeyValueSize < crashpad::Annotation::kValueMaxSize,
"max crash key value length above what crashpad supports");
using ExtraCrashKeys =
std::deque<crash_reporter::CrashKeyString<kMaxCrashKeyValueSize>>;
ExtraCrashKeys& GetExtraCrashKeys() {
static base::NoDestructor<ExtraCrashKeys> extra_keys;
return *extra_keys;

View File

@@ -114,6 +114,9 @@ const char kNodeIntegration[] = "nodeIntegration";
// Enable context isolation of Electron APIs and preload script
const char kContextIsolation[] = "contextIsolation";
// Enable world safe passing of values when using "executeJavaScript"
const char kWorldSafeExecuteJavaScript[] = "worldSafeExecuteJavaScript";
// Instance ID of guest WebContents.
const char kGuestInstanceID[] = "guestInstanceId";
@@ -235,6 +238,7 @@ const char kPreloadScript[] = "preload";
const char kPreloadScripts[] = "preload-scripts";
const char kNodeIntegration[] = "node-integration";
const char kContextIsolation[] = "context-isolation";
const char kWorldSafeExecuteJavaScript[] = "world-safe-execute-javascript";
const char kGuestInstanceID[] = "guest-instance-id";
const char kOpenerID[] = "opener-id";
const char kScrollBounce[] = "scroll-bounce";

View File

@@ -62,6 +62,7 @@ extern const char kPreloadScript[];
extern const char kPreloadURL[];
extern const char kNodeIntegration[];
extern const char kContextIsolation[];
extern const char kWorldSafeExecuteJavaScript[];
extern const char kGuestInstanceID[];
extern const char kExperimentalFeatures[];
extern const char kOpenerID[];
@@ -120,6 +121,7 @@ extern const char kPreloadScript[];
extern const char kPreloadScripts[];
extern const char kNodeIntegration[];
extern const char kContextIsolation[];
extern const char kWorldSafeExecuteJavaScript[];
extern const char kGuestInstanceID[];
extern const char kOpenerID[];
extern const char kScrollBounce[];

View File

@@ -21,6 +21,7 @@
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_includes.h"
#include "shell/common/options_switches.h"
#include "shell/renderer/api/electron_api_spell_check_client.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/blink/public/common/web_cache/web_cache_resource_type_stats.h"
@@ -120,25 +121,83 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
explicit ScriptExecutionCallback(
gin_helper::Promise<v8::Local<v8::Value>> promise,
bool world_safe_result,
CompletionCallback callback)
: promise_(std::move(promise)), callback_(std::move(callback)) {}
: promise_(std::move(promise)),
world_safe_result_(world_safe_result),
callback_(std::move(callback)) {}
~ScriptExecutionCallback() override = default;
void CopyResultToCallingContextAndFinalize(
v8::Isolate* isolate,
const v8::Local<v8::Value>& result) {
blink::CloneableMessage ret;
bool success;
std::string error_message;
{
v8::TryCatch try_catch(isolate);
success = gin::ConvertFromV8(isolate, result, &ret);
if (try_catch.HasCaught()) {
auto message = try_catch.Message();
if (message.IsEmpty() ||
!gin::ConvertFromV8(isolate, message->Get(), &error_message)) {
error_message =
"An unknown exception occurred while getting the result of "
"the script";
}
}
}
if (!success) {
// Failed convert so we send undefined everywhere
if (callback_)
std::move(callback_).Run(
v8::Undefined(isolate),
v8::Exception::Error(
v8::String::NewFromUtf8(isolate, error_message.c_str())
.ToLocalChecked()));
promise_.RejectWithErrorMessage(error_message);
} else {
v8::Local<v8::Context> context = promise_.GetContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::Value> cloned_value = gin::ConvertToV8(isolate, ret);
if (callback_)
std::move(callback_).Run(cloned_value, v8::Undefined(isolate));
promise_.Resolve(cloned_value);
}
}
void Completed(
const blink::WebVector<v8::Local<v8::Value>>& result) override {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
if (!result.empty()) {
if (!result[0].IsEmpty()) {
// Right now only single results per frame is supported.
if (!callback_.is_null())
std::move(callback_).Run(result[0], v8::Undefined(isolate));
promise_.Resolve(result[0]);
v8::Local<v8::Value> value = result[0];
// Either world safe results are disabled or the result was created in
// the same world as the caller or the result is not an object and
// therefore does not have a prototype chain to protect
bool should_clone_value =
world_safe_result_ &&
!(value->IsObject() &&
promise_.GetContext() ==
value.As<v8::Object>()->CreationContext()) &&
value->IsObject();
if (should_clone_value) {
CopyResultToCallingContextAndFinalize(isolate, value);
} else {
// Right now only single results per frame is supported.
if (callback_)
std::move(callback_).Run(value, v8::Undefined(isolate));
promise_.Resolve(value);
}
} else {
const char* error_message =
"Script failed to execute, this normally means an error "
"was thrown. Check the renderer console for the error.";
if (!callback_.is_null()) {
v8::Local<v8::Context> context = promise_.GetContext();
v8::Context::Scope context_scope(context);
std::move(callback_).Run(
v8::Undefined(isolate),
v8::Exception::Error(
@@ -152,6 +211,8 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
"WebFrame was removed before script could run. This normally means "
"the underlying frame was destroyed";
if (!callback_.is_null()) {
v8::Local<v8::Context> context = promise_.GetContext();
v8::Context::Scope context_scope(context);
std::move(callback_).Run(
v8::Undefined(isolate),
v8::Exception::Error(v8::String::NewFromUtf8(isolate, error_message)
@@ -164,6 +225,7 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
private:
gin_helper::Promise<v8::Local<v8::Value>> promise_;
bool world_safe_result_;
CompletionCallback callback_;
DISALLOW_COPY_AND_ASSIGN(ScriptExecutionCallback);
@@ -491,10 +553,13 @@ v8::Local<v8::Promise> ExecuteJavaScript(gin_helper::Arguments* args,
ScriptExecutionCallback::CompletionCallback completion_callback;
args->GetNext(&completion_callback);
bool world_safe_exec_js = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWorldSafeExecuteJavaScript);
render_frame->GetWebFrame()->RequestExecuteScriptAndReturnValue(
blink::WebScriptSource(blink::WebString::FromUTF16(code)),
has_user_gesture,
new ScriptExecutionCallback(std::move(promise),
new ScriptExecutionCallback(std::move(promise), world_safe_exec_js,
std::move(completion_callback)));
return handle;
@@ -554,13 +619,16 @@ v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
blink::WebURL(GURL(url)), start_line));
}
bool world_safe_exec_js = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWorldSafeExecuteJavaScript);
// Debugging tip: if you see a crash stack trace beginning from this call,
// then it is very likely that some exception happened when executing the
// "content_script/init.js" script.
render_frame->GetWebFrame()->RequestExecuteScriptInIsolatedWorld(
world_id, &sources.front(), sources.size(), has_user_gesture,
scriptExecutionType,
new ScriptExecutionCallback(std::move(promise),
new ScriptExecutionCallback(std::move(promise), world_safe_exec_js,
std::move(completion_callback)));
return handle;

View File

@@ -200,11 +200,11 @@ bool ElectronRendererClient::ShouldFork(blink::WebLocalFrame* frame,
return http_method == "GET";
}
void ElectronRendererClient::DidInitializeWorkerContextOnWorkerThread(
void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
v8::Local<v8::Context> context) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNodeIntegrationInWorker)) {
WebWorkerObserver::GetCurrent()->ContextCreated(context);
WebWorkerObserver::GetCurrent()->WorkerScriptReadyForEvaluation(context);
}
}

View File

@@ -47,7 +47,7 @@ class ElectronRendererClient : public RendererClientBase {
const std::string& http_method,
bool is_initial_navigation,
bool is_server_redirect) override;
void DidInitializeWorkerContextOnWorkerThread(
void WorkerScriptReadyForEvaluationOnWorkerThread(
v8::Local<v8::Context> context) override;
void WillDestroyWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) override;

View File

@@ -42,7 +42,8 @@ WebWorkerObserver::~WebWorkerObserver() {
asar::ClearArchives();
}
void WebWorkerObserver::ContextCreated(v8::Local<v8::Context> worker_context) {
void WebWorkerObserver::WorkerScriptReadyForEvaluation(
v8::Local<v8::Context> worker_context) {
v8::Context::Scope context_scope(worker_context);
// Start the embed thread.

View File

@@ -21,7 +21,7 @@ class WebWorkerObserver {
// Returns the WebWorkerObserver for current worker thread.
static WebWorkerObserver* GetCurrent();
void ContextCreated(v8::Local<v8::Context> context);
void WorkerScriptReadyForEvaluation(v8::Local<v8::Context> context);
void ContextWillDestroy(v8::Local<v8::Context> context);
private:

View File

@@ -38,6 +38,8 @@ type CrashInfo = {
globalParam: 'globalValue' | undefined
addedThenRemoved: 'to-be-removed' | undefined
longParam: string | undefined
'electron.v8-fatal.location': string | undefined
'electron.v8-fatal.message': string | undefined
}
function checkCrash (expectedProcessType: string, fields: CrashInfo) {
@@ -247,23 +249,60 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
expect(crash.rendererSpecific).to.equal('rs');
expect(crash.addedThenRemoved).to.be.undefined();
});
it('contains v8 crash keys when a v8 crash occurs', async () => {
const { remotely } = await startRemoteControlApp();
const { port, waitForCrash } = await startServer();
await remotely((port: number) => {
require('electron').crashReporter.start({
submitURL: `http://127.0.0.1:${port}`,
ignoreSystemCrashHandler: true
});
}, [port]);
remotely(() => {
const { BrowserWindow } = require('electron');
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
bw.loadURL('about:blank');
bw.webContents.executeJavaScript('process._linkedBinding(\'electron_common_v8_util\').triggerFatalErrorForTesting()');
});
const crash = await waitForCrash();
expect(crash.prod).to.equal('Electron');
expect(crash._productName).to.equal('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');
});
});
});
ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
it('should truncate extra values longer than 127 characters', async () => {
function stitchLongCrashParam (crash: any, paramKey: string) {
if (crash[paramKey]) return crash[paramKey];
let chunk = 1;
let stitched = '';
while (crash[`${paramKey}__${chunk}`]) {
stitched += crash[`${paramKey}__${chunk}`];
chunk++;
}
return stitched;
}
it('should truncate extra values longer than 5 * 4096 characters', async () => {
const { port, waitForCrash } = await startServer();
const { remotely } = await startRemoteControlApp();
remotely((port: number) => {
require('electron').crashReporter.start({
submitURL: `http://127.0.0.1:${port}`,
ignoreSystemCrashHandler: true,
extra: { 'longParam': 'a'.repeat(130) }
extra: { longParam: 'a'.repeat(100000) }
});
setTimeout(() => process.crash());
}, port);
const crash = await waitForCrash();
expect(crash).to.have.property('longParam', 'a'.repeat(127));
expect(stitchLongCrashParam(crash, 'longParam')).to.have.lengthOf(160 * 127, 'crash should have truncated longParam');
});
it('should omit extra keys with names longer than the maximum', async () => {
@@ -436,7 +475,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
await remotely(() => {
require('electron').crashReporter.start({
submitURL: 'http://127.0.0.1',
extra: { 'extra1': 'hi' }
extra: { extra1: 'hi' }
});
});
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
@@ -469,8 +508,8 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
crashReporter.start({ submitURL: 'http://' });
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
bw.loadURL('about:blank');
await bw.webContents.executeJavaScript(`require('electron').crashReporter.addExtraParameter('hello', 'world')`);
return bw.webContents.executeJavaScript(`require('electron').crashReporter.getParameters()`);
await bw.webContents.executeJavaScript('require(\'electron\').crashReporter.addExtraParameter(\'hello\', \'world\')');
return bw.webContents.executeJavaScript('require(\'electron\').crashReporter.getParameters()');
});
if (process.platform === 'linux') {
// On Linux, 'getParameters' will also include the global parameters,

View File

@@ -2,12 +2,48 @@ import { expect } from 'chai';
import * as path from 'path';
import { BrowserWindow, ipcMain } from 'electron/main';
import { closeAllWindows } from './window-helpers';
import { emittedOnce } from './events-helpers';
describe('webFrame module', () => {
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
afterEach(closeAllWindows);
for (const worldSafe of [true, false]) {
it(`can use executeJavaScript with world safe mode ${worldSafe ? 'enabled' : 'disabled'}`, async () => {
const w = new BrowserWindow({
show: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
worldSafeExecuteJavaScript: worldSafe,
preload: path.join(fixtures, 'pages', 'world-safe-preload.js')
}
});
const isSafe = emittedOnce(ipcMain, 'executejs-safe');
w.loadURL('about:blank');
const [, wasSafe] = await isSafe;
expect(wasSafe).to.equal(worldSafe);
});
}
it('can use executeJavaScript with world safe mode enabled and catch conversion errors', async () => {
const w = new BrowserWindow({
show: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
worldSafeExecuteJavaScript: true,
preload: path.join(fixtures, 'pages', 'world-safe-preload-error.js')
}
});
const execError = emittedOnce(ipcMain, 'executejs-safe');
w.loadURL('about:blank');
const [, error] = await execError;
expect(error).to.not.equal(null, 'Error should not be null');
expect(error).to.have.property('message', 'Uncaught Error: An object could not be cloned.');
});
it('calls a spellcheck provider', async () => {
const w = new BrowserWindow({
show: false,

View File

@@ -441,6 +441,30 @@ describe('chromium features', () => {
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html'));
});
it('should not crash when nodeIntegration is enabled', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true
}
});
w.webContents.on('ipc-message', (event, channel, message) => {
if (channel === 'reload') {
w.webContents.reload();
} else if (channel === 'error') {
done(`unexpected error : ${message}`);
} else if (channel === 'response') {
expect(message).to.equal('Hello from serviceWorker!');
done();
}
});
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html'));
});
});
describe('navigator.geolocation', () => {

View File

@@ -16,7 +16,12 @@ describe('chrome extensions', () => {
let server: http.Server;
let url: string;
before(async () => {
server = http.createServer((req, res) => res.end(emptyPage));
server = http.createServer((req, res) => {
if (req.url === '/cors') {
res.setHeader('Access-Control-Allow-Origin', 'http://example.com');
}
res.end(emptyPage);
});
await new Promise(resolve => server.listen(0, '127.0.0.1', () => {
url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
resolve();
@@ -32,6 +37,19 @@ describe('chrome extensions', () => {
});
});
function fetch (contents: WebContents, url: string) {
return contents.executeJavaScript(`fetch(${JSON.stringify(url)})`);
}
it('bypasses CORS in requests made from extensions', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
w.loadURL(`${extension.url}bare-page.html`);
await emittedOnce(w.webContents, 'dom-ready');
await expect(fetch(w.webContents, `${url}/cors`)).to.not.be.rejectedWith(TypeError);
});
it('loads an extension', async () => {
// NB. we have to use a persist: session (i.e. non-OTR) because the
// extension registry is redirected to the main session. so installing an

View File

@@ -1,5 +1,6 @@
{
"name": "ui-page",
"version": "1.0",
"manifest_version": 2
"manifest_version": 2,
"permissions": ["<all_urls>"]
}

View File

@@ -0,0 +1,10 @@
const { ipcRenderer, webFrame } = require('electron');
webFrame.executeJavaScript(`(() => {
return Object(Symbol('a'));
})()`).catch((err) => {
// Considered safe if the object is constructed in this world
ipcRenderer.send('executejs-safe', err);
}).then(() => {
ipcRenderer.send('executejs-safe', null);
});

View File

@@ -0,0 +1,8 @@
const { ipcRenderer, webFrame } = require('electron');
webFrame.executeJavaScript(`(() => {
return {};
})()`).then((obj) => {
// Considered safe if the object is constructed in this world
ipcRenderer.send('executejs-safe', obj.constructor === Object);
});

View File

@@ -174,6 +174,15 @@ describe('node feature', () => {
});
});
describe('URL handling in the renderer process', () => {
it('can successfully handle WHATWG URLs constructed by Blink', () => {
const url = new URL('file://' + path.resolve(fixtures, 'pages', 'base-page.html'));
expect(() => {
fs.createReadStream(url);
}).to.not.throw();
});
});
describe('error thrown in main process node context', () => {
it('gets emitted as a process uncaughtException event', () => {
const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello');

View File

@@ -43,6 +43,7 @@ declare namespace NodeJS {
weaklyTrackValue(value: any): void;
clearWeaklyTrackedValues(): void;
getWeaklyTrackedValues(): any[];
triggerFatalErrorForTesting(): void;
}
type DataPipe = {