mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
34 Commits
v20.0.0-al
...
v20.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e774d4f348 | ||
|
|
3e18fba853 | ||
|
|
fa1382d980 | ||
|
|
8f738edc40 | ||
|
|
5b4577af20 | ||
|
|
cf38b9e375 | ||
|
|
bbc84b0e9a | ||
|
|
c655ed075a | ||
|
|
c48e0b8ab6 | ||
|
|
46d609fc2c | ||
|
|
03fe3c42d4 | ||
|
|
952e2905b9 | ||
|
|
7619dd51bf | ||
|
|
e2b823fb07 | ||
|
|
a1dcdc4664 | ||
|
|
2b2900a2f1 | ||
|
|
d87bad743c | ||
|
|
33670ac928 | ||
|
|
8ecbc11aa2 | ||
|
|
4ab3b1d624 | ||
|
|
bce95c4886 | ||
|
|
5b583c8808 | ||
|
|
c20d6e54c5 | ||
|
|
ff7f2a5091 | ||
|
|
10062c9a5b | ||
|
|
82a6e9bd5a | ||
|
|
10131e36f3 | ||
|
|
37078297c6 | ||
|
|
48f4ef65ed | ||
|
|
e174aac845 | ||
|
|
dd1a692aba | ||
|
|
aa05c43994 | ||
|
|
16561d08ae | ||
|
|
edc0a47c89 |
14
BUILD.gn
14
BUILD.gn
@@ -87,7 +87,10 @@ if (is_linux) {
|
||||
# implementation. In future, this file can be extended to contain
|
||||
# gtk4 stubs to switch gtk version in runtime.
|
||||
generate_stubs("electron_gtk_stubs") {
|
||||
sigs = [ "shell/browser/ui/electron_gtk.sigs" ]
|
||||
sigs = [
|
||||
"shell/browser/ui/electron_gdk_pixbuf.sigs",
|
||||
"shell/browser/ui/electron_gtk.sigs",
|
||||
]
|
||||
extra_header = "shell/browser/ui/electron_gtk.fragment"
|
||||
output_name = "electron_gtk_stubs"
|
||||
public_deps = [ "//ui/gtk:gtk_config" ]
|
||||
@@ -371,6 +374,7 @@ source_set("electron_lib") {
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//components/autofill/core/common:features",
|
||||
"//components/certificate_transparency",
|
||||
"//components/embedder_support:browser_util",
|
||||
"//components/language/core/browser",
|
||||
"//components/net_log",
|
||||
"//components/network_hints/browser",
|
||||
@@ -726,14 +730,6 @@ source_set("electron_lib") {
|
||||
|
||||
sources += get_target_outputs(":electron_fuses")
|
||||
|
||||
if (is_win && enable_win_dark_mode_window_ui) {
|
||||
sources += [
|
||||
"shell/browser/win/dark_mode.cc",
|
||||
"shell/browser/win/dark_mode.h",
|
||||
]
|
||||
libs += [ "uxtheme.lib" ]
|
||||
}
|
||||
|
||||
if (allow_runtime_configurable_key_storage) {
|
||||
defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ]
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
20.0.0-alpha.4
|
||||
20.0.0-beta.4
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -34,16 +34,6 @@ environment:
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
|
||||
GOMA_FALLBACK_ON_AUTH_FAILURE: true
|
||||
notifications:
|
||||
- provider: Webhook
|
||||
url: https://electron-mission-control.herokuapp.com/rest/appveyor-hook
|
||||
method: POST
|
||||
headers:
|
||||
x-mission-control-secret:
|
||||
secure: 90BLVPcqhJPG7d24v0q/RRray6W3wDQ8uVQlQjOHaBWkw1i8FoA1lsjr2C/v1dVok+tS2Pi6KxDctPUkwIb4T27u4RhvmcPzQhVpfwVJAG9oNtq+yKN7vzHfg7k/pojEzVdJpQLzeJGcSrZu7VY39Q==
|
||||
on_build_success: false
|
||||
on_build_failure: true
|
||||
on_build_status_changed: false
|
||||
build_script:
|
||||
- ps: >-
|
||||
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
|
||||
|
||||
@@ -19,7 +19,6 @@ buildflag_header("buildflags") {
|
||||
"ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions",
|
||||
"ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker",
|
||||
"ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture",
|
||||
"ENABLE_WIN_DARK_MODE_WINDOW_UI=$enable_win_dark_mode_window_ui",
|
||||
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -31,7 +31,4 @@ declare_args() {
|
||||
|
||||
# Enable Spellchecker support
|
||||
enable_builtin_spellchecker = true
|
||||
|
||||
# Undocumented Windows dark mode API
|
||||
enable_win_dark_mode_window_ui = false
|
||||
}
|
||||
|
||||
@@ -425,13 +425,17 @@ Possible values are:
|
||||
|
||||
* On Linux, possible types are `desktop`, `dock`, `toolbar`, `splash`,
|
||||
`notification`.
|
||||
* On macOS, possible types are `desktop`, `textured`.
|
||||
* On macOS, possible types are `desktop`, `textured`, `panel`.
|
||||
* The `textured` type adds metal gradient appearance
|
||||
(`NSWindowStyleMaskTexturedBackground`).
|
||||
* The `desktop` type places the window at the desktop background window level
|
||||
(`kCGDesktopWindowLevel - 1`). Note that desktop window will not receive
|
||||
focus, keyboard or mouse events, but you can use `globalShortcut` to receive
|
||||
input sparingly.
|
||||
* The `panel` type enables the window to float on top of full-screened apps
|
||||
by adding the `NSWindowStyleMaskNonactivatingPanel` style mask,normally
|
||||
reserved for NSPanel, at runtime. Also, the window will appear on all
|
||||
spaces (desktops).
|
||||
* On Windows, possible type is `toolbar`.
|
||||
|
||||
### Instance Events
|
||||
|
||||
BIN
docs/images/windows-taskbar-icon-overlay.png
Normal file
BIN
docs/images/windows-taskbar-icon-overlay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/images/windows-taskbar-jumplist.png
Normal file
BIN
docs/images/windows-taskbar-jumplist.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
BIN
docs/images/windows-taskbar-thumbnail-toolbar.png
Normal file
BIN
docs/images/windows-taskbar-thumbnail-toolbar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
@@ -41,10 +41,9 @@ as quoted from [MSDN][msdn-jumplist]:
|
||||
> confuse the user who does not expect that portion of the destination list to
|
||||
> change.
|
||||
|
||||

|
||||

|
||||
|
||||
> NOTE: The screenshot above is an example of general tasks of
|
||||
Internet Explorer
|
||||
> NOTE: The screenshot above is an example of general tasks for Microsoft Edge
|
||||
|
||||
Unlike the dock menu in macOS which is a real menu, user tasks in Windows work
|
||||
like application shortcuts. For example, when a user clicks a task, the program
|
||||
@@ -109,7 +108,7 @@ As quoted from [MSDN][msdn-thumbnail]:
|
||||
> For example, Windows Media Player might offer standard media transport controls
|
||||
> such as play, pause, mute, and stop.
|
||||
|
||||

|
||||

|
||||
|
||||
> NOTE: The screenshot above is an example of thumbnail toolbar of Windows
|
||||
Media Player
|
||||
@@ -176,7 +175,7 @@ As quoted from [MSDN][msdn-icon-overlay]:
|
||||
> network status, messenger status, or new mail. The user should not be
|
||||
> presented with constantly changing overlays or animations.
|
||||
|
||||

|
||||

|
||||
|
||||
> NOTE: The screenshot above is an example of overlay on a taskbar button
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ filenames = {
|
||||
"shell/browser/ui/win/notify_icon.h",
|
||||
"shell/browser/ui/win/taskbar_host.cc",
|
||||
"shell/browser/ui/win/taskbar_host.h",
|
||||
"shell/browser/win/dark_mode.cc",
|
||||
"shell/browser/win/dark_mode.h",
|
||||
"shell/browser/win/scoped_hstring.cc",
|
||||
"shell/browser/win/scoped_hstring.h",
|
||||
"shell/common/api/electron_api_native_image_win.cc",
|
||||
@@ -165,6 +167,8 @@ filenames = {
|
||||
"shell/browser/ui/cocoa/electron_native_widget_mac.mm",
|
||||
"shell/browser/ui/cocoa/electron_ns_window_delegate.h",
|
||||
"shell/browser/ui/cocoa/electron_ns_window_delegate.mm",
|
||||
"shell/browser/ui/cocoa/electron_ns_panel.h",
|
||||
"shell/browser/ui/cocoa/electron_ns_panel.mm",
|
||||
"shell/browser/ui/cocoa/electron_ns_window.h",
|
||||
"shell/browser/ui/cocoa/electron_ns_window.mm",
|
||||
"shell/browser/ui/cocoa/electron_preview_item.h",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
const { nativeTheme } = process._linkedBinding('electron_common_native_theme');
|
||||
const { nativeTheme } = process._linkedBinding('electron_browser_native_theme');
|
||||
|
||||
module.exports = nativeTheme;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const {
|
||||
Notification: ElectronNotification,
|
||||
isSupported
|
||||
} = process._linkedBinding('electron_common_notification');
|
||||
} = process._linkedBinding('electron_browser_notification');
|
||||
|
||||
ElectronNotification.isSupported = isSupported;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { createScreen } = process._linkedBinding('electron_common_screen');
|
||||
const { createScreen } = process._linkedBinding('electron_browser_screen');
|
||||
|
||||
let _screen: Electron.Screen;
|
||||
|
||||
|
||||
@@ -504,6 +504,7 @@ WebContents.prototype._callWindowOpenHandler = function (event: Electron.Event,
|
||||
if (!this._windowOpenHandler) {
|
||||
return defaultResponse;
|
||||
}
|
||||
|
||||
const response = this._windowOpenHandler(details);
|
||||
|
||||
if (typeof response !== 'object') {
|
||||
@@ -666,7 +667,15 @@ WebContents.prototype._init = function () {
|
||||
postBody,
|
||||
disposition
|
||||
};
|
||||
const result = this._callWindowOpenHandler(event, details);
|
||||
|
||||
let result: ReturnType<typeof this._callWindowOpenHandler>;
|
||||
try {
|
||||
result = this._callWindowOpenHandler(event, details);
|
||||
} catch (err) {
|
||||
event.preventDefault();
|
||||
throw err;
|
||||
}
|
||||
|
||||
const options = result.browserWindowConstructorOptions;
|
||||
if (!event.defaultPrevented) {
|
||||
openGuestWindow({
|
||||
@@ -697,7 +706,15 @@ WebContents.prototype._init = function () {
|
||||
referrer,
|
||||
postBody
|
||||
};
|
||||
const result = this._callWindowOpenHandler(event, details);
|
||||
|
||||
let result: ReturnType<typeof this._callWindowOpenHandler>;
|
||||
try {
|
||||
result = this._callWindowOpenHandler(event, details);
|
||||
} catch (err) {
|
||||
event.preventDefault();
|
||||
throw err;
|
||||
}
|
||||
|
||||
windowOpenOutlivesOpenerOption = result.outlivesOpener;
|
||||
windowOpenOverriddenOptions = result.browserWindowConstructorOptions;
|
||||
if (!event.defaultPrevented) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* out-of-process (cross-origin) are created here. "Embedder" roughly means
|
||||
* "parent."
|
||||
*/
|
||||
import { BrowserWindow } from 'electron/main';
|
||||
import { BrowserWindow, deprecate } from 'electron/main';
|
||||
import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main';
|
||||
import { parseFeatures } from '@electron/internal/browser/parse-features-string';
|
||||
|
||||
@@ -155,6 +155,10 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs,
|
||||
...parseContentTypeFormat(postData)
|
||||
} : null;
|
||||
|
||||
if (embedder.listenerCount('new-window') > 0) {
|
||||
deprecate.log('The new-window event is deprecated and will be removed. Please use contents.setWindowOpenHandler() instead.');
|
||||
}
|
||||
|
||||
embedder.emit(
|
||||
'new-window',
|
||||
event,
|
||||
|
||||
@@ -68,6 +68,10 @@ ipcMainUtils.handleSync(IPC_MESSAGES.BROWSER_SANDBOX_LOAD, async function (event
|
||||
};
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync(IPC_MESSAGES.BROWSER_NONSANDBOX_LOAD, function (event) {
|
||||
return { preloadPaths: event.sender._getPreloadPaths() };
|
||||
});
|
||||
|
||||
ipcMainInternal.on(IPC_MESSAGES.BROWSER_PRELOAD_ERROR, function (event, preloadPath: string, error: Error) {
|
||||
event.sender.emit('preload-error', event, preloadPath, error);
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ export const enum IPC_MESSAGES {
|
||||
BROWSER_GET_LAST_WEB_PREFERENCES = 'BROWSER_GET_LAST_WEB_PREFERENCES',
|
||||
BROWSER_PRELOAD_ERROR = 'BROWSER_PRELOAD_ERROR',
|
||||
BROWSER_SANDBOX_LOAD = 'BROWSER_SANDBOX_LOAD',
|
||||
BROWSER_NONSANDBOX_LOAD = 'BROWSER_NONSANDBOX_LOAD',
|
||||
BROWSER_WINDOW_CLOSE = 'BROWSER_WINDOW_CLOSE',
|
||||
BROWSER_GET_PROCESS_MEMORY_INFO = 'BROWSER_GET_PROCESS_MEMORY_INFO',
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as path from 'path';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
import type * as ipcRendererInternalModule from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import type * as ipcRendererUtilsModule from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
|
||||
const Module = require('module');
|
||||
|
||||
@@ -38,6 +39,7 @@ require('../common/reset-search-paths');
|
||||
require('@electron/internal/common/init');
|
||||
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') as typeof ipcRendererInternalModule;
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') as typeof ipcRendererUtilsModule;
|
||||
|
||||
process.getProcessMemoryInfo = () => {
|
||||
return ipcRendererInternal.invoke<Electron.ProcessMemoryInfo>(IPC_MESSAGES.BROWSER_GET_PROCESS_MEMORY_INFO);
|
||||
@@ -48,15 +50,8 @@ const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_co
|
||||
const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||
|
||||
const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
|
||||
const preloadScript = mainFrame.getWebPreference('preload');
|
||||
const preloadScripts = mainFrame.getWebPreference('preloadScripts');
|
||||
const appPath = hasSwitch('app-path') ? getSwitchValue('app-path') : null;
|
||||
|
||||
// The webContents preload script is loaded after the session preload scripts.
|
||||
if (preloadScript) {
|
||||
preloadScripts.push(preloadScript);
|
||||
}
|
||||
|
||||
// Common renderer initialization
|
||||
require('@electron/internal/renderer/common-init');
|
||||
|
||||
@@ -127,8 +122,9 @@ if (nodeIntegration) {
|
||||
}
|
||||
}
|
||||
|
||||
const { preloadPaths } = ipcRendererUtils.invokeSync(IPC_MESSAGES.BROWSER_NONSANDBOX_LOAD);
|
||||
// Load the preload scripts.
|
||||
for (const preloadScript of preloadScripts) {
|
||||
for (const preloadScript of preloadPaths) {
|
||||
try {
|
||||
Module._load(preloadScript);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { webFrame } from 'electron';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { mainFrame: webFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||
|
||||
let shouldLog: boolean | null = null;
|
||||
|
||||
const { platform, execPath, env } = process;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "20.0.0-alpha.4",
|
||||
"version": "20.0.0-beta.4",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -111,3 +111,4 @@ make_gtk_getlibgtk_public.patch
|
||||
build_disable_print_content_analysis.patch
|
||||
custom_protocols_plzserviceworker.patch
|
||||
feat_filter_out_non-shareable_windows_in_the_current_application_in.patch
|
||||
posix_replace_doubleforkandexec_with_forkandspawn.patch
|
||||
|
||||
@@ -8,10 +8,10 @@ WebPreferences of in-process child windows, rather than relying on
|
||||
process-level command line switches, as before.
|
||||
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
index 6d51f6a77c21b60ab756dbf8d4961b351a2e2b07..c6dc3cebfb32489a5e50b850ed02a066bf3f258e 100644
|
||||
index 6d51f6a77c21b60ab756dbf8d4961b351a2e2b07..26b820b3ce387e01900cec4227ff290113a833c3 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
@@ -142,6 +142,20 @@ WebPreferences::WebPreferences()
|
||||
@@ -142,6 +142,19 @@ WebPreferences::WebPreferences()
|
||||
fake_no_alloc_direct_call_for_testing_enabled(false),
|
||||
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
||||
record_whole_document(false),
|
||||
@@ -20,7 +20,6 @@ index 6d51f6a77c21b60ab756dbf8d4961b351a2e2b07..c6dc3cebfb32489a5e50b850ed02a066
|
||||
+ is_webview(false),
|
||||
+ hidden_page(false),
|
||||
+ offscreen(false),
|
||||
+ preload(base::FilePath::StringType()),
|
||||
+ node_integration(false),
|
||||
+ node_integration_in_worker(false),
|
||||
+ node_integration_in_sub_frames(false),
|
||||
@@ -33,21 +32,10 @@ index 6d51f6a77c21b60ab756dbf8d4961b351a2e2b07..c6dc3cebfb32489a5e50b850ed02a066
|
||||
accelerated_video_decode_enabled(false),
|
||||
animation_policy(
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
index 187fd8d9818693262256d5cbddd5e02659ba903d..f166517f4e70ced310253539fb6197f6b3af559c 100644
|
||||
index 187fd8d9818693262256d5cbddd5e02659ba903d..28e5178361e03d1ac851fa74214931b2332dd9c2 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
@@ -23,6 +23,10 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
!data.ReadCursiveFontFamilyMap(&out->cursive_font_family_map) ||
|
||||
!data.ReadFantasyFontFamilyMap(&out->fantasy_font_family_map) ||
|
||||
!data.ReadMathFontFamilyMap(&out->math_font_family_map) ||
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ !data.ReadPreloads(&out->preloads) ||
|
||||
+ !data.ReadPreload(&out->preload) ||
|
||||
+ // End Electron-specific WebPreferences.
|
||||
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
||||
&out->lazy_frame_loading_distance_thresholds_px) ||
|
||||
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
||||
@@ -148,6 +152,19 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -148,6 +148,19 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
data.fake_no_alloc_direct_call_for_testing_enabled();
|
||||
out->v8_cache_options = data.v8_cache_options();
|
||||
out->record_whole_document = data.record_whole_document();
|
||||
@@ -68,7 +56,7 @@ index 187fd8d9818693262256d5cbddd5e02659ba903d..f166517f4e70ced310253539fb6197f6
|
||||
out->accelerated_video_decode_enabled =
|
||||
data.accelerated_video_decode_enabled();
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
index 59947bfd3c042e5f7d3993967fece9b519f93472..368ec7fb398409cd6386269934eaffcce356793f 100644
|
||||
index 59947bfd3c042e5f7d3993967fece9b519f93472..cb2a53f6147767394585ed371744d8a140aace71 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
@@ -10,6 +10,7 @@
|
||||
@@ -79,17 +67,15 @@ index 59947bfd3c042e5f7d3993967fece9b519f93472..368ec7fb398409cd6386269934eaffcc
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "third_party/blink/public/common/common_export.h"
|
||||
#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-shared.h"
|
||||
@@ -154,6 +155,22 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||
@@ -154,6 +155,20 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||
blink::mojom::V8CacheOptions v8_cache_options;
|
||||
bool record_whole_document;
|
||||
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ std::vector<base::FilePath> preloads;
|
||||
+ bool context_isolation;
|
||||
+ bool is_webview;
|
||||
+ bool hidden_page;
|
||||
+ bool offscreen;
|
||||
+ base::FilePath preload;
|
||||
+ bool node_integration;
|
||||
+ bool node_integration_in_worker;
|
||||
+ bool node_integration_in_sub_frames;
|
||||
@@ -103,7 +89,7 @@ index 59947bfd3c042e5f7d3993967fece9b519f93472..368ec7fb398409cd6386269934eaffcc
|
||||
// only controls whether or not the "document.cookie" field is properly
|
||||
// connected to the backing store, for instance if you wanted to be able to
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
index bd425a5869477de9095c6a41c683609d065b08c0..3a5d7450aa40e8a7614e83f316d3d0fb59583381 100644
|
||||
index bd425a5869477de9095c6a41c683609d065b08c0..c7ba3ff52c9de01028c9e2be214e20cd91cbf309 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
@@ -6,6 +6,7 @@
|
||||
@@ -114,15 +100,11 @@ index bd425a5869477de9095c6a41c683609d065b08c0..3a5d7450aa40e8a7614e83f316d3d0fb
|
||||
#include "mojo/public/cpp/bindings/struct_traits.h"
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "third_party/blink/public/common/common_export.h"
|
||||
@@ -428,6 +429,60 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -428,6 +429,52 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
return r.record_whole_document;
|
||||
}
|
||||
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ static const std::vector<base::FilePath>& preloads(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.preloads;
|
||||
+ }
|
||||
+
|
||||
+ static bool context_isolation(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.context_isolation;
|
||||
+ }
|
||||
@@ -139,10 +121,6 @@ index bd425a5869477de9095c6a41c683609d065b08c0..3a5d7450aa40e8a7614e83f316d3d0fb
|
||||
+ return r.offscreen;
|
||||
+ }
|
||||
+
|
||||
+ static const base::FilePath& preload(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.preload;
|
||||
+ }
|
||||
+
|
||||
+ static bool node_integration(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.node_integration;
|
||||
+ }
|
||||
@@ -176,7 +154,7 @@ index bd425a5869477de9095c6a41c683609d065b08c0..3a5d7450aa40e8a7614e83f316d3d0fb
|
||||
return r.cookie_enabled;
|
||||
}
|
||||
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
index 738ac646e075f7a37ba2a263c7799a755e63d3fb..80d6ae17d34ef5c7fd14e21c4e1ee3ed4ef9c5fe 100644
|
||||
index 738ac646e075f7a37ba2a263c7799a755e63d3fb..40ab2a4b9a1f9de5f201502adebc166a48fccd35 100644
|
||||
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
@@ -10,6 +10,7 @@ import "third_party/blink/public/mojom/v8_cache_options.mojom";
|
||||
@@ -187,17 +165,15 @@ index 738ac646e075f7a37ba2a263c7799a755e63d3fb..80d6ae17d34ef5c7fd14e21c4e1ee3ed
|
||||
|
||||
enum PointerType {
|
||||
kPointerNone = 1, // 1 << 0
|
||||
@@ -206,6 +207,22 @@ struct WebPreferences {
|
||||
@@ -206,6 +207,20 @@ struct WebPreferences {
|
||||
V8CacheOptions v8_cache_options;
|
||||
bool record_whole_document;
|
||||
|
||||
+ // Begin Electron-specific WebPreferences.
|
||||
+ array<mojo_base.mojom.FilePath> preloads;
|
||||
+ bool context_isolation;
|
||||
+ bool is_webview;
|
||||
+ bool hidden_page;
|
||||
+ bool offscreen;
|
||||
+ mojo_base.mojom.FilePath preload;
|
||||
+ bool node_integration;
|
||||
+ bool node_integration_in_worker;
|
||||
+ bool node_integration_in_sub_frames;
|
||||
|
||||
@@ -59,10 +59,10 @@ index ad366d0fd4c3a637d75a102ab56984f0d01bfc04..d63eb133fd4bab1ea309bb8c742acf88
|
||||
// true if register successfully, or false if 1) the specificied |accelerator|
|
||||
// has been registered by another caller or other native applications, or
|
||||
diff --git a/content/browser/media/media_keys_listener_manager_impl.cc b/content/browser/media/media_keys_listener_manager_impl.cc
|
||||
index b954f8dde00d4f5257223c464e9145a6bef48900..b58999f295586a61bcc2648488a8b28f15d80a7e 100644
|
||||
index b954f8dde00d4f5257223c464e9145a6bef48900..ee9da826014d3aae9675daac6cdbc0f447a14efd 100644
|
||||
--- a/content/browser/media/media_keys_listener_manager_impl.cc
|
||||
+++ b/content/browser/media/media_keys_listener_manager_impl.cc
|
||||
@@ -56,7 +56,12 @@ bool MediaKeysListenerManagerImpl::StartWatchingMediaKey(
|
||||
@@ -56,7 +56,11 @@ bool MediaKeysListenerManagerImpl::StartWatchingMediaKey(
|
||||
CanActiveMediaSessionControllerReceiveEvents();
|
||||
|
||||
// Tell the underlying MediaKeysListener to listen for the key.
|
||||
@@ -71,12 +71,11 @@ index b954f8dde00d4f5257223c464e9145a6bef48900..b58999f295586a61bcc2648488a8b28f
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ !media_key_handling_enabled_ &&
|
||||
+#endif // BUILDFLAG(IS_MAC)
|
||||
+ should_start_watching &&
|
||||
+ media_keys_listener_ &&
|
||||
+ should_start_watching && media_keys_listener_ &&
|
||||
!media_keys_listener_->StartWatchingMediaKey(key_code)) {
|
||||
return false;
|
||||
}
|
||||
@@ -239,18 +244,18 @@ void MediaKeysListenerManagerImpl::StartListeningForMediaKeysIfNecessary() {
|
||||
@@ -239,6 +243,7 @@ void MediaKeysListenerManagerImpl::StartListeningForMediaKeysIfNecessary() {
|
||||
#endif
|
||||
|
||||
if (system_media_controls_) {
|
||||
@@ -84,19 +83,22 @@ index b954f8dde00d4f5257223c464e9145a6bef48900..b58999f295586a61bcc2648488a8b28f
|
||||
system_media_controls_->AddObserver(this);
|
||||
system_media_controls_notifier_ =
|
||||
std::make_unique<SystemMediaControlsNotifier>(
|
||||
system_media_controls_.get());
|
||||
- } else {
|
||||
- // If we can't access system media controls, then directly listen for media
|
||||
- // key keypresses instead.
|
||||
- media_keys_listener_ = ui::MediaKeysListener::Create(
|
||||
- this, ui::MediaKeysListener::Scope::kGlobal);
|
||||
- DCHECK(media_keys_listener_);
|
||||
@@ -251,6 +256,19 @@ void MediaKeysListenerManagerImpl::StartListeningForMediaKeysIfNecessary() {
|
||||
DCHECK(media_keys_listener_);
|
||||
}
|
||||
|
||||
+ // Directly listen for media key keypresses when using GlobalShortcuts.
|
||||
+ media_keys_listener_ = ui::MediaKeysListener::Create(
|
||||
+ this, ui::MediaKeysListener::Scope::kGlobal);
|
||||
+ DCHECK(media_keys_listener_);
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ // Chromium's implementation of SystemMediaControls falls
|
||||
+ // down into MPRemoteCommandCenter, which makes it such that an app will not
|
||||
+ // will not receive remote control events until it begins playing audio.
|
||||
+ // If there's not already a MediaKeysListener instance, create one so
|
||||
+ // that globalShortcuts work correctly.
|
||||
+ if (!media_keys_listener_) {
|
||||
+ media_keys_listener_ = ui::MediaKeysListener::Create(
|
||||
+ this, ui::MediaKeysListener::Scope::kGlobal);
|
||||
+ DCHECK(media_keys_listener_);
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
EnsureAuxiliaryServices();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGtk public
|
||||
Subject: Make gtk::GetLibGtk and gtk::GetLibGdkPixbuf public
|
||||
|
||||
Allows embedders to get a handle to the gtk library
|
||||
already loaded in the process.
|
||||
Allows embedders to get a handle to the gtk and
|
||||
gdk_pixbuf libraries already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..40b706ed7cde206e98274025148604760b7477f9 100644
|
||||
index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..65b097cfab72b92f301968715eb218ef0e468567 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -86,12 +86,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
@@ -66,11 +66,6 @@ void* GetLibGio() {
|
||||
return libgio;
|
||||
}
|
||||
|
||||
-void* GetLibGdkPixbuf() {
|
||||
- static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
- return libgdk_pixbuf;
|
||||
-}
|
||||
-
|
||||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
return libgtk4;
|
||||
}
|
||||
|
||||
@@ -23,10 +35,15 @@ index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..40b706ed7cde206e9827402514860476
|
||||
bool LoadGtk3() {
|
||||
if (!GetLibGtk3(false))
|
||||
return false;
|
||||
@@ -134,6 +128,12 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -134,6 +123,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
+void* GetLibGdkPixbuf() {
|
||||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGtk() {
|
||||
+ if (GtkCheckVersion(4))
|
||||
+ return GetLibGtk4();
|
||||
@@ -37,13 +54,16 @@ index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..40b706ed7cde206e9827402514860476
|
||||
static bool loaded = LoadGtkImpl();
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 57e55b9e749b43d327deff449a530e1f435a8e8b..2245974f91be4a691d82f54b55e12e44ae2000c5 100644
|
||||
index 57e55b9e749b43d327deff449a530e1f435a8e8b..37720be9e393d192b3b7db13a007431a9ce77ddc 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -34,6 +34,9 @@ using SkColor = uint32_t;
|
||||
@@ -34,6 +34,12 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gtk library in the process.
|
||||
+void* GetLibGtk();
|
||||
+
|
||||
|
||||
@@ -0,0 +1,641 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Darshan Sen <raisinten@gmail.com>
|
||||
Date: Fri, 17 Jun 2022 13:19:32 +0530
|
||||
Subject: posix: Replace DoubleForkAndExec() with ForkAndSpawn()
|
||||
|
||||
The DoubleForkAndExec() function was taking over 622 milliseconds to run
|
||||
on macOS 11 (BigSur) on Intel i5-1038NG7. I did some debugging by adding
|
||||
some custom traces and found that the fork() syscall is the bottleneck
|
||||
here, i.e., the first fork() takes around 359 milliseconds and the
|
||||
nested fork() takes around 263 milliseconds. Replacing the nested fork()
|
||||
and exec() with posix_spawn() reduces the time consumption to 257
|
||||
milliseconds!
|
||||
|
||||
See https://github.com/libuv/libuv/pull/3064 to know why fork() is so
|
||||
slow on macOS and why posix_spawn() is a better replacement.
|
||||
|
||||
Another point to note is that even base::LaunchProcess() from Chromium
|
||||
calls posix_spawnp() on macOS -
|
||||
https://source.chromium.org/chromium/chromium/src/+/8f8d82dea0fa8f11f57c74dbb65126f8daba58f7:base/process/launch_mac.cc;l=295-296
|
||||
|
||||
Change-Id: I25c6ee9629a1ae5d0c32b361b56a1ce0b4b0fd26
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3641386
|
||||
Reviewed-by: Mark Mentovai <mark@chromium.org>
|
||||
Commit-Queue: Mark Mentovai <mark@chromium.org>
|
||||
|
||||
diff --git a/third_party/crashpad/crashpad/AUTHORS b/third_party/crashpad/crashpad/AUTHORS
|
||||
index 8dcac3238870920d374b86033d05d77ebde351e9..02103924332eddbd158c04f8a395bb4a247e8bd9 100644
|
||||
--- a/third_party/crashpad/crashpad/AUTHORS
|
||||
+++ b/third_party/crashpad/crashpad/AUTHORS
|
||||
@@ -12,3 +12,4 @@ Opera Software ASA
|
||||
Vewd Software AS
|
||||
LG Electronics, Inc.
|
||||
MIPS Technologies, Inc.
|
||||
+Darshan Sen <raisinten@gmail.com>
|
||||
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
|
||||
index 53c2dadac9d6cb74a3c3e07d9917e9162474b8a0..6761db0e8ac5edba6fac90cdf79e38dfd4cd9d70 100644
|
||||
--- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
|
||||
+++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
|
||||
@@ -45,7 +45,7 @@
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/address_sanitizer.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
#include "util/posix/scoped_mmap.h"
|
||||
#include "util/posix/signals.h"
|
||||
|
||||
@@ -454,7 +454,7 @@ bool CrashpadClient::StartHandler(
|
||||
|
||||
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
|
||||
argv.push_back("--shared-client-connection");
|
||||
- if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
|
||||
+ if (!ForkAndSpawn(argv, nullptr, handler_sock.get(), false, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -609,7 +609,7 @@ bool CrashpadClient::StartJavaHandlerForClient(
|
||||
int socket) {
|
||||
std::vector<std::string> argv = BuildAppProcessArgs(
|
||||
class_name, database, metrics_dir, url, annotations, arguments, socket);
|
||||
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
|
||||
@@ -658,7 +658,7 @@ bool CrashpadClient::StartHandlerWithLinkerForClient(
|
||||
annotations,
|
||||
arguments,
|
||||
socket);
|
||||
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -692,7 +692,7 @@ bool CrashpadClient::StartHandlerForClient(
|
||||
|
||||
argv.push_back(FormatArgumentInt("initial-client-fd", socket));
|
||||
|
||||
- return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
|
||||
+ return ForkAndSpawn(argv, nullptr, socket, true, nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
|
||||
index 39e35678ecdd036f8c8ae27c973c27102b77da96..84385f2569f2bd00ca8aed0aa332fb450b2de1d3 100644
|
||||
--- a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
|
||||
+++ b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "util/mach/notify_server.h"
|
||||
#include "util/misc/clock.h"
|
||||
#include "util/misc/implicit_cast.h"
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@@ -343,7 +343,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
|
||||
// this parent process, which was probably using the exception server now
|
||||
// being restarted. The handler can’t monitor itself for its own crashes via
|
||||
// this interface.
|
||||
- if (!DoubleForkAndExec(
|
||||
+ if (!ForkAndSpawn(
|
||||
argv,
|
||||
nullptr,
|
||||
server_write_fd.get(),
|
||||
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
|
||||
index 9e58d94aa499fdb7271a78ea21a1dcc1b12e3a52..3caa3b987b35be575558a312026cf6f19485c418 100644
|
||||
--- a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
|
||||
+++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/misc/metrics.h"
|
||||
#include "util/misc/uuid.h"
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@@ -266,12 +266,11 @@ bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(
|
||||
argv.push_back("--always_allow_feedback");
|
||||
}
|
||||
|
||||
- if (!DoubleForkAndExec(argv,
|
||||
- nullptr /* envp */,
|
||||
- file_writer.fd() /* preserve_fd */,
|
||||
- false /* use_path */,
|
||||
- nullptr /* child_function */)) {
|
||||
- LOG(ERROR) << "DoubleForkAndExec failed";
|
||||
+ if (!ForkAndSpawn(argv,
|
||||
+ nullptr /* envp */,
|
||||
+ file_writer.fd() /* preserve_fd */,
|
||||
+ false /* use_path */,
|
||||
+ nullptr /* child_function */)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
|
||||
return false;
|
||||
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn
|
||||
index 30f03b13f3a018e6a96b0689452a344992675c23..64fa2e9e89b700d8a265a9737cb6a30a210cdffe 100644
|
||||
--- a/third_party/crashpad/crashpad/util/BUILD.gn
|
||||
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
|
||||
@@ -296,10 +296,10 @@ crashpad_static_library("util") {
|
||||
sources += [
|
||||
"posix/close_multiple.cc",
|
||||
"posix/close_multiple.h",
|
||||
- "posix/double_fork_and_exec.cc",
|
||||
- "posix/double_fork_and_exec.h",
|
||||
"posix/drop_privileges.cc",
|
||||
"posix/drop_privileges.h",
|
||||
+ "posix/fork_and_spawn.cc",
|
||||
+ "posix/fork_and_spawn.h",
|
||||
"posix/process_info.h",
|
||||
|
||||
# These map signals to and from strings. While Fuchsia defines some of
|
||||
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc b/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc
|
||||
deleted file mode 100644
|
||||
index 1960430954d3f6459dce688493db5c42047567b0..0000000000000000000000000000000000000000
|
||||
--- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc
|
||||
+++ /dev/null
|
||||
@@ -1,166 +0,0 @@
|
||||
-// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
-//
|
||||
-// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-// you may not use this file except in compliance with the License.
|
||||
-// You may obtain a copy of the License at
|
||||
-//
|
||||
-// http://www.apache.org/licenses/LICENSE-2.0
|
||||
-//
|
||||
-// Unless required by applicable law or agreed to in writing, software
|
||||
-// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-// See the License for the specific language governing permissions and
|
||||
-// limitations under the License.
|
||||
-
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
-
|
||||
-#include <stdlib.h>
|
||||
-#include <string.h>
|
||||
-#include <sys/wait.h>
|
||||
-#include <unistd.h>
|
||||
-
|
||||
-#include "base/check_op.h"
|
||||
-#include "base/logging.h"
|
||||
-#include "base/posix/eintr_wrapper.h"
|
||||
-#include "base/strings/stringprintf.h"
|
||||
-#include "util/posix/close_multiple.h"
|
||||
-
|
||||
-namespace crashpad {
|
||||
-
|
||||
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
|
||||
- const std::vector<std::string>* envp,
|
||||
- int preserve_fd,
|
||||
- bool use_path,
|
||||
- void (*child_function)()) {
|
||||
- DCHECK(!envp || !use_path);
|
||||
-
|
||||
- // argv_c contains const char* pointers and is terminated by nullptr. This is
|
||||
- // suitable for passing to execv(). Although argv_c is not used in the parent
|
||||
- // process, it must be built in the parent process because it’s unsafe to do
|
||||
- // so in the child or grandchild process.
|
||||
- std::vector<const char*> argv_c;
|
||||
- argv_c.reserve(argv.size() + 1);
|
||||
- for (const std::string& argument : argv) {
|
||||
- argv_c.push_back(argument.c_str());
|
||||
- }
|
||||
- argv_c.push_back(nullptr);
|
||||
-
|
||||
- std::vector<const char*> envp_c;
|
||||
- if (envp) {
|
||||
- envp_c.reserve(envp->size() + 1);
|
||||
- for (const std::string& variable : *envp) {
|
||||
- envp_c.push_back(variable.c_str());
|
||||
- }
|
||||
- envp_c.push_back(nullptr);
|
||||
- }
|
||||
-
|
||||
- // Double-fork(). The three processes involved are parent, child, and
|
||||
- // grandchild. The grandchild will call execv(). The child exits immediately
|
||||
- // after spawning the grandchild, so the grandchild becomes an orphan and its
|
||||
- // parent process ID becomes 1. This relieves the parent and child of the
|
||||
- // responsibility to reap the grandchild with waitpid() or similar. The
|
||||
- // grandchild is expected to outlive the parent process, so the parent
|
||||
- // shouldn’t be concerned with reaping it. This approach means that accidental
|
||||
- // early termination of the handler process will not result in a zombie
|
||||
- // process.
|
||||
- pid_t pid = fork();
|
||||
- if (pid < 0) {
|
||||
- PLOG(ERROR) << "fork";
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- if (pid == 0) {
|
||||
- // Child process.
|
||||
-
|
||||
- if (child_function) {
|
||||
- child_function();
|
||||
- }
|
||||
-
|
||||
- // Call setsid(), creating a new process group and a new session, both led
|
||||
- // by this process. The new process group has no controlling terminal. This
|
||||
- // disconnects it from signals generated by the parent process’ terminal.
|
||||
- //
|
||||
- // setsid() is done in the child instead of the grandchild so that the
|
||||
- // grandchild will not be a session leader. If it were a session leader, an
|
||||
- // accidental open() of a terminal device without O_NOCTTY would make that
|
||||
- // terminal the controlling terminal.
|
||||
- //
|
||||
- // It’s not desirable for the grandchild to have a controlling terminal. The
|
||||
- // grandchild manages its own lifetime, such as by monitoring clients on its
|
||||
- // own and exiting when it loses all clients and when it deems it
|
||||
- // appropraite to do so. It may serve clients in different process groups or
|
||||
- // sessions than its original client, and receiving signals intended for its
|
||||
- // original client’s process group could be harmful in that case.
|
||||
- PCHECK(setsid() != -1) << "setsid";
|
||||
-
|
||||
- pid = fork();
|
||||
- if (pid < 0) {
|
||||
- PLOG(FATAL) << "fork";
|
||||
- }
|
||||
-
|
||||
- if (pid > 0) {
|
||||
- // Child process.
|
||||
-
|
||||
- // _exit() instead of exit(), because fork() was called.
|
||||
- _exit(EXIT_SUCCESS);
|
||||
- }
|
||||
-
|
||||
- // Grandchild process.
|
||||
-
|
||||
- CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
|
||||
-
|
||||
- // &argv_c[0] is a pointer to a pointer to const char data, but because of
|
||||
- // how C (not C++) works, execvp() wants a pointer to a const pointer to
|
||||
- // char data. It modifies neither the data nor the pointers, so the
|
||||
- // const_cast is safe.
|
||||
- char* const* argv_for_execv = const_cast<char* const*>(&argv_c[0]);
|
||||
-
|
||||
- if (envp) {
|
||||
- // This cast is safe for the same reason that the argv_for_execv cast is.
|
||||
- char* const* envp_for_execv = const_cast<char* const*>(&envp_c[0]);
|
||||
- execve(argv_for_execv[0], argv_for_execv, envp_for_execv);
|
||||
- PLOG(FATAL) << "execve " << argv_for_execv[0];
|
||||
- }
|
||||
-
|
||||
- if (use_path) {
|
||||
- execvp(argv_for_execv[0], argv_for_execv);
|
||||
- PLOG(FATAL) << "execvp " << argv_for_execv[0];
|
||||
- }
|
||||
-
|
||||
- execv(argv_for_execv[0], argv_for_execv);
|
||||
- PLOG(FATAL) << "execv " << argv_for_execv[0];
|
||||
- }
|
||||
-
|
||||
- // waitpid() for the child, so that it does not become a zombie process. The
|
||||
- // child normally exits quickly.
|
||||
- //
|
||||
- // Failures from this point on may result in the accumulation of a zombie, but
|
||||
- // should not be considered fatal. Log only warnings, but don’t treat these
|
||||
- // failures as a failure of the function overall.
|
||||
- int status;
|
||||
- pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
|
||||
- if (wait_pid == -1) {
|
||||
- PLOG(WARNING) << "waitpid";
|
||||
- return true;
|
||||
- }
|
||||
- DCHECK_EQ(wait_pid, pid);
|
||||
-
|
||||
- if (WIFSIGNALED(status)) {
|
||||
- int sig = WTERMSIG(status);
|
||||
- LOG(WARNING) << base::StringPrintf(
|
||||
- "intermediate process terminated by signal %d (%s)%s",
|
||||
- sig,
|
||||
- strsignal(sig),
|
||||
- WCOREDUMP(status) ? " (core dumped)" : "");
|
||||
- } else if (!WIFEXITED(status)) {
|
||||
- LOG(WARNING) << base::StringPrintf(
|
||||
- "intermediate process: unknown termination 0x%x", status);
|
||||
- } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
|
||||
- LOG(WARNING) << "intermediate process exited with code "
|
||||
- << WEXITSTATUS(status);
|
||||
- }
|
||||
-
|
||||
- return true;
|
||||
-}
|
||||
-
|
||||
-} // namespace crashpad
|
||||
diff --git a/third_party/crashpad/crashpad/util/posix/fork_and_spawn.cc b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c6a95bbfdcba45995b0034789c8bdb4423a25642
|
||||
--- /dev/null
|
||||
+++ b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.cc
|
||||
@@ -0,0 +1,235 @@
|
||||
+// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
+//
|
||||
+// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+// you may not use this file except in compliance with the License.
|
||||
+// You may obtain a copy of the License at
|
||||
+//
|
||||
+// http://www.apache.org/licenses/LICENSE-2.0
|
||||
+//
|
||||
+// Unless required by applicable law or agreed to in writing, software
|
||||
+// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+// See the License for the specific language governing permissions and
|
||||
+// limitations under the License.
|
||||
+
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <spawn.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <sys/wait.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include "base/check.h"
|
||||
+#include "base/check_op.h"
|
||||
+#include "base/logging.h"
|
||||
+#include "base/posix/eintr_wrapper.h"
|
||||
+#include "base/strings/stringprintf.h"
|
||||
+#include "build/build_config.h"
|
||||
+#include "util/posix/close_multiple.h"
|
||||
+
|
||||
+extern char** environ;
|
||||
+
|
||||
+namespace crashpad {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+#if BUILDFLAG(IS_APPLE)
|
||||
+
|
||||
+class PosixSpawnAttr {
|
||||
+ public:
|
||||
+ PosixSpawnAttr() {
|
||||
+ PCHECK((errno = posix_spawnattr_init(&attr_)) == 0)
|
||||
+ << "posix_spawnattr_init";
|
||||
+ }
|
||||
+
|
||||
+ PosixSpawnAttr(const PosixSpawnAttr&) = delete;
|
||||
+ PosixSpawnAttr& operator=(const PosixSpawnAttr&) = delete;
|
||||
+
|
||||
+ ~PosixSpawnAttr() {
|
||||
+ PCHECK((errno = posix_spawnattr_destroy(&attr_)) == 0)
|
||||
+ << "posix_spawnattr_destroy";
|
||||
+ }
|
||||
+
|
||||
+ void SetFlags(short flags) {
|
||||
+ PCHECK((errno = posix_spawnattr_setflags(&attr_, flags)) == 0)
|
||||
+ << "posix_spawnattr_setflags";
|
||||
+ }
|
||||
+
|
||||
+ const posix_spawnattr_t* Get() const { return &attr_; }
|
||||
+
|
||||
+ private:
|
||||
+ posix_spawnattr_t attr_;
|
||||
+};
|
||||
+
|
||||
+class PosixSpawnFileActions {
|
||||
+ public:
|
||||
+ PosixSpawnFileActions() {
|
||||
+ PCHECK((errno = posix_spawn_file_actions_init(&file_actions_)) == 0)
|
||||
+ << "posix_spawn_file_actions_init";
|
||||
+ }
|
||||
+
|
||||
+ PosixSpawnFileActions(const PosixSpawnFileActions&) = delete;
|
||||
+ PosixSpawnFileActions& operator=(const PosixSpawnFileActions&) = delete;
|
||||
+
|
||||
+ ~PosixSpawnFileActions() {
|
||||
+ PCHECK((errno = posix_spawn_file_actions_destroy(&file_actions_)) == 0)
|
||||
+ << "posix_spawn_file_actions_destroy";
|
||||
+ }
|
||||
+
|
||||
+ void AddInheritedFileDescriptor(int fd) {
|
||||
+ PCHECK((errno = posix_spawn_file_actions_addinherit_np(&file_actions_,
|
||||
+ fd)) == 0)
|
||||
+ << "posix_spawn_file_actions_addinherit_np";
|
||||
+ }
|
||||
+
|
||||
+ const posix_spawn_file_actions_t* Get() const { return &file_actions_; }
|
||||
+
|
||||
+ private:
|
||||
+ posix_spawn_file_actions_t file_actions_;
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+bool ForkAndSpawn(const std::vector<std::string>& argv,
|
||||
+ const std::vector<std::string>* envp,
|
||||
+ int preserve_fd,
|
||||
+ bool use_path,
|
||||
+ void (*child_function)()) {
|
||||
+ // argv_c contains const char* pointers and is terminated by nullptr. This is
|
||||
+ // suitable for passing to posix_spawn() or posix_spawnp(). Although argv_c is
|
||||
+ // not used in the parent process, it must be built in the parent process
|
||||
+ // because it’s unsafe to do so in the child.
|
||||
+ std::vector<const char*> argv_c;
|
||||
+ argv_c.reserve(argv.size() + 1);
|
||||
+ for (const std::string& argument : argv) {
|
||||
+ argv_c.push_back(argument.c_str());
|
||||
+ }
|
||||
+ argv_c.push_back(nullptr);
|
||||
+
|
||||
+ std::vector<const char*> envp_c;
|
||||
+ if (envp) {
|
||||
+ envp_c.reserve(envp->size() + 1);
|
||||
+ for (const std::string& variable : *envp) {
|
||||
+ envp_c.push_back(variable.c_str());
|
||||
+ }
|
||||
+ envp_c.push_back(nullptr);
|
||||
+ }
|
||||
+
|
||||
+ // The three processes involved are parent, child, and grandchild. The child
|
||||
+ // exits immediately after spawning the grandchild, so the grandchild becomes
|
||||
+ // an orphan and its parent process ID becomes 1. This relieves the parent and
|
||||
+ // child of the responsibility to reap the grandchild with waitpid() or
|
||||
+ // similar. The grandchild is expected to outlive the parent process, so the
|
||||
+ // parent shouldn’t be concerned with reaping it. This approach means that
|
||||
+ // accidental early termination of the handler process will not result in a
|
||||
+ // zombie process.
|
||||
+ pid_t pid = fork();
|
||||
+ if (pid < 0) {
|
||||
+ PLOG(ERROR) << "fork";
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (pid == 0) {
|
||||
+ // Child process.
|
||||
+
|
||||
+ if (child_function) {
|
||||
+ child_function();
|
||||
+ }
|
||||
+
|
||||
+ // Call setsid(), creating a new process group and a new session, both led
|
||||
+ // by this process. The new process group has no controlling terminal. This
|
||||
+ // disconnects it from signals generated by the parent process’ terminal.
|
||||
+ //
|
||||
+ // setsid() is done in the child instead of the grandchild so that the
|
||||
+ // grandchild will not be a session leader. If it were a session leader, an
|
||||
+ // accidental open() of a terminal device without O_NOCTTY would make that
|
||||
+ // terminal the controlling terminal.
|
||||
+ //
|
||||
+ // It’s not desirable for the grandchild to have a controlling terminal. The
|
||||
+ // grandchild manages its own lifetime, such as by monitoring clients on its
|
||||
+ // own and exiting when it loses all clients and when it deems it
|
||||
+ // appropraite to do so. It may serve clients in different process groups or
|
||||
+ // sessions than its original client, and receiving signals intended for its
|
||||
+ // original client’s process group could be harmful in that case.
|
||||
+ PCHECK(setsid() != -1) << "setsid";
|
||||
+
|
||||
+ // &argv_c[0] is a pointer to a pointer to const char data, but because of
|
||||
+ // how C (not C++) works, posix_spawn() and posix_spawnp() want a pointer to
|
||||
+ // a const pointer to char data. They modifies neither the data nor the
|
||||
+ // pointers, so the const_cast is safe.
|
||||
+ char* const* argv_for_spawn = const_cast<char* const*>(argv_c.data());
|
||||
+
|
||||
+ // This cast is safe for the same reason that the argv_for_spawn cast is.
|
||||
+ char* const* envp_for_spawn =
|
||||
+ envp ? const_cast<char* const*>(envp_c.data()) : environ;
|
||||
+
|
||||
+#if BUILDFLAG(IS_APPLE)
|
||||
+ PosixSpawnAttr attr;
|
||||
+ attr.SetFlags(POSIX_SPAWN_CLOEXEC_DEFAULT);
|
||||
+
|
||||
+ PosixSpawnFileActions file_actions;
|
||||
+ for (int fd = 0; fd <= STDERR_FILENO; ++fd) {
|
||||
+ file_actions.AddInheritedFileDescriptor(fd);
|
||||
+ }
|
||||
+ file_actions.AddInheritedFileDescriptor(preserve_fd);
|
||||
+
|
||||
+ const posix_spawnattr_t* attr_p = attr.Get();
|
||||
+ const posix_spawn_file_actions_t* file_actions_p = file_actions.Get();
|
||||
+#else
|
||||
+ CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
|
||||
+
|
||||
+ const posix_spawnattr_t* attr_p = nullptr;
|
||||
+ const posix_spawn_file_actions_t* file_actions_p = nullptr;
|
||||
+#endif
|
||||
+
|
||||
+ auto posix_spawn_fp = use_path ? posix_spawnp : posix_spawn;
|
||||
+ if ((errno = posix_spawn_fp(&pid,
|
||||
+ argv_for_spawn[0],
|
||||
+ file_actions_p,
|
||||
+ attr_p,
|
||||
+ argv_for_spawn,
|
||||
+ envp_for_spawn)) != 0) {
|
||||
+ PLOG(FATAL) << (use_path ? "posix_spawnp" : "posix_spawn");
|
||||
+ }
|
||||
+
|
||||
+ // _exit() instead of exit(), because fork() was called.
|
||||
+ _exit(EXIT_SUCCESS);
|
||||
+ }
|
||||
+
|
||||
+ // waitpid() for the child, so that it does not become a zombie process. The
|
||||
+ // child normally exits quickly.
|
||||
+ //
|
||||
+ // Failures from this point on may result in the accumulation of a zombie, but
|
||||
+ // should not be considered fatal. Log only warnings, but don’t treat these
|
||||
+ // failures as a failure of the function overall.
|
||||
+ int status;
|
||||
+ pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
|
||||
+ if (wait_pid == -1) {
|
||||
+ PLOG(WARNING) << "waitpid";
|
||||
+ return true;
|
||||
+ }
|
||||
+ DCHECK_EQ(wait_pid, pid);
|
||||
+
|
||||
+ if (WIFSIGNALED(status)) {
|
||||
+ int sig = WTERMSIG(status);
|
||||
+ LOG(WARNING) << base::StringPrintf(
|
||||
+ "intermediate process terminated by signal %d (%s)%s",
|
||||
+ sig,
|
||||
+ strsignal(sig),
|
||||
+ WCOREDUMP(status) ? " (core dumped)" : "");
|
||||
+ } else if (!WIFEXITED(status)) {
|
||||
+ LOG(WARNING) << base::StringPrintf(
|
||||
+ "intermediate process: unknown termination 0x%x", status);
|
||||
+ } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
|
||||
+ LOG(WARNING) << "intermediate process exited with code "
|
||||
+ << WEXITSTATUS(status);
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+} // namespace crashpad
|
||||
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.h
|
||||
similarity index 76%
|
||||
rename from third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h
|
||||
rename to third_party/crashpad/crashpad/util/posix/fork_and_spawn.h
|
||||
index 02fc0f28f196b447132a2dcfaebdaaa5a916a38a..fc55aa3a37652e4ba18c66db90124abd9cad2e51 100644
|
||||
--- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h
|
||||
+++ b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.h
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
-#ifndef CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
|
||||
-#define CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
|
||||
+#ifndef CRASHPAD_UTIL_POSIX_FORK_AND_SPAWN_H_
|
||||
+#define CRASHPAD_UTIL_POSIX_FORK_AND_SPAWN_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -23,7 +23,7 @@ namespace crashpad {
|
||||
//! \brief Executes a (grand-)child process.
|
||||
//!
|
||||
//! The grandchild process will be started through the
|
||||
-//! double-`fork()`-and-`execv()` pattern. This allows the grandchild to fully
|
||||
+//! `fork()`-and-`posix_spawn()` pattern. This allows the grandchild to fully
|
||||
//! disassociate from the parent. The grandchild will not be a member of the
|
||||
//! parent’s process group or session and will not have a controlling terminal,
|
||||
//! providing isolation from signals not intended for it. The grandchild’s
|
||||
@@ -37,7 +37,7 @@ namespace crashpad {
|
||||
//! \param[in] argv The argument vector to start the grandchild process with.
|
||||
//! `argv[0]` is used as the path to the executable.
|
||||
//! \param[in] envp A vector of environment variables of the form `var=value` to
|
||||
-//! be passed to `execve()`. If this value is `nullptr`, the current
|
||||
+//! be passed to `posix_spawn()`. If this value is `nullptr`, the current
|
||||
//! environment is used.
|
||||
//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild
|
||||
//! process. This file descriptor is inherited in addition to the three file
|
||||
@@ -45,16 +45,13 @@ namespace crashpad {
|
||||
//! if no additional file descriptors are to be inherited.
|
||||
//! \param[in] use_path Whether to consult the `PATH` environment variable when
|
||||
//! requested to start an executable at a non-absolute path. If `false`,
|
||||
-//! `execv()`, which does not consult `PATH`, will be used. If `true`,
|
||||
-//! `execvp()`, which does consult `PATH`, will be used.
|
||||
+//! `posix_spawn()`, which does not consult `PATH`, will be used. If `true`,
|
||||
+//! `posix_spawnp()`, which does consult `PATH`, will be used.
|
||||
//! \param[in] child_function If not `nullptr`, this function will be called in
|
||||
-//! the intermediate child process, prior to the second `fork()`. Take note
|
||||
+//! the intermediate child process, prior to the `posix_spawn()`. Take note
|
||||
//! that this function will run in the context of a forked process, and must
|
||||
//! be safe for that purpose.
|
||||
//!
|
||||
-//! Setting both \a envp to a value other than `nullptr` and \a use_path to
|
||||
-//! `true` is not currently supported.
|
||||
-//!
|
||||
//! \return `true` on success, and `false` on failure with a message logged.
|
||||
//! Only failures that occur in the parent process that indicate a definite
|
||||
//! failure to start the the grandchild are reported in the return value.
|
||||
@@ -63,12 +60,12 @@ namespace crashpad {
|
||||
//! terminating. The caller assumes the responsibility for detecting such
|
||||
//! failures, for example, by observing a failure to perform a successful
|
||||
//! handshake with the grandchild process.
|
||||
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
|
||||
- const std::vector<std::string>* envp,
|
||||
- int preserve_fd,
|
||||
- bool use_path,
|
||||
- void (*child_function)());
|
||||
+bool ForkAndSpawn(const std::vector<std::string>& argv,
|
||||
+ const std::vector<std::string>* envp,
|
||||
+ int preserve_fd,
|
||||
+ bool use_path,
|
||||
+ void (*child_function)());
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
-#endif // CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
|
||||
+#endif // CRASHPAD_UTIL_POSIX_FORK_AND_SPAWN_H_
|
||||
@@ -28,6 +28,7 @@ const defaultOptions = [
|
||||
'--mode=debug',
|
||||
'default',
|
||||
`--skip-tests=${DISABLED_TESTS.join(',')}`,
|
||||
'--flaky-tests=dontcare',
|
||||
'--shell',
|
||||
utils.getAbsoluteElectronExec(),
|
||||
'-J'
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
"Tarball": "debian_bullseye_arm_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_arm64": {
|
||||
"Sha1Sum": "de38cc85d51a820c3307e1937f3c2d3cedcce988",
|
||||
"Sha1Sum": "5a56c1ef714154ea5003bcafb16f21b0f8dde023",
|
||||
"SysrootDir": "debian_bullseye_arm64-sysroot",
|
||||
"Tarball": "debian_bullseye_arm64_sysroot.tar.xz"
|
||||
"Tarball": "debian_sid_arm64_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_armel": {
|
||||
"Sha1Sum": "db15aab39af3cfbc55a8ff0386943db1b78a1eab",
|
||||
|
||||
@@ -174,4 +174,4 @@ bool Converter<ui::NativeTheme::ThemeSource>::FromV8(
|
||||
|
||||
} // namespace gin
|
||||
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_common_native_theme, Initialize)
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_browser_native_theme, Initialize)
|
||||
|
||||
@@ -304,4 +304,4 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_common_notification, Initialize)
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_browser_notification, Initialize)
|
||||
|
||||
@@ -185,4 +185,4 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_common_screen, Initialize)
|
||||
NODE_LINKED_MODULE_CONTEXT_AWARE(electron_browser_screen, Initialize)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
|
||||
#include "chrome/browser/ui/views/eye_dropper/eye_dropper.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/embedder_support/user_agent_utils.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "components/prefs/scoped_user_pref_update.h"
|
||||
#include "components/security_state/content/content_utils.h"
|
||||
@@ -659,9 +660,10 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
||||
session_.Reset(isolate, session.ToV8());
|
||||
|
||||
web_contents->SetUserAgentOverride(blink::UserAgentOverride::UserAgentOnly(
|
||||
GetBrowserContext()->GetUserAgent()),
|
||||
false);
|
||||
absl::optional<std::string> user_agent_override =
|
||||
GetBrowserContext()->GetUserAgentOverride();
|
||||
if (user_agent_override)
|
||||
SetUserAgent(*user_agent_override);
|
||||
web_contents->SetUserData(kElectronApiWebContentsKey,
|
||||
std::make_unique<UserDataLink>(GetWeakPtr()));
|
||||
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
|
||||
@@ -867,9 +869,10 @@ void WebContents::InitWithSessionAndOptions(
|
||||
|
||||
AutofillDriverFactory::CreateForWebContents(web_contents());
|
||||
|
||||
web_contents()->SetUserAgentOverride(blink::UserAgentOverride::UserAgentOnly(
|
||||
GetBrowserContext()->GetUserAgent()),
|
||||
false);
|
||||
absl::optional<std::string> user_agent_override =
|
||||
GetBrowserContext()->GetUserAgentOverride();
|
||||
if (user_agent_override)
|
||||
SetUserAgent(*user_agent_override);
|
||||
|
||||
if (IsGuest()) {
|
||||
NativeWindow* owner_window = nullptr;
|
||||
@@ -2150,8 +2153,7 @@ void WebContents::LoadURL(const GURL& url,
|
||||
|
||||
std::string user_agent;
|
||||
if (options.Get("userAgent", &user_agent))
|
||||
web_contents()->SetUserAgentOverride(
|
||||
blink::UserAgentOverride::UserAgentOnly(user_agent), false);
|
||||
SetUserAgent(user_agent);
|
||||
|
||||
std::string extra_headers;
|
||||
if (options.Get("extraHeaders", &extra_headers))
|
||||
@@ -2366,8 +2368,12 @@ void WebContents::ForcefullyCrashRenderer() {
|
||||
}
|
||||
|
||||
void WebContents::SetUserAgent(const std::string& user_agent) {
|
||||
web_contents()->SetUserAgentOverride(
|
||||
blink::UserAgentOverride::UserAgentOnly(user_agent), false);
|
||||
blink::UserAgentOverride ua_override;
|
||||
ua_override.ua_string_override = user_agent;
|
||||
if (!user_agent.empty())
|
||||
ua_override.ua_metadata_override = embedder_support::GetUserAgentMetadata();
|
||||
|
||||
web_contents()->SetUserAgentOverride(ua_override, false);
|
||||
}
|
||||
|
||||
std::string WebContents::GetUserAgent() {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/chrome_version.h"
|
||||
#include "components/embedder_support/user_agent_utils.h"
|
||||
#include "components/net_log/chrome_net_log.h"
|
||||
#include "components/network_hints/common/network_hints.mojom.h"
|
||||
#include "content/browser/keyboard_lock/keyboard_lock_service_impl.h" // nogncheck
|
||||
@@ -496,11 +497,6 @@ void ElectronBrowserClient::OverrideWebkitPrefs(
|
||||
? blink::mojom::PreferredColorScheme::kDark
|
||||
: blink::mojom::PreferredColorScheme::kLight;
|
||||
|
||||
auto preloads =
|
||||
SessionPreferences::GetValidPreloads(web_contents->GetBrowserContext());
|
||||
if (!preloads.empty())
|
||||
prefs->preloads = preloads;
|
||||
|
||||
SetFontDefaults(prefs);
|
||||
|
||||
// Custom preferences of guest page.
|
||||
@@ -1159,6 +1155,10 @@ void ElectronBrowserClient::SetUserAgent(const std::string& user_agent) {
|
||||
user_agent_override_ = user_agent;
|
||||
}
|
||||
|
||||
blink::UserAgentMetadata ElectronBrowserClient::GetUserAgentMetadata() {
|
||||
return embedder_support::GetUserAgentMetadata();
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
|
||||
int frame_tree_node_id,
|
||||
ukm::SourceIdObj ukm_source_id,
|
||||
|
||||
@@ -93,6 +93,7 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
|
||||
std::string GetUserAgent() override;
|
||||
void SetUserAgent(const std::string& user_agent);
|
||||
blink::UserAgentMetadata GetUserAgentMetadata() override;
|
||||
|
||||
content::SerialDelegate* GetSerialDelegate() override;
|
||||
|
||||
|
||||
@@ -107,8 +107,6 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
|
||||
protocol_registry_(base::WrapUnique(new ProtocolRegistry)),
|
||||
in_memory_(in_memory),
|
||||
ssl_config_(network::mojom::SSLConfig::New()) {
|
||||
user_agent_ = ElectronBrowserClient::Get()->GetUserAgent();
|
||||
|
||||
// Read options.
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
use_cache_ = !command_line->HasSwitch(switches::kDisableHttpCache);
|
||||
@@ -305,6 +303,11 @@ ElectronBrowserContext::GetSpecialStoragePolicy() {
|
||||
}
|
||||
|
||||
std::string ElectronBrowserContext::GetUserAgent() const {
|
||||
return user_agent_.value_or(ElectronBrowserClient::Get()->GetUserAgent());
|
||||
}
|
||||
|
||||
absl::optional<std::string> ElectronBrowserContext::GetUserAgentOverride()
|
||||
const {
|
||||
return user_agent_;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
|
||||
void SetUserAgent(const std::string& user_agent);
|
||||
std::string GetUserAgent() const;
|
||||
absl::optional<std::string> GetUserAgentOverride() const;
|
||||
bool CanUseHttpCache() const;
|
||||
int GetMaxCacheSize() const;
|
||||
ResolveProxyHelper* GetResolveProxyHelper();
|
||||
@@ -170,7 +171,7 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
std::unique_ptr<predictors::PreconnectManager> preconnect_manager_;
|
||||
std::unique_ptr<ProtocolRegistry> protocol_registry_;
|
||||
|
||||
std::string user_agent_;
|
||||
absl::optional<std::string> user_agent_;
|
||||
base::FilePath path_;
|
||||
bool in_memory_ = false;
|
||||
bool use_cache_ = true;
|
||||
|
||||
@@ -371,6 +371,10 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
|
||||
electron::UninitializeElectron_gtk();
|
||||
}
|
||||
|
||||
electron::InitializeElectron_gdk_pixbuf(gtk::GetLibGdkPixbuf());
|
||||
CHECK(electron::IsElectron_gdk_pixbufInitialized())
|
||||
<< "Failed to initialize libgdk_pixbuf-2.0.so.0";
|
||||
|
||||
// Chromium does not respect GTK dark theme setting, but they may change
|
||||
// in future and this code might be no longer needed. Check the Chromium
|
||||
// issue to keep updated:
|
||||
|
||||
@@ -27,6 +27,9 @@ void NativeBrowserViewViews::SetAutoResizeFlags(uint8_t flags) {
|
||||
|
||||
void NativeBrowserViewViews::UpdateDraggableRegions(
|
||||
const std::vector<mojom::DraggableRegionPtr>& regions) {
|
||||
if (&draggable_regions_ != ®ions)
|
||||
draggable_regions_ = mojo::Clone(regions);
|
||||
|
||||
// We need to snap the regions to the bounds of the current BrowserView.
|
||||
// For example, if an attached BrowserView is draggable but its bounds are
|
||||
// { x: 200, y: 100, width: 300, height: 300 }
|
||||
@@ -35,12 +38,10 @@ void NativeBrowserViewViews::UpdateDraggableRegions(
|
||||
// assumed that the regions begin in the top left corner as they
|
||||
// would for the main client window.
|
||||
auto const offset = GetBounds().OffsetFromOrigin();
|
||||
auto snapped_regions = mojo::Clone(regions);
|
||||
for (auto& snapped_region : snapped_regions) {
|
||||
for (auto& snapped_region : draggable_regions_) {
|
||||
snapped_region->bounds.Offset(offset);
|
||||
}
|
||||
|
||||
draggable_region_ = DraggableRegionsToSkRegion(snapped_regions);
|
||||
draggable_region_ = DraggableRegionsToSkRegion(draggable_regions_);
|
||||
}
|
||||
|
||||
void NativeBrowserViewViews::SetAutoResizeProportions(
|
||||
@@ -128,6 +129,12 @@ void NativeBrowserViewViews::SetBounds(const gfx::Rect& bounds) {
|
||||
auto* view = iwc_view->GetView();
|
||||
view->SetBoundsRect(bounds);
|
||||
ResetAutoResizeProportions();
|
||||
|
||||
view->InvalidateLayout();
|
||||
view->SchedulePaint();
|
||||
|
||||
// Ensure draggable regions are properly updated to reflect new bounds.
|
||||
UpdateDraggableRegions(draggable_regions_);
|
||||
}
|
||||
|
||||
gfx::Rect NativeBrowserViewViews::GetBounds() {
|
||||
|
||||
@@ -165,6 +165,8 @@ class NativeWindowMac : public NativeWindow,
|
||||
|
||||
void UpdateVibrancyRadii(bool fullscreen);
|
||||
|
||||
void UpdateWindowOriginalFrame();
|
||||
|
||||
// Set the attribute of NSWindow while work around a bug of zoom button.
|
||||
bool HasStyleMask(NSUInteger flag) const;
|
||||
void SetStyleMask(bool on, NSUInteger flag);
|
||||
|
||||
@@ -317,7 +317,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
params.bounds = bounds;
|
||||
params.delegate = this;
|
||||
params.type = views::Widget::InitParams::TYPE_WINDOW;
|
||||
params.native_widget = new ElectronNativeWidgetMac(this, styleMask, widget());
|
||||
params.native_widget =
|
||||
new ElectronNativeWidgetMac(this, windowType, styleMask, widget());
|
||||
widget()->Init(std::move(params));
|
||||
SetCanResize(resizable);
|
||||
window_ = static_cast<ElectronNSWindow*>(
|
||||
@@ -355,6 +356,10 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
NSWindowCollectionBehaviorIgnoresCycle)];
|
||||
}
|
||||
|
||||
if (windowType == "panel") {
|
||||
[window_ setLevel:NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
bool focusable;
|
||||
if (options.Get(options::kFocusable, &focusable) && !focusable)
|
||||
[window_ setDisableKeyOrMainWindow:YES];
|
||||
@@ -451,7 +456,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
SetContentView(new views::View());
|
||||
AddContentViewLayers();
|
||||
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
original_level_ = [window_ level];
|
||||
}
|
||||
|
||||
@@ -613,7 +618,7 @@ void NativeWindowMac::Maximize() {
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
[window_ zoom:nil];
|
||||
|
||||
if (!is_visible) {
|
||||
@@ -657,7 +662,7 @@ void NativeWindowMac::Minimize() {
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
[window_ miniaturize:nil];
|
||||
}
|
||||
|
||||
@@ -701,7 +706,7 @@ void NativeWindowMac::SetFullScreen(bool fullscreen) {
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
|
||||
// This needs to be set here because it can be the case that
|
||||
// SetFullScreen is called by a user before windowWillEnterFullScreen
|
||||
@@ -737,6 +742,7 @@ void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
|
||||
|
||||
[window_ setFrame:cocoa_bounds display:YES animate:animate];
|
||||
user_set_bounds_maximized_ = IsMaximized() ? true : false;
|
||||
UpdateWindowOriginalFrame();
|
||||
}
|
||||
|
||||
gfx::Rect NativeWindowMac::GetBounds() {
|
||||
@@ -810,6 +816,7 @@ void NativeWindowMac::MoveTop() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetResizable(bool resizable) {
|
||||
ScopedDisableResize disable_resize;
|
||||
SetStyleMask(resizable, NSWindowStyleMaskResizable);
|
||||
SetCanResize(resizable);
|
||||
}
|
||||
@@ -1009,7 +1016,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
|
||||
// Take note of the current window size and level
|
||||
if (IsNormal()) {
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
original_level_ = [window_ level];
|
||||
}
|
||||
|
||||
@@ -1406,6 +1413,10 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::UpdateWindowOriginalFrame() {
|
||||
original_frame_ = [window_ frame];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
NSVisualEffectView* vibrantView = [window_ vibrantView];
|
||||
|
||||
@@ -1510,12 +1521,15 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
|
||||
void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
|
||||
window_button_visibility_ = visible;
|
||||
// The visibility of window buttons are managed by |buttons_proxy_| if the
|
||||
// style is customButtonsOnHover.
|
||||
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover)
|
||||
if (buttons_proxy_) {
|
||||
if (visible)
|
||||
[buttons_proxy_ redraw];
|
||||
[buttons_proxy_ setVisible:visible];
|
||||
else
|
||||
}
|
||||
|
||||
if (title_bar_style_ != TitleBarStyle::kCustomButtonsOnHover)
|
||||
InternalSetWindowButtonVisibility(visible);
|
||||
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
}
|
||||
|
||||
|
||||
@@ -876,6 +876,11 @@ bool NativeWindowViews::IsMovable() {
|
||||
void NativeWindowViews::SetMinimizable(bool minimizable) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), minimizable, WS_MINIMIZEBOX);
|
||||
if (titlebar_overlay_enabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
minimizable_ = minimizable;
|
||||
}
|
||||
@@ -891,6 +896,11 @@ bool NativeWindowViews::IsMinimizable() {
|
||||
void NativeWindowViews::SetMaximizable(bool maximizable) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), maximizable, WS_MAXIMIZEBOX);
|
||||
if (titlebar_overlay_enabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
maximizable_ = maximizable;
|
||||
}
|
||||
@@ -926,6 +936,11 @@ void NativeWindowViews::SetClosable(bool closable) {
|
||||
} else {
|
||||
EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
|
||||
}
|
||||
if (titlebar_overlay_enabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_NATIVE_WIDGET_MAC_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_NATIVE_WIDGET_MAC_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ui/views/widget/native_widget_mac.h"
|
||||
|
||||
namespace electron {
|
||||
@@ -14,6 +16,7 @@ class NativeWindowMac;
|
||||
class ElectronNativeWidgetMac : public views::NativeWidgetMac {
|
||||
public:
|
||||
ElectronNativeWidgetMac(NativeWindowMac* shell,
|
||||
const std::string& window_type,
|
||||
NSUInteger style_mask,
|
||||
views::internal::NativeWidgetDelegate* delegate);
|
||||
~ElectronNativeWidgetMac() override;
|
||||
@@ -29,6 +32,7 @@ class ElectronNativeWidgetMac : public views::NativeWidgetMac {
|
||||
|
||||
private:
|
||||
NativeWindowMac* shell_;
|
||||
std::string window_type_;
|
||||
NSUInteger style_mask_;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,24 +4,34 @@
|
||||
|
||||
#include "shell/browser/ui/cocoa/electron_native_widget_mac.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shell/browser/ui/cocoa/electron_ns_panel.h"
|
||||
#include "shell/browser/ui/cocoa/electron_ns_window.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
ElectronNativeWidgetMac::ElectronNativeWidgetMac(
|
||||
NativeWindowMac* shell,
|
||||
const std::string& window_type,
|
||||
NSUInteger style_mask,
|
||||
views::internal::NativeWidgetDelegate* delegate)
|
||||
: views::NativeWidgetMac(delegate),
|
||||
shell_(shell),
|
||||
window_type_(window_type),
|
||||
style_mask_(style_mask) {}
|
||||
|
||||
ElectronNativeWidgetMac::~ElectronNativeWidgetMac() = default;
|
||||
|
||||
NativeWidgetMacNSWindow* ElectronNativeWidgetMac::CreateNSWindow(
|
||||
const remote_cocoa::mojom::CreateWindowParams* params) {
|
||||
return [[[ElectronNSWindow alloc] initWithShell:shell_
|
||||
styleMask:style_mask_] autorelease];
|
||||
if (window_type_ == "panel") {
|
||||
return [[[ElectronNSPanel alloc] initWithShell:shell_
|
||||
styleMask:style_mask_] autorelease];
|
||||
} else {
|
||||
return [[[ElectronNSWindow alloc] initWithShell:shell_
|
||||
styleMask:style_mask_] autorelease];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
17
shell/browser/ui/cocoa/electron_ns_panel.h
Normal file
17
shell/browser/ui/cocoa/electron_ns_panel.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2022 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_NS_PANEL_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_NS_PANEL_H_
|
||||
|
||||
#include "shell/browser/ui/cocoa/electron_ns_window.h"
|
||||
|
||||
@interface ElectronNSPanel : ElectronNSWindow
|
||||
@property NSWindowStyleMask styleMask;
|
||||
@property NSWindowStyleMask originalStyleMask;
|
||||
- (id)initWithShell:(electron::NativeWindowMac*)shell
|
||||
styleMask:(NSUInteger)styleMask;
|
||||
@end
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_NS_PANEL_H_
|
||||
39
shell/browser/ui/cocoa/electron_ns_panel.mm
Normal file
39
shell/browser/ui/cocoa/electron_ns_panel.mm
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2022 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/ui/cocoa/electron_ns_panel.h"
|
||||
|
||||
@implementation ElectronNSPanel
|
||||
|
||||
@synthesize originalStyleMask;
|
||||
|
||||
- (id)initWithShell:(electron::NativeWindowMac*)shell
|
||||
styleMask:(NSUInteger)styleMask {
|
||||
if (self = [super initWithShell:shell styleMask:styleMask]) {
|
||||
originalStyleMask = styleMask;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@dynamic styleMask;
|
||||
// The Nonactivating mask is reserverd for NSPanel,
|
||||
// but we can use this workaround to add it at runtime
|
||||
- (NSWindowStyleMask)styleMask {
|
||||
return originalStyleMask | NSWindowStyleMaskNonactivatingPanel;
|
||||
}
|
||||
|
||||
- (void)setStyleMask:(NSWindowStyleMask)styleMask {
|
||||
originalStyleMask = styleMask;
|
||||
// Notify change of style mask.
|
||||
[super setStyleMask:styleMask];
|
||||
}
|
||||
|
||||
- (void)setCollectionBehavior:(NSWindowCollectionBehavior)collectionBehavior {
|
||||
NSWindowCollectionBehavior panelBehavior =
|
||||
(NSWindowCollectionBehaviorCanJoinAllSpaces |
|
||||
NSWindowCollectionBehaviorFullScreenAuxiliary);
|
||||
[super setCollectionBehavior:collectionBehavior | panelBehavior];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -16,13 +16,14 @@ class NativeWindowMac;
|
||||
// Prevents window from resizing during the scope.
|
||||
class ScopedDisableResize {
|
||||
public:
|
||||
ScopedDisableResize() { disable_resize_ = true; }
|
||||
~ScopedDisableResize() { disable_resize_ = false; }
|
||||
ScopedDisableResize() { disable_resize_++; }
|
||||
~ScopedDisableResize() { disable_resize_--; }
|
||||
|
||||
static bool IsResizeDisabled() { return disable_resize_; }
|
||||
// True if there are 1+ nested ScopedDisableResize objects in the scope
|
||||
static bool IsResizeDisabled() { return disable_resize_ > 0; }
|
||||
|
||||
private:
|
||||
static bool disable_resize_;
|
||||
static int disable_resize_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
bool ScopedDisableResize::disable_resize_ = false;
|
||||
int ScopedDisableResize::disable_resize_ = 0;
|
||||
|
||||
} // namespace electron
|
||||
|
||||
|
||||
@@ -203,6 +203,7 @@ using FullScreenTransitionState =
|
||||
// windowDidDeminiaturize
|
||||
level_ = [window level];
|
||||
shell_->SetWindowLevel(NSNormalWindowLevel);
|
||||
shell_->UpdateWindowOriginalFrame();
|
||||
}
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification*)notification {
|
||||
|
||||
@@ -10,53 +10,78 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "shell/browser/ui/drag_util.h"
|
||||
|
||||
namespace electron {
|
||||
// Contents largely copied from
|
||||
// chrome/browser/download/drag_download_item_mac.mm.
|
||||
|
||||
@interface DragDownloadItemSource : NSObject <NSDraggingSource>
|
||||
@end
|
||||
|
||||
@implementation DragDownloadItemSource
|
||||
|
||||
- (NSDragOperation)draggingSession:(NSDraggingSession*)session
|
||||
sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
|
||||
return NSDragOperationEvery;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace {
|
||||
|
||||
// Write information about the file being dragged to the pasteboard.
|
||||
void AddFilesToPasteboard(NSPasteboard* pasteboard,
|
||||
const std::vector<base::FilePath>& files) {
|
||||
NSMutableArray* fileList = [NSMutableArray array];
|
||||
for (const base::FilePath& file : files)
|
||||
[fileList addObject:base::SysUTF8ToNSString(file.value())];
|
||||
[pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType]
|
||||
owner:nil];
|
||||
[pasteboard setPropertyList:fileList forType:NSFilenamesPboardType];
|
||||
id<NSDraggingSource> GetDraggingSource() {
|
||||
static id<NSDraggingSource> source = [[DragDownloadItemSource alloc] init];
|
||||
return source;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace electron {
|
||||
|
||||
void DragFileItems(const std::vector<base::FilePath>& files,
|
||||
const gfx::Image& icon,
|
||||
gfx::NativeView view) {
|
||||
NSPasteboard* pasteboard =
|
||||
[NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
|
||||
AddFilesToPasteboard(pasteboard, files);
|
||||
auto* native_view = view.GetNativeNSView();
|
||||
NSPoint current_position =
|
||||
[[native_view window] mouseLocationOutsideOfEventStream];
|
||||
current_position =
|
||||
[native_view backingAlignedRect:NSMakeRect(current_position.x,
|
||||
current_position.y, 0, 0)
|
||||
options:NSAlignAllEdgesOutward]
|
||||
.origin;
|
||||
|
||||
NSMutableArray* file_items = [NSMutableArray array];
|
||||
for (auto const& file : files) {
|
||||
NSURL* file_url =
|
||||
[NSURL fileURLWithPath:base::SysUTF8ToNSString(file.value())];
|
||||
NSDraggingItem* file_item = [[[NSDraggingItem alloc]
|
||||
initWithPasteboardWriter:file_url] autorelease];
|
||||
NSImage* file_image = icon.ToNSImage();
|
||||
NSSize image_size = file_image.size;
|
||||
NSRect image_rect = NSMakeRect(current_position.x - image_size.width / 2,
|
||||
current_position.y - image_size.height / 2,
|
||||
image_size.width, image_size.height);
|
||||
[file_item setDraggingFrame:image_rect contents:file_image];
|
||||
[file_items addObject:file_item];
|
||||
}
|
||||
|
||||
// Synthesize a drag event, since we don't have access to the actual event
|
||||
// that initiated a drag (possibly consumed by the Web UI, for example).
|
||||
NSWindow* window = [view.GetNativeNSView() window];
|
||||
NSPoint position = [window mouseLocationOutsideOfEventStream];
|
||||
NSPoint position = [[native_view window] mouseLocationOutsideOfEventStream];
|
||||
NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
|
||||
NSEvent* dragEvent = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDragged
|
||||
location:position
|
||||
modifierFlags:NSEventMaskLeftMouseDragged
|
||||
timestamp:eventTime
|
||||
windowNumber:[window windowNumber]
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
NSEvent* dragEvent =
|
||||
[NSEvent mouseEventWithType:NSEventTypeLeftMouseDragged
|
||||
location:position
|
||||
modifierFlags:NSEventMaskLeftMouseDragged
|
||||
timestamp:eventTime
|
||||
windowNumber:[[native_view window] windowNumber]
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
|
||||
// Run the drag operation.
|
||||
[window dragImage:icon.ToNSImage()
|
||||
at:position
|
||||
offset:NSZeroSize
|
||||
event:dragEvent
|
||||
pasteboard:pasteboard
|
||||
source:view.GetNativeNSView()
|
||||
slideBack:YES];
|
||||
[native_view beginDraggingSessionWithItems:file_items
|
||||
event:dragEvent
|
||||
source:GetDraggingSource()];
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
3
shell/browser/ui/electron_gdk_pixbuf.sigs
Normal file
3
shell/browser/ui/electron_gdk_pixbuf.sigs
Normal file
@@ -0,0 +1,3 @@
|
||||
GdkPixbuf* gdk_pixbuf_new(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height)
|
||||
GdkPixbuf* gdk_pixbuf_scale_simple(const GdkPixbuf* src, int dest_width, int dest_height, GdkInterpType interp_type)
|
||||
guchar* gdk_pixbuf_get_pixels(const GdkPixbuf* pixbuf)
|
||||
@@ -1 +1,2 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
@@ -4,4 +4,4 @@ void gtk_native_dialog_show(GtkNativeDialog* self);
|
||||
void gtk_native_dialog_hide(GtkNativeDialog* self);
|
||||
gint gtk_native_dialog_run(GtkNativeDialog* self);
|
||||
void gtk_native_dialog_destroy(GtkNativeDialog* self);
|
||||
GType gtk_native_dialog_get_type(void);
|
||||
GType gtk_native_dialog_get_type(void);
|
||||
|
||||
@@ -32,8 +32,11 @@ static const int kPreviewWidth = 256;
|
||||
static const int kPreviewHeight = 512;
|
||||
|
||||
std::string MakeCaseInsensitivePattern(const std::string& extension) {
|
||||
std::string pattern("*.");
|
||||
// If the extension is the "all files" extension, no change needed.
|
||||
if (extension == "*")
|
||||
return extension;
|
||||
|
||||
std::string pattern("*.");
|
||||
for (std::size_t i = 0, n = extension.size(); i < n; i++) {
|
||||
char ch = extension[i];
|
||||
if (!base::IsAsciiAlpha(ch)) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "third_party/skia/include/core/SkUnPreMultiply.h"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/native_window_observer.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
|
||||
@@ -148,11 +148,30 @@ void WinCaptionButtonContainer::UpdateButtons() {
|
||||
restore_button_->SetVisible(is_maximized);
|
||||
maximize_button_->SetVisible(!is_maximized);
|
||||
|
||||
const bool minimizable = frame_view_->window()->IsMinimizable();
|
||||
minimize_button_->SetEnabled(minimizable);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled.
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
maximize_button_->SetEnabled(!is_touch);
|
||||
|
||||
// The maximize button should only be enabled if the window is
|
||||
// maximizable *and* touch mode is disabled.
|
||||
const bool maximizable = frame_view_->window()->IsMaximizable();
|
||||
maximize_button_->SetEnabled(!is_touch && maximizable);
|
||||
|
||||
const bool closable = frame_view_->window()->IsClosable();
|
||||
close_button_->SetEnabled(closable);
|
||||
|
||||
// If all three of closable, maximizable, and minimizable are disabled,
|
||||
// Windows natively only shows the disabled closable button. Copy that
|
||||
// behavior here.
|
||||
if (!maximizable && !closable && !minimizable) {
|
||||
minimize_button_->SetVisible(false);
|
||||
maximize_button_->SetVisible(false);
|
||||
}
|
||||
|
||||
InvalidateLayout();
|
||||
}
|
||||
} // namespace electron
|
||||
|
||||
@@ -41,6 +41,11 @@ class WinCaptionButtonContainer : public views::View,
|
||||
gfx::Size GetButtonSize() const;
|
||||
void SetButtonSize(gfx::Size size);
|
||||
|
||||
// Sets caption button visibility and enabled state based on window state.
|
||||
// Only one of maximize or restore button should ever be visible at the same
|
||||
// time, and both are disabled in tablet UI mode.
|
||||
void UpdateButtons();
|
||||
|
||||
private:
|
||||
// views::View:
|
||||
void AddedToWidget() override;
|
||||
@@ -52,11 +57,6 @@ class WinCaptionButtonContainer : public views::View,
|
||||
|
||||
void ResetWindowControls();
|
||||
|
||||
// Sets caption button visibility and enabled state based on window state.
|
||||
// Only one of maximize or restore button should ever be visible at the same
|
||||
// time, and both are disabled in tablet UI mode.
|
||||
void UpdateButtons();
|
||||
|
||||
WinFrameView* const frame_view_;
|
||||
WinCaptionButton* const minimize_button_;
|
||||
WinCaptionButton* const maximize_button_;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/win_caption_button.h"
|
||||
#include "shell/browser/ui/views/win_caption_button_container.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -43,6 +44,9 @@ class WinFrameView : public FramelessView {
|
||||
|
||||
NativeWindowViews* window() const { return window_; }
|
||||
views::Widget* frame() const { return frame_; }
|
||||
WinCaptionButtonContainer* caption_button_container() {
|
||||
return caption_button_container_;
|
||||
}
|
||||
|
||||
bool IsMaximized() const;
|
||||
|
||||
|
||||
@@ -7,13 +7,10 @@
|
||||
#include "base/win/windows_version.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "shell/browser/win/dark_mode.h"
|
||||
#include "ui/base/win/hwnd_metrics.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_WIN_DARK_MODE_WINDOW_UI)
|
||||
#include "shell/browser/win/dark_mode.h"
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
|
||||
ElectronDesktopWindowTreeHostWin::ElectronDesktopWindowTreeHostWin(
|
||||
@@ -29,14 +26,13 @@ bool ElectronDesktopWindowTreeHostWin::PreHandleMSG(UINT message,
|
||||
WPARAM w_param,
|
||||
LPARAM l_param,
|
||||
LRESULT* result) {
|
||||
#if BUILDFLAG(ENABLE_WIN_DARK_MODE_WINDOW_UI)
|
||||
if (message == WM_NCCREATE) {
|
||||
HWND const hwnd = GetAcceleratedWidget();
|
||||
auto const theme_source =
|
||||
ui::NativeTheme::GetInstanceForNativeUi()->theme_source();
|
||||
win::SetDarkModeForWindow(hwnd, theme_source);
|
||||
const bool dark_mode_supported = win::IsDarkModeSupported();
|
||||
if (dark_mode_supported && message == WM_NCCREATE) {
|
||||
win::SetDarkModeForWindow(GetAcceleratedWidget());
|
||||
ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this);
|
||||
} else if (dark_mode_supported && message == WM_DESTROY) {
|
||||
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
return native_window_view_->PreHandleMSG(message, w_param, l_param, result);
|
||||
}
|
||||
@@ -99,4 +95,9 @@ bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets(
|
||||
return false;
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostWin::OnNativeThemeUpdated(
|
||||
ui::NativeTheme* observed_theme) {
|
||||
win::SetDarkModeForWindow(GetAcceleratedWidget());
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
class ElectronDesktopWindowTreeHostWin
|
||||
: public views::DesktopWindowTreeHostWin {
|
||||
class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
|
||||
public ::ui::NativeThemeObserver {
|
||||
public:
|
||||
ElectronDesktopWindowTreeHostWin(
|
||||
NativeWindowViews* native_window_view,
|
||||
@@ -37,6 +37,9 @@ class ElectronDesktopWindowTreeHostWin
|
||||
bool GetClientAreaInsets(gfx::Insets* insets,
|
||||
HMONITOR monitor) const override;
|
||||
|
||||
// ui::NativeThemeObserver:
|
||||
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
|
||||
|
||||
private:
|
||||
NativeWindowViews* native_window_view_; // weak ref
|
||||
};
|
||||
|
||||
@@ -486,10 +486,6 @@ void WebContentsPreferences::OverrideWebkitPrefs(
|
||||
|
||||
prefs->offscreen = offscreen_;
|
||||
|
||||
// The preload script.
|
||||
if (preload_path_)
|
||||
prefs->preload = *preload_path_;
|
||||
|
||||
prefs->node_integration = node_integration_;
|
||||
prefs->node_integration_in_worker = node_integration_in_worker_;
|
||||
prefs->node_integration_in_sub_frames = node_integration_in_sub_frames_;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Microsoft Inc. All rights reserved.
|
||||
// Copyright (c) 2022 Microsoft Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-CHROMIUM file.
|
||||
|
||||
@@ -6,173 +6,57 @@
|
||||
|
||||
#include <dwmapi.h> // DwmSetWindowAttribute()
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/scoped_native_library.h"
|
||||
#include "base/win/pe_image.h"
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/windows_version.h"
|
||||
|
||||
// This flag works since Win10 20H1 but is not documented until Windows 11
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
|
||||
// This namespace contains code originally from
|
||||
// https://github.com/ysc3839/win32-darkmode/
|
||||
// governed by the MIT license and (c) Richard Yu
|
||||
// https://github.com/microsoft/terminal
|
||||
// governed by the MIT license and (c) Microsoft Corporation.
|
||||
namespace {
|
||||
|
||||
// 1903 18362
|
||||
enum PreferredAppMode { Default, AllowDark, ForceDark, ForceLight, Max };
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||
HRESULT TrySetWindowTheme(HWND hWnd, bool dark) {
|
||||
const BOOL isDarkMode = dark;
|
||||
HRESULT result = DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&isDarkMode, sizeof(isDarkMode));
|
||||
|
||||
bool g_darkModeSupported = false;
|
||||
bool g_darkModeEnabled = false;
|
||||
DWORD g_buildNumber = 0;
|
||||
if (FAILED(result))
|
||||
return result;
|
||||
|
||||
enum WINDOWCOMPOSITIONATTRIB {
|
||||
WCA_USEDARKMODECOLORS = 26 // build 18875+
|
||||
};
|
||||
struct WINDOWCOMPOSITIONATTRIBDATA {
|
||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
||||
PVOID pvData;
|
||||
SIZE_T cbData;
|
||||
};
|
||||
|
||||
using fnSetWindowCompositionAttribute =
|
||||
BOOL(WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
|
||||
fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr;
|
||||
|
||||
bool IsHighContrast() {
|
||||
HIGHCONTRASTW highContrast = {sizeof(highContrast)};
|
||||
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast),
|
||||
&highContrast, FALSE))
|
||||
return highContrast.dwFlags & HCF_HIGHCONTRASTON;
|
||||
return false;
|
||||
}
|
||||
|
||||
void RefreshTitleBarThemeColor(HWND hWnd, bool dark) {
|
||||
LONG ldark = dark;
|
||||
if (g_buildNumber >= 20161) {
|
||||
// DWMA_USE_IMMERSIVE_DARK_MODE = 20
|
||||
DwmSetWindowAttribute(hWnd, 20, &ldark, sizeof dark);
|
||||
return;
|
||||
}
|
||||
if (g_buildNumber >= 18363) {
|
||||
auto data = WINDOWCOMPOSITIONATTRIBDATA{WCA_USEDARKMODECOLORS, &ldark,
|
||||
sizeof ldark};
|
||||
_SetWindowCompositionAttribute(hWnd, &data);
|
||||
return;
|
||||
}
|
||||
DwmSetWindowAttribute(hWnd, 0x13, &ldark, sizeof ldark);
|
||||
}
|
||||
|
||||
void InitDarkMode() {
|
||||
// confirm that we're running on a version of Windows
|
||||
// where the Dark Mode API is known
|
||||
auto* os_info = base::win::OSInfo::GetInstance();
|
||||
g_buildNumber = os_info->version_number().build;
|
||||
auto const version = os_info->version();
|
||||
if ((version < base::win::Version::WIN10_RS5) ||
|
||||
(version > base::win::Version::WIN10_20H1)) {
|
||||
return;
|
||||
|
||||
// Toggle the nonclient area active state to force a redraw (Win10 workaround)
|
||||
if (version < base::win::Version::WIN11) {
|
||||
HWND activeWindow = GetActiveWindow();
|
||||
SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0);
|
||||
SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0);
|
||||
}
|
||||
|
||||
// load "SetWindowCompositionAttribute", used in RefreshTitleBarThemeColor()
|
||||
_SetWindowCompositionAttribute =
|
||||
reinterpret_cast<decltype(_SetWindowCompositionAttribute)>(
|
||||
base::win::GetUser32FunctionPointer("SetWindowCompositionAttribute"));
|
||||
if (_SetWindowCompositionAttribute == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load the dark mode functions from uxtheme.dll
|
||||
// * RefreshImmersiveColorPolicyState()
|
||||
// * ShouldAppsUseDarkMode()
|
||||
// * AllowDarkModeForApp()
|
||||
// * SetPreferredAppMode()
|
||||
// * AllowDarkModeForApp() (build < 18362)
|
||||
// * SetPreferredAppMode() (build >= 18362)
|
||||
|
||||
base::NativeLibrary uxtheme =
|
||||
base::PinSystemLibrary(FILE_PATH_LITERAL("uxtheme.dll"));
|
||||
if (!uxtheme) {
|
||||
return;
|
||||
}
|
||||
auto ux_pei = base::win::PEImage(uxtheme);
|
||||
auto get_ux_proc_from_ordinal = [&ux_pei](int ordinal, auto* setme) {
|
||||
FARPROC proc = ux_pei.GetProcAddress(reinterpret_cast<LPCSTR>(ordinal));
|
||||
*setme = reinterpret_cast<decltype(*setme)>(proc);
|
||||
};
|
||||
|
||||
// ordinal 104
|
||||
using fnRefreshImmersiveColorPolicyState = VOID(WINAPI*)();
|
||||
fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = {};
|
||||
get_ux_proc_from_ordinal(104, &_RefreshImmersiveColorPolicyState);
|
||||
|
||||
// ordinal 132
|
||||
using fnShouldAppsUseDarkMode = BOOL(WINAPI*)();
|
||||
fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = {};
|
||||
get_ux_proc_from_ordinal(132, &_ShouldAppsUseDarkMode);
|
||||
|
||||
// ordinal 135, in 1809
|
||||
using fnAllowDarkModeForApp = BOOL(WINAPI*)(BOOL allow);
|
||||
fnAllowDarkModeForApp _AllowDarkModeForApp = {};
|
||||
|
||||
// ordinal 135, in 1903
|
||||
typedef PreferredAppMode(WINAPI *
|
||||
fnSetPreferredAppMode)(PreferredAppMode appMode);
|
||||
fnSetPreferredAppMode _SetPreferredAppMode = {};
|
||||
|
||||
if (g_buildNumber < 18362) {
|
||||
get_ux_proc_from_ordinal(135, &_AllowDarkModeForApp);
|
||||
} else {
|
||||
get_ux_proc_from_ordinal(135, &_SetPreferredAppMode);
|
||||
}
|
||||
|
||||
// dark mode is supported iff we found the functions
|
||||
g_darkModeSupported = _RefreshImmersiveColorPolicyState &&
|
||||
_ShouldAppsUseDarkMode &&
|
||||
(_AllowDarkModeForApp || _SetPreferredAppMode);
|
||||
if (!g_darkModeSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
// initial setup: allow dark mode to be used
|
||||
if (_AllowDarkModeForApp) {
|
||||
_AllowDarkModeForApp(true);
|
||||
} else if (_SetPreferredAppMode) {
|
||||
_SetPreferredAppMode(AllowDark);
|
||||
}
|
||||
_RefreshImmersiveColorPolicyState();
|
||||
|
||||
// check to see if dark mode is currently enabled
|
||||
g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace electron {
|
||||
|
||||
void EnsureInitialized() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
::InitDarkMode();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDarkPreferred(ui::NativeTheme::ThemeSource theme_source) {
|
||||
switch (theme_source) {
|
||||
case ui::NativeTheme::ThemeSource::kForcedLight:
|
||||
return false;
|
||||
case ui::NativeTheme::ThemeSource::kForcedDark:
|
||||
return g_darkModeSupported;
|
||||
case ui::NativeTheme::ThemeSource::kSystem:
|
||||
return g_darkModeEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
namespace win {
|
||||
|
||||
void SetDarkModeForWindow(HWND hWnd,
|
||||
ui::NativeTheme::ThemeSource theme_source) {
|
||||
EnsureInitialized();
|
||||
RefreshTitleBarThemeColor(hWnd, IsDarkPreferred(theme_source));
|
||||
bool IsDarkModeSupported() {
|
||||
auto* os_info = base::win::OSInfo::GetInstance();
|
||||
auto const version = os_info->version();
|
||||
|
||||
return version >= base::win::Version::WIN10_20H1;
|
||||
}
|
||||
|
||||
void SetDarkModeForWindow(HWND hWnd) {
|
||||
ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi();
|
||||
bool dark =
|
||||
theme->ShouldUseDarkColors() && !theme->UserHasContrastPreference();
|
||||
|
||||
TrySetWindowTheme(hWnd, dark);
|
||||
}
|
||||
|
||||
} // namespace win
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Microsoft Inc. All rights reserved.
|
||||
// Copyright (c) 2022 Microsoft Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE-CHROMIUM file.
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace electron {
|
||||
|
||||
namespace win {
|
||||
|
||||
void SetDarkModeForWindow(HWND hWnd, ui::NativeTheme::ThemeSource theme_source);
|
||||
bool IsDarkModeSupported();
|
||||
void SetDarkModeForWindow(HWND hWnd);
|
||||
|
||||
} // namespace win
|
||||
|
||||
|
||||
@@ -54,10 +54,6 @@ bool IsPictureInPictureEnabled() {
|
||||
return BUILDFLAG(ENABLE_PICTURE_IN_PICTURE);
|
||||
}
|
||||
|
||||
bool IsWinDarkModeWindowUiEnabled() {
|
||||
return BUILDFLAG(ENABLE_WIN_DARK_MODE_WINDOW_UI);
|
||||
}
|
||||
|
||||
bool IsComponentBuild() {
|
||||
#if defined(COMPONENT_BUILD)
|
||||
return true;
|
||||
@@ -84,7 +80,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
dict.SetMethod("isPictureInPictureEnabled", &IsPictureInPictureEnabled);
|
||||
dict.SetMethod("isComponentBuild", &IsComponentBuild);
|
||||
dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled);
|
||||
dict.SetMethod("isWinDarkModeWindowUiEnabled", &IsWinDarkModeWindowUiEnabled);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/bundle_locations.h"
|
||||
#include "base/mac/foundation_util.h"
|
||||
@@ -21,7 +22,8 @@
|
||||
namespace asar {
|
||||
|
||||
absl::optional<base::FilePath> Archive::RelativePath() const {
|
||||
base::FilePath bundle_path = base::mac::MainBundlePath().Append("Contents");
|
||||
base::FilePath bundle_path = base::MakeAbsoluteFilePath(
|
||||
base::mac::MainBundlePath().Append("Contents"));
|
||||
|
||||
base::FilePath relative_path;
|
||||
if (!bundle_path.AppendRelativePath(path_, &relative_path))
|
||||
|
||||
@@ -55,13 +55,16 @@
|
||||
V(electron_browser_in_app_purchase) \
|
||||
V(electron_browser_menu) \
|
||||
V(electron_browser_message_port) \
|
||||
V(electron_browser_native_theme) \
|
||||
V(electron_browser_net) \
|
||||
V(electron_browser_notification) \
|
||||
V(electron_browser_power_monitor) \
|
||||
V(electron_browser_power_save_blocker) \
|
||||
V(electron_browser_protocol) \
|
||||
V(electron_browser_printing) \
|
||||
V(electron_browser_safe_storage) \
|
||||
V(electron_browser_session) \
|
||||
V(electron_browser_screen) \
|
||||
V(electron_browser_system_preferences) \
|
||||
V(electron_browser_base_window) \
|
||||
V(electron_browser_tray) \
|
||||
@@ -77,9 +80,6 @@
|
||||
V(electron_common_environment) \
|
||||
V(electron_common_features) \
|
||||
V(electron_common_native_image) \
|
||||
V(electron_common_native_theme) \
|
||||
V(electron_common_notification) \
|
||||
V(electron_common_screen) \
|
||||
V(electron_common_shell) \
|
||||
V(electron_common_v8_util) \
|
||||
V(electron_renderer_context_bridge) \
|
||||
|
||||
@@ -496,9 +496,7 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||
|
||||
const auto& prefs = render_frame->GetBlinkPreferences();
|
||||
|
||||
if (pref_name == options::kPreloadScripts) {
|
||||
return gin::ConvertToV8(isolate, prefs.preloads);
|
||||
} else if (pref_name == "isWebView") {
|
||||
if (pref_name == "isWebView") {
|
||||
// FIXME(zcbenz): For child windows opened with window.open('') from
|
||||
// webview, the WebPreferences is inherited from webview and the value
|
||||
// of |is_webview| is wrong.
|
||||
@@ -513,8 +511,6 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||
return gin::ConvertToV8(isolate, prefs.hidden_page);
|
||||
} else if (pref_name == options::kOffscreen) {
|
||||
return gin::ConvertToV8(isolate, prefs.offscreen);
|
||||
} else if (pref_name == options::kPreloadScript) {
|
||||
return gin::ConvertToV8(isolate, prefs.preload.value());
|
||||
} else if (pref_name == options::kNodeIntegration) {
|
||||
return gin::ConvertToV8(isolate, prefs.node_integration);
|
||||
} else if (pref_name == options::kNodeIntegrationInWorker) {
|
||||
|
||||
@@ -24,15 +24,6 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||
return static_cast<GURL>(render_frame->GetWebFrame()->GetDocument().Url())
|
||||
.SchemeIs("chrome-extension");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronRendererClient::ElectronRendererClient()
|
||||
: node_bindings_(
|
||||
NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)),
|
||||
@@ -77,15 +68,7 @@ void ElectronRendererClient::DidCreateScriptContext(
|
||||
|
||||
// Only load Node.js if we are a main frame or a devtools extension
|
||||
// unless Node.js support has been explicitly enabled for subframes.
|
||||
auto prefs = render_frame->GetBlinkPreferences();
|
||||
bool is_main_frame = render_frame->IsMainFrame();
|
||||
bool is_devtools = IsDevToolsExtension(render_frame);
|
||||
bool allow_node_in_subframes = prefs.node_integration_in_sub_frames;
|
||||
|
||||
bool should_load_node =
|
||||
(is_main_frame || is_devtools || allow_node_in_subframes) &&
|
||||
!IsWebViewFrame(renderer_context, render_frame);
|
||||
if (!should_load_node)
|
||||
if (!ShouldLoadPreload(renderer_context, render_frame))
|
||||
return;
|
||||
|
||||
injected_frames_.insert(render_frame);
|
||||
|
||||
@@ -36,16 +36,6 @@ namespace {
|
||||
const char kLifecycleKey[] = "lifecycle";
|
||||
const char kModuleCacheKey[] = "native-module-cache";
|
||||
|
||||
bool IsDevTools(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"devtools");
|
||||
}
|
||||
|
||||
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"chrome-extension");
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> GetModuleCache(v8::Isolate* isolate) {
|
||||
auto context = isolate->GetCurrentContext();
|
||||
gin_helper::Dictionary global(isolate, context->Global());
|
||||
@@ -207,17 +197,7 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext(
|
||||
// Only allow preload for the main frame or
|
||||
// For devtools we still want to run the preload_bundle script
|
||||
// Or when nodeSupport is explicitly enabled in sub frames
|
||||
bool is_main_frame = render_frame->IsMainFrame();
|
||||
bool is_devtools =
|
||||
IsDevTools(render_frame) || IsDevToolsExtension(render_frame);
|
||||
|
||||
bool allow_node_in_sub_frames =
|
||||
render_frame->GetBlinkPreferences().node_integration_in_sub_frames;
|
||||
|
||||
bool should_load_preload =
|
||||
(is_main_frame || is_devtools || allow_node_in_sub_frames) &&
|
||||
!IsWebViewFrame(context, render_frame);
|
||||
if (!should_load_preload)
|
||||
if (!ShouldLoadPreload(context, render_frame))
|
||||
return;
|
||||
|
||||
injected_frames_.insert(render_frame);
|
||||
|
||||
@@ -134,6 +134,16 @@ class ChromePdfInternalPluginDelegate final
|
||||
// static
|
||||
RendererClientBase* g_renderer_client_base = nullptr;
|
||||
|
||||
bool IsDevTools(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"devtools");
|
||||
}
|
||||
|
||||
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"chrome-extension");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RendererClientBase::RendererClientBase() {
|
||||
@@ -196,6 +206,19 @@ void RendererClientBase::BindProcess(v8::Isolate* isolate,
|
||||
process->SetReadOnly("contextId", context_id);
|
||||
}
|
||||
|
||||
bool RendererClientBase::ShouldLoadPreload(
|
||||
v8::Handle<v8::Context> context,
|
||||
content::RenderFrame* render_frame) const {
|
||||
auto prefs = render_frame->GetBlinkPreferences();
|
||||
bool is_main_frame = render_frame->IsMainFrame();
|
||||
bool is_devtools =
|
||||
IsDevTools(render_frame) || IsDevToolsExtension(render_frame);
|
||||
bool allow_node_in_sub_frames = prefs.node_integration_in_sub_frames;
|
||||
|
||||
return (is_main_frame || is_devtools || allow_node_in_sub_frames) &&
|
||||
!IsWebViewFrame(context, render_frame);
|
||||
}
|
||||
|
||||
void RendererClientBase::RenderThreadStarted() {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@ class RendererClientBase : public content::ContentRendererClient
|
||||
gin_helper::Dictionary* process,
|
||||
content::RenderFrame* render_frame);
|
||||
|
||||
bool ShouldLoadPreload(v8::Handle<v8::Context> context,
|
||||
content::RenderFrame* render_frame) const;
|
||||
|
||||
// content::ContentRendererClient:
|
||||
void RenderThreadStarted() override;
|
||||
void ExposeInterfacesToBrowser(mojo::BinderMap* binders) override;
|
||||
|
||||
@@ -1228,6 +1228,7 @@ describe('BrowserWindow module', () => {
|
||||
await resize;
|
||||
expectBoundsEqual(w.getNormalBounds(), w.getBounds());
|
||||
});
|
||||
|
||||
it('checks normal bounds after move', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
@@ -1246,6 +1247,51 @@ describe('BrowserWindow module', () => {
|
||||
await maximize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and maximize', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
w.maximize();
|
||||
await maximize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and maximize', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
w.maximize();
|
||||
await maximize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('checks normal bounds when unmaximized', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.once('maximize', () => {
|
||||
@@ -1257,6 +1303,7 @@ describe('BrowserWindow module', () => {
|
||||
await unmaximize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('does not change size for a frameless window with min size', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1277,6 +1324,7 @@ describe('BrowserWindow module', () => {
|
||||
await unmaximize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('correctly checks transparent window maximization state', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1296,6 +1344,7 @@ describe('BrowserWindow module', () => {
|
||||
await unmaximize;
|
||||
expect(w.isMaximized()).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns the correct value for windows with an aspect ratio', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1325,6 +1374,41 @@ describe('BrowserWindow module', () => {
|
||||
await minimize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and minimize', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const minimize = emittedOnce(w, 'minimize');
|
||||
w.minimize();
|
||||
await minimize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
|
||||
expect(original).to.deep.equal(normal);
|
||||
expectBoundsEqual(normal, w.getBounds());
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and minimize', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const minimize = emittedOnce(w, 'minimize');
|
||||
w.minimize();
|
||||
await minimize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
|
||||
expect(original).to.deep.equal(normal);
|
||||
expectBoundsEqual(normal, w.getBounds());
|
||||
});
|
||||
|
||||
it('checks normal bounds when restored', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.once('minimize', () => {
|
||||
@@ -1336,6 +1420,7 @@ describe('BrowserWindow module', () => {
|
||||
await restore;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('does not change size for a frameless window with min size', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1381,6 +1466,50 @@ describe('BrowserWindow module', () => {
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and fullscreen', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.fullScreen = true;
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and fullscreen', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.fullScreen = true;
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('checks normal bounds when unfullscreen\'ed', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.once('enter-full-screen', () => {
|
||||
@@ -1418,6 +1547,50 @@ describe('BrowserWindow module', () => {
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and fullscreen', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and fullscreen', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('checks normal bounds when unfullscreen\'ed', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.show();
|
||||
@@ -1823,6 +1996,42 @@ describe('BrowserWindow module', () => {
|
||||
w.setWindowButtonVisibility(false);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(false);
|
||||
});
|
||||
|
||||
it('correctly updates when entering/exiting fullscreen for hidden style', async () => {
|
||||
const w = new BrowserWindow({ show: false, frame: false, titleBarStyle: 'hidden' });
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
w.setWindowButtonVisibility(false);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(false);
|
||||
|
||||
const enterFS = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await enterFS;
|
||||
|
||||
const leaveFS = emittedOnce(w, 'leave-full-screen');
|
||||
w.setFullScreen(false);
|
||||
await leaveFS;
|
||||
|
||||
w.setWindowButtonVisibility(true);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
});
|
||||
|
||||
it('correctly updates when entering/exiting fullscreen for hiddenInset style', async () => {
|
||||
const w = new BrowserWindow({ show: false, frame: false, titleBarStyle: 'hiddenInset' });
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
w.setWindowButtonVisibility(false);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(false);
|
||||
|
||||
const enterFS = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await enterFS;
|
||||
|
||||
const leaveFS = emittedOnce(w, 'leave-full-screen');
|
||||
w.setFullScreen(false);
|
||||
await leaveFS;
|
||||
|
||||
w.setWindowButtonVisibility(true);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'darwin')('BrowserWindow.setVibrancy(type)', () => {
|
||||
@@ -3090,7 +3299,15 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
w.webContents.setWindowOpenHandler(() => ({
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: { show: false, webPreferences: { contextIsolation: false, webviewTag: true, nodeIntegrationInSubFrames: true } }
|
||||
overrideBrowserWindowOptions: {
|
||||
show: false,
|
||||
webPreferences: {
|
||||
contextIsolation: false,
|
||||
webviewTag: true,
|
||||
nodeIntegrationInSubFrames: true,
|
||||
preload
|
||||
}
|
||||
}
|
||||
}));
|
||||
w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
|
||||
options.show = false;
|
||||
|
||||
@@ -1508,6 +1508,68 @@ describe('chromium features', () => {
|
||||
expect(focus).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigator.userAgentData', () => {
|
||||
// These tests are done on an http server because navigator.userAgentData
|
||||
// requires a secure context.
|
||||
let server: http.Server;
|
||||
let serverUrl: string;
|
||||
before(async () => {
|
||||
server = http.createServer((req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('');
|
||||
});
|
||||
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
serverUrl = `http://localhost:${(server.address() as any).port}`;
|
||||
});
|
||||
after(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('is not empty', () => {
|
||||
it('by default', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL(serverUrl);
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('when there is a session-wide UA override', async () => {
|
||||
const ses = session.fromPartition(`${Math.random()}`);
|
||||
ses.setUserAgent('foobar');
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
|
||||
await w.loadURL(serverUrl);
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('when there is a WebContents-specific UA override', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.webContents.setUserAgent('foo');
|
||||
await w.loadURL(serverUrl);
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('when there is a WebContents-specific UA override at load time', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL(serverUrl, {
|
||||
userAgent: 'foo'
|
||||
});
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
});
|
||||
|
||||
describe('brand list', () => {
|
||||
it('contains chromium', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL(serverUrl);
|
||||
const brands = await w.webContents.executeJavaScript('navigator.userAgentData.brands');
|
||||
expect(brands.map((b: any) => b.brand)).to.include('Chromium');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('font fallback', () => {
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
[
|
||||
[
|
||||
"top=5,left=10,resizable=no",
|
||||
{
|
||||
"sender": "[WebContents]",
|
||||
"frameId": 1,
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"top": 5,
|
||||
"left": 10,
|
||||
"resizable": false,
|
||||
"x": 10,
|
||||
"y": 5,
|
||||
"webPreferences": {
|
||||
"contextIsolation": true,
|
||||
"nodeIntegration": false,
|
||||
"webviewTag": false,
|
||||
"nodeIntegrationInSubFrames": false
|
||||
},
|
||||
"webContents": "[WebContents]"
|
||||
},
|
||||
[],
|
||||
{
|
||||
"url": "",
|
||||
"policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
null
|
||||
],
|
||||
[
|
||||
"zoomFactor=2,resizable=0,x=0,y=10",
|
||||
{
|
||||
"sender": "[WebContents]",
|
||||
"frameId": 1,
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"resizable": false,
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"webPreferences": {
|
||||
"zoomFactor": "2",
|
||||
"contextIsolation": true,
|
||||
"nodeIntegration": false,
|
||||
"webviewTag": false,
|
||||
"nodeIntegrationInSubFrames": false
|
||||
},
|
||||
"webContents": "[WebContents]"
|
||||
},
|
||||
[],
|
||||
{
|
||||
"url": "",
|
||||
"policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
null
|
||||
],
|
||||
[
|
||||
"backgroundColor=gray,webPreferences=0,x=100,y=100",
|
||||
{
|
||||
"sender": "[WebContents]",
|
||||
"frameId": 1,
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"backgroundColor": "gray",
|
||||
"webPreferences": {
|
||||
"contextIsolation": true,
|
||||
"nodeIntegration": false,
|
||||
"webviewTag": false,
|
||||
"nodeIntegrationInSubFrames": false
|
||||
},
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"webContents": "[WebContents]"
|
||||
},
|
||||
[],
|
||||
{
|
||||
"url": "",
|
||||
"policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
null
|
||||
],
|
||||
[
|
||||
"x=50,y=20,title=sup",
|
||||
{
|
||||
"sender": "[WebContents]",
|
||||
"frameId": 1,
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"x": 50,
|
||||
"y": 20,
|
||||
"title": "sup",
|
||||
"webPreferences": {
|
||||
"contextIsolation": true,
|
||||
"nodeIntegration": false,
|
||||
"webviewTag": false,
|
||||
"nodeIntegrationInSubFrames": false
|
||||
},
|
||||
"webContents": "[WebContents]"
|
||||
},
|
||||
[],
|
||||
{
|
||||
"url": "",
|
||||
"policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
null
|
||||
],
|
||||
[
|
||||
"show=false,top=1,left=1",
|
||||
{
|
||||
"sender": "[WebContents]",
|
||||
"frameId": 1,
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": false,
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"top": 1,
|
||||
"left": 1,
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"webPreferences": {
|
||||
"contextIsolation": true,
|
||||
"nodeIntegration": false,
|
||||
"webviewTag": false,
|
||||
"nodeIntegrationInSubFrames": false
|
||||
},
|
||||
"webContents": "[WebContents]"
|
||||
},
|
||||
[],
|
||||
{
|
||||
"url": "",
|
||||
"policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
null
|
||||
]
|
||||
]
|
||||
@@ -79,6 +79,58 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('does not fire window creation events if the handler callback throws an error', (done) => {
|
||||
const error = new Error('oh no');
|
||||
const listeners = process.listeners('uncaughtException');
|
||||
process.removeAllListeners('uncaughtException');
|
||||
process.on('uncaughtException', (thrown) => {
|
||||
try {
|
||||
expect(thrown).to.equal(error);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
} finally {
|
||||
process.removeAllListeners('uncaughtException');
|
||||
listeners.forEach((listener) => process.on('uncaughtException', listener));
|
||||
}
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
|
||||
|
||||
browserWindow.webContents.setWindowOpenHandler(() => {
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
|
||||
it('does not fire window creation events if the handler callback returns a bad result', async () => {
|
||||
const bad = new Promise((resolve) => {
|
||||
browserWindow.webContents.setWindowOpenHandler(() => {
|
||||
setTimeout(resolve);
|
||||
return [1, 2, 3] as any;
|
||||
});
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
|
||||
|
||||
await bad;
|
||||
});
|
||||
|
||||
it('does not fire window creation events if an override returns action: deny', async () => {
|
||||
const denied = new Promise((resolve) => {
|
||||
browserWindow.webContents.setWindowOpenHandler(() => {
|
||||
@@ -87,11 +139,11 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
});
|
||||
});
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not to be called with an overridden window.open');
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not to be called with an overridden window.open');
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
|
||||
@@ -107,11 +159,11 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
});
|
||||
});
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not to be called with an overridden window.open');
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not to be called with an overridden window.open');
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
await browserWindow.webContents.loadURL('data:text/html,<a target="_blank" href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
|
||||
@@ -129,11 +181,11 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
});
|
||||
});
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not to be called with an overridden window.open');
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not to be called with an overridden window.open');
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
await browserWindow.webContents.loadURL('data:text/html,<a href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
|
||||
|
||||
@@ -554,6 +554,18 @@ describe('<webview> tag', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('page-title-updated event', () => {
|
||||
it('emits when title is set', async () => {
|
||||
loadWebView(webview, {
|
||||
src: `file://${fixtures}/pages/a.html`
|
||||
});
|
||||
const { title, explicitSet } = await waitForEvent(webview, 'page-title-updated');
|
||||
|
||||
expect(title).to.equal('test');
|
||||
expect(explicitSet).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('page-title-set event', () => {
|
||||
it('emits when title is set', async () => {
|
||||
loadWebView(webview, {
|
||||
|
||||
15
typings/internal-ambient.d.ts
vendored
15
typings/internal-ambient.d.ts
vendored
@@ -27,7 +27,6 @@ declare namespace NodeJS {
|
||||
isPictureInPictureEnabled(): boolean;
|
||||
isExtensionsEnabled(): boolean;
|
||||
isComponentBuild(): boolean;
|
||||
isWinDarkModeWindowUiEnabled(): boolean;
|
||||
}
|
||||
|
||||
interface IpcRendererBinding {
|
||||
@@ -108,8 +107,6 @@ declare namespace NodeJS {
|
||||
isWebView: boolean;
|
||||
hiddenPage: boolean;
|
||||
nodeIntegration: boolean;
|
||||
preload: string
|
||||
preloadScripts: string[];
|
||||
webviewTag: boolean;
|
||||
}
|
||||
|
||||
@@ -181,12 +178,6 @@ declare namespace NodeJS {
|
||||
_linkedBinding(name: 'electron_common_environment'): EnvironmentBinding;
|
||||
_linkedBinding(name: 'electron_common_features'): FeaturesBinding;
|
||||
_linkedBinding(name: 'electron_common_native_image'): { nativeImage: typeof Electron.NativeImage };
|
||||
_linkedBinding(name: 'electron_common_native_theme'): { nativeTheme: Electron.NativeTheme };
|
||||
_linkedBinding(name: 'electron_common_notification'): {
|
||||
isSupported(): boolean;
|
||||
Notification: typeof Electron.Notification;
|
||||
}
|
||||
_linkedBinding(name: 'electron_common_screen'): { createScreen(): Electron.Screen };
|
||||
_linkedBinding(name: 'electron_common_shell'): Electron.Shell;
|
||||
_linkedBinding(name: 'electron_common_v8_util'): V8UtilBinding;
|
||||
_linkedBinding(name: 'electron_browser_app'): { app: Electron.App, App: Function };
|
||||
@@ -218,6 +209,7 @@ declare namespace NodeJS {
|
||||
_linkedBinding(name: 'electron_browser_message_port'): {
|
||||
createPair(): { port1: Electron.MessagePortMain, port2: Electron.MessagePortMain };
|
||||
};
|
||||
_linkedBinding(name: 'electron_browser_native_theme'): { nativeTheme: Electron.NativeTheme };
|
||||
_linkedBinding(name: 'electron_browser_net'): {
|
||||
isOnline(): boolean;
|
||||
isValidHeaderName: (headerName: string) => boolean;
|
||||
@@ -227,10 +219,15 @@ declare namespace NodeJS {
|
||||
net: any;
|
||||
createURLLoader(options: CreateURLLoaderOptions): URLLoader;
|
||||
};
|
||||
_linkedBinding(name: 'electron_browser_notification'): {
|
||||
isSupported(): boolean;
|
||||
Notification: typeof Electron.Notification;
|
||||
}
|
||||
_linkedBinding(name: 'electron_browser_power_monitor'): PowerMonitorBinding;
|
||||
_linkedBinding(name: 'electron_browser_power_save_blocker'): { powerSaveBlocker: Electron.PowerSaveBlocker };
|
||||
_linkedBinding(name: 'electron_browser_safe_storage'): { safeStorage: Electron.SafeStorage };
|
||||
_linkedBinding(name: 'electron_browser_session'): typeof Electron.Session;
|
||||
_linkedBinding(name: 'electron_browser_screen'): { createScreen(): Electron.Screen };
|
||||
_linkedBinding(name: 'electron_browser_system_preferences'): { systemPreferences: Electron.SystemPreferences };
|
||||
_linkedBinding(name: 'electron_browser_tray'): { Tray: Electron.Tray };
|
||||
_linkedBinding(name: 'electron_browser_view'): { View: Electron.View };
|
||||
|
||||
Reference in New Issue
Block a user