mirror of
https://github.com/electron/electron.git
synced 2026-03-19 03:02:02 -04:00
Compare commits
7 Commits
fix-audio-
...
fullscreen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2051696e1d | ||
|
|
acd01e15e2 | ||
|
|
fc3a0abf19 | ||
|
|
fa2b9ac466 | ||
|
|
481e224992 | ||
|
|
90c9de70ac | ||
|
|
9d0a0a5459 |
2
.github/workflows/update-website-docs.yml
vendored
2
.github/workflows/update-website-docs.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
echo "isLatestRelease=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Trigger website docs update
|
||||
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease }}
|
||||
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease == 'true' }}
|
||||
env:
|
||||
GH_REPO: electron/website
|
||||
GH_TOKEN: ${{ fromJSON(steps.secret-service.outputs.secrets).WEBSITE_DOCS_UPDATER_APP_TOKEN }}
|
||||
|
||||
@@ -23,11 +23,6 @@ Creates a new touch bar with the specified items. Use
|
||||
> The TouchBar API is currently experimental and may change or be
|
||||
> removed in future Electron releases.
|
||||
|
||||
> [!TIP]
|
||||
> If you don't have a MacBook with Touch Bar, you can use
|
||||
> [Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator)
|
||||
> to test Touch Bar usage in your app.
|
||||
|
||||
### Static Properties
|
||||
|
||||
#### `TouchBarButton`
|
||||
|
||||
@@ -14,6 +14,23 @@ const DEPS_REGEX = /chromium_version':\n +'(.+?)',/m;
|
||||
const CL_REGEX = /https:\/\/chromium-review\.googlesource\.com\/c\/(?:chromium\/src|v8\/v8)\/\+\/(\d+)(#\S+)?/g;
|
||||
const ROLLER_BRANCH_PATTERN = /^roller\/chromium\/(.+)$/;
|
||||
|
||||
function getCurrentBranch () {
|
||||
// In CI, use `GITHUB_HEAD_REF` since we checkout the PR branch in detached HEAD state
|
||||
if (process.env.GITHUB_HEAD_REF) {
|
||||
return process.env.GITHUB_HEAD_REF;
|
||||
}
|
||||
|
||||
try {
|
||||
return execSync('git rev-parse --abbrev-ref HEAD', {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8'
|
||||
}).trim();
|
||||
} catch {
|
||||
console.error('Could not determine current git branch');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function getCommitsSinceMergeBase (mergeBase) {
|
||||
try {
|
||||
const output = execSync(`git log --format=%H%n%B%n---COMMIT_END--- ${mergeBase}..HEAD`, {
|
||||
@@ -92,17 +109,7 @@ async function getGerritPatchDetails (clUrl) {
|
||||
}
|
||||
|
||||
async function main () {
|
||||
let currentBranch;
|
||||
|
||||
try {
|
||||
currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8'
|
||||
}).trim();
|
||||
} catch {
|
||||
console.error('Could not determine current git branch');
|
||||
process.exit(1);
|
||||
}
|
||||
const currentBranch = getCurrentBranch();
|
||||
|
||||
// Check if we're on a roller/chromium/* branch
|
||||
const branchMatch = ROLLER_BRANCH_PATTERN.exec(currentBranch);
|
||||
|
||||
@@ -280,16 +280,22 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowShow() {
|
||||
if (!web_contents_shown_) {
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
}
|
||||
BaseWindow::OnWindowShow();
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowHide() {
|
||||
web_contents()->WasOccluded();
|
||||
web_contents_shown_ = false;
|
||||
BaseWindow::OnWindowHide();
|
||||
}
|
||||
|
||||
void BrowserWindow::Show() {
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
BaseWindow::Show();
|
||||
}
|
||||
|
||||
@@ -298,6 +304,7 @@ void BrowserWindow::ShowInactive() {
|
||||
if (IsModal())
|
||||
return;
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
BaseWindow::ShowInactive();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class BrowserWindow : public BaseWindow,
|
||||
// Helpers.
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
bool web_contents_shown_ = false;
|
||||
v8::Global<v8::Value> web_contents_view_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
|
||||
|
||||
@@ -4746,6 +4746,19 @@ gin_helper::Handle<WebContents> WebContents::CreateFromWebPreferences(
|
||||
existing_preferences->SetFromDictionary(web_preferences_dict);
|
||||
web_contents->SetBackgroundColor(
|
||||
existing_preferences->GetBackgroundColor());
|
||||
|
||||
double zoom_factor;
|
||||
if (web_preferences.Get(options::kZoomFactor, &zoom_factor)) {
|
||||
auto* zoom_controller = WebContentsZoomController::FromWebContents(
|
||||
web_contents->web_contents());
|
||||
if (zoom_controller) {
|
||||
zoom_controller->SetDefaultZoomFactor(zoom_factor);
|
||||
// Also set the current zoom level immediately, since the page
|
||||
// has already navigated by the time we wrap the webContents.
|
||||
zoom_controller->SetZoomLevel(
|
||||
blink::ZoomFactorToZoomLevel(zoom_factor));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create one if not.
|
||||
|
||||
@@ -33,7 +33,7 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
|
||||
gin_helper::Handle<WebContents> web_contents)
|
||||
: View(web_contents->inspectable_web_contents()->GetView()),
|
||||
web_contents_(isolate, web_contents.ToV8()),
|
||||
api_web_contents_(web_contents.get()) {
|
||||
api_web_contents_(web_contents->GetWeakPtr()) {
|
||||
set_delete_view(false);
|
||||
view()->SetProperty(
|
||||
views::kFlexBehaviorKey,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "shell/browser/api/electron_api_view.h"
|
||||
#include "shell/browser/draggable_region_provider.h"
|
||||
@@ -63,7 +63,7 @@ class WebContentsView : public View,
|
||||
|
||||
// Keep a reference to v8 wrapper.
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
raw_ptr<api::WebContents> api_web_contents_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
};
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -92,6 +92,13 @@ InspectableWebContentsView::InspectableWebContentsView(
|
||||
}
|
||||
|
||||
InspectableWebContentsView::~InspectableWebContentsView() {
|
||||
if (devtools_window_web_view_)
|
||||
devtools_window_web_view_->SetWebContents(nullptr);
|
||||
if (devtools_web_view_)
|
||||
devtools_web_view_->SetWebContents(nullptr);
|
||||
if (contents_web_view_)
|
||||
contents_web_view_->SetWebContents(nullptr);
|
||||
|
||||
if (devtools_window_)
|
||||
inspectable_web_contents()->SaveDevToolsBounds(
|
||||
devtools_window_->GetWindowBoundsInScreen());
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "components/input/native_web_keyboard_event.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/ui/views/menu_bar.h"
|
||||
#include "ui/events/keycodes/dom/keycode_converter.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
namespace electron {
|
||||
@@ -21,9 +22,21 @@ bool IsAltKey(const input::NativeWebKeyboardEvent& event) {
|
||||
|
||||
bool IsAltModifier(const input::NativeWebKeyboardEvent& event) {
|
||||
using Mods = input::NativeWebKeyboardEvent::Modifiers;
|
||||
|
||||
// AltGraph (AltGr) should not be treated as a single Alt keypress for
|
||||
// menu-bar toggling.
|
||||
if (event.windows_key_code == ui::VKEY_ALTGR ||
|
||||
ui::KeycodeConverter::DomKeyToKeyString(event.dom_key) == "AltGraph") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (event.GetModifiers() & Mods::kKeyModifiers) == Mods::kAltKey;
|
||||
}
|
||||
|
||||
bool IsSingleAltKey(const input::NativeWebKeyboardEvent& event) {
|
||||
return IsAltKey(event) && IsAltModifier(event);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RootView::RootView(NativeWindow* window)
|
||||
@@ -98,7 +111,7 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
|
||||
return;
|
||||
|
||||
// Show accelerator when "Alt" is pressed.
|
||||
if (menu_bar_visible_ && IsAltKey(event))
|
||||
if (menu_bar_visible_ && IsSingleAltKey(event))
|
||||
menu_bar_->SetAcceleratorVisibility(
|
||||
event.GetType() == blink::WebInputEvent::Type::kRawKeyDown);
|
||||
|
||||
@@ -121,11 +134,11 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
|
||||
|
||||
// Toggle the menu bar only when a single Alt is released.
|
||||
if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown &&
|
||||
IsAltKey(event)) {
|
||||
IsSingleAltKey(event)) {
|
||||
// When a single Alt is pressed:
|
||||
menu_bar_alt_pressed_ = true;
|
||||
} else if (event.GetType() == blink::WebInputEvent::Type::kKeyUp &&
|
||||
IsAltKey(event) && menu_bar_alt_pressed_) {
|
||||
IsSingleAltKey(event) && menu_bar_alt_pressed_) {
|
||||
// When a single Alt is released right after a Alt is pressed:
|
||||
menu_bar_alt_pressed_ = false;
|
||||
if (menu_bar_autohide_)
|
||||
|
||||
@@ -275,12 +275,16 @@ bool WinFrameView::GetShouldPaintAsActive() {
|
||||
}
|
||||
|
||||
gfx::Size WinFrameView::GetMinimumSize() const {
|
||||
if (!window_)
|
||||
return gfx::Size();
|
||||
// Chromium expects minimum size to be in content dimensions on Windows
|
||||
// because it adds the frame border automatically in OnGetMinMaxInfo.
|
||||
return window_->GetContentMinimumSize();
|
||||
}
|
||||
|
||||
gfx::Size WinFrameView::GetMaximumSize() const {
|
||||
if (!window_)
|
||||
return gfx::Size();
|
||||
// Chromium expects minimum size to be in content dimensions on Windows
|
||||
// because it adds the frame border automatically in OnGetMinMaxInfo.
|
||||
gfx::Size size = window_->GetContentMaximumSize();
|
||||
|
||||
@@ -216,9 +216,9 @@ void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kNodeIntegrationInWorker)) {
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (!current)
|
||||
current = WebWorkerObserver::Create();
|
||||
current->WorkerScriptReadyForEvaluation(context);
|
||||
if (current)
|
||||
return;
|
||||
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
#include "gin/converter.h"
|
||||
#include "shell/common/api/electron_bindings.h"
|
||||
#include "shell/common/gin_helper/event_emitter_caller.h"
|
||||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
@@ -48,21 +48,6 @@ WebWorkerObserver::~WebWorkerObserver() = default;
|
||||
|
||||
void WebWorkerObserver::WorkerScriptReadyForEvaluation(
|
||||
v8::Local<v8::Context> worker_context) {
|
||||
active_context_count_++;
|
||||
|
||||
if (environments_.empty()) {
|
||||
// First context on this thread - do full Node.js initialization.
|
||||
InitializeNewEnvironment(worker_context);
|
||||
} else {
|
||||
// Thread is being reused (AudioWorklet thread pooling). Share the
|
||||
// existing Node.js environment with the new context instead of
|
||||
// reinitializing, which would break existing contexts on this thread.
|
||||
ShareEnvironmentWithContext(worker_context);
|
||||
}
|
||||
}
|
||||
|
||||
void WebWorkerObserver::InitializeNewEnvironment(
|
||||
v8::Local<v8::Context> worker_context) {
|
||||
v8::Context::Scope context_scope(worker_context);
|
||||
v8::Isolate* const isolate = v8::Isolate::GetCurrent();
|
||||
v8::MicrotasksScope microtasks_scope(
|
||||
@@ -121,160 +106,26 @@ void WebWorkerObserver::InitializeNewEnvironment(
|
||||
environments_.insert(std::move(env));
|
||||
}
|
||||
|
||||
void WebWorkerObserver::ShareEnvironmentWithContext(
|
||||
v8::Local<v8::Context> worker_context) {
|
||||
v8::Context::Scope context_scope(worker_context);
|
||||
v8::Isolate* const isolate = v8::Isolate::GetCurrent();
|
||||
v8::MicrotasksScope microtasks_scope(
|
||||
worker_context, v8::MicrotasksScope::kDoNotRunMicrotasks);
|
||||
|
||||
// Get the existing environment from the first context on this thread.
|
||||
DCHECK(!environments_.empty());
|
||||
node::Environment* env = environments_.begin()->get();
|
||||
|
||||
// Initialize the V8 context for Node.js use.
|
||||
v8::Maybe<bool> initialized = node::InitializeContext(worker_context);
|
||||
CHECK(!initialized.IsNothing() && initialized.FromJust());
|
||||
|
||||
// Assign the existing Node.js environment to this new context so that
|
||||
// node::Environment::GetCurrent(context) returns the shared environment.
|
||||
env->AssignToContext(worker_context, env->principal_realm(),
|
||||
node::ContextInfo("electron_worker"));
|
||||
|
||||
// Get process and require from the original context to make Node.js
|
||||
// APIs available in the new context.
|
||||
v8::Local<v8::Context> original_context = env->context();
|
||||
v8::Local<v8::Object> original_global = original_context->Global();
|
||||
v8::Local<v8::Object> new_global = worker_context->Global();
|
||||
|
||||
v8::Local<v8::Value> process_value;
|
||||
CHECK(original_global
|
||||
->Get(original_context, gin::StringToV8(isolate, "process"))
|
||||
.ToLocal(&process_value));
|
||||
|
||||
v8::Local<v8::Value> require_value;
|
||||
CHECK(original_global
|
||||
->Get(original_context, gin::StringToV8(isolate, "require"))
|
||||
.ToLocal(&require_value));
|
||||
|
||||
// Set up 'global' as an alias for globalThis. Node.js bootstrapping normally
|
||||
// does this during LoadEnvironment, but we skip full bootstrap for shared
|
||||
// contexts.
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "global"), new_global)
|
||||
.Check();
|
||||
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "process"), process_value)
|
||||
.Check();
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "require"), require_value)
|
||||
.Check();
|
||||
|
||||
// Copy Buffer from the original context if it exists.
|
||||
v8::Local<v8::Value> buffer_value;
|
||||
if (original_global->Get(original_context, gin::StringToV8(isolate, "Buffer"))
|
||||
.ToLocal(&buffer_value) &&
|
||||
!buffer_value->IsUndefined()) {
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "Buffer"), buffer_value)
|
||||
.Check();
|
||||
}
|
||||
|
||||
// Restore the Blink implementations of web APIs that Node.js may
|
||||
// have deleted. For first-context init this is done by the node_init script
|
||||
// but we can't run that for shared contexts (it calls internalBinding).
|
||||
// Instead, copy the blink-prefixed values set during first init.
|
||||
for (const std::string_view key :
|
||||
{"fetch", "Response", "FormData", "Request", "Headers", "EventSource"}) {
|
||||
// First, check if the new context has a working Blink version.
|
||||
v8::MaybeLocal<v8::Value> blink_value =
|
||||
new_global->Get(worker_context, gin::StringToV8(isolate, key));
|
||||
if (!blink_value.IsEmpty() && !blink_value.ToLocalChecked()->IsUndefined())
|
||||
continue;
|
||||
// If not, copy from the original context.
|
||||
std::string blink_key = base::StrCat({"blink", key});
|
||||
v8::Local<v8::Value> orig_value;
|
||||
if (original_global->Get(original_context, gin::StringToV8(isolate, key))
|
||||
.ToLocal(&orig_value) &&
|
||||
!orig_value->IsUndefined()) {
|
||||
new_global->Set(worker_context, gin::StringToV8(isolate, key), orig_value)
|
||||
.Check();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebWorkerObserver::ContextWillDestroy(v8::Local<v8::Context> context) {
|
||||
node::Environment* env = node::Environment::GetCurrent(context);
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
active_context_count_--;
|
||||
|
||||
if (active_context_count_ == 0) {
|
||||
// Last context on this thread — full cleanup.
|
||||
{
|
||||
v8::Context::Scope context_scope(env->context());
|
||||
|
||||
// Emit the "exit" event on the process object. We avoid using
|
||||
// gin_helper::EmitEvent here because it goes through
|
||||
// CallMethodWithArgs, which creates a node::CallbackScope. During
|
||||
// worker shutdown (PrepareForShutdownOnWorkerThread), the
|
||||
// CallbackScope destructor's InternalCallbackScope::Close() tries to
|
||||
// process ticks and microtask checkpoints, which can SEGV because the
|
||||
// worker context is being torn down by Blink.
|
||||
v8::Isolate* isolate = env->isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> ctx = env->context();
|
||||
v8::Local<v8::Value> emit_v;
|
||||
if (env->process_object()
|
||||
->Get(ctx, gin::StringToV8(isolate, "emit"))
|
||||
.ToLocal(&emit_v) &&
|
||||
emit_v->IsFunction()) {
|
||||
v8::Local<v8::Value> args[] = {gin::StringToV8(isolate, "exit")};
|
||||
v8::TryCatch try_catch(isolate);
|
||||
emit_v.As<v8::Function>()
|
||||
->Call(ctx, env->process_object(), 1, args)
|
||||
.FromMaybe(v8::Local<v8::Value>());
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent UvRunOnce from using the environment after it's destroyed.
|
||||
node_bindings_->set_uv_env(nullptr);
|
||||
|
||||
// Destroying the node environment will also run the uv loop.
|
||||
{
|
||||
util::ExplicitMicrotasksScope microtasks_scope(
|
||||
context->GetMicrotaskQueue());
|
||||
environments_.clear();
|
||||
}
|
||||
|
||||
// ElectronBindings is tracking node environments.
|
||||
electron_bindings_->EnvironmentDestroyed(env);
|
||||
|
||||
// Do NOT destroy the observer here. The worker thread may be reused
|
||||
// for another worklet context (e.g., AudioWorklet thread pooling or
|
||||
// PaintWorklet after page reload). Destroying the observer would
|
||||
// force a new NodeBindings to be created, but Node.js cannot be
|
||||
// fully reinitialized on the same thread (the allocator shim can
|
||||
// only be loaded once). Instead, keep the observer and its
|
||||
// NodeBindings alive so they can be reused. The environments_ set
|
||||
// is now empty, so WorkerScriptReadyForEvaluation will call
|
||||
// InitializeNewEnvironment on the next context.
|
||||
} else {
|
||||
// Other contexts still use the shared environment. Just unassign
|
||||
// this context from the environment if it's not the primary context
|
||||
// (the primary context must stay assigned because env->context()
|
||||
// references it, and UvRunOnce enters that context scope).
|
||||
if (context != env->context()) {
|
||||
env->UnassignFromContext(context);
|
||||
}
|
||||
// If the destroyed context IS the primary context, we leave the env
|
||||
// assigned to it. The env's PrincipalRealm holds a Global<Context>
|
||||
// reference that keeps the V8 context alive even though Blink has
|
||||
// torn down its side. This is safe because UvRunOnce only needs
|
||||
// the V8 context scope, not Blink-side objects.
|
||||
if (env) {
|
||||
v8::Context::Scope context_scope(env->context());
|
||||
gin_helper::EmitEvent(env->isolate(), env->process_object(), "exit");
|
||||
}
|
||||
|
||||
// Destroying the node environment will also run the uv loop.
|
||||
{
|
||||
util::ExplicitMicrotasksScope microtasks_scope(
|
||||
context->GetMicrotaskQueue());
|
||||
base::EraseIf(environments_,
|
||||
[env](auto const& item) { return item.get() == env; });
|
||||
}
|
||||
|
||||
// ElectronBindings is tracking node environments.
|
||||
electron_bindings_->EnvironmentDestroyed(env);
|
||||
|
||||
if (lazy_tls->Get())
|
||||
lazy_tls->Set(nullptr);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -40,17 +40,9 @@ class WebWorkerObserver {
|
||||
void ContextWillDestroy(v8::Local<v8::Context> context);
|
||||
|
||||
private:
|
||||
// Full initialization for the first context on a thread.
|
||||
void InitializeNewEnvironment(v8::Local<v8::Context> context);
|
||||
// Share existing environment with a new context on a reused thread.
|
||||
void ShareEnvironmentWithContext(v8::Local<v8::Context> context);
|
||||
|
||||
std::unique_ptr<NodeBindings> node_bindings_;
|
||||
std::unique_ptr<ElectronBindings> electron_bindings_;
|
||||
base::flat_set<std::shared_ptr<node::Environment>> environments_;
|
||||
|
||||
// Number of active contexts using the environment on this thread.
|
||||
size_t active_context_count_ = 0;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -3977,6 +3977,28 @@ describe('BrowserWindow module', () => {
|
||||
expect(webPreferences!.contextIsolation).to.equal(false);
|
||||
});
|
||||
|
||||
it('should apply zoomFactor from setWindowOpenHandler overrideBrowserWindowOptions', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
sandbox: true
|
||||
}
|
||||
});
|
||||
|
||||
w.webContents.setWindowOpenHandler(() => ({
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
webPreferences: {
|
||||
zoomFactor: 2.0
|
||||
}
|
||||
}
|
||||
}));
|
||||
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
||||
const [childWindow] = await once(w.webContents, 'did-create-window') as [BrowserWindow, any];
|
||||
await once(childWindow.webContents, 'did-finish-load');
|
||||
expect(childWindow.webContents.getZoomFactor()).to.be.closeTo(2.0, 0.1);
|
||||
});
|
||||
|
||||
it('should set ipc event sender correctly', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
@@ -5903,6 +5925,23 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'linux')('menu bar AltGr behavior', () => {
|
||||
it('does not toggle auto-hide menu bar visibility', async () => {
|
||||
const w = new BrowserWindow({ show: false, autoHideMenuBar: true });
|
||||
w.setMenuBarVisibility(false);
|
||||
expect(w.isMenuBarVisible()).to.be.false('isMenuBarVisible');
|
||||
|
||||
w.show();
|
||||
await once(w, 'show');
|
||||
w.webContents.focus();
|
||||
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'AltGr' });
|
||||
w.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'AltGr' });
|
||||
await setTimeout();
|
||||
|
||||
expect(w.isMenuBarVisible()).to.be.false('isMenuBarVisible');
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform !== 'darwin')('when fullscreen state is changed', () => {
|
||||
it('correctly remembers state prior to fullscreen change', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
|
||||
@@ -59,10 +59,11 @@ describe('WebContentsView', () => {
|
||||
const browserWindow = new BrowserWindow();
|
||||
|
||||
const webContentsView = new WebContentsView();
|
||||
webContentsView.webContents.loadURL('about:blank');
|
||||
webContentsView.webContents.destroy();
|
||||
const wc = webContentsView.webContents;
|
||||
wc.loadURL('about:blank');
|
||||
wc.destroy();
|
||||
|
||||
const destroyed = once(webContentsView.webContents, 'destroyed');
|
||||
const destroyed = once(wc, 'destroyed');
|
||||
await destroyed;
|
||||
expect(() => browserWindow.contentView.addChildView(webContentsView)).to.throw(
|
||||
'Can\'t add a destroyed child view to a parent view'
|
||||
@@ -90,13 +91,14 @@ describe('WebContentsView', () => {
|
||||
const w = new BaseWindow({ show: false });
|
||||
const v = new View();
|
||||
const wcv = new WebContentsView();
|
||||
const wc = wcv.webContents;
|
||||
w.setContentView(v);
|
||||
v.addChildView(wcv);
|
||||
await wcv.webContents.loadURL('about:blank');
|
||||
const destroyed = once(wcv.webContents, 'destroyed');
|
||||
wcv.webContents.executeJavaScript('window.close()');
|
||||
await wc.loadURL('about:blank');
|
||||
const destroyed = once(wc, 'destroyed');
|
||||
wc.executeJavaScript('window.close()');
|
||||
await destroyed;
|
||||
expect(wcv.webContents.isDestroyed()).to.be.true();
|
||||
expect(wc.isDestroyed()).to.be.true();
|
||||
v.removeChildView(wcv);
|
||||
});
|
||||
|
||||
@@ -170,18 +172,19 @@ describe('WebContentsView', () => {
|
||||
it('does not crash when closed via window.close()', async () => {
|
||||
const bw = new BrowserWindow();
|
||||
const wcv = new WebContentsView();
|
||||
const wc = wcv.webContents;
|
||||
|
||||
await bw.loadURL('data:text/html,<h1>Main Window</h1>');
|
||||
bw.contentView.addChildView(wcv);
|
||||
|
||||
const dto = new Promise<boolean>((resolve) => {
|
||||
wcv.webContents.on('blur', () => {
|
||||
const devToolsOpen = wcv.webContents.isDevToolsOpened();
|
||||
wc.on('blur', () => {
|
||||
const devToolsOpen = !wc.isDestroyed() && wc.isDevToolsOpened();
|
||||
resolve(devToolsOpen);
|
||||
});
|
||||
});
|
||||
|
||||
wcv.webContents.loadURL('data:text/html,<script>window.close()</script>');
|
||||
wc.loadURL('data:text/html,<script>window.close()</script>');
|
||||
|
||||
const open = await dto;
|
||||
expect(open).to.be.false();
|
||||
|
||||
Reference in New Issue
Block a user