mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
14 Commits
v10.0.0-be
...
v10.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
276d17a063 | ||
|
|
f6ddfd70b3 | ||
|
|
04f652a53b | ||
|
|
04c5f4acac | ||
|
|
ca15aa70d6 | ||
|
|
96bc80ace7 | ||
|
|
e3cf1ab030 | ||
|
|
04e3a95b85 | ||
|
|
58045d85c8 | ||
|
|
08fc9dfc40 | ||
|
|
5fb3e0359d | ||
|
|
325444e1c8 | ||
|
|
bc4884788c | ||
|
|
0a766acc73 |
@@ -1 +1 @@
|
||||
10.0.0-beta.17
|
||||
10.0.0-beta.20
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "ui-page",
|
||||
"version": "1.0",
|
||||
"manifest_version": 2
|
||||
"manifest_version": 2,
|
||||
"permissions": ["<all_urls>"]
|
||||
}
|
||||
|
||||
10
spec/fixtures/pages/world-safe-preload-error.js
vendored
Normal file
10
spec/fixtures/pages/world-safe-preload-error.js
vendored
Normal 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);
|
||||
});
|
||||
8
spec/fixtures/pages/world-safe-preload.js
vendored
Normal file
8
spec/fixtures/pages/world-safe-preload.js
vendored
Normal 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);
|
||||
});
|
||||
@@ -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');
|
||||
|
||||
1
typings/internal-ambient.d.ts
vendored
1
typings/internal-ambient.d.ts
vendored
@@ -43,6 +43,7 @@ declare namespace NodeJS {
|
||||
weaklyTrackValue(value: any): void;
|
||||
clearWeaklyTrackedValues(): void;
|
||||
getWeaklyTrackedValues(): any[];
|
||||
triggerFatalErrorForTesting(): void;
|
||||
}
|
||||
|
||||
type DataPipe = {
|
||||
|
||||
Reference in New Issue
Block a user