Compare commits

..

9 Commits

Author SHA1 Message Date
John Kleinschmidt
80de0b6acd build: peg to a specific depot_tools so that we can use goma on older branches (#41336)
chore: peg to a specific depot_tools so that we can use goma on older branches
2024-02-14 13:35:26 -05:00
Charles Kerr
28485340b0 build: allow custom refs for patch import & export (#41317)
feat: allow custom refs for patch import & export (#41306)

* feat: allow custom refs for patch import & export

feat: add Patch-Dir metainfo, a sibling to Patch-Filename

* chore: copyediting

* refactor: minor copyediting
2024-02-12 23:11:28 +01:00
trop[bot]
de85def21b fix: destroy NodeService message pipe last (#41302)
refactor: destroy NodeService message pipe last

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-02-11 22:25:45 +01:00
trop[bot]
c9ff1d38e4 fix: crash on macOS non-programmatic close (#41298)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-02-11 21:14:55 +01:00
Charles Kerr
3649df1043 build: export matching patches (#41292) 2024-02-09 17:29:28 -06:00
electron-roller[bot]
89817d6a02 chore: bump chromium to 120.0.6099.283 (28-x-y) (#41262)
chore: bump chromium in DEPS to 120.0.6099.283

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2024-02-09 22:44:49 +01:00
trop[bot]
8177a16f9a docs: note EXIF data unsupported in nativeImage (#41283)
* docs: note EXIF data unsupported in nativeImage

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update docs/api/native-image.md

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-02-08 11:57:44 -06:00
Shelley Vohr
abd7850ee4 fix: validate printToPDF margins against pageSize (#41267) 2024-02-08 11:15:42 +09:00
Shelley Vohr
b0c3534ecb fix: webview zoom level persistence on navigation (#41268) 2024-02-08 11:15:19 +09:00
21 changed files with 239 additions and 96 deletions

View File

@@ -252,6 +252,10 @@ step-depot-tools-get: &step-depot-tools-get
name: Get depot tools
command: |
git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
cd depot_tools
git fetch --depth 1 origin f76550541c751f956ef9287f2695a6c8a74bf709
git checkout f76550541c751f956ef9287f2695a6c8a74bf709
cd ..
if [ "`uname`" == "Darwin" ]; then
# remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems
sed -i '' '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'120.0.6099.276',
'120.0.6099.283',
'node_version':
'v18.18.2',
'nan_version':

View File

@@ -94,6 +94,11 @@ for:
Remove-Item -Recurse -Force $pwd\build-tools
}
- git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
- ps: |
cd depot_tools
git fetch --depth 1 origin f76550541c751f956ef9287f2695a6c8a74bf709
git checkout f76550541c751f956ef9287f2695a6c8a74bf709
cd ..
- ps: New-Item -Name depot_tools\.disable_auto_update -ItemType File
- depot_tools\bootstrap\win_tools.bat
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"

View File

@@ -92,6 +92,11 @@ for:
Remove-Item -Recurse -Force $pwd\build-tools
}
- git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
- ps: |
cd depot_tools
git fetch --depth 1 origin f76550541c751f956ef9287f2695a6c8a74bf709
git checkout f76550541c751f956ef9287f2695a6c8a74bf709
cd ..
- ps: New-Item -Name depot_tools\.disable_auto_update -ItemType File
- depot_tools\bootstrap\win_tools.bat
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"

View File

@@ -51,6 +51,13 @@ Check the _Size requirements_ section in [this article][icons].
[icons]: https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-icons
:::note
EXIF metadata is currently not supported and will not be taken into account during
image encoding and decoding.
:::
## High Resolution Image
On platforms that have high-DPI support such as Apple Retina displays, you can

View File

@@ -220,6 +220,16 @@ function parsePageSize (pageSize: string | ElectronInternal.PageSize) {
let pendingPromise: Promise<any> | undefined;
WebContents.prototype.printToPDF = async function (options) {
const margins = checkType(options.margins ?? {}, 'object', 'margins');
const pageSize = parsePageSize(options.pageSize ?? 'letter');
const { top, bottom, left, right } = margins;
const validHeight = [top, bottom].every(u => u === undefined || u <= pageSize.paperHeight);
const validWidth = [left, right].every(u => u === undefined || u <= pageSize.paperWidth);
if (!validHeight || !validWidth) {
throw new Error('margins must be less than or equal to pageSize');
}
const printSettings = {
requestID: getNextId(),
landscape: checkType(options.landscape ?? false, 'boolean', 'landscape'),
@@ -235,7 +245,7 @@ WebContents.prototype.printToPDF = async function (options) {
pageRanges: checkType(options.pageRanges ?? '', 'string', 'pageRanges'),
preferCSSPageSize: checkType(options.preferCSSPageSize ?? false, 'boolean', 'preferCSSPageSize'),
generateTaggedPDF: checkType(options.generateTaggedPDF ?? false, 'boolean', 'generateTaggedPDF'),
...parsePageSize(options.pageSize ?? 'letter')
...pageSize
};
if (this._printToPDF) {

View File

@@ -1,25 +1,14 @@
{
"src/electron/patches/chromium": "src",
"src/electron/patches/boringssl": "src/third_party/boringssl/src",
"src/electron/patches/devtools_frontend": "src/third_party/devtools-frontend/src",
"src/electron/patches/ffmpeg": "src/third_party/ffmpeg",
"src/electron/patches/v8": "src/v8",
"src/electron/patches/node": "src/third_party/electron_node",
"src/electron/patches/nan": "src/third_party/nan",
"src/electron/patches/perfetto": "src/third_party/perfetto",
"src/electron/patches/squirrel.mac": "src/third_party/squirrel.mac",
"src/electron/patches/Mantle": "src/third_party/squirrel.mac/vendor/Mantle",
"src/electron/patches/ReactiveObjC": "src/third_party/squirrel.mac/vendor/ReactiveObjC",
"src/electron/patches/webrtc": "src/third_party/webrtc"
}
[
{ "patch_dir": "src/electron/patches/chromium", "repo": "src" },
{ "patch_dir": "src/electron/patches/boringssl", "repo": "src/third_party/boringssl/src" },
{ "patch_dir": "src/electron/patches/devtools_frontend", "repo": "src/third_party/devtools-frontend/src" },
{ "patch_dir": "src/electron/patches/ffmpeg", "repo": "src/third_party/ffmpeg" },
{ "patch_dir": "src/electron/patches/v8", "repo": "src/v8" },
{ "patch_dir": "src/electron/patches/node", "repo": "src/third_party/electron_node" },
{ "patch_dir": "src/electron/patches/nan", "repo": "src/third_party/nan" },
{ "patch_dir": "src/electron/patches/perfetto", "repo": "src/third_party/perfetto" },
{ "patch_dir": "src/electron/patches/squirrel.mac", "repo": "src/third_party/squirrel.mac" },
{ "patch_dir": "src/electron/patches/Mantle", "repo": "src/third_party/squirrel.mac/vendor/Mantle" },
{ "patch_dir": "src/electron/patches/ReactiveObjC", "repo": "src/third_party/squirrel.mac/vendor/ReactiveObjC" },
{ "patch_dir": "src/electron/patches/webrtc", "repo": "src/third_party/webrtc" }
]

View File

@@ -3,19 +3,30 @@
import argparse
import json
import os
import warnings
from lib import git
from lib.patches import patch_from_dir
THREEWAY = "ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES" in os.environ
def apply_patches(dirs):
threeway = os.environ.get("ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES")
for patch_dir, repo in dirs.items():
if os.path.exists(repo):
git.import_patches(repo=repo, patch_data=patch_from_dir(patch_dir),
threeway=threeway is not None,
committer_name="Electron Scripts", committer_email="scripts@electron")
def apply_patches(target):
repo = target.get('repo')
if not os.path.exists(repo):
warnings.warn('repo not found: %s' % repo)
return
patch_dir = target.get('patch_dir')
git.import_patches(
committer_email="scripts@electron",
committer_name="Electron Scripts",
patch_data=patch_from_dir(patch_dir),
repo=repo,
threeway=THREEWAY,
)
def apply_config(config):
for target in config:
apply_patches(target)
def parse_args():
parser = argparse.ArgumentParser(description='Apply Electron patches')
@@ -26,9 +37,8 @@ def parse_args():
def main():
configs = parse_args().config
for config_json in configs:
apply_patches(json.load(config_json))
for config_json in parse_args().config:
apply_config(json.load(config_json))
if __name__ == '__main__':

View File

@@ -3,14 +3,27 @@
import argparse
import json
import os
import warnings
from lib import git
def export_patches(dirs, dry_run):
for patch_dir, repo in dirs.items():
if os.path.exists(repo):
git.export_patches(repo=repo, out_dir=patch_dir, dry_run=dry_run)
def export_patches(target, dry_run):
repo = target.get('repo')
if not os.path.exists(repo):
warnings.warn('repo not found: %s' % repo)
return
git.export_patches(
dry_run=dry_run,
grep=target.get('grep'),
out_dir=target.get('patch_dir'),
repo=repo
)
def export_config(config, dry_run):
for target in config:
export_patches(target, dry_run)
def parse_args():
@@ -28,7 +41,7 @@ def main():
configs = parse_args().config
dry_run = parse_args().dry_run
for config_json in configs:
export_patches(json.load(config_json), dry_run)
export_config(json.load(config_json), dry_run)
if __name__ == '__main__':

View File

@@ -10,13 +10,15 @@ def main(argv):
parser.add_argument("-o", "--output",
help="directory into which exported patches will be written",
required=True)
parser.add_argument("--grep",
help="only export patches matching a keyword")
parser.add_argument("patch_range",
nargs='?',
help="range of patches to export. Defaults to all commits since the "
"most recent tag or remote branch.")
args = parser.parse_args(argv)
git.export_patches('.', args.output, patch_range=args.patch_range)
git.export_patches('.', args.output, patch_range=args.patch_range, grep=args.grep)
if __name__ == '__main__':

View File

@@ -15,6 +15,9 @@ import re
import subprocess
import sys
from .patches import PATCH_FILENAME_PREFIX, is_patch_location_line
UPSTREAM_HEAD='refs/patches/upstream-head'
def is_repo_root(path):
path_exists = os.path.exists(path)
@@ -77,14 +80,10 @@ def am(repo, patch_data, threeway=False, directory=None, exclude=None,
proc.returncode))
def import_patches(repo, **kwargs):
def import_patches(repo, ref=UPSTREAM_HEAD, **kwargs):
"""same as am(), but we save the upstream HEAD so we can refer to it when we
later export patches"""
update_ref(
repo=repo,
ref='refs/patches/upstream-head',
newvalue='HEAD'
)
update_ref(repo=repo, ref=ref, newvalue='HEAD')
am(repo=repo, **kwargs)
@@ -94,32 +93,18 @@ def update_ref(repo, ref, newvalue):
return subprocess.check_call(args)
def get_upstream_head(repo):
args = [
'git',
'-C',
repo,
'rev-parse',
'--verify',
'refs/patches/upstream-head',
]
def get_commit_for_ref(repo, ref):
args = ['git', '-C', repo, 'rev-parse', '--verify', ref]
return subprocess.check_output(args).decode('utf-8').strip()
def get_commit_count(repo, commit_range):
args = [
'git',
'-C',
repo,
'rev-list',
'--count',
commit_range
]
args = ['git', '-C', repo, 'rev-list', '--count', commit_range]
return int(subprocess.check_output(args).decode('utf-8').strip())
def guess_base_commit(repo):
def guess_base_commit(repo, ref):
"""Guess which commit the patches might be based on"""
try:
upstream_head = get_upstream_head(repo)
upstream_head = get_commit_for_ref(repo, ref)
num_commits = get_commit_count(repo, upstream_head + '..')
return [upstream_head, num_commits]
except subprocess.CalledProcessError:
@@ -183,6 +168,16 @@ def split_patches(patch_data):
patches[-1].append(line)
return patches
def filter_patches(patches, key):
"""Return patches that include the specified key"""
if key is None:
return patches
matches = []
for patch in patches:
if any(key in line for line in patch):
matches.append(patch)
continue
return matches
def munge_subject_to_filename(subject):
"""Derive a suitable filename from a commit's subject"""
@@ -195,8 +190,8 @@ def get_file_name(patch):
"""Return the name of the file to which the patch should be written"""
file_name = None
for line in patch:
if line.startswith('Patch-Filename: '):
file_name = line[len('Patch-Filename: '):]
if line.startswith(PATCH_FILENAME_PREFIX):
file_name = line[len(PATCH_FILENAME_PREFIX):]
break
# If no patch-filename header, munge the subject.
if not file_name:
@@ -209,19 +204,18 @@ def get_file_name(patch):
def join_patch(patch):
"""Joins and formats patch contents"""
return ''.join(remove_patch_filename(patch)).rstrip('\n') + '\n'
return ''.join(remove_patch_location(patch)).rstrip('\n') + '\n'
def remove_patch_filename(patch):
"""Strip out the Patch-Filename trailer from a patch's message body"""
def remove_patch_location(patch):
"""Strip out the patch location lines from a patch's message body"""
force_keep_next_line = False
n = len(patch)
for i, l in enumerate(patch):
is_patchfilename = l.startswith('Patch-Filename: ')
next_is_patchfilename = i < len(patch) - 1 and patch[i + 1].startswith(
'Patch-Filename: '
)
skip_line = is_patch_location_line(l)
skip_next = i < n - 1 and is_patch_location_line(patch[i + 1])
if not force_keep_next_line and (
is_patchfilename or (next_is_patchfilename and len(l.rstrip()) == 0)
skip_line or (skip_next and len(l.rstrip()) == 0)
):
pass # drop this line
else:
@@ -237,18 +231,24 @@ def to_utf8(patch):
return unicode(patch, "utf-8")
def export_patches(repo, out_dir, patch_range=None, dry_run=False):
def export_patches(repo, out_dir,
patch_range=None, ref=UPSTREAM_HEAD,
dry_run=False, grep=None):
if not os.path.exists(repo):
sys.stderr.write(
"Skipping patches in {} because it does not exist.\n".format(repo)
)
return
if patch_range is None:
patch_range, num_patches = guess_base_commit(repo)
patch_range, num_patches = guess_base_commit(repo, ref)
sys.stderr.write("Exporting {} patches in {} since {}\n".format(
num_patches, repo, patch_range[0:7]))
patch_data = format_patch(repo, patch_range)
patches = split_patches(patch_data)
if grep:
olen = len(patches)
patches = filter_patches(patches, grep)
sys.stderr.write("Exporting {} of {} patches\n".format(len(patches), olen))
try:
os.mkdir(out_dir)

View File

@@ -3,19 +3,27 @@
import codecs
import os
PATCH_DIR_PREFIX = "Patch-Dir: "
PATCH_FILENAME_PREFIX = "Patch-Filename: "
PATCH_LINE_PREFIXES = (PATCH_DIR_PREFIX, PATCH_FILENAME_PREFIX)
def is_patch_location_line(line):
return line.startswith(PATCH_LINE_PREFIXES)
def read_patch(patch_dir, patch_filename):
"""Read a patch from |patch_dir/filename| and amend the commit message with
metadata about the patch file it came from."""
ret = []
added_filename_line = False
added_patch_location = False
patch_path = os.path.join(patch_dir, patch_filename)
with codecs.open(patch_path, encoding='utf-8') as f:
for l in f.readlines():
line_has_correct_start = l.startswith('diff -') or l.startswith('---')
if not added_filename_line and line_has_correct_start:
ret.append('Patch-Filename: {}\n'.format(patch_filename))
added_filename_line = True
if not added_patch_location and line_has_correct_start:
ret.append('{}{}\n'.format(PATCH_DIR_PREFIX, patch_dir))
ret.append('{}{}\n'.format(PATCH_FILENAME_PREFIX, patch_filename))
added_patch_location = True
ret.append(l)
return ''.join(ret)

View File

@@ -258,10 +258,9 @@ const LINTERS = [
process.exit(1);
}
const config = JSON.parse(fs.readFileSync(patchesConfig, 'utf8'));
for (const key of Object.keys(config)) {
for (const target of JSON.parse(fs.readFileSync(patchesConfig, 'utf8'))) {
// The directory the config points to should exist
const targetPatchesDir = path.resolve(__dirname, '../../..', key);
const targetPatchesDir = path.resolve(__dirname, '../../..', target.patch_dir);
if (!fs.existsSync(targetPatchesDir)) {
console.error(
`target patch directory: "${targetPatchesDir}" does not exist`

View File

@@ -14,7 +14,9 @@ from lib.patches import patch_from_dir
def patched_file_paths(patches_config):
for patch_dir, repo in patches_config.items():
for target in patches_config:
patch_dir = target.get('patch_dir')
repo = target.get('repo')
for line in patch_from_dir(patch_dir).split("\n"):
if line.startswith("+++"):
yield posixpath.join(repo, line[6:])

View File

@@ -447,11 +447,14 @@ void NativeWindowMac::Close() {
if ([window_ attachedSheet])
[window_ endSheet:[window_ attachedSheet]];
// window_ could be nil after performClose.
bool should_notify = is_modal() && parent() && IsVisible();
[window_ performClose:nil];
// Closing a sheet doesn't trigger windowShouldClose,
// so we need to manually call it ourselves here.
if (is_modal() && parent() && IsVisible()) {
if (should_notify) {
NotifyWindowCloseButtonClicked();
}
}

View File

@@ -74,6 +74,7 @@ bool WebContentsZoomController::SetZoomLevel(double level) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
// Cannot zoom in disabled mode. Also, don't allow changing zoom level on
// a crashed tab, an error page or an interstitial page.
if (zoom_mode_ == ZOOM_MODE_DISABLED ||
@@ -90,7 +91,7 @@ bool WebContentsZoomController::SetZoomLevel(double level) {
zoom_level_ = level;
ZoomChangedEventData zoom_change_data(web_contents(), old_zoom_level,
zoom_level_, false /* temporary */,
zoom_level_, true /* temporary */,
zoom_mode_);
for (auto& observer : observers_)
observer.OnZoomChanged(zoom_change_data);
@@ -150,7 +151,7 @@ void WebContentsZoomController::SetTemporaryZoomLevel(double level) {
// Notify observers of zoom level changes.
ZoomChangedEventData zoom_change_data(web_contents(), zoom_level_, level,
true /* temporary */, zoom_mode_);
for (WebContentsZoomObserver& observer : observers_)
for (auto& observer : observers_)
observer.OnZoomChanged(zoom_change_data);
}
@@ -264,8 +265,10 @@ void WebContentsZoomController::ResetZoomModeOnNavigationIfNeeded(
double old_zoom_level = zoom_map->GetZoomLevel(web_contents());
double new_zoom_level = zoom_map->GetZoomLevelForHostAndScheme(
url.scheme(), net::GetHostOrSpecFromURL(url));
event_data_ = std::make_unique<ZoomChangedEventData>(
web_contents(), old_zoom_level, new_zoom_level, false, ZOOM_MODE_DEFAULT);
// The call to ClearTemporaryZoomLevel() doesn't generate any events from
// HostZoomMap, but the call to UpdateState() at the end of
// DidFinishNavigation will notify our observers.
@@ -291,11 +294,12 @@ void WebContentsZoomController::DidFinishNavigation(
if (!navigation_handle->IsSameDocument()) {
ResetZoomModeOnNavigationIfNeeded(navigation_handle->GetURL());
SetZoomFactorOnNavigationIfNeeded(navigation_handle->GetURL());
// If the main frame's content has changed, the new page may have a
// different zoom level from the old one.
UpdateState(std::string());
}
// If the main frame's content has changed, the new page may have a different
// zoom level from the old one.
UpdateState(std::string());
DCHECK(!event_data_);
}

View File

@@ -61,6 +61,12 @@ class NodeService : public node::mojom::NodeService {
void Initialize(node::mojom::NodeServiceParamsPtr params) override;
private:
// This needs to be initialized first so that it can be destroyed last
// after the node::Environment is destroyed. This ensures that if
// there are crashes in the node::Environment destructor, they
// will be propagated to the exit handler.
mojo::Receiver<node::mojom::NodeService> receiver_{this};
bool node_env_stopped_ = false;
const std::unique_ptr<NodeBindings> node_bindings_;
@@ -73,8 +79,6 @@ class NodeService : public node::mojom::NodeService {
// depends-on: js_env_'s isolate
std::shared_ptr<node::Environment> node_env_;
mojo::Receiver<node::mojom::NodeService> receiver_{this};
};
} // namespace electron

View File

@@ -2047,6 +2047,20 @@ describe('webContents module', () => {
}
});
it('rejects when margins exceed physical page size', async () => {
await w.loadURL('data:text/html,<h1>Hello, World!</h1>');
await expect(w.webContents.printToPDF({
pageSize: 'Letter',
margins: {
top: 100,
bottom: 100,
left: 5,
right: 5
}
})).to.eventually.be.rejectedWith('margins must be less than or equal to pageSize');
});
it('does not crash when called multiple times in parallel', async () => {
await w.loadURL('data:text/html,<h1>Hello, World!</h1>');

View File

@@ -0,0 +1,31 @@
<html>
<body>
<webview nodeintegration src="app://host1" id="view" partition="webview-temp" />
</body>
<script>
const { ipcRenderer, webFrame } = require('electron')
const view = document.getElementById('view')
let values = {
initialZoomLevel: 0,
switchZoomLevel: 0,
finalZoomLevel: 0,
}
view.addEventListener('dom-ready', async () => {
view.setZoomLevel(2.0)
values.initialZoomLevel = view.getZoomLevel()
await view.loadURL('app://host2')
view.setZoomLevel(3.0)
values.switchZoomLevel = view.getZoomLevel()
await view.loadURL('app://host1')
values.finalZoomLevel = view.getZoomLevel()
ipcRenderer.send('webview-zoom-persist-level', values)
})
</script>
</html>

View File

@@ -946,6 +946,14 @@ describe('node feature', () => {
});
};
let called = false;
process.once('unhandledRejection', () => {
if (called) return;
done(new Error('catch block is delayed to next tick'));
called = true;
});
setTimeout(() => {
f3().catch(() => {
clearTimeout(timer);

View File

@@ -353,6 +353,31 @@ describe('<webview> tag', function () {
expect(zoomLevel).to.equal(1);
});
it('maintains the zoom level for a given host in the same session after navigation', () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
webviewTag: true,
nodeIntegration: true,
contextIsolation: false
}
});
const zoomPromise = new Promise<void>((resolve) => {
ipcMain.on('webview-zoom-persist-level', (_event, values) => {
resolve(values);
});
});
w.loadFile(path.join(fixtures, 'pages', 'webview-zoom-change-persist-host.html'));
expect(zoomPromise).to.eventually.deep.equal({
initialZoomLevel: 2,
switchZoomLevel: 3,
finalZoomLevel: 2
});
});
it('maintains zoom level on navigation', async () => {
const w = new BrowserWindow({
show: false,