Compare commits

...

20 Commits

Author SHA1 Message Date
Sudowoodo Release Bot
57b863c213 Bump v21.0.0-nightly.20220610 2022-06-10 06:02:06 -07:00
Michaela Laurencin
52c0a4fafc docs: add macOS height option info (#34451) 2022-06-09 15:17:27 -04:00
Sudowoodo Release Bot
1302ebf50e Bump v21.0.0-nightly.20220609 2022-06-09 06:01:17 -07:00
Shelley Vohr
37d93b0482 fix: update normal bounds prior to minimizing (#34473) 2022-06-09 10:48:50 +02:00
Shelley Vohr
2fd0194e94 build: update clang-format script (#34286) 2022-06-08 21:26:41 +02:00
Sudowoodo Release Bot
6d50717eed Bump v21.0.0-nightly.20220608 2022-06-08 06:00:42 -07:00
David Sanders
289128b96c chore: use --root instead of --project_root for cpplint (#34456)
--root has been improved upstream in depot_tools to better handle Windows:
https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/3648533
2022-06-08 10:29:39 +02:00
Shelley Vohr
16db5a112e fix: html fullscreen transitions stacking (#32905)
* fix: html fullscreen transitions stacking

* spec: move webview test to spec-main
2022-06-07 12:59:50 -04:00
Sudowoodo Release Bot
f44ecb7f03 Bump v21.0.0-nightly.20220607 2022-06-07 07:30:55 -07:00
Sudowoodo Release Bot
8e97f3badf Revert "Bump v21.0.0-nightly.20220607"
This reverts commit a203123473.
2022-06-07 07:12:33 -07:00
Sudowoodo Release Bot
a203123473 Bump v21.0.0-nightly.20220607 2022-06-07 07:11:11 -07:00
Sudowoodo Release Bot
c750936328 Revert "Bump v21.0.0-nightly.20220607"
This reverts commit 5f0f517486.
2022-06-07 07:09:18 -07:00
Sudowoodo Release Bot
5f0f517486 Bump v21.0.0-nightly.20220607 2022-06-07 06:01:42 -07:00
Shelley Vohr
4ec2de659f chore: fix nan spec runner on macOS (#34447) 2022-06-07 12:09:08 +02:00
David Sanders
30d15715a9 build: switch to --use-color flag for clang-tidy (#34457)
chore: switch to --use-color flag for clang-tidy
2022-06-07 09:49:52 +02:00
Samuel Maddock
882fa36940 test: fix for flaky renderer crash test (#34452) 2022-06-06 17:39:58 -04:00
Sudowoodo Release Bot
e56f626b94 Bump v21.0.0-nightly.20220606 2022-06-06 06:02:02 -07:00
Charles Kerr
92b0f3e808 build: add --unique option to release notes script (#34296)
Useful when looking for changes unique to a single branch
2022-06-06 14:51:10 +09:00
Will Anderson
4f99e3e46c docs: fix did-frame-navigate example in webFrameMain docs (#34419)
docs: fix did-frame-navigate example in webFrameMain docs
2022-06-06 14:49:14 +09:00
electron-roller[bot]
f39c1a35e5 chore: bump node to v16.15.1 (main) (#34424) 2022-06-03 16:13:27 +02:00
28 changed files with 491 additions and 159 deletions

2
DEPS
View File

@@ -4,7 +4,7 @@ vars = {
'chromium_version':
'104.0.5073.0',
'node_version':
'v16.15.0',
'v16.15.1',
'nan_version':
'16fa32231e2ccd89d2804b3f765319128b20c4ac',
'squirrel.mac_version':

View File

@@ -1 +1 @@
21.0.0-nightly.20220603
21.0.0-nightly.20220610

View File

@@ -16,7 +16,7 @@ win.loadURL('https://twitter.com')
win.webContents.on(
'did-frame-navigate',
(event, url, isMainFrame, frameProcessId, frameRoutingId) => {
(event, url, httpResponseCode, httpStatusText, isMainFrame, frameProcessId, frameRoutingId) => {
const frame = webFrameMain.fromId(frameProcessId, frameRoutingId)
if (frame) {
const code = 'document.body.innerHTML = document.body.innerHTML.replaceAll("heck", "h*ck")'

View File

@@ -115,7 +115,7 @@ const win = new BrowserWindow({
})
```
On Windows, you can also specify additional parameters. The color of the overlay and its symbols can be specified by setting `titleBarOverlay` to an object and using the `color` and `symbolColor` properties respectively. The height of the overlay can also be specified with the `height` property.
On either platform `titleBarOverlay` can also be an object. On both macOS and Windows, the height of the overlay can be specified with the `height` property. On Windows, the color of the overlay and its symbols can be specified using the `color` and `symbolColor` properties respectively.
If a color option is not specified, the color will default to its system color for the window control buttons. Similarly, if the height option is not specified it will default to the default height:

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "21.0.0-nightly.20220603",
"version": "21.0.0-nightly.20220610",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
@@ -77,7 +77,7 @@
"scripts": {
"asar": "asar",
"generate-version-json": "node script/generate-version-json.js",
"lint": "node ./script/lint.js && npm run lint:clang-format && npm run lint:docs",
"lint": "node ./script/lint.js && npm run lint:docs",
"lint:js": "node ./script/lint.js --js",
"lint:clang-format": "python3 script/run-clang-format.py -r -c shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
"lint:clang-tidy": "ts-node ./script/run-clang-tidy.ts",
@@ -94,6 +94,7 @@
"gn-typescript-definitions": "npm run create-typescript-definitions && shx cp electron.d.ts",
"pre-flight": "pre-flight",
"gn-check": "node ./script/gn-check.js",
"gn-format": "python3 script/run-gn-format.py",
"precommit": "lint-staged",
"preinstall": "node -e 'process.exit(0)'",
"prepack": "check-for-leaks",
@@ -124,7 +125,7 @@
],
"*.{gn,gni}": [
"npm run gn-check",
"python3 script/run-gn-format.py"
"npm run gn-format"
],
"*.py": [
"node script/lint.js --py --fix --only --"

View File

@@ -1799,7 +1799,7 @@ index 0000000000000000000000000000000000000000..d1d6b51e8c0c5bc6a5d09e217eb30483
+ args = rebase_path(inputs + outputs, root_build_dir)
+}
diff --git a/src/node_version.h b/src/node_version.h
index 29c9be6366d63be7b340b35cea141e4d7e7f71cc..587735f2ddc0e5d93edd8644d121c6fb31fc4378 100644
index 39f6406dd2b52e16a2be5c00c554da30a806ead9..36117c5b36c65f0a8a9bb9c421bc74b82f2b1f3a 100644
--- a/src/node_version.h
+++ b/src/node_version.h
@@ -89,7 +89,10 @@

View File

@@ -7,7 +7,7 @@ common.gypi is a file that's included in the node header bundle, despite
the fact that we do not build node with gyp.
diff --git a/common.gypi b/common.gypi
index bdc2c105abeddc4c8e434ead05ebc0d7d82cfae8..3fd1d4ddddc109dfd87f4ba6115948f1c31b1261 100644
index 3cfed562577978c41a256fc779f6a3a172e65b3d..96d512630e9727467aa523c2dfc1a4cf9275465b 100644
--- a/common.gypi
+++ b/common.gypi
@@ -84,6 +84,23 @@

View File

@@ -6,7 +6,7 @@ Subject: fix: add v8_enable_reverse_jsargs defines in common.gypi
This can be removed once node upgrades V8 and inevitably has to do this exact same thing. Also hi node people if you are looking at this.
diff --git a/common.gypi b/common.gypi
index 3fd1d4ddddc109dfd87f4ba6115948f1c31b1261..fd4e0b38eb6ecf81b23186ec663499d1e685fdf8 100644
index 96d512630e9727467aa523c2dfc1a4cf9275465b..bdfe81d11cc50f492c93fe48f6946765b6fb5238 100644
--- a/common.gypi
+++ b/common.gypi
@@ -82,6 +82,8 @@

View File

@@ -44,7 +44,7 @@ function spawnAndCheckExitCode (cmd, args, opts) {
}
function cpplint (args) {
args.unshift(`--project_root=${SOURCE_ROOT}`);
args.unshift(`--root=${SOURCE_ROOT}`);
const result = childProcess.spawnSync(IS_WINDOWS ? 'cpplint.bat' : 'cpplint.py', args, { encoding: 'utf8', shell: true });
// cpplint.py writes EVERYTHING to stderr, including status messages
if (result.stderr) {
@@ -70,9 +70,9 @@ const LINTERS = [{
test: filename => filename.endsWith('.cc') || (filename.endsWith('.h') && !isObjCHeader(filename)),
run: (opts, filenames) => {
if (opts.fix) {
spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]);
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', '--fix', ...filenames]);
} else {
spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]);
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', ...filenames]);
}
cpplint(filenames);
}
@@ -82,9 +82,9 @@ const LINTERS = [{
test: filename => filename.endsWith('.mm') || (filename.endsWith('.h') && isObjCHeader(filename)),
run: (opts, filenames) => {
if (opts.fix) {
spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]);
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', '--fix', ...filenames]);
} else {
spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]);
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', ...filenames]);
}
const filter = [
'-readability/braces',

View File

@@ -18,7 +18,8 @@ const args = require('minimist')(process.argv.slice(2), {
});
async function main () {
const nodeDir = path.resolve(BASE, `out/${utils.getOutDir({ shouldLog: true })}/gen/node_headers`);
const outDir = utils.getOutDir({ shouldLog: true });
const nodeDir = path.resolve(BASE, 'out', outDir, 'gen', 'node_headers');
const env = Object.assign({}, process.env, {
npm_config_nodedir: nodeDir,
npm_config_msvs_version: '2019',
@@ -31,6 +32,25 @@ async function main () {
const cxx = path.resolve(clangDir, 'clang++');
const ld = path.resolve(clangDir, 'lld');
const platformFlags = [];
if (process.platform === 'darwin') {
const sdkPath = path.resolve(BASE, 'out', outDir, 'sdk', 'xcode_links');
const sdks = (await fs.promises.readdir(sdkPath)).filter(fileName => fileName.endsWith('.sdk'));
const sdkToUse = sdks[0];
if (!sdkToUse) {
console.error('Could not find an SDK to use for the NAN tests');
process.exit(1);
}
if (sdks.length) {
console.warn(`Multiple SDKs found in the xcode_links directory - using ${sdkToUse}`);
}
platformFlags.push(
`-isysroot ${path.resolve(sdkPath, sdkToUse)}`
);
}
// TODO(ckerr) this is cribbed from read obj/electron/electron_app.ninja.
// Maybe it would be better to have this script literally open up that
// file and pull cflags_cc from it instead of using bespoke code here?
@@ -41,15 +61,17 @@ async function main () {
`-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++')}"`,
`-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++', 'trunk', 'include')}"`,
`-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++abi', 'trunk', 'include')}"`,
'-fPIC'
'-fPIC',
...platformFlags
].join(' ');
const ldflags = [
'-stdlib=libc++',
'-fuse-ld=lld',
`-L"${path.resolve(BASE, 'out', `${utils.getOutDir({ shouldLog: true })}`, 'obj', 'buildtools', 'third_party', 'libc++abi')}"`,
`-L"${path.resolve(BASE, 'out', `${utils.getOutDir({ shouldLog: true })}`, 'obj', 'buildtools', 'third_party', 'libc++')}"`,
'-lc++abi'
`-L"${path.resolve(BASE, 'out', outDir, 'obj', 'buildtools', 'third_party', 'libc++abi')}"`,
`-L"${path.resolve(BASE, 'out', outDir, 'obj', 'buildtools', 'third_party', 'libc++')}"`,
'-lc++abi',
...platformFlags
].join(' ');
if (process.platform !== 'win32') {
@@ -66,6 +88,7 @@ async function main () {
cwd: NAN_DIR,
stdio: 'inherit'
});
if (buildStatus !== 0) {
console.error('Failed to build nan test modules');
return process.exit(buildStatus);

View File

@@ -147,7 +147,7 @@ const getPreviousPoint = async (point) => {
}
};
async function getReleaseNotes (range, newVersion) {
async function getReleaseNotes (range, newVersion, unique) {
const rangeList = range.split('..') || ['HEAD'];
const to = rangeList.pop();
const from = rangeList.pop() || (await getPreviousPoint(to));
@@ -158,7 +158,7 @@ async function getReleaseNotes (range, newVersion) {
const notes = await notesGenerator.get(from, to, newVersion);
const ret = {
text: notesGenerator.render(notes)
text: notesGenerator.render(notes, unique)
};
if (notes.unknown.length) {
@@ -170,7 +170,7 @@ async function getReleaseNotes (range, newVersion) {
async function main () {
const opts = minimist(process.argv.slice(2), {
boolean: ['help'],
boolean: ['help', 'unique'],
string: ['version']
});
opts.range = opts._.shift();
@@ -179,13 +179,14 @@ async function main () {
console.log(`
easy usage: ${name} version
full usage: ${name} [begin..]end [--version version]
full usage: ${name} [begin..]end [--version version] [--unique]
* 'begin' and 'end' are two git references -- tags, branches, etc --
from which the release notes are generated.
* if omitted, 'begin' defaults to the previous tag in end's branch.
* if omitted, 'version' defaults to 'end'. Specifying a version is
useful if you're making notes on a new version that isn't tagged yet.
* '--unique' omits changes that also landed in other branches.
For example, these invocations are equivalent:
${process.argv[1]} v4.0.1
@@ -194,7 +195,7 @@ For example, these invocations are equivalent:
return 0;
}
const notes = await getReleaseNotes(opts.range, opts.version);
const notes = await getReleaseNotes(opts.range, opts.version, opts.unique);
console.log(notes.text);
if (notes.warning) {
throw new Error(notes.warning);

View File

@@ -596,10 +596,14 @@ function renderDescription (commit) {
const renderNote = (commit, excludeBranch) =>
`* ${renderDescription(commit)} ${renderLink(commit)} ${renderTrops(commit, excludeBranch)}\n`;
const renderNotes = (notes) => {
const renderNotes = (notes, unique = false) => {
const rendered = [`# Release Notes for ${notes.name}\n\n`];
const renderSection = (title, commits) => {
const renderSection = (title, commits, unique) => {
if (unique) {
// omit changes that also landed in other branches
commits = commits.filter((commit) => renderTrops(commit, notes.toBranch).length === 0);
}
if (commits.length > 0) {
rendered.push(
`## ${title}\n\n`,
@@ -608,17 +612,17 @@ const renderNotes = (notes) => {
}
};
renderSection('Breaking Changes', notes.breaking);
renderSection('Features', notes.feat);
renderSection('Fixes', notes.fix);
renderSection('Other Changes', notes.other);
renderSection('Breaking Changes', notes.breaking, unique);
renderSection('Features', notes.feat, unique);
renderSection('Fixes', notes.fix, unique);
renderSection('Other Changes', notes.other, unique);
if (notes.docs.length) {
const docs = notes.docs.map(commit => renderLink(commit)).sort();
rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n');
}
renderSection('Unknown', notes.unknown);
renderSection('Unknown', notes.unknown, unique);
return rendered.join('');
};

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""A wrapper script around clang-format, suitable for linting multiple files
and to use for continuous integration.
This is an alternative API for the clang-format command line.
It runs over multiple files and directories in parallel.
A diff output is produced and a sensible exit code is returned.
@@ -11,6 +12,7 @@ from __future__ import print_function, unicode_literals
import argparse
import codecs
import difflib
import errno
import fnmatch
import io
import multiprocessing
@@ -26,13 +28,28 @@ from functools import partial
from lib.util import get_buildtools_executable
DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx,mm'
DEFAULT_CLANG_FORMAT_IGNORE = '.clang-format-ignore'
class ExitStatus:
SUCCESS = 0
DIFF = 1
TROUBLE = 2
def excludes_from_file(ignore_file):
excludes = []
try:
with io.open(ignore_file, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('#'):
continue
pattern = line.rstrip()
if not pattern:
continue
excludes.append(pattern)
except EnvironmentError as e:
if e.errno != errno.ENOENT:
raise
return excludes
def list_files(files, recursive=False, extensions=None, exclude=None):
if extensions is None:
@@ -77,15 +94,13 @@ def make_diff(diff_file, original, reformatted):
class DiffError(Exception):
def __init__(self, message, errs=None):
# pylint: disable=R1725
super(DiffError, self).__init__(message)
super().__init__(message)
self.errs = errs or []
class UnexpectedError(Exception):
def __init__(self, message, exc=None):
# pylint: disable=R1725
super(UnexpectedError, self).__init__(message)
super().__init__(message)
self.formatted_traceback = traceback.format_exc()
self.exc = exc
@@ -112,6 +127,11 @@ def run_clang_format_diff(args, file_name):
invocation = [args.clang_format_executable, file_name]
if args.fix:
invocation.append('-i')
if args.style:
invocation.extend(['--style', args.style])
if args.dry_run:
print(" ".join(invocation))
return [], []
try:
proc = subprocess.Popen(
' '.join(invocation),
@@ -121,19 +141,13 @@ def run_clang_format_diff(args, file_name):
shell=True)
except OSError as exc:
# pylint: disable=W0707
raise DiffError(str(exc))
proc_stdout = proc.stdout
proc_stderr = proc.stderr
if sys.version_info[0] == 3:
proc_stdout = proc_stdout.detach()
proc_stderr = proc_stderr.detach()
# make the pipes compatible with Python 3,
# reading lines should output unicode
encoding = 'utf-8'
proc_stdout = codecs.getreader(encoding)(proc_stdout)
proc_stderr = codecs.getreader(encoding)(proc_stderr)
outs = list(proc_stdout.readlines())
errs = list(proc_stderr.readlines())
raise DiffError(
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(invocation), exc
)
)
outs = list(proc.stdout.readlines())
errs = list(proc.stderr.readlines())
proc.wait()
if proc.returncode:
raise DiffError("clang-format exited with status {}: '{}'".format(
@@ -212,6 +226,11 @@ def main():
'--recursive',
action='store_true',
help='run recursively over directories')
parser.add_argument(
'-d',
'--dry-run',
action='store_true',
help='just print the list of files')
parser.add_argument('files', metavar='file', nargs='+')
parser.add_argument(
'-q',
@@ -242,6 +261,10 @@ def main():
default=[],
help='exclude paths matching the given glob-like pattern(s)'
' from recursive search')
parser.add_argument(
'--style',
help='formatting style to apply '
'(LLVM/Google/Chromium/Mozilla/WebKit)')
args = parser.parse_args()
@@ -269,13 +292,14 @@ def main():
parse_files = []
if args.changed:
popen = subprocess.Popen(
'git diff --name-only --cached',
stdout = subprocess.Popen(
"git diff --name-only --cached",
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True
)
for line in popen.stdout:
shell=True,
universal_newlines=True
).communicate()[0].split("\n")
for line in stdout:
file_name = line.rstrip()
# don't check deleted files
if os.path.isfile(file_name):
@@ -284,14 +308,17 @@ def main():
else:
parse_files = args.files
excludes = excludes_from_file(DEFAULT_CLANG_FORMAT_IGNORE)
excludes.extend(args.exclude)
files = list_files(
parse_files,
recursive=args.recursive,
exclude=args.exclude,
exclude=excludes,
extensions=args.extensions.split(','))
if not files:
return 0
return ExitStatus.SUCCESS
njobs = args.j
if njobs == 0:

View File

@@ -1,4 +1,3 @@
import chalk from 'chalk';
import * as childProcess from 'child_process';
import * as fs from 'fs';
import * as klaw from 'klaw';
@@ -143,7 +142,7 @@ async function runClangTidy (
jobs: number = 1
): Promise<boolean> {
const cmd = path.resolve(LLVM_BIN, 'clang-tidy');
const args = [`-p=${outDir}`];
const args = [`-p=${outDir}`, '--use-color'];
if (checks) args.push(`--checks=${checks}`);
@@ -202,38 +201,7 @@ async function runClangTidy (
while (filenames) {
results.push(
await spawnAsync(cmd, [...args, ...filenames], {}).then((result) => {
// We lost color, so recolorize because it's much more legible
// There's a --use-color flag for clang-tidy but it has no effect
// on Windows at the moment, so just recolor for everyone
let state = null;
for (const line of result.stdout.split('\n')) {
if (line.includes(' warning: ')) {
console.log(
line
.split(' warning: ')
.map((part) => chalk.whiteBright(part))
.join(chalk.magentaBright(' warning: '))
);
state = 'code-line';
} else if (line.includes(' note: ')) {
const lineParts = line.split(' note: ');
lineParts[0] = chalk.whiteBright(lineParts[0]);
console.log(lineParts.join(chalk.grey(' note: ')));
state = 'code-line';
} else if (line.startsWith('error:')) {
console.log(
chalk.redBright('error: ') + line.split(' ').slice(1).join(' ')
);
} else if (state === 'code-line') {
console.log(line);
state = 'post-code-line';
} else if (state === 'post-code-line') {
console.log(chalk.greenBright(line));
} else {
console.log(line);
}
}
console.log(result.stdout);
if (result.status !== 0) {
console.error(result.stderr);

View File

@@ -3673,7 +3673,12 @@ void WebContents::EnumerateDirectory(
bool WebContents::IsFullscreenForTabOrPending(
const content::WebContents* source) {
return html_fullscreen_;
bool transition_fs = owner_window()
? owner_window()->fullscreen_transition_state() !=
NativeWindow::FullScreenTransitionState::NONE
: false;
return html_fullscreen_ || transition_fs;
}
bool WebContents::TakeFocus(content::WebContents* source, bool reverse) {
@@ -3995,9 +4000,8 @@ void WebContents::SetHtmlApiFullscreen(bool enter_fullscreen) {
? !web_preferences->ShouldDisableHtmlFullscreenWindowResize()
: true;
if (html_fullscreenable) {
if (html_fullscreenable)
owner_window_->SetFullScreen(enter_fullscreen);
}
UpdateHtmlApiFullscreen(enter_fullscreen);
native_fullscreen_ = false;

View File

@@ -718,6 +718,15 @@ std::string NativeWindow::GetAccessibleTitle() {
return base::UTF16ToUTF8(accessible_title_);
}
void NativeWindow::HandlePendingFullscreenTransitions() {
if (pending_transitions_.empty())
return;
bool next_transition = pending_transitions_.front();
pending_transitions_.pop();
SetFullScreen(next_transition);
}
// static
int32_t NativeWindow::next_id_ = 0;

View File

@@ -7,6 +7,7 @@
#include <list>
#include <memory>
#include <queue>
#include <string>
#include <vector>
@@ -317,6 +318,17 @@ class NativeWindow : public base::SupportsUserData,
observers_.RemoveObserver(obs);
}
enum class FullScreenTransitionState { ENTERING, EXITING, NONE };
// Handle fullscreen transitions.
void HandlePendingFullscreenTransitions();
void set_fullscreen_transition_state(FullScreenTransitionState state) {
fullscreen_transition_state_ = state;
}
FullScreenTransitionState fullscreen_transition_state() const {
return fullscreen_transition_state_;
}
views::Widget* widget() const { return widget_.get(); }
views::View* content_view() const { return content_view_; }
@@ -375,6 +387,10 @@ class NativeWindow : public base::SupportsUserData,
// The "titleBarStyle" option.
TitleBarStyle title_bar_style_ = TitleBarStyle::kNormal;
std::queue<bool> pending_transitions_;
FullScreenTransitionState fullscreen_transition_state_ =
FullScreenTransitionState::NONE;
private:
std::unique_ptr<views::Widget> widget_;

View File

@@ -8,7 +8,6 @@
#import <Cocoa/Cocoa.h>
#include <memory>
#include <queue>
#include <string>
#include <vector>
@@ -166,17 +165,14 @@ 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);
void SetCollectionBehavior(bool on, NSUInteger flag);
void SetWindowLevel(int level);
enum class FullScreenTransitionState { ENTERING, EXITING, NONE };
// Handle fullscreen transitions.
void SetFullScreenTransitionState(FullScreenTransitionState state);
void HandlePendingFullscreenTransitions();
bool HandleDeferredClose();
void SetHasDeferredWindowClose(bool defer_close) {
has_deferred_window_close_ = defer_close;
@@ -247,13 +243,6 @@ class NativeWindowMac : public NativeWindow,
bool zoom_to_page_width_ = false;
absl::optional<gfx::Point> traffic_light_position_;
std::queue<bool> pending_transitions_;
FullScreenTransitionState fullscreen_transition_state() const {
return fullscreen_transition_state_;
}
FullScreenTransitionState fullscreen_transition_state_ =
FullScreenTransitionState::NONE;
// Trying to close an NSWindow during a fullscreen transition will cause the
// window to lock up. Use this to track if CloseWindow was called during a
// fullscreen transition, to defer the -[NSWindow close] call until the

View File

@@ -447,7 +447,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
SetContentView(new views::View());
AddContentViewLayers();
original_frame_ = [window_ frame];
UpdateWindowOriginalFrame();
original_level_ = [window_ level];
}
@@ -582,11 +582,6 @@ bool NativeWindowMac::IsVisible() {
return [window_ isVisible] && !occluded && !IsMinimized();
}
void NativeWindowMac::SetFullScreenTransitionState(
FullScreenTransitionState state) {
fullscreen_transition_state_ = state;
}
bool NativeWindowMac::IsEnabled() {
return [window_ attachedSheet] == nil;
}
@@ -614,7 +609,7 @@ void NativeWindowMac::Maximize() {
// Take note of the current window size
if (IsNormal())
original_frame_ = [window_ frame];
UpdateWindowOriginalFrame();
[window_ zoom:nil];
if (!is_visible) {
@@ -658,7 +653,7 @@ void NativeWindowMac::Minimize() {
// Take note of the current window size
if (IsNormal())
original_frame_ = [window_ frame];
UpdateWindowOriginalFrame();
[window_ miniaturize:nil];
}
@@ -670,15 +665,6 @@ bool NativeWindowMac::IsMinimized() {
return [window_ isMiniaturized];
}
void NativeWindowMac::HandlePendingFullscreenTransitions() {
if (pending_transitions_.empty())
return;
bool next_transition = pending_transitions_.front();
pending_transitions_.pop();
SetFullScreen(next_transition);
}
bool NativeWindowMac::HandleDeferredClose() {
if (has_deferred_window_close_) {
SetHasDeferredWindowClose(false);
@@ -711,7 +697,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
@@ -747,6 +733,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() {
@@ -1019,7 +1006,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];
}
@@ -1416,6 +1403,10 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
}
}
void NativeWindowMac::UpdateWindowOriginalFrame() {
original_frame_ = [window_ frame];
}
void NativeWindowMac::SetVibrancy(const std::string& type) {
NSVisualEffectView* vibrantView = [window_ vibrantView];

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 21,0,0,20220603
PRODUCTVERSION 21,0,0,20220603
FILEVERSION 21,0,0,20220610
PRODUCTVERSION 21,0,0,20220610
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -203,6 +203,7 @@ using FullScreenTransitionState =
// windowDidDeminiaturize
level_ = [window level];
shell_->SetWindowLevel(NSNormalWindowLevel);
shell_->UpdateWindowOriginalFrame();
}
- (void)windowDidMiniaturize:(NSNotification*)notification {
@@ -237,7 +238,7 @@ using FullScreenTransitionState =
// Store resizable mask so it can be restored after exiting fullscreen.
is_resizable_ = shell_->HasStyleMask(NSWindowStyleMaskResizable);
shell_->SetFullScreenTransitionState(FullScreenTransitionState::ENTERING);
shell_->set_fullscreen_transition_state(FullScreenTransitionState::ENTERING);
shell_->NotifyWindowWillEnterFullScreen();
@@ -246,7 +247,7 @@ using FullScreenTransitionState =
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
shell_->SetFullScreenTransitionState(FullScreenTransitionState::NONE);
shell_->set_fullscreen_transition_state(FullScreenTransitionState::NONE);
shell_->NotifyWindowEnterFullScreen();
@@ -257,13 +258,13 @@ using FullScreenTransitionState =
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
shell_->SetFullScreenTransitionState(FullScreenTransitionState::EXITING);
shell_->set_fullscreen_transition_state(FullScreenTransitionState::EXITING);
shell_->NotifyWindowWillLeaveFullScreen();
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
shell_->SetFullScreenTransitionState(FullScreenTransitionState::NONE);
shell_->set_fullscreen_transition_state(FullScreenTransitionState::NONE);
shell_->SetResizable(is_resizable_);
shell_->NotifyWindowLeaveFullScreen();

View File

@@ -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();
@@ -4731,19 +4904,80 @@ describe('BrowserWindow module', () => {
expect(w.isFullScreen()).to.be.false('is fullscreen');
});
it('handles several HTML fullscreen transitions', async () => {
const w = new BrowserWindow();
await w.loadFile(path.join(fixtures, 'pages', 'a.html'));
expect(w.isFullScreen()).to.be.false('is fullscreen');
const enterFullScreen = emittedOnce(w, 'enter-full-screen');
const leaveFullScreen = emittedOnce(w, 'leave-full-screen');
await w.webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
await enterFullScreen;
await w.webContents.executeJavaScript('document.exitFullscreen()', true);
await leaveFullScreen;
expect(w.isFullScreen()).to.be.false('is fullscreen');
await delay();
await w.webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
await enterFullScreen;
await w.webContents.executeJavaScript('document.exitFullscreen()', true);
await leaveFullScreen;
expect(w.isFullScreen()).to.be.false('is fullscreen');
});
it('handles several transitions in close proximity', async () => {
const w = new BrowserWindow();
expect(w.isFullScreen()).to.be.false('is fullscreen');
const enterFS = emittedNTimes(w, 'enter-full-screen', 2);
const leaveFS = emittedNTimes(w, 'leave-full-screen', 2);
w.setFullScreen(true);
w.setFullScreen(false);
w.setFullScreen(true);
w.setFullScreen(false);
const enterFullScreen = emittedNTimes(w, 'enter-full-screen', 2);
await enterFullScreen;
await Promise.all([enterFS, leaveFS]);
expect(w.isFullScreen()).to.be.true('not fullscreen');
expect(w.isFullScreen()).to.be.false('not fullscreen');
});
it('handles several chromium-initiated transitions in close proximity', async () => {
const w = new BrowserWindow();
await w.loadFile(path.join(fixtures, 'pages', 'a.html'));
expect(w.isFullScreen()).to.be.false('is fullscreen');
let enterCount = 0;
let exitCount = 0;
const done = new Promise<void>(resolve => {
const checkDone = () => {
if (enterCount === 2 && exitCount === 2) resolve();
};
w.webContents.on('enter-html-full-screen', () => {
enterCount++;
checkDone();
});
w.webContents.on('leave-html-full-screen', () => {
exitCount++;
checkDone();
});
});
await w.webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
await w.webContents.executeJavaScript('document.exitFullscreen()');
await w.webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
await w.webContents.executeJavaScript('document.exitFullscreen()');
await done;
});
it('does not crash when exiting simpleFullScreen (properties)', async () => {

View File

@@ -230,7 +230,9 @@ describe('webFrameMain module', () => {
// Keep reference to mainFrame alive throughout crash and recovery.
const { mainFrame } = w.webContents;
await w.webContents.loadURL(server.url);
const crashEvent = emittedOnce(w.webContents, 'render-process-gone');
w.webContents.forcefullyCrashRenderer();
await crashEvent;
await w.webContents.loadURL(server.url);
// Log just to keep mainFrame in scope.
console.log('mainFrame.url', mainFrame.url);
@@ -244,7 +246,9 @@ describe('webFrameMain module', () => {
// Keep reference to mainFrame alive throughout crash and recovery.
const { mainFrame } = w.webContents;
await w.webContents.loadURL(server.url);
const crashEvent = emittedOnce(w.webContents, 'render-process-gone');
w.webContents.forcefullyCrashRenderer();
await crashEvent;
// A short wait seems to be required to reproduce the crash.
await new Promise(resolve => setTimeout(resolve, 100));
await w.webContents.loadURL(crossOriginUrl);

View File

@@ -10,7 +10,7 @@ import * as url from 'url';
import * as ChildProcess from 'child_process';
import { EventEmitter } from 'events';
import { promisify } from 'util';
import { ifit, ifdescribe, delay, defer } from './spec-helpers';
import { ifit, ifdescribe, defer, delay } from './spec-helpers';
import { AddressInfo } from 'net';
import { PipeTransport } from './pipe-transport';
@@ -1590,7 +1590,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', ()
server.close();
});
it('can fullscreen from out-of-process iframes (OOPIFs)', async () => {
ifit(process.platform !== 'darwin')('can fullscreen from out-of-process iframes (non-macOS)', async () => {
const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange');
const html =
'<iframe style="width: 0" frameborder=0 src="http://localhost:8989" allowfullscreen></iframe>';
@@ -1614,8 +1614,37 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', ()
expect(width).to.equal(0);
});
ifit(process.platform === 'darwin')('can fullscreen from out-of-process iframes (macOS)', async () => {
await emittedOnce(w, 'enter-full-screen');
const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange');
const html =
'<iframe style="width: 0" frameborder=0 src="http://localhost:8989" allowfullscreen></iframe>';
w.loadURL(`data:text/html,${html}`);
await fullscreenChange;
const fullscreenWidth = await w.webContents.executeJavaScript(
"document.querySelector('iframe').offsetWidth"
);
expect(fullscreenWidth > 0).to.be.true();
await w.webContents.executeJavaScript(
"document.querySelector('iframe').contentWindow.postMessage('exitFullscreen', '*')"
);
await emittedOnce(w.webContents, 'leave-html-full-screen');
const width = await w.webContents.executeJavaScript(
"document.querySelector('iframe').offsetWidth"
);
expect(width).to.equal(0);
w.setFullScreen(false);
await emittedOnce(w, 'leave-full-screen');
});
// TODO(jkleinsc) fix this flaky test on WOA
ifit(process.platform !== 'win32' || process.arch !== 'arm64')('can fullscreen from in-process iframes', async () => {
if (process.platform === 'darwin') await emittedOnce(w, 'enter-full-screen');
const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange');
w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html'));
await fullscreenChange;

View File

@@ -2,6 +2,7 @@
<div id="div">
WebView
</div>
<video></video>
<script type="text/javascript" charset="utf-8">
const {ipcRenderer} = require('electron')
ipcRenderer.send('webview-ready')

View File

@@ -426,11 +426,16 @@ describe('<webview> tag', function () {
contextIsolation: false
}
});
const attachPromise = emittedOnce(w.webContents, 'did-attach-webview');
const loadPromise = emittedOnce(w.webContents, 'did-finish-load');
const readyPromise = emittedOnce(ipcMain, 'webview-ready');
w.loadFile(path.join(__dirname, 'fixtures', 'webview', 'fullscreen', 'main.html'));
const [, webview] = await attachPromise;
await readyPromise;
await Promise.all([readyPromise, loadPromise]);
return [w, webview];
};
@@ -442,17 +447,38 @@ describe('<webview> tag', function () {
closeAllWindows();
});
it('should make parent frame element fullscreen too', async () => {
ifit(process.platform !== 'darwin')('should make parent frame element fullscreen too (non-macOS)', async () => {
const [w, webview] = await loadWebViewWindow();
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false();
const parentFullscreen = emittedOnce(ipcMain, 'fullscreenchange');
await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
await parentFullscreen;
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true();
const close = emittedOnce(w, 'closed');
w.close();
await emittedOnce(w, 'closed');
await close;
});
ifit(process.platform === 'darwin')('should make parent frame element fullscreen too (macOS)', async () => {
const [w, webview] = await loadWebViewWindow();
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false();
const parentFullscreen = emittedOnce(ipcMain, 'fullscreenchange');
const enterHTMLFS = emittedOnce(w.webContents, 'enter-html-full-screen');
const leaveHTMLFS = emittedOnce(w.webContents, 'leave-html-full-screen');
await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true();
await webview.executeJavaScript('document.exitFullscreen()');
await Promise.all([enterHTMLFS, leaveHTMLFS, parentFullscreen]);
const close = emittedOnce(w, 'closed');
w.close();
await close;
});
// FIXME(zcbenz): Fullscreen events do not work on Linux.
@@ -468,8 +494,9 @@ describe('<webview> tag', function () {
await delay(0);
expect(w.isFullScreen()).to.be.false();
const close = emittedOnce(w, 'closed');
w.close();
await emittedOnce(w, 'closed');
await close;
});
// Sending ESC via sendInputEvent only works on Windows.
@@ -485,8 +512,9 @@ describe('<webview> tag', function () {
await delay(0);
expect(w.isFullScreen()).to.be.false();
const close = emittedOnce(w, 'closed');
w.close();
await emittedOnce(w, 'closed');
await close;
});
it('pressing ESC should emit the leave-html-full-screen event', async () => {
@@ -513,11 +541,27 @@ describe('<webview> tag', function () {
const leaveFSWindow = emittedOnce(w, 'leave-html-full-screen');
const leaveFSWebview = emittedOnce(webContents, 'leave-html-full-screen');
webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' });
await leaveFSWindow;
await leaveFSWebview;
await leaveFSWindow;
const close = emittedOnce(w, 'closed');
w.close();
await emittedOnce(w, 'closed');
await close;
});
it('should support user gesture', async () => {
const [w, webview] = await loadWebViewWindow();
const waitForEnterHtmlFullScreen = emittedOnce(webview, 'enter-html-full-screen');
const jsScript = "document.querySelector('video').webkitRequestFullscreen()";
webview.executeJavaScript(jsScript, true);
await waitForEnterHtmlFullScreen;
const close = emittedOnce(w, 'closed');
w.close();
await close;
});
});

View File

@@ -1 +1 @@
<video></video>
<video></video>

View File

@@ -910,20 +910,6 @@ describe('<webview> tag', function () {
});
describe('executeJavaScript', () => {
it('should support user gesture', async () => {
await loadWebView(webview, {
src: `file://${fixtures}/pages/fullscreen.html`
});
// Event handler has to be added before js execution.
const waitForEnterHtmlFullScreen = waitForEvent(webview, 'enter-html-full-screen');
const jsScript = "document.querySelector('video').webkitRequestFullscreen()";
webview.executeJavaScript(jsScript, true);
return waitForEnterHtmlFullScreen;
});
it('can return the result of the executed script', async () => {
await loadWebView(webview, {
src: 'about:blank'