From cb798f26b8b6a82b365af91bf90fe1ea5780290f Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Mon, 24 May 2021 08:28:09 -0700 Subject: [PATCH 01/79] test: rebuild nan tests with libc++ and libc++abi (#29294) * test: rebuild nan tests with libc++ and libc++abi (#29281) * test: re-enable nan test: typedarrays-test.js Fixes #28414. I've confirmed this fix wfm on Linux. Pushing into a PR to get CI to run it out on Win and Mac platforms too. * chore: clarify comment * test: fix NAN test string alignment * test: (wip) add ldflags, archive file for libc++ * test: (wip) add libc++ to CircleCI * test: (wip) add llvm flags * test: (wip) change ldflag syntax * test: (wip) build libc++abi as static * fix: correct ldflags * test: add ld env * fix: do not commit this * test: add lld from src to circleci * test: add lld link to ld * chore: preserve third_party * seems legit * sam swears this works kinda sort of sometimes' : * build: add gn visibility patch * chore: update patches * build: check for flatten_relative_to = false * build: upload zip files, add to release.js validation * debug: what the hell gn * build: add libcxx gni to lint ignore Linting the file adjusted the licenses array, which only contains one value, and causes the gn check to fail later * build: also use nan-spec-runner flags on Windows * build: add linked flags for win32 only * build: build libc++ as source on win * build: clean up patch, add -fPIC for IA32 * build: delete libcxx .a files from root * build: rename libc++.zip, clean up upload per platform * build: fix gni lint * ci: add libcxx gen to circleci config * build: correct libcxx-object syntax Co-authored-by: Samuel Attard Co-authored-by: Charles Kerr Co-authored-by: clavin Co-authored-by: Samuel Attard Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard * build: correct libcxx_objects build action name * build: only upload libcxx headers on linux * build: ensure object files are included even if unparsable Co-authored-by: Charles Kerr Co-authored-by: clavin Co-authored-by: Samuel Attard Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard Co-authored-by: Samuel Attard --- .circleci/config.yml | 20 ++ BUILD.gn | 44 ++++ build/zip.py | 15 +- build/zip_libcxx.py | 47 +++++ filenames.libcxx.gni | 194 ++++++++++++++++++ filenames.libcxxabi.gni | 6 + patches/chromium/.patches | 1 + .../build_libc_as_static_library.patch | 46 +++++ patches/nan/.patches | 1 + patches/nan/nan_string_test_alignment.patch | 22 ++ script/gen-libc++-filenames.js | 48 +++++ script/nan-spec-runner.js | 41 +++- script/release/release.js | 6 + script/release/uploaders/upload.py | 16 ++ 14 files changed, 500 insertions(+), 7 deletions(-) create mode 100644 build/zip_libcxx.py create mode 100644 filenames.libcxx.gni create mode 100644 filenames.libcxxabi.gni create mode 100644 patches/chromium/build_libc_as_static_library.patch create mode 100644 patches/nan/nan_string_test_alignment.patch create mode 100644 script/gen-libc++-filenames.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 08da988ba843f..cfacd1d8ea23c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -505,6 +505,7 @@ step-gn-check: &step-gn-check gn check out/Default //electron/shell/common/api:mojo # Check the hunspell filenames node electron/script/gen-hunspell-filenames.js --check + node electron/script/gen-libc++-filenames.js --check step-electron-build: &step-electron-build run: @@ -687,6 +688,11 @@ step-persist-data-for-tests: &step-persist-data-for-tests - src/third_party/electron_node - src/third_party/nan - src/cross-arch-snapshots + - src/third_party/llvm-build + - src/build/linux + - src/buildtools/third_party/libc++ + - src/buildtools/third_party/libc++abi + - src/out/Default/obj/buildtools/third_party step-electron-dist-unzip: &step-electron-dist-unzip run: @@ -837,6 +843,17 @@ step-hunspell-store: &step-hunspell-store path: src/out/Default/hunspell_dictionaries.zip destination: hunspell_dictionaries.zip +step-maybe-generate-libcxx: &step-maybe-generate-libcxx + run: + name: maybe generate libcxx + command: | + cd src + if [ "`uname`" == "Linux" ]; then + ninja -C out/Default electron:libcxx_headers_zip -j $NUMBER_OF_NINJA_PROCESSES + ninja -C out/Default electron:libcxxabi_headers_zip -j $NUMBER_OF_NINJA_PROCESSES + ninja -C out/Default electron:libcxx_objects_zip -j $NUMBER_OF_NINJA_PROCESSES + fi + step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols run: name: Generate breakpad symbols @@ -1687,6 +1704,9 @@ commands: - *step-hunspell-build - *step-hunspell-store + # libcxx + - *step-maybe-generate-libcxx + # typescript defs - *step-maybe-generate-typescript-defs diff --git a/BUILD.gn b/BUILD.gn index 992ef531d7615..0f59b9c14cfba 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -25,6 +25,8 @@ import("electron_paks.gni") import("filenames.auto.gni") import("filenames.gni") import("filenames.hunspell.gni") +import("filenames.libcxx.gni") +import("filenames.libcxxabi.gni") if (is_mac) { import("//build/config/mac/rules.gni") @@ -1292,13 +1294,18 @@ template("dist_zip") { "testonly", ]) flatten = false + flatten_relative_to = false if (defined(invoker.flatten)) { flatten = invoker.flatten + if (defined(invoker.flatten_relative_to)) { + flatten_relative_to = invoker.flatten_relative_to + } } args = rebase_path(outputs + [ _runtime_deps_file ], root_build_dir) + [ target_cpu, target_os, "$flatten", + "$flatten_relative_to", ] } } @@ -1389,6 +1396,43 @@ dist_zip("hunspell_dictionaries_zip") { outputs = [ "$root_build_dir/hunspell_dictionaries.zip" ] } +copy("libcxx_headers") { + sources = libcxx_headers + libcxx_licenses + outputs = [ "$target_gen_dir/electron_libcxx_include/{{source_root_relative_dir}}/{{source_file_part}}" ] +} + +dist_zip("libcxx_headers_zip") { + data_deps = [ ":libcxx_headers" ] + flatten = true + flatten_relative_to = rebase_path( + "$target_gen_dir/electron_libcxx_include/buildtools/third_party/libc++/trunk", + "$root_out_dir") + + outputs = [ "$root_build_dir/libcxx_headers.zip" ] +} + +copy("libcxxabi_headers") { + sources = libcxxabi_headers + libcxxabi_licenses + outputs = [ "$target_gen_dir/electron_libcxxabi_include/{{source_root_relative_dir}}/{{source_file_part}}" ] +} + +dist_zip("libcxxabi_headers_zip") { + data_deps = [ ":libcxxabi_headers" ] + flatten = true + flatten_relative_to = rebase_path( + "$target_gen_dir/electron_libcxxabi_include/buildtools/third_party/libc++abi/trunk", + "$root_out_dir") + + outputs = [ "$root_build_dir/libcxxabi_headers.zip" ] +} + +action("libcxx_objects_zip") { + deps = [ "//buildtools/third_party/libc++" ] + script = "build/zip_libcxx.py" + outputs = [ "$root_build_dir/libcxx_objects.zip" ] + args = rebase_path(outputs) +} + group("electron") { public_deps = [ ":electron_app" ] } diff --git a/build/zip.py b/build/zip.py index 720141712d891..a12a7fd4b6205 100644 --- a/build/zip.py +++ b/build/zip.py @@ -71,7 +71,7 @@ def execute(argv): raise e def main(argv): - dist_zip, runtime_deps, target_cpu, _, flatten_val = argv + dist_zip, runtime_deps, target_cpu, _, flatten_val, flatten_relative_to = argv should_flatten = flatten_val == "true" dist_files = set() with open(runtime_deps) as f: @@ -98,11 +98,18 @@ def main(argv): if basename == 'chrome_sandbox' else dep ) + name_to_write = arcname + if should_flatten: + if flatten_relative_to: + if name_to_write.startswith(flatten_relative_to): + name_to_write = name_to_write[len(flatten_relative_to):] + else: + name_to_write = os.path.basename(arcname) + else: + name_to_write = os.path.basename(arcname) z.write( dep, - os.path.basename(arcname) - if should_flatten - else arcname, + name_to_write, ) if __name__ == '__main__': diff --git a/build/zip_libcxx.py b/build/zip_libcxx.py new file mode 100644 index 0000000000000..5eec83d2261b9 --- /dev/null +++ b/build/zip_libcxx.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +from __future__ import print_function +import os +import subprocess +import sys +import zipfile + +def execute(argv): + try: + output = subprocess.check_output(argv, stderr=subprocess.STDOUT) + return output + except subprocess.CalledProcessError as e: + print(e.output) + raise e + +def get_object_files(base_path, archive_name): + archive_file = os.path.join(base_path, archive_name) + output = execute(['nm', '-g', archive_file]).decode('ascii') + object_files = set() + lines = output.split("\n") + for line in lines: + if line.startswith(base_path): + object_file = line.split(":")[0] + object_files.add(object_file) + if line.startswith('nm: '): + object_file = line.split(":")[1].lstrip() + object_files.add(object_file) + return list(object_files) + [archive_file] + +def main(argv): + dist_zip, = argv + out_dir = os.path.dirname(dist_zip) + base_path_libcxx = os.path.join(out_dir, 'obj/buildtools/third_party/libc++') + base_path_libcxxabi = os.path.join(out_dir, 'obj/buildtools/third_party/libc++abi') + object_files_libcxx = get_object_files(base_path_libcxx, 'libc++.a') + object_files_libcxxabi = get_object_files(base_path_libcxxabi, 'libc++abi.a') + with zipfile.ZipFile( + dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True + ) as z: + object_files_libcxx.sort() + for object_file in object_files_libcxx: + z.write(object_file, os.path.relpath(object_file, base_path_libcxx)) + for object_file in object_files_libcxxabi: + z.write(object_file, os.path.relpath(object_file, base_path_libcxxabi)) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/filenames.libcxx.gni b/filenames.libcxx.gni new file mode 100644 index 0000000000000..1e9c6ffcbca3d --- /dev/null +++ b/filenames.libcxx.gni @@ -0,0 +1,194 @@ +libcxx_headers = [ + "//buildtools/third_party/libc++/trunk/include/CMakeLists.txt", + "//buildtools/third_party/libc++/trunk/include/__availability", + "//buildtools/third_party/libc++/trunk/include/__bit_reference", + "//buildtools/third_party/libc++/trunk/include/__bits", + "//buildtools/third_party/libc++/trunk/include/__bsd_locale_defaults.h", + "//buildtools/third_party/libc++/trunk/include/__bsd_locale_fallbacks.h", + "//buildtools/third_party/libc++/trunk/include/__config", + "//buildtools/third_party/libc++/trunk/include/__config_site.in", + "//buildtools/third_party/libc++/trunk/include/__debug", + "//buildtools/third_party/libc++/trunk/include/__errc", + "//buildtools/third_party/libc++/trunk/include/__functional_03", + "//buildtools/third_party/libc++/trunk/include/__functional_base", + "//buildtools/third_party/libc++/trunk/include/__functional_base_03", + "//buildtools/third_party/libc++/trunk/include/__hash_table", + "//buildtools/third_party/libc++/trunk/include/__libcpp_version", + "//buildtools/third_party/libc++/trunk/include/__locale", + "//buildtools/third_party/libc++/trunk/include/__memory/allocator_traits.h", + "//buildtools/third_party/libc++/trunk/include/__memory/base.h", + "//buildtools/third_party/libc++/trunk/include/__memory/pointer_traits.h", + "//buildtools/third_party/libc++/trunk/include/__memory/utilities.h", + "//buildtools/third_party/libc++/trunk/include/__mutex_base", + "//buildtools/third_party/libc++/trunk/include/__node_handle", + "//buildtools/third_party/libc++/trunk/include/__nullptr", + "//buildtools/third_party/libc++/trunk/include/__split_buffer", + "//buildtools/third_party/libc++/trunk/include/__sso_allocator", + "//buildtools/third_party/libc++/trunk/include/__std_stream", + "//buildtools/third_party/libc++/trunk/include/__string", + "//buildtools/third_party/libc++/trunk/include/__support/android/locale_bionic.h", + "//buildtools/third_party/libc++/trunk/include/__support/fuchsia/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/ibm/limits.h", + "//buildtools/third_party/libc++/trunk/include/__support/ibm/locale_mgmt_aix.h", + "//buildtools/third_party/libc++/trunk/include/__support/ibm/nanosleep.h", + "//buildtools/third_party/libc++/trunk/include/__support/ibm/support.h", + "//buildtools/third_party/libc++/trunk/include/__support/ibm/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/musl/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/newlib/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/nuttx/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/openbsd/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/solaris/floatingpoint.h", + "//buildtools/third_party/libc++/trunk/include/__support/solaris/wchar.h", + "//buildtools/third_party/libc++/trunk/include/__support/solaris/xlocale.h", + "//buildtools/third_party/libc++/trunk/include/__support/win32/limits_msvc_win32.h", + "//buildtools/third_party/libc++/trunk/include/__support/win32/locale_win32.h", + "//buildtools/third_party/libc++/trunk/include/__support/xlocale/__nop_locale_mgmt.h", + "//buildtools/third_party/libc++/trunk/include/__support/xlocale/__posix_l_fallback.h", + "//buildtools/third_party/libc++/trunk/include/__support/xlocale/__strtonum_fallback.h", + "//buildtools/third_party/libc++/trunk/include/__threading_support", + "//buildtools/third_party/libc++/trunk/include/__tree", + "//buildtools/third_party/libc++/trunk/include/__tuple", + "//buildtools/third_party/libc++/trunk/include/__undef_macros", + "//buildtools/third_party/libc++/trunk/include/algorithm", + "//buildtools/third_party/libc++/trunk/include/any", + "//buildtools/third_party/libc++/trunk/include/array", + "//buildtools/third_party/libc++/trunk/include/atomic", + "//buildtools/third_party/libc++/trunk/include/barrier", + "//buildtools/third_party/libc++/trunk/include/bit", + "//buildtools/third_party/libc++/trunk/include/bitset", + "//buildtools/third_party/libc++/trunk/include/cassert", + "//buildtools/third_party/libc++/trunk/include/ccomplex", + "//buildtools/third_party/libc++/trunk/include/cctype", + "//buildtools/third_party/libc++/trunk/include/cerrno", + "//buildtools/third_party/libc++/trunk/include/cfenv", + "//buildtools/third_party/libc++/trunk/include/cfloat", + "//buildtools/third_party/libc++/trunk/include/charconv", + "//buildtools/third_party/libc++/trunk/include/chrono", + "//buildtools/third_party/libc++/trunk/include/cinttypes", + "//buildtools/third_party/libc++/trunk/include/ciso646", + "//buildtools/third_party/libc++/trunk/include/climits", + "//buildtools/third_party/libc++/trunk/include/clocale", + "//buildtools/third_party/libc++/trunk/include/cmath", + "//buildtools/third_party/libc++/trunk/include/codecvt", + "//buildtools/third_party/libc++/trunk/include/compare", + "//buildtools/third_party/libc++/trunk/include/complex", + "//buildtools/third_party/libc++/trunk/include/complex.h", + "//buildtools/third_party/libc++/trunk/include/concepts", + "//buildtools/third_party/libc++/trunk/include/condition_variable", + "//buildtools/third_party/libc++/trunk/include/csetjmp", + "//buildtools/third_party/libc++/trunk/include/csignal", + "//buildtools/third_party/libc++/trunk/include/cstdarg", + "//buildtools/third_party/libc++/trunk/include/cstdbool", + "//buildtools/third_party/libc++/trunk/include/cstddef", + "//buildtools/third_party/libc++/trunk/include/cstdint", + "//buildtools/third_party/libc++/trunk/include/cstdio", + "//buildtools/third_party/libc++/trunk/include/cstdlib", + "//buildtools/third_party/libc++/trunk/include/cstring", + "//buildtools/third_party/libc++/trunk/include/ctgmath", + "//buildtools/third_party/libc++/trunk/include/ctime", + "//buildtools/third_party/libc++/trunk/include/ctype.h", + "//buildtools/third_party/libc++/trunk/include/cwchar", + "//buildtools/third_party/libc++/trunk/include/cwctype", + "//buildtools/third_party/libc++/trunk/include/deque", + "//buildtools/third_party/libc++/trunk/include/errno.h", + "//buildtools/third_party/libc++/trunk/include/exception", + "//buildtools/third_party/libc++/trunk/include/execution", + "//buildtools/third_party/libc++/trunk/include/experimental/__config", + "//buildtools/third_party/libc++/trunk/include/experimental/__memory", + "//buildtools/third_party/libc++/trunk/include/experimental/algorithm", + "//buildtools/third_party/libc++/trunk/include/experimental/coroutine", + "//buildtools/third_party/libc++/trunk/include/experimental/deque", + "//buildtools/third_party/libc++/trunk/include/experimental/filesystem", + "//buildtools/third_party/libc++/trunk/include/experimental/forward_list", + "//buildtools/third_party/libc++/trunk/include/experimental/functional", + "//buildtools/third_party/libc++/trunk/include/experimental/iterator", + "//buildtools/third_party/libc++/trunk/include/experimental/list", + "//buildtools/third_party/libc++/trunk/include/experimental/map", + "//buildtools/third_party/libc++/trunk/include/experimental/memory_resource", + "//buildtools/third_party/libc++/trunk/include/experimental/propagate_const", + "//buildtools/third_party/libc++/trunk/include/experimental/regex", + "//buildtools/third_party/libc++/trunk/include/experimental/set", + "//buildtools/third_party/libc++/trunk/include/experimental/simd", + "//buildtools/third_party/libc++/trunk/include/experimental/string", + "//buildtools/third_party/libc++/trunk/include/experimental/type_traits", + "//buildtools/third_party/libc++/trunk/include/experimental/unordered_map", + "//buildtools/third_party/libc++/trunk/include/experimental/unordered_set", + "//buildtools/third_party/libc++/trunk/include/experimental/utility", + "//buildtools/third_party/libc++/trunk/include/experimental/vector", + "//buildtools/third_party/libc++/trunk/include/ext/__hash", + "//buildtools/third_party/libc++/trunk/include/ext/hash_map", + "//buildtools/third_party/libc++/trunk/include/ext/hash_set", + "//buildtools/third_party/libc++/trunk/include/fenv.h", + "//buildtools/third_party/libc++/trunk/include/filesystem", + "//buildtools/third_party/libc++/trunk/include/float.h", + "//buildtools/third_party/libc++/trunk/include/format", + "//buildtools/third_party/libc++/trunk/include/forward_list", + "//buildtools/third_party/libc++/trunk/include/fstream", + "//buildtools/third_party/libc++/trunk/include/functional", + "//buildtools/third_party/libc++/trunk/include/future", + "//buildtools/third_party/libc++/trunk/include/initializer_list", + "//buildtools/third_party/libc++/trunk/include/inttypes.h", + "//buildtools/third_party/libc++/trunk/include/iomanip", + "//buildtools/third_party/libc++/trunk/include/ios", + "//buildtools/third_party/libc++/trunk/include/iosfwd", + "//buildtools/third_party/libc++/trunk/include/iostream", + "//buildtools/third_party/libc++/trunk/include/istream", + "//buildtools/third_party/libc++/trunk/include/iterator", + "//buildtools/third_party/libc++/trunk/include/latch", + "//buildtools/third_party/libc++/trunk/include/limits", + "//buildtools/third_party/libc++/trunk/include/limits.h", + "//buildtools/third_party/libc++/trunk/include/list", + "//buildtools/third_party/libc++/trunk/include/locale", + "//buildtools/third_party/libc++/trunk/include/locale.h", + "//buildtools/third_party/libc++/trunk/include/map", + "//buildtools/third_party/libc++/trunk/include/math.h", + "//buildtools/third_party/libc++/trunk/include/memory", + "//buildtools/third_party/libc++/trunk/include/module.modulemap", + "//buildtools/third_party/libc++/trunk/include/mutex", + "//buildtools/third_party/libc++/trunk/include/new", + "//buildtools/third_party/libc++/trunk/include/numbers", + "//buildtools/third_party/libc++/trunk/include/numeric", + "//buildtools/third_party/libc++/trunk/include/optional", + "//buildtools/third_party/libc++/trunk/include/ostream", + "//buildtools/third_party/libc++/trunk/include/queue", + "//buildtools/third_party/libc++/trunk/include/random", + "//buildtools/third_party/libc++/trunk/include/ratio", + "//buildtools/third_party/libc++/trunk/include/regex", + "//buildtools/third_party/libc++/trunk/include/scoped_allocator", + "//buildtools/third_party/libc++/trunk/include/semaphore", + "//buildtools/third_party/libc++/trunk/include/set", + "//buildtools/third_party/libc++/trunk/include/setjmp.h", + "//buildtools/third_party/libc++/trunk/include/shared_mutex", + "//buildtools/third_party/libc++/trunk/include/span", + "//buildtools/third_party/libc++/trunk/include/sstream", + "//buildtools/third_party/libc++/trunk/include/stack", + "//buildtools/third_party/libc++/trunk/include/stdbool.h", + "//buildtools/third_party/libc++/trunk/include/stddef.h", + "//buildtools/third_party/libc++/trunk/include/stdexcept", + "//buildtools/third_party/libc++/trunk/include/stdint.h", + "//buildtools/third_party/libc++/trunk/include/stdio.h", + "//buildtools/third_party/libc++/trunk/include/stdlib.h", + "//buildtools/third_party/libc++/trunk/include/streambuf", + "//buildtools/third_party/libc++/trunk/include/string", + "//buildtools/third_party/libc++/trunk/include/string.h", + "//buildtools/third_party/libc++/trunk/include/string_view", + "//buildtools/third_party/libc++/trunk/include/strstream", + "//buildtools/third_party/libc++/trunk/include/system_error", + "//buildtools/third_party/libc++/trunk/include/tgmath.h", + "//buildtools/third_party/libc++/trunk/include/thread", + "//buildtools/third_party/libc++/trunk/include/tuple", + "//buildtools/third_party/libc++/trunk/include/type_traits", + "//buildtools/third_party/libc++/trunk/include/typeindex", + "//buildtools/third_party/libc++/trunk/include/typeinfo", + "//buildtools/third_party/libc++/trunk/include/unordered_map", + "//buildtools/third_party/libc++/trunk/include/unordered_set", + "//buildtools/third_party/libc++/trunk/include/utility", + "//buildtools/third_party/libc++/trunk/include/valarray", + "//buildtools/third_party/libc++/trunk/include/variant", + "//buildtools/third_party/libc++/trunk/include/vector", + "//buildtools/third_party/libc++/trunk/include/version", + "//buildtools/third_party/libc++/trunk/include/wchar.h", + "//buildtools/third_party/libc++/trunk/include/wctype.h", +] + +libcxx_licenses = [ "//buildtools/third_party/libc++/trunk/LICENSE.TXT" ] diff --git a/filenames.libcxxabi.gni b/filenames.libcxxabi.gni new file mode 100644 index 0000000000000..813f95070a2fa --- /dev/null +++ b/filenames.libcxxabi.gni @@ -0,0 +1,6 @@ +libcxxabi_headers = [ + "//buildtools/third_party/libc++abi/trunk/include/__cxxabi_config.h", + "//buildtools/third_party/libc++abi/trunk/include/cxxabi.h", +] + +libcxxabi_licenses = [ "//buildtools/third_party/libc++abi/trunk/LICENSE.TXT" ] diff --git a/patches/chromium/.patches b/patches/chromium/.patches index de3d43644b94e..8e2d84985171f 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -109,3 +109,4 @@ fix_expose_decrementcapturercount_in_web_contents_impl.patch add_setter_for_browsermainloop_result_code.patch cherry-pick-8089dbfc616f.patch x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch +build_libc_as_static_library.patch diff --git a/patches/chromium/build_libc_as_static_library.patch b/patches/chromium/build_libc_as_static_library.patch new file mode 100644 index 0000000000000..47a77b4ff15e1 --- /dev/null +++ b/patches/chromium/build_libc_as_static_library.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: VerteDinde +Date: Wed, 12 May 2021 13:19:19 -0700 +Subject: build_libc++_as_static_library + +Build libc++ as static library to compile and pass +nan tests + +diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn +index 65f6e8585ad374ad59e7670babfedd2fbdfbaa43..7928a03447347f441e1e7dfc43f7a8e475eb120b 100644 +--- a/buildtools/third_party/libc++/BUILD.gn ++++ b/buildtools/third_party/libc++/BUILD.gn +@@ -32,7 +32,11 @@ config("winver") { + if (libcxx_is_shared) { + _libcxx_target_type = "shared_library" + } else { +- _libcxx_target_type = "source_set" ++ if (is_win) { ++ _libcxx_target_type = "source_set" ++ } else { ++ _libcxx_target_type = "static_library" ++ } + } + target(_libcxx_target_type, "libc++") { + # Most things that need to depend on libc++ should do so via the implicit +@@ -40,6 +44,7 @@ target(_libcxx_target_type, "libc++") { + # need to explicitly depend on libc++. + visibility = [ + "//build/config:common_deps", ++ "//electron:libcxx_objects_zip", + "//third_party/catapult/devil:devil", + ] + if (is_linux && !is_chromeos) { +diff --git a/buildtools/third_party/libc++abi/BUILD.gn b/buildtools/third_party/libc++abi/BUILD.gn +index 8b1da01ce87ff6db8e67938d4c083312cfa3101f..1668eba70db1933a434709c0140fe125991249b3 100644 +--- a/buildtools/third_party/libc++abi/BUILD.gn ++++ b/buildtools/third_party/libc++abi/BUILD.gn +@@ -4,7 +4,7 @@ + + import("//build/config/c++/c++.gni") + +-source_set("libc++abi") { ++static_library("libc++abi") { + if (export_libcxxabi_from_executables) { + visibility = [ "//build/config:executable_deps" ] + } else { diff --git a/patches/nan/.patches b/patches/nan/.patches index 778dd3193db81..e40404e7c5a8a 100644 --- a/patches/nan/.patches +++ b/patches/nan/.patches @@ -1 +1,2 @@ api_simplify_scriptorigin.patch +nan_string_test_alignment.patch diff --git a/patches/nan/nan_string_test_alignment.patch b/patches/nan/nan_string_test_alignment.patch new file mode 100644 index 0000000000000..c5e848ccbe36e --- /dev/null +++ b/patches/nan/nan_string_test_alignment.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: clavin +Date: Wed, 12 May 2021 12:43:07 -0600 +Subject: nan_string_test_alignment + +Modifies a NAN test to avoid a debug check pertaining to efficient string alignment. + +diff --git a/test/cpp/strings.cpp b/test/cpp/strings.cpp +index 95edeac91a4ec6a5f5cd80aa36dca8a55eb53f2a..0ad5cb7095490ac1eb454318582a9a683cb14be1 100644 +--- a/test/cpp/strings.cpp ++++ b/test/cpp/strings.cpp +@@ -26,7 +26,9 @@ NAN_METHOD(EncodeHex) { + } + + NAN_METHOD(EncodeUCS2) { +- info.GetReturnValue().Set(Encode("h\0e\0l\0l\0o\0", 10, UCS2)); ++ // This odd declaration is to get the string data aligned to a 2-byte boundary ++ const uint16_t str[] = {'h', 'e', 'l', 'l', 'o'}; ++ info.GetReturnValue().Set(Encode(reinterpret_cast(str), 10, UCS2)); + } + + Persistent returnUtf8String_persistent; diff --git a/script/gen-libc++-filenames.js b/script/gen-libc++-filenames.js new file mode 100644 index 0000000000000..0d083c4ea2d62 --- /dev/null +++ b/script/gen-libc++-filenames.js @@ -0,0 +1,48 @@ +const fs = require('fs'); +const path = require('path'); + +const check = process.argv.includes('--check'); + +function findAllHeaders (basePath) { + const allFiles = fs.readdirSync(basePath); + const toReturn = []; + for (const file of allFiles) { + const absPath = path.resolve(basePath, file); + if (fs.statSync(absPath).isDirectory()) { + toReturn.push(...findAllHeaders(absPath)); + } else { + toReturn.push(absPath); + } + } + return toReturn; +} + +for (const folder of ['libc++', 'libc++abi']) { + const prettyName = folder.replace(/\+/g, 'x'); + + const libcxxIncludeDir = path.resolve(__dirname, '..', '..', 'buildtools', 'third_party', folder, 'trunk', 'include'); + const gclientPath = `buildtools/third_party/${folder}/trunk/include`; + + const headers = findAllHeaders(libcxxIncludeDir).map(absPath => path.relative(path.resolve(__dirname, '../..', gclientPath), absPath)); + + const content = `${prettyName}_headers = [ + ${headers.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, +] + +${prettyName}_licenses = [ "//buildtools/third_party/${folder}/trunk/LICENSE.TXT" ] +`; + + const filenamesPath = path.resolve(__dirname, '..', `filenames.${prettyName}.gni`); + + if (check) { + const currentContent = fs.readFileSync(filenamesPath, 'utf8'); + if (currentContent !== content) { + console.log('currentContent: ', currentContent); + console.log('content: ', content); + throw new Error(`${prettyName} filenames need to be regenerated, latest generation does not match current file. Please run node gen-libc++-filenames.js`); + } + } else { + console.log(filenamesPath); + fs.writeFileSync(filenamesPath, content); + } +} diff --git a/script/nan-spec-runner.js b/script/nan-spec-runner.js index e4c077f3e1eb0..32db5fda7d387 100644 --- a/script/nan-spec-runner.js +++ b/script/nan-spec-runner.js @@ -25,7 +25,43 @@ async function main () { npm_config_arch: process.env.NPM_CONFIG_ARCH, npm_config_yes: 'true' }); - const { status: buildStatus } = cp.spawnSync(NPX_CMD, ['node-gyp', 'rebuild', '--directory', 'test', '-j', 'max'], { + + const clangDir = path.resolve(BASE, 'third_party', 'llvm-build', 'Release+Asserts', 'bin'); + const cc = path.resolve(clangDir, 'clang'); + const cxx = path.resolve(clangDir, 'clang++'); + const ld = path.resolve(clangDir, 'lld'); + + // 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? + // I think it's unlikely to work; but if it does, it would be more futureproof + const cxxflags = [ + '-std=c++14', + '-nostdinc++', + '-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS', // needed by next line + `-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++', 'trunk', 'include')}"`, + `-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++abi', 'trunk', 'include')}"`, + '-fPIC' + ].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' + ].join(' '); + + if (process.platform !== 'win32') { + env.CC = cc; + env.CFLAGS = cxxflags; + env.CXX = cxx; + env.LD = ld; + env.CXXFLAGS = cxxflags; + env.LDFLAGS = ldflags; + } + + const { status: buildStatus } = cp.spawnSync(NPX_CMD, ['node-gyp', 'rebuild', '--verbose', '--directory', 'test', '-j', 'max'], { env, cwd: NAN_DIR, stdio: 'inherit' @@ -48,8 +84,7 @@ async function main () { const onlyTests = args.only && args.only.split(','); const DISABLED_TESTS = [ - 'nannew-test.js', - 'typedarrays-test.js' // TODO(nornagon): https://github.com/electron/electron/issues/28414 + 'nannew-test.js' ]; const testsToRun = fs.readdirSync(path.resolve(NAN_DIR, 'test', 'js')) .filter(test => !DISABLED_TESTS.includes(test)) diff --git a/script/release/release.js b/script/release/release.js index 6185019c9e701..37ed602ddf5f7 100755 --- a/script/release/release.js +++ b/script/release/release.js @@ -139,6 +139,12 @@ function assetsForVersion (version, validatingRelease) { 'electron-api.json', 'electron.d.ts', 'hunspell_dictionaries.zip', + 'libcxx_headers.zip', + 'libcxxabi_headers.zip', + `libcxx-objects-${version}-linux-arm64.zip`, + `libcxx-objects-${version}-linux-armv7l.zip`, + `libcxx-objects-${version}-linux-ia32.zip`, + `libcxx-objects-${version}-linux-x64.zip`, `ffmpeg-${version}-darwin-x64.zip`, `ffmpeg-${version}-darwin-arm64.zip`, `ffmpeg-${version}-linux-arm64.zip`, diff --git a/script/release/uploaders/upload.py b/script/release/uploaders/upload.py index 8ad471b3749fe..6d62d16a0fbb1 100755 --- a/script/release/uploaders/upload.py +++ b/script/release/uploaders/upload.py @@ -37,6 +37,8 @@ DEBUG_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'debug') TOOLCHAIN_PROFILE_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'toolchain-profile') +CXX_OBJECTS_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, + 'libcxx_objects') def main(): @@ -95,6 +97,20 @@ def main(): shutil.copy2(os.path.join(OUT_DIR, 'debug.zip'), debug_zip) upload_electron(release, debug_zip, args) + # Upload libcxx_objects.zip for linux only + libcxx_objects = get_zip_name('libcxx-objects', ELECTRON_VERSION) + libcxx_objects_zip = os.path.join(OUT_DIR, libcxx_objects) + shutil.copy2(os.path.join(OUT_DIR, 'libcxx_objects.zip'), libcxx_objects_zip) + upload_electron(release, libcxx_objects_zip, args) + + # Upload headers.zip and abi_headers.zip as non-platform specific + if get_target_arch() == "x64": + cxx_headers_zip = os.path.join(OUT_DIR, 'libcxx_headers.zip') + upload_electron(release, cxx_headers_zip, args) + + abi_headers_zip = os.path.join(OUT_DIR, 'libcxxabi_headers.zip') + upload_electron(release, abi_headers_zip, args) + # Upload free version of ffmpeg. ffmpeg = get_zip_name('ffmpeg', ELECTRON_VERSION) ffmpeg_zip = os.path.join(OUT_DIR, ffmpeg) From 3651c0411f25def02573a29116f34dbf1d4f0508 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Mon, 24 May 2021 09:04:22 -0700 Subject: [PATCH 02/79] Bump v13.0.0 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index e32b8a9446335..70045d3c9a674 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.0.0-beta.28 \ No newline at end of file +13.0.0 \ No newline at end of file diff --git a/package.json b/package.json index b10bde8d3b81a..df7e2b1615dc4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.0.0-beta.28", + "version": "13.0.0", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 483a17cde6172..e3bc0e2bbf1fd 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,0,0,28 - PRODUCTVERSION 13,0,0,28 + FILEVERSION 13,0,0,0 + PRODUCTVERSION 13,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From aba7834a175f79f9de03ce149737017b20d5a8eb Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 25 May 2021 09:54:27 -0700 Subject: [PATCH 03/79] chore: empty commit to force 13.0.1 From 51213f656bfac341f40da9fb84eff27df25d9d68 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Tue, 25 May 2021 09:58:02 -0700 Subject: [PATCH 04/79] Bump v13.0.1 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 70045d3c9a674..5923531dee787 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.0.0 \ No newline at end of file +13.0.1 \ No newline at end of file diff --git a/package.json b/package.json index df7e2b1615dc4..57442663d2eb2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.0.0", + "version": "13.0.1", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index e3bc0e2bbf1fd..a973c64cfb816 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,0,0,0 - PRODUCTVERSION 13,0,0,0 + FILEVERSION 13,0,1,0 + PRODUCTVERSION 13,0,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "13.0.0" + VALUE "FileVersion", "13.0.1" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "13.0.0" + VALUE "ProductVersion", "13.0.1" VALUE "SquirrelAwareVersion", "1" END END From c04fa309447cfa48266828aa34ffb4b0f5565ede Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 16:36:28 -0400 Subject: [PATCH 05/79] build: ensure that mksnapshot for Apple Silicon has all of the needed files for snapshot generation (#29339) * build: include mksnapshot args in arm64 mksnapshot.zip * get gen/v8/embedded.S from proper location Co-authored-by: John Kleinschmidt --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index cfacd1d8ea23c..d8c47f323af12 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -531,6 +531,7 @@ step-electron-build: &step-electron-build ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES ninja -C out/Default tools/v8_context_snapshot -j $NUMBER_OF_NINJA_PROCESSES gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args + (cd out/Default; zip mksnapshot.zip mksnapshot_args clang_x64_v8_arm64/gen/v8/embedded.S) rm -rf out/Default/clang_x64_v8_arm64/gen rm -rf out/Default/clang_x64_v8_arm64/obj rm -rf out/Default/clang_x64/obj From 7bcc2a2762ffbf450f9fe667818271fc56fd7b6d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 12:39:11 -0700 Subject: [PATCH 06/79] docs: fix link to `docs/fiddle/quick-start` (#29351) Co-authored-by: Erick Zhao --- docs/tutorial/quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 6cfdbf9c939c7..4a0c3bba005a2 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -440,7 +440,7 @@ window.addEventListener('DOMContentLoaded', () => { ``` -```fiddle docs/fiddles/quickstart +```fiddle docs/fiddles/quick-start ``` To summarize all the steps we've done: From 6683606449cfa4a102896390250082095d63ebb5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 12:41:30 -0700 Subject: [PATCH 07/79] docs: Update notifications (main) docs (#29358) * remove version info from index.html page * remove nodeIntegration * format code and update readme * add note to user in index.html Co-authored-by: Jeremy Foster --- .../fiddles/features/notifications/main/index.html | 6 +----- docs/fiddles/features/notifications/main/main.js | 14 +++++--------- docs/tutorial/notifications.md | 11 +++++------ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/docs/fiddles/features/notifications/main/index.html b/docs/fiddles/features/notifications/main/index.html index a3855d2640d8a..3c23f9066d9c1 100644 --- a/docs/fiddles/features/notifications/main/index.html +++ b/docs/fiddles/features/notifications/main/index.html @@ -7,10 +7,6 @@

Hello World!

-

- We are using node , - Chrome , - and Electron . -

+

After launching this application, you should see the system notification.

diff --git a/docs/fiddles/features/notifications/main/main.js b/docs/fiddles/features/notifications/main/main.js index 2f9dec51e70e0..f6e6f867ccc88 100644 --- a/docs/fiddles/features/notifications/main/main.js +++ b/docs/fiddles/features/notifications/main/main.js @@ -3,21 +3,17 @@ const { app, BrowserWindow, Notification } = require('electron') function createWindow () { const win = new BrowserWindow({ width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } + height: 600 }) win.loadFile('index.html') } +const NOTIFICATION_TITLE = 'Basic Notification' +const NOTIFICATION_BODY = 'Notification from the Main process' + function showNotification () { - const notification = { - title: 'Basic Notification', - body: 'Notification from the Main process' - } - new Notification(notification).show() + new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show() } app.whenReady().then(createWindow).then(showNotification) diff --git a/docs/tutorial/notifications.md b/docs/tutorial/notifications.md index 072e3de9aef9d..2ba5abb2b7a52 100644 --- a/docs/tutorial/notifications.md +++ b/docs/tutorial/notifications.md @@ -55,18 +55,17 @@ Starting with a working application from the ```javascript fiddle='docs/fiddles/features/notifications/main' const { Notification } = require('electron') +const NOTIFICATION_TITLE = 'Basic Notification' +const NOTIFICATION_BODY = 'Notification from the Main process' + function showNotification () { - const notification = { - title: 'Basic Notification', - body: 'Notification from the Main process' - } - new Notification(notification).show() + new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show() } app.whenReady().then(createWindow).then(showNotification) ``` -After launching the Electron application, you should see the notification: +After launching the Electron application, you should see the system notification: ![Notification in the Main process](../images/notification-main.png) From e7d3b43db07321ca31e0d4d6560241dfe430c5c3 Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Thu, 27 May 2021 13:48:28 -0700 Subject: [PATCH 08/79] docs: change 14.0.0 stable dates and currently supported versions (#29377) * docs: update 14.0.0 stable dates (#29255) * docs: update 14.0.0 stable dates * Update docs/tutorial/electron-timelines.md Co-authored-by: Keeley Hammond Co-authored-by: Keeley Hammond * docs: update currently supported versions for 13.0.0 release (#29295) Co-authored-by: Sofia Nguy Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com> --- docs/tutorial/electron-timelines.md | 3 ++- docs/tutorial/support.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/electron-timelines.md b/docs/tutorial/electron-timelines.md index fdc9fce57d744..dc0b089b44132 100644 --- a/docs/tutorial/electron-timelines.md +++ b/docs/tutorial/electron-timelines.md @@ -19,4 +19,5 @@ | 10.0.0 | 2020-05-21 | 2020-08-25 | M85 | v12.16 | | 11.0.0 | 2020-08-27 | 2020-11-17 | M87 | v12.18 | | 12.0.0 | 2020-11-19 | 2021-03-02 | M89 | v14.16 | -| 13.0.0 | 2021-03-04 | 2021-05-25 | M91 | v14.x | +| 13.0.0 | 2021-03-04 | 2021-05-25 | M91 | v14.16 | +| 14.0.0 | 2021-05-27 | 2021-08-31 | M93 | TBD | diff --git a/docs/tutorial/support.md b/docs/tutorial/support.md index 06878301f2efc..ff74b491bca99 100644 --- a/docs/tutorial/support.md +++ b/docs/tutorial/support.md @@ -64,9 +64,9 @@ until the maintainers feel the maintenance burden is too high to continue doing ### Currently supported versions +* 13.x.y * 12.x.y * 11.x.y -* 10.x.y ### End-of-life From 3e658c47827c9e44dfc5183c0080159e6de4e3c4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 17:24:11 -0700 Subject: [PATCH 09/79] docs: remove freenode channel from support list (#29382) Co-authored-by: Jeremy Rose --- docs/tutorial/support.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/tutorial/support.md b/docs/tutorial/support.md index ff74b491bca99..6dd7b56dd61d0 100644 --- a/docs/tutorial/support.md +++ b/docs/tutorial/support.md @@ -16,7 +16,6 @@ you can interact with the community in these locations: * Sharing ideas with other Electron app developers * And more! * [`electron`](https://discuss.atom.io/c/electron) category on the Atom forums -* `#atom-shell` channel on Freenode * `#electron` channel on [Atom's Slack](https://discuss.atom.io/t/join-us-on-slack/16638?source_topic_id=25406) * [`electron-ru`](https://telegram.me/electron_ru) *(Russian)* * [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)* From 72e336445ab13b8c51e2ce74cd8f8e1a5e033b49 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 18:44:28 -0700 Subject: [PATCH 10/79] refactor: getCurrentBranch respects main (#29389) * refactor: getCurrentBranch respects main * add note about migration Co-authored-by: Jeremy Rose --- script/lib/utils.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/script/lib/utils.js b/script/lib/utils.js index a0c869530d083..55f678b43ed1c 100644 --- a/script/lib/utils.js +++ b/script/lib/utils.js @@ -6,6 +6,9 @@ const ELECTRON_DIR = path.resolve(__dirname, '..', '..'); const SRC_DIR = path.resolve(ELECTRON_DIR, '..'); const RELEASE_BRANCH_PATTERN = /(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))/; +// TODO(main-migration): Simplify once main branch is renamed +const MAIN_BRANCH_PATTERN = /^(main|master)$/; +const ORIGIN_MAIN_BRANCH_PATTERN = /^origin\/(main|master)$/; require('colors'); const pass = '✓'.green; @@ -73,7 +76,7 @@ async function handleGitCall (args, gitDir) { async function getCurrentBranch (gitDir) { let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir); - if (branch !== 'master' && !RELEASE_BRANCH_PATTERN.test(branch)) { + if (!MAIN_BRANCH_PATTERN.test(branch) && !RELEASE_BRANCH_PATTERN.test(branch)) { const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir); const branches = (await handleGitCall([ 'branch', @@ -82,7 +85,7 @@ async function getCurrentBranch (gitDir) { '--remote' ], gitDir)).split('\n'); - branch = branches.filter(b => b.trim() === 'master' || b.trim() === 'origin/master' || RELEASE_BRANCH_PATTERN.test(b.trim()))[0]; + branch = branches.find(b => MAIN_BRANCH_PATTERN.test(b.trim()) || ORIGIN_MAIN_BRANCH_PATTERN.test(b.trim()) || RELEASE_BRANCH_PATTERN.test(b.trim())); if (!branch) { console.log(`${fail} no release branch exists for this ref`); process.exit(1); From 68afcd16e49df535ca01c40c9ee14b983537cb9a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 18:44:53 -0700 Subject: [PATCH 11/79] refactor: version-utils respects main (#29391) Co-authored-by: Jeremy Rose --- script/release/version-utils.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/script/release/version-utils.js b/script/release/version-utils.js index 58a70c0097150..68974f49ee4eb 100644 --- a/script/release/version-utils.js +++ b/script/release/version-utils.js @@ -65,8 +65,9 @@ async function nextNightly (v) { const pre = `nightly.${getCurrentDate()}`; const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim(); - if (branch === 'master') { - next = semver.inc(await getLastMajorForMaster(), 'major'); + // TODO(main-migration): Simplify once main branch is renamed + if (branch === 'master' || branch === 'main') { + next = semver.inc(await getLastMajorForMain(), 'major'); } else if (isStable(v)) { next = semver.inc(next, 'patch'); } @@ -74,7 +75,7 @@ async function nextNightly (v) { return `${next}-${pre}`; } -async function getLastMajorForMaster () { +async function getLastMajorForMain () { let branchNames; const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR); if (result.exitCode === 0) { From 7d18988317d18914f1e55f4fd7bb33352a8ee67c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 18:52:34 -0700 Subject: [PATCH 12/79] docs: fix typos in clang-tidy examples (#29354) Co-authored-by: David Sanders --- docs/development/clang-tidy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/development/clang-tidy.md b/docs/development/clang-tidy.md index 21210b8f6c7b4..b529d91b889b1 100644 --- a/docs/development/clang-tidy.md +++ b/docs/development/clang-tidy.md @@ -10,12 +10,12 @@ files, you need to have built Electron so that it knows which compiler flags were used. There is one required option for the script `--output-dir`, which tells the script which build directory to pull the compilation information from. A typical usage would be: -`npm run lint:clang-tiy --out-dir ../out/Testing` +`npm run lint:clang-tidy --out-dir ../out/Testing` With no filenames provided, all C/C++/Objective-C files will be checked. You can provide a list of files to be checked by passing the filenames after the options: -`npm run lint:clang-tiy --out-dir ../out/Testing shell/browser/api/electron_api_app.cc` +`npm run lint:clang-tidy --out-dir ../out/Testing shell/browser/api/electron_api_app.cc` While `clang-tidy` has a [long list](https://clang.llvm.org/extra/clang-tidy/checks/list.html) From 7276a1fd8cda4e6ee09a20e502da445c13fee685 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 18:53:08 -0700 Subject: [PATCH 13/79] Update quick-start.md (#29372) In version 13.0.1 preload process should added to webPreferences. Co-authored-by: aydon <41415004+congjiye@users.noreply.github.com> --- docs/tutorial/quick-start.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 4a0c3bba005a2..58cfa0d54193b 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -299,7 +299,9 @@ function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, - preload: path.join(__dirname, 'preload.js') + webPreferences: { + preload: path.join(__dirname, 'preload.js') + } }) win.loadFile('index.html') From 8481e5c08a80a6079d3dbea383c0a975c46fb89d Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 19:03:50 -0700 Subject: [PATCH 14/79] chore: bump chromium in DEPS to 91.0.4472.77 (#29321) Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 6e7c5d1b51ccd..c746bfc74d933 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ gclient_gn_args = [ vars = { 'chromium_version': - '91.0.4472.69', + '91.0.4472.77', 'node_version': 'v14.16.0', 'nan_version': From bb065beaa66da529500f48e52d03a736c1793db9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 31 May 2021 10:19:55 +0200 Subject: [PATCH 15/79] docs: update link to Chromium's coding style guide (#29397) Co-authored-by: David Sanders --- docs/development/coding-style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/coding-style.md b/docs/development/coding-style.md index 324513ff86527..498740061f574 100644 --- a/docs/development/coding-style.md +++ b/docs/development/coding-style.md @@ -25,7 +25,7 @@ You can run `npm run lint` to show any style issues detected by `cpplint` and ## C++ and Python For C++ and Python, we follow Chromium's [Coding -Style](https://www.chromium.org/developers/coding-style). You can use +Style](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/styleguide/styleguide.md). You can use [clang-format](clang-format.md) to format the C++ code automatically. There is also a script `script/cpplint.py` to check whether all files conform. From 4f203eee4a879e9c4da4c2a29fb9ebac8bbcff2b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 31 May 2021 10:32:42 +0200 Subject: [PATCH 16/79] refactor: publish-to-npm respects main (#29409) --- script/release/publish-to-npm.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/script/release/publish-to-npm.js b/script/release/publish-to-npm.js index 54554fe144697..61cdffe380daf 100644 --- a/script/release/publish-to-npm.js +++ b/script/release/publish-to-npm.js @@ -111,8 +111,9 @@ new Promise((resolve, reject) => { const currentBranch = await getCurrentBranch(); if (release.tag_name.indexOf('nightly') > 0) { - if (currentBranch === 'master') { - // Nightlies get published to their own module, so master nightlies should be tagged as latest + // TODO(main-migration): Simplify once main branch is renamed. + if (currentBranch === 'master' || currentBranch === 'main') { + // Nightlies get published to their own module, so they should be tagged as latest npmTag = 'latest'; } else { npmTag = `nightly-${currentBranch}`; @@ -127,10 +128,10 @@ new Promise((resolve, reject) => { JSON.stringify(currentJson, null, 2) ); } else { - if (currentBranch === 'master') { - // This should never happen, master releases should be nightly releases + if (currentBranch === 'master' || currentBranch === 'main') { + // This should never happen, main releases should be nightly releases // this is here just-in-case - npmTag = 'master'; + throw new Error('Unreachable release phase, can\'t tag a non-nightly release on the main branch'); } else if (!release.prerelease) { // Tag the release with a `2-0-x` style tag npmTag = currentBranch; From 134c8b7ed3edc84300762d045115ed4f1f370d92 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:37:50 +0900 Subject: [PATCH 17/79] Fixes issue with reference links (#29430) Co-authored-by: Vishal <34849822+vthukral94@users.noreply.github.com> --- docs/tutorial/introduction.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/tutorial/introduction.md b/docs/tutorial/introduction.md index f9f0fc9a16bfc..feaaf52029f93 100644 --- a/docs/tutorial/introduction.md +++ b/docs/tutorial/introduction.md @@ -49,11 +49,11 @@ is a great place to get advice from other Electron app developers. the [GitHub issue tracker][issue-tracker] to see if any existing issues match your problem. If not, feel free to fill out our bug report template and submit a new issue. -[chromium](https://www.chromium.org/) -[node](https://nodejs.org/) -[mdn-guide](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web) -[node-guide](https://nodejs.dev/learn) -[comic](https://www.google.com/googlebooks/chrome/) -[fiddle](https://electronjs.org/fiddle) -[issue-tracker](https://github.com/electron/electron/issues) -[discord](https://discord.gg/electron) +[chromium]: https://www.chromium.org/ +[node]: https://nodejs.org/ +[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web +[node-guide]: https://nodejs.dev/learn +[comic]: https://www.google.com/googlebooks/chrome/ +[fiddle]: https://electronjs.org/fiddle +[issue-tracker]: https://github.com/electron/electron/issues +[discord]: https://discord.gg/electron From c647007ccc4b53da8a67dd330a6379d396ddd8ab Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 15:25:19 +0900 Subject: [PATCH 18/79] fix: correctly handle Alt+Key shortcuts (#29443) Co-authored-by: Cheng Zhao --- shell/browser/ui/views/menu_bar.cc | 2 ++ shell/browser/ui/views/root_view.cc | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/shell/browser/ui/views/menu_bar.cc b/shell/browser/ui/views/menu_bar.cc index a55a8128dbd9b..40e624eba3a92 100644 --- a/shell/browser/ui/views/menu_bar.cc +++ b/shell/browser/ui/views/menu_bar.cc @@ -79,6 +79,8 @@ void MenuBar::SetAcceleratorVisibility(bool visible) { } MenuBar::View* MenuBar::FindAccelChild(char16_t key) { + if (key == 0) + return nullptr; for (auto* child : GetChildrenInZOrder()) { if (static_cast(child)->accelerator() == key) return child; diff --git a/shell/browser/ui/views/root_view.cc b/shell/browser/ui/views/root_view.cc index 49e5988f642ea..085cc4ead2d04 100644 --- a/shell/browser/ui/views/root_view.cc +++ b/shell/browser/ui/views/root_view.cc @@ -119,18 +119,18 @@ void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) { // Show the submenu when "Alt+Key" is pressed. if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown && - !IsAltKey(event) && IsAltModifier(event)) { - if (menu_bar_->HasAccelerator(event.windows_key_code)) { - if (!menu_bar_visible_) { - SetMenuBarVisibility(true); - - View* focused_view = GetFocusManager()->GetFocusedView(); - last_focused_view_tracker_->SetView(focused_view); - menu_bar_->RequestFocus(); - } + event.windows_key_code >= ui::VKEY_A && + event.windows_key_code <= ui::VKEY_Z && IsAltModifier(event) && + menu_bar_->HasAccelerator(event.windows_key_code)) { + if (!menu_bar_visible_) { + SetMenuBarVisibility(true); - menu_bar_->ActivateAccelerator(event.windows_key_code); + View* focused_view = GetFocusManager()->GetFocusedView(); + last_focused_view_tracker_->SetView(focused_view); + menu_bar_->RequestFocus(); } + + menu_bar_->ActivateAccelerator(event.windows_key_code); return; } From d0d6e88e5a4add8d8e20a779eeccf5142ffb4076 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 15:25:32 +0900 Subject: [PATCH 19/79] chore: use consistent parameter names (#29442) Co-authored-by: David Sanders --- shell/browser/api/electron_api_app.h | 2 +- shell/browser/api/electron_api_auto_updater.h | 2 +- shell/browser/api/electron_api_download_item.cc | 12 +++++------- shell/browser/api/electron_api_download_item.h | 8 ++++---- shell/browser/api/electron_api_notification.h | 2 +- shell/browser/api/electron_api_url_loader.h | 2 +- shell/browser/auto_updater.h | 4 ++-- .../browser/extensions/electron_messaging_delegate.h | 2 +- shell/browser/file_select_helper.h | 6 +++--- shell/browser/native_window.h | 2 +- shell/browser/native_window_mac.h | 2 +- .../notifications/mac/notification_presenter_mac.h | 2 +- shell/browser/osr/osr_render_widget_host_view.h | 4 ++-- shell/browser/relauncher.h | 10 +++++----- shell/browser/ui/accelerator_util.h | 2 +- shell/browser/web_dialog_helper.h | 2 +- shell/common/asar/archive.cc | 10 +++++----- shell/renderer/api/electron_api_spell_check_client.h | 2 +- shell/renderer/renderer_client_base.h | 2 +- 19 files changed, 38 insertions(+), 40 deletions(-) diff --git a/shell/browser/api/electron_api_app.h b/shell/browser/api/electron_api_app.h index ba6b33fdfef69..1b52ffd356736 100644 --- a/shell/browser/api/electron_api_app.h +++ b/shell/browser/api/electron_api_app.h @@ -126,7 +126,7 @@ class App : public ElectronBrowserClient::Delegate, base::OnceClosure SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, - net::ClientCertIdentityList client_certs, + net::ClientCertIdentityList identities, std::unique_ptr delegate) override; bool CanCreateWindow(content::RenderFrameHost* opener, const GURL& opener_url, diff --git a/shell/browser/api/electron_api_auto_updater.h b/shell/browser/api/electron_api_auto_updater.h index de72c84bef57d..125a906275c8e 100644 --- a/shell/browser/api/electron_api_auto_updater.h +++ b/shell/browser/api/electron_api_auto_updater.h @@ -35,7 +35,7 @@ class AutoUpdater : public gin::Wrappable, ~AutoUpdater() override; // Delegate implementations. - void OnError(const std::string& error) override; + void OnError(const std::string& message) override; void OnError(const std::string& message, const int code, const std::string& domain) override; diff --git a/shell/browser/api/electron_api_download_item.cc b/shell/browser/api/electron_api_download_item.cc index e839c7ac88949..53b32bace2ac4 100644 --- a/shell/browser/api/electron_api_download_item.cc +++ b/shell/browser/api/electron_api_download_item.cc @@ -74,17 +74,15 @@ const void* kElectronApiDownloadItemKey = &kElectronApiDownloadItemKey; gin::WrapperInfo DownloadItem::kWrapperInfo = {gin::kEmbedderNativeGin}; // static -DownloadItem* DownloadItem::FromDownloadItem( - download::DownloadItem* download_item) { +DownloadItem* DownloadItem::FromDownloadItem(download::DownloadItem* item) { // ^- say that 7 times fast in a row auto* data = static_cast( - download_item->GetUserData(kElectronApiDownloadItemKey)); + item->GetUserData(kElectronApiDownloadItemKey)); return data ? data->download_item.get() : nullptr; } -DownloadItem::DownloadItem(v8::Isolate* isolate, - download::DownloadItem* download_item) - : download_item_(download_item), isolate_(isolate) { +DownloadItem::DownloadItem(v8::Isolate* isolate, download::DownloadItem* item) + : download_item_(item), isolate_(isolate) { download_item_->AddObserver(this); download_item_->SetUserData( kElectronApiDownloadItemKey, @@ -119,7 +117,7 @@ void DownloadItem::OnDownloadUpdated(download::DownloadItem* item) { } } -void DownloadItem::OnDownloadDestroyed(download::DownloadItem* download_item) { +void DownloadItem::OnDownloadDestroyed(download::DownloadItem* /*item*/) { download_item_ = nullptr; Unpin(); } diff --git a/shell/browser/api/electron_api_download_item.h b/shell/browser/api/electron_api_download_item.h index d63c5ff498e09..cd86a39245959 100644 --- a/shell/browser/api/electron_api_download_item.h +++ b/shell/browser/api/electron_api_download_item.h @@ -30,7 +30,7 @@ class DownloadItem : public gin::Wrappable, static gin::Handle FromOrCreate(v8::Isolate* isolate, download::DownloadItem* item); - static DownloadItem* FromDownloadItem(download::DownloadItem*); + static DownloadItem* FromDownloadItem(download::DownloadItem* item); // gin::Wrappable static gin::WrapperInfo kWrapperInfo; @@ -44,14 +44,14 @@ class DownloadItem : public gin::Wrappable, file_dialog::DialogSettings GetSaveDialogOptions() const; private: - DownloadItem(v8::Isolate* isolate, download::DownloadItem* download_item); + DownloadItem(v8::Isolate* isolate, download::DownloadItem* item); ~DownloadItem() override; bool CheckAlive() const; // download::DownloadItem::Observer - void OnDownloadUpdated(download::DownloadItem* download) override; - void OnDownloadDestroyed(download::DownloadItem* download) override; + void OnDownloadUpdated(download::DownloadItem* item) override; + void OnDownloadDestroyed(download::DownloadItem* item) override; // JS API void Pause(); diff --git a/shell/browser/api/electron_api_notification.h b/shell/browser/api/electron_api_notification.h index 64a6cbec319f3..75fc8fef880b5 100644 --- a/shell/browser/api/electron_api_notification.h +++ b/shell/browser/api/electron_api_notification.h @@ -86,7 +86,7 @@ class Notification : public gin::Wrappable, void SetHasReply(bool new_has_reply); void SetUrgency(const std::u16string& new_urgency); void SetTimeoutType(const std::u16string& new_timeout_type); - void SetReplyPlaceholder(const std::u16string& new_reply_placeholder); + void SetReplyPlaceholder(const std::u16string& new_placeholder); void SetSound(const std::u16string& sound); void SetActions(const std::vector& actions); void SetCloseButtonText(const std::u16string& text); diff --git a/shell/browser/api/electron_api_url_loader.h b/shell/browser/api/electron_api_url_loader.h index 67c24e83dc94a..e8e91e7808bcf 100644 --- a/shell/browser/api/electron_api_url_loader.h +++ b/shell/browser/api/electron_api_url_loader.h @@ -97,7 +97,7 @@ class SimpleURLLoaderWrapper int64_t sent_bytes) override {} void Clone( mojo::PendingReceiver - listener) override; + observer) override; // SimpleURLLoader callbacks void OnResponseStarted(const GURL& final_url, diff --git a/shell/browser/auto_updater.h b/shell/browser/auto_updater.h index e84ed751ce3cd..2324dee521c61 100644 --- a/shell/browser/auto_updater.h +++ b/shell/browser/auto_updater.h @@ -24,9 +24,9 @@ namespace auto_updater { class Delegate { public: // An error happened. - virtual void OnError(const std::string& error) {} + virtual void OnError(const std::string& message) {} - virtual void OnError(const std::string& error, + virtual void OnError(const std::string& message, const int code, const std::string& domain) {} diff --git a/shell/browser/extensions/electron_messaging_delegate.h b/shell/browser/extensions/electron_messaging_delegate.h index 6ef1a8c07189c..3befdea451804 100644 --- a/shell/browser/extensions/electron_messaging_delegate.h +++ b/shell/browser/extensions/electron_messaging_delegate.h @@ -45,7 +45,7 @@ class ElectronMessagingDelegate : public MessagingDelegate { void QueryIncognitoConnectability( content::BrowserContext* context, const Extension* extension, - content::WebContents* web_contents, + content::WebContents* source_contents, const GURL& url, base::OnceCallback callback) override; diff --git a/shell/browser/file_select_helper.h b/shell/browser/file_select_helper.h index 78a678483ec93..a392d8cad7265 100644 --- a/shell/browser/file_select_helper.h +++ b/shell/browser/file_select_helper.h @@ -73,11 +73,11 @@ class FileSelectHelper : public content::WebContentsObserver, // of the package. void ProcessSelectedFilesMac(const std::vector& files); - // Saves the paths of |zipped_files| for later deletion. Passes |files| to the - // render view host. + // Saves the paths of |temporary_files| for later deletion. Passes |files| to + // the render view host. void ProcessSelectedFilesMacOnUIThread( const std::vector& files, - const std::vector& zipped_files); + const std::vector& temporary_files); // Zips the package at |path| into a temporary destination. Returns the // temporary destination, if the zip was successful. Otherwise returns an diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 0da492ef9b7d0..6b808ec1c6aa6 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -99,7 +99,7 @@ class NativeWindow : public base::SupportsUserData, virtual bool IsNormal(); virtual gfx::Rect GetNormalBounds() = 0; virtual void SetSizeConstraints( - const extensions::SizeConstraints& size_constraints); + const extensions::SizeConstraints& window_constraints); virtual extensions::SizeConstraints GetSizeConstraints() const; virtual void SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints); diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 3aa9aa90a6614..c2b2c76e7ba00 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -78,7 +78,7 @@ class NativeWindowMac : public NativeWindow, bool IsClosable() override; void SetAlwaysOnTop(ui::ZOrderLevel z_order, const std::string& level, - int relativeLevel) override; + int relative_level) override; ui::ZOrderLevel GetZOrderLevel() override; void Center() override; void Invalidate() override; diff --git a/shell/browser/notifications/mac/notification_presenter_mac.h b/shell/browser/notifications/mac/notification_presenter_mac.h index 234a11aeebb16..e4e3ae0eb0d17 100644 --- a/shell/browser/notifications/mac/notification_presenter_mac.h +++ b/shell/browser/notifications/mac/notification_presenter_mac.h @@ -16,7 +16,7 @@ class CocoaNotification; class NotificationPresenterMac : public NotificationPresenter { public: - CocoaNotification* GetNotification(NSUserNotification* notif); + CocoaNotification* GetNotification(NSUserNotification* ns_notification); NotificationPresenterMac(); ~NotificationPresenterMac() override; diff --git a/shell/browser/osr/osr_render_widget_host_view.h b/shell/browser/osr/osr_render_widget_host_view.h index 6dc50afd6b3b3..d01261481d413 100644 --- a/shell/browser/osr/osr_render_widget_host_view.h +++ b/shell/browser/osr/osr_render_widget_host_view.h @@ -112,7 +112,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, // content::RenderWidgetHostViewBase: void ResetFallbackToFirstNavigationSurface() override; - void InitAsPopup(content::RenderWidgetHostView* rwhv, + void InitAsPopup(content::RenderWidgetHostView* parent_host_view, const gfx::Rect& rect) override; void UpdateCursor(const content::WebCursor&) override; void SetIsLoading(bool is_loading) override; @@ -126,7 +126,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, const gfx::Rect& src_rect, const gfx::Size& output_size, base::OnceCallback callback) override; - void GetScreenInfo(blink::ScreenInfo* results) override; + void GetScreenInfo(blink::ScreenInfo* screen_info) override; void TransformPointToRootSurface(gfx::PointF* point) override; gfx::Rect GetBoundsInRootWindow(void) override; base::Optional GetDisplayFeature() override; diff --git a/shell/browser/relauncher.h b/shell/browser/relauncher.h index 761fbb02dbe86..2916dd18a12ad 100644 --- a/shell/browser/relauncher.h +++ b/shell/browser/relauncher.h @@ -50,10 +50,10 @@ using StringVector = base::CommandLine::StringVector; // Relaunches the application using the helper application associated with the // currently running instance of Chrome in the parent browser process as the -// executable for the relauncher process. |args| is an argv-style vector of -// command line arguments of the form normally passed to execv. args[0] is +// executable for the relauncher process. |argv| is an argv-style vector of +// command line arguments of the form normally passed to execv. argv[0] is // also the path to the relaunched process. Because the relauncher process -// will ultimately launch the relaunched process via Launch Services, args[0] +// will ultimately launch the relaunched process via Launch Services, argv[0] // may be either a pathname to an executable file or a pathname to an .app // bundle directory. The caller should exit soon after RelaunchApp returns // successfully. Returns true on success, although some failures can occur @@ -63,7 +63,7 @@ bool RelaunchApp(const StringVector& argv); // Identical to RelaunchApp, but uses |helper| as the path to the relauncher // process, and allows additional arguments to be supplied to the relauncher -// process in relauncher_args. Unlike args[0], |helper| must be a pathname to +// process in relauncher_args. Unlike argv[0], |helper| must be a pathname to // an executable file. The helper path given must be from the same version of // Chrome as the running parent browser process, as there are no guarantees // that the parent and relauncher processes from different versions will be @@ -72,7 +72,7 @@ bool RelaunchApp(const StringVector& argv); // location's helper. bool RelaunchAppWithHelper(const base::FilePath& helper, const StringVector& relauncher_args, - const StringVector& args); + const StringVector& argv); // The entry point from ChromeMain into the relauncher process. int RelauncherMain(const content::MainFunctionParams& main_parameters); diff --git a/shell/browser/ui/accelerator_util.h b/shell/browser/ui/accelerator_util.h index e3b942dc189db..1eca1f3aff130 100644 --- a/shell/browser/ui/accelerator_util.h +++ b/shell/browser/ui/accelerator_util.h @@ -20,7 +20,7 @@ typedef struct { typedef std::map AcceleratorTable; // Parse a string as an accelerator. -bool StringToAccelerator(const std::string& description, +bool StringToAccelerator(const std::string& shortcut, ui::Accelerator* accelerator); // Generate a table that contains memu model's accelerators and command ids. diff --git a/shell/browser/web_dialog_helper.h b/shell/browser/web_dialog_helper.h index 3ef07327b7bbc..424db19fa6507 100644 --- a/shell/browser/web_dialog_helper.h +++ b/shell/browser/web_dialog_helper.h @@ -35,7 +35,7 @@ class WebDialogHelper { const blink::mojom::FileChooserParams& params); void EnumerateDirectory(content::WebContents* web_contents, scoped_refptr listener, - const base::FilePath& path); + const base::FilePath& dir); private: NativeWindow* window_; diff --git a/shell/common/asar/archive.cc b/shell/common/asar/archive.cc index 89c5a8d418c90..f6c6b5e3ebbb8 100644 --- a/shell/common/asar/archive.cc +++ b/shell/common/asar/archive.cc @@ -237,7 +237,7 @@ bool Archive::Stat(const base::FilePath& path, Stats* stats) { } bool Archive::Readdir(const base::FilePath& path, - std::vector* list) { + std::vector* files) { if (!header_) return false; @@ -245,13 +245,13 @@ bool Archive::Readdir(const base::FilePath& path, if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node)) return false; - const base::DictionaryValue* files; - if (!GetFilesNode(header_.get(), node, &files)) + const base::DictionaryValue* files_node; + if (!GetFilesNode(header_.get(), node, &files_node)) return false; - base::DictionaryValue::Iterator iter(*files); + base::DictionaryValue::Iterator iter(*files_node); while (!iter.IsAtEnd()) { - list->push_back(base::FilePath::FromUTF8Unsafe(iter.key())); + files->push_back(base::FilePath::FromUTF8Unsafe(iter.key())); iter.Advance(); } return true; diff --git a/shell/renderer/api/electron_api_spell_check_client.h b/shell/renderer/api/electron_api_spell_check_client.h index 72b5572fbcb80..c91d3e85e6063 100644 --- a/shell/renderer/api/electron_api_spell_check_client.h +++ b/shell/renderer/api/electron_api_spell_check_client.h @@ -76,7 +76,7 @@ class SpellCheckClient : public blink::WebSpellCheckPanelHostClient, // Output variable contraction_words will contain individual // words in the contraction. bool IsContraction(const SpellCheckScope& scope, - const std::u16string& word, + const std::u16string& contraction, std::vector* contraction_words); // Callback for the JS API which returns the list of misspelled words. diff --git a/shell/renderer/renderer_client_base.h b/shell/renderer/renderer_client_base.h index 4193ab622f988..387667d1ef894 100644 --- a/shell/renderer/renderer_client_base.h +++ b/shell/renderer/renderer_client_base.h @@ -65,7 +65,7 @@ class RendererClientBase : public content::ContentRendererClient #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) // service_manager::LocalInterfaceProvider implementation. void GetInterface(const std::string& name, - mojo::ScopedMessagePipeHandle request_handle) override; + mojo::ScopedMessagePipeHandle interface_pipe) override; #endif virtual void DidCreateScriptContext(v8::Handle context, From b44024d66089663ee9eaede30b439d375d034f9d Mon Sep 17 00:00:00 2001 From: David Sanders Date: Tue, 1 Jun 2021 01:51:39 -0700 Subject: [PATCH 20/79] fix: add service worker schemes from command line in renderer (#29425) (#29446) --- shell/browser/api/electron_api_protocol.cc | 18 +++++---- shell/browser/api/electron_api_protocol.h | 2 + shell/renderer/renderer_client_base.cc | 6 +++ spec-main/chromium-spec.ts | 37 +++++++++++++++++++ .../service-worker/custom-scheme-index.html | 21 +++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 spec/fixtures/pages/service-worker/custom-scheme-index.html diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index 952e32d0d3566..ad4c5a8b38a6c 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -89,6 +89,16 @@ std::vector GetStandardSchemes() { return g_standard_schemes; } +void AddServiceWorkerScheme(const std::string& scheme) { + // There is no API to add service worker scheme, but there is an API to + // return const reference to the schemes vector. + // If in future the API is changed to return a copy instead of reference, + // the compilation will fail, and we should add a patch at that time. + auto& mutable_schemes = + const_cast&>(content::GetServiceWorkerSchemes()); + mutable_schemes.push_back(scheme); +} + void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, v8::Local val) { std::vector custom_schemes; @@ -125,13 +135,7 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, } if (custom_scheme.options.allowServiceWorkers) { service_worker_schemes.push_back(custom_scheme.scheme); - // There is no API to add service worker scheme, but there is an API to - // return const reference to the schemes vector. - // If in future the API is changed to return a copy instead of reference, - // the compilation will fail, and we should add a patch at that time. - auto& mutable_schemes = const_cast&>( - content::GetServiceWorkerSchemes()); - mutable_schemes.push_back(custom_scheme.scheme); + AddServiceWorkerScheme(custom_scheme.scheme); } if (custom_scheme.options.stream) { g_streaming_schemes.push_back(custom_scheme.scheme); diff --git a/shell/browser/api/electron_api_protocol.h b/shell/browser/api/electron_api_protocol.h index 0725e4cea2b88..d0420ff14ad9e 100644 --- a/shell/browser/api/electron_api_protocol.h +++ b/shell/browser/api/electron_api_protocol.h @@ -22,6 +22,8 @@ namespace api { std::vector GetStandardSchemes(); +void AddServiceWorkerScheme(const std::string& scheme); + void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, v8::Local val); diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index 13bfebc7af7f3..19139a54e4b00 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -22,6 +22,7 @@ #include "electron/buildflags/buildflags.h" #include "media/blink/multibuffer_data_source.h" #include "printing/buildflags/buildflags.h" +#include "shell/browser/api/electron_api_protocol.h" #include "shell/common/color_util.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/options_switches.h" @@ -99,6 +100,11 @@ RendererClientBase* g_renderer_client_base = nullptr; RendererClientBase::RendererClientBase() { auto* command_line = base::CommandLine::ForCurrentProcess(); + // Parse --service-worker-schemes=scheme1,scheme2 + std::vector service_worker_schemes_list = + ParseSchemesCLISwitch(command_line, switches::kServiceWorkerSchemes); + for (const std::string& scheme : service_worker_schemes_list) + electron::api::AddServiceWorkerScheme(scheme); // Parse --standard-schemes=scheme1,scheme2 std::vector standard_schemes_list = ParseSchemesCLISwitch(command_line, switches::kStandardSchemes); diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 9a4e5e5e2006f..ba6d2da71b417 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -581,6 +581,43 @@ describe('chromium features', () => { w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html')); }); + it('should register for custom scheme', (done) => { + const customSession = session.fromPartition('custom-scheme'); + const { serviceWorkerScheme } = global as any; + customSession.protocol.registerFileProtocol(serviceWorkerScheme, (request, callback) => { + let file = url.parse(request.url).pathname!; + if (file[0] === '/' && process.platform === 'win32') file = file.slice(1); + + callback({ path: path.normalize(file) } as any); + }); + + const w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true, + session: customSession, + contextIsolation: false + } + }); + w.webContents.on('ipc-message', (event, channel, message) => { + if (channel === 'reload') { + w.webContents.reload(); + } else if (channel === 'error') { + done(`unexpected error : ${message}`); + } else if (channel === 'response') { + expect(message).to.equal('Hello from serviceWorker!'); + customSession.clearStorageData({ + storages: ['serviceworkers'] + }).then(() => { + customSession.protocol.uninterceptProtocol(serviceWorkerScheme); + done(); + }); + } + }); + w.webContents.on('crashed', () => done(new Error('WebContents crashed.'))); + w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'custom-scheme-index.html')); + }); + it('should not crash when nodeIntegration is enabled', (done) => { const w = new BrowserWindow({ show: false, diff --git a/spec/fixtures/pages/service-worker/custom-scheme-index.html b/spec/fixtures/pages/service-worker/custom-scheme-index.html new file mode 100644 index 0000000000000..e5be928f04163 --- /dev/null +++ b/spec/fixtures/pages/service-worker/custom-scheme-index.html @@ -0,0 +1,21 @@ + From 737b08d130e0fabb6568008f4bbbe351ef595d10 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:12:23 +0900 Subject: [PATCH 21/79] fix: Alt+Click should not toggle menu bar (#29450) Co-authored-by: Cheng Zhao --- shell/browser/native_window_views.cc | 15 +++++++-------- shell/browser/native_window_views.h | 2 -- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 6815fff43cfcd..14c6da65dacba 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -332,12 +332,12 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, last_window_state_ = ui::SHOW_STATE_NORMAL; #endif -#if defined(OS_LINUX) - // Listen to move events. + // Listen to mouse events. aura::Window* window = GetNativeWindow(); if (window) window->AddPreTargetHandler(this); +#if defined(OS_LINUX) // On linux after the widget is initialized we might have to force set the // bounds if the bounds are smaller than the current display SetBounds(gfx::Rect(GetPosition(), bounds.size()), false); @@ -352,11 +352,9 @@ NativeWindowViews::~NativeWindowViews() { SetForwardMouseMessages(false); #endif -#if defined(OS_LINUX) aura::Window* window = GetNativeWindow(); if (window) window->RemovePreTargetHandler(this); -#endif } void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) { @@ -1450,11 +1448,9 @@ void NativeWindowViews::OnWidgetBoundsChanged(views::Widget* changed_widget, } void NativeWindowViews::OnWidgetDestroying(views::Widget* widget) { -#if defined(OS_LINUX) aura::Window* window = GetNativeWindow(); if (window) window->RemovePreTargetHandler(this); -#endif } void NativeWindowViews::OnWidgetDestroyed(views::Widget* changed_widget) { @@ -1572,17 +1568,20 @@ void NativeWindowViews::HandleKeyboardEvent( root_view_->HandleKeyEvent(event); } -#if defined(OS_LINUX) void NativeWindowViews::OnMouseEvent(ui::MouseEvent* event) { if (event->type() != ui::ET_MOUSE_PRESSED) return; + // Alt+Click should not toggle menu bar. + root_view_->ResetAltState(); + +#if defined(OS_LINUX) if (event->changed_button_flags() == ui::EF_BACK_MOUSE_BUTTON) NotifyWindowExecuteAppCommand(kBrowserBackward); else if (event->changed_button_flags() == ui::EF_FORWARD_MOUSE_BUTTON) NotifyWindowExecuteAppCommand(kBrowserForward); -} #endif +} ui::WindowShowState NativeWindowViews::GetRestoredState() { if (IsMaximized()) diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 06a65802ff470..937667bd452df 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -224,10 +224,8 @@ class NativeWindowViews : public NativeWindow, content::WebContents*, const content::NativeWebKeyboardEvent& event) override; -#if defined(OS_LINUX) // ui::EventHandler: void OnMouseEvent(ui::MouseEvent* event) override; -#endif // Returns the restore state for the window. ui::WindowShowState GetRestoredState(); From f95d2bdfd16552d348252cac0c1f2d9def5c9033 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:12:51 +0900 Subject: [PATCH 22/79] chore: remove duplicate option get for CustomScheme (#29453) Co-authored-by: David Sanders --- shell/browser/api/electron_api_protocol.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index ad4c5a8b38a6c..47add09b573a9 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -66,7 +66,6 @@ struct Converter { // options are optional. Default values specified in SchemeOptions are used if (dict.Get("privileges", &opt)) { opt.Get("standard", &(out->options.standard)); - opt.Get("supportFetchAPI", &(out->options.supportFetchAPI)); opt.Get("secure", &(out->options.secure)); opt.Get("bypassCSP", &(out->options.bypassCSP)); opt.Get("allowServiceWorkers", &(out->options.allowServiceWorkers)); From 8e6819ae716f34b03c43d4a9aab49133676bb0f0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 14:07:09 -0700 Subject: [PATCH 23/79] refactor: use main in release-notes (#29412) * refactor: use main in release-notes * fix: use default_branch in release-notes (#29415) Co-authored-by: Jeremy Rose Co-authored-by: Jeremy Rose --- script/release/notes/index.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/script/release/notes/index.js b/script/release/notes/index.js index 8cb7650fae1a6..644b76490fe79 100755 --- a/script/release/notes/index.js +++ b/script/release/notes/index.js @@ -8,6 +8,11 @@ const semver = require('semver'); const { ELECTRON_DIR } = require('../../lib/utils'); const notesGenerator = require('./notes.js'); +const { Octokit } = require('@octokit/rest'); +const octokit = new Octokit({ + auth: process.env.ELECTRON_GITHUB_TOKEN +}); + const semverify = version => version.replace(/^origin\//, '').replace(/[xy]/g, '0').replace(/-/g, '.'); const runGit = async (args) => { @@ -37,13 +42,17 @@ const getTagsOf = async (point) => { }; const getTagsOnBranch = async (point) => { - const masterTags = await getTagsOf('master'); - if (point === 'master') { - return masterTags; + const { data: { default_branch: defaultBranch } } = await octokit.repos.get({ + owner: 'electron', + repo: 'electron' + }); + const mainTags = await getTagsOf(defaultBranch); + if (point === defaultBranch) { + return mainTags; } - const masterTagsSet = new Set(masterTags); - return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag)); + const mainTagsSet = new Set(mainTags); + return (await getTagsOf(point)).filter(tag => !mainTagsSet.has(tag)); }; const getBranchOf = async (point) => { @@ -66,7 +75,8 @@ const getAllBranches = async () => { return branches.split('\n') .map(branch => branch.trim()) .filter(branch => !!branch) - .filter(branch => branch !== 'origin/HEAD -> origin/master') + // TODO(main-migration): Simplify once branch rename is complete. + .filter(branch => branch !== 'origin/HEAD -> origin/master' && branch !== 'origin/HEAD -> origin/main') .sort(); } catch (err) { console.error('Failed to fetch all branches'); From 92653901646d4a083e7e88362759a9881e6f34f3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 2 Jun 2021 16:17:16 +0900 Subject: [PATCH 24/79] docs: Updated "progress bar" fiddle feature in docs (#29470) * improve progress bar fiddle * add comments to code snippet * edits to progress-bar tutorial * remove versions and nodeIntegration * limit line length to 100 * implement standard linter suggestions * add indeterminate and clear timers * update to have reader replace all of main.js * remove extra button * loop the progress bar * add logic to show reset state briefly * Update docs/tutorial/progress-bar.md Co-authored-by: Erick Zhao * chore: fix lint Co-authored-by: Jeremy Foster Co-authored-by: Cheng Zhao Co-authored-by: Erick Zhao --- docs/fiddles/features/progress-bar/index.html | 9 ++- docs/fiddles/features/progress-bar/main.js | 30 ++++++-- docs/tutorial/progress-bar.md | 70 +++++++++++++++---- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/docs/fiddles/features/progress-bar/index.html b/docs/fiddles/features/progress-bar/index.html index a3855d2640d8a..d68c5129a6c2b 100644 --- a/docs/fiddles/features/progress-bar/index.html +++ b/docs/fiddles/features/progress-bar/index.html @@ -7,10 +7,9 @@

Hello World!

-

- We are using node , - Chrome , - and Electron . -

+

Keep an eye on the dock (Mac) or taskbar (Windows, Unity) for this application!

+

It should indicate a progress that advances from 0 to 100%.

+

It should then show indeterminate (Windows) or pin at 100% (other operating systems) + briefly and then loop.

diff --git a/docs/fiddles/features/progress-bar/main.js b/docs/fiddles/features/progress-bar/main.js index a53bf6b9856f0..c400638359011 100644 --- a/docs/fiddles/features/progress-bar/main.js +++ b/docs/fiddles/features/progress-bar/main.js @@ -1,21 +1,39 @@ const { app, BrowserWindow } = require('electron') +let progressInterval + function createWindow () { const win = new BrowserWindow({ width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } + height: 600 }) win.loadFile('index.html') - win.setProgressBar(0.5) -} + const INCREMENT = 0.03 + const INTERVAL_DELAY = 100 // ms + + let c = 0 + progressInterval = setInterval(() => { + // update progress bar to next value + // values between 0 and 1 will show progress, >1 will show indeterminate or stick at 100% + win.setProgressBar(c) + + // increment or reset progress bar + if (c < 2) { + c += INCREMENT + } else { + c = (-INCREMENT * 5) // reset to a bit less than 0 to show reset state + } + }, INTERVAL_DELAY) +} app.whenReady().then(createWindow) +// before the app is terminated, clear both timers +app.on('before-quit', () => { + clearInterval(progressInterval) +}) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { diff --git a/docs/tutorial/progress-bar.md b/docs/tutorial/progress-bar.md index 9d4832dfa6f9a..4b01c63ea4242 100644 --- a/docs/tutorial/progress-bar.md +++ b/docs/tutorial/progress-bar.md @@ -31,30 +31,70 @@ currently at 63% towards completion, you would call it as `setProgressBar(0.63)`. Setting the parameter to negative values (e.g. `-1`) will remove the progress -bar, whereas setting it to values greater than `1` (e.g. `2`) will switch the -progress bar to indeterminate mode (Windows-only -- it will clamp to 100% -otherwise). In this mode, a progress bar remains active but does not show an -actual percentage. Use this mode for situations when you do not know how long -an operation will take to complete. +bar. Setting it to a value greater than `1` will indicate an indeterminate progress bar +in Windows or clamp to 100% in other operating systems. An indeterminate progress bar +remains active but does not show an actual percentage, and is used for situations when +you do not know how long an operation will take to complete. See the [API documentation for more options and modes][setprogressbar]. ## Example -Starting with a working application from the -[Quick Start Guide](quick-start.md), add the following lines to the -`main.js` file: +In this example, we add a progress bar to the main window that increments over time +using Node.js timers. ```javascript fiddle='docs/fiddles/features/progress-bar' -const { BrowserWindow } = require('electron') -const win = new BrowserWindow() - -win.setProgressBar(0.5) +const { app, BrowserWindow } = require('electron') + +let progressInterval + +function createWindow () { + const win = new BrowserWindow({ + width: 800, + height: 600 + }) + + win.loadFile('index.html') + + const INCREMENT = 0.03 + const INTERVAL_DELAY = 100 // ms + + let c = 0 + progressInterval = setInterval(() => { + // update progress bar to next value + // values between 0 and 1 will show progress, >1 will show indeterminate or stick at 100% + win.setProgressBar(c) + + // increment or reset progress bar + if (c < 2) c += INCREMENT + else c = 0 + }, INTERVAL_DELAY) +} + +app.whenReady().then(createWindow) + +// before the app is terminated, clear both timers +app.on('before-quit', () => { + clearInterval(progressInterval) +}) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } +}) ``` -After launching the Electron application, you should see the bar in -the dock (macOS) or taskbar (Windows, Unity), indicating the progress -percentage you just defined. +After launching the Electron application, the dock (macOS) or taskbar (Windows, Unity) +should show a progress bar that starts at zero and progresses through 100% to completion. +It should then show indeterminate (Windows) or pin to 100% (other operating systems) +briefly and then loop. ![macOS dock progress bar](../images/dock-progress-bar.png) From e4a3f064de9c36c6d5901e38c73599686e7c6795 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 2 Jun 2021 22:24:59 +0900 Subject: [PATCH 25/79] build: Support building Electron on msys2 (#29478) Electron already seems to support `cygwin`, so `msys` is a natural addition. This is the only required change as far as I can see on my local development environment, as otherwise the build scripts don't realize that msys = windows. Notes: none Signed-off-by: Juan Cruz Viotti Co-authored-by: Juan Cruz Viotti --- script/lib/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/script/lib/config.py b/script/lib/config.py index 9b0a6e23e8aa0..e5a79b8d23144 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -18,6 +18,7 @@ PLATFORM = { 'cygwin': 'win32', + 'msys': 'win32', 'darwin': 'darwin', 'linux': 'linux', 'linux2': 'linux', From 93d9aaa99c3a39c9582ef265b2de16975fabaaa6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 2 Jun 2021 22:25:19 +0900 Subject: [PATCH 26/79] chore: don't use after move (#29479) Co-authored-by: David Sanders --- shell/browser/native_window_mac.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index de01c88fa2d2f..3df4687514657 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1461,7 +1461,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { base::Optional position) { traffic_light_position_ = std::move(position); if (buttons_view_) { - [buttons_view_ setMargin:position]; + [buttons_view_ setMargin:traffic_light_position_]; [buttons_view_ viewDidMoveToWindow]; } } From 6855004aa5365cb1ced2bcde6e63320dcb551a38 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 2 Jun 2021 14:05:17 -0700 Subject: [PATCH 27/79] refactor: point prepare-release at main (#29496) Co-authored-by: Jeremy Rose --- script/release/prepare-release.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/release/prepare-release.js b/script/release/prepare-release.js index c70546ce79dfd..d83050e0c7ca8 100755 --- a/script/release/prepare-release.js +++ b/script/release/prepare-release.js @@ -112,7 +112,7 @@ async function createRelease (branchToTarget, isBeta) { name: `electron ${newVersion}`, body: releaseBody, prerelease: releaseIsPrelease, - target_commitish: newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget + target_commitish: newVersion.indexOf('nightly') !== -1 ? 'main' : branchToTarget }).catch(err => { console.log(`${fail} Error creating new release: `, err); process.exit(1); @@ -180,7 +180,7 @@ async function promptForVersion (version) { }); } -// function to determine if there have been commits to master since the last release +// function to determine if there have been commits to main since the last release async function changesToRelease () { const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g'); const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR); From 6e169089f054867a2f1de8303d7fe89205cb3cee Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Wed, 2 Jun 2021 17:03:01 -0700 Subject: [PATCH 28/79] feat: support loading debug urls with loadURL() (#29404) (#29491) --- shell/browser/api/electron_api_web_contents.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 6b48a4fc7f978..d46c4d9d82b4a 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2013,7 +2013,8 @@ void WebContents::LoadURL(const GURL& url, // Calling LoadURLWithParams() can trigger JS which destroys |this|. auto weak_this = GetWeakPtr(); - params.transition_type = ui::PAGE_TRANSITION_TYPED; + params.transition_type = ui::PageTransitionFromInt( + ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); params.should_clear_history_list = true; params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE; // Discord non-committed entries to ensure that we don't re-use a pending From 2156a9064f46e8da7bab409fbe813fe0d697c727 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Wed, 2 Jun 2021 17:04:35 -0700 Subject: [PATCH 29/79] feat: add experimental cookie encryption support (#29493) * feat: add experimental cookie encryption support (#27524) * feat: add experimental cookie encryption support on macOS * chore: fix TODO * update patches * feat: make cookie encryption work on windows * chore: update cookie encryption support comments * fix: only call OSCrypt::Init on windows * chore: make cookie encryption work on linux * Update shell/browser/net/system_network_context_manager.cc Co-authored-by: Jeremy Rose * chore: fix lint * chore: update patches * chore: update patches to upstreamed variants * chore: use chrome ::switches constants * chore: remove bad patch * build: disable cookie encryption by default * chore: update patches * fix: provide std::string to NoDestructor * chore: fix macos, nodestructor syntax * build: fix macOS build due to mismatch in DEFINE Co-authored-by: Electron Bot Co-authored-by: Jeremy Rose * chore: update patches Co-authored-by: Electron Bot Co-authored-by: Jeremy Rose --- BUILD.gn | 8 +- build/args/all.gn | 3 + build/fuses/build.py | 4 +- build/fuses/{fuses.json => fuses.json5} | 3 +- docs/tutorial/fuses.md | 2 +- patches/chromium/.patches | 2 + ...t_optionally_configurable_at_runtime.patch | 132 ++++++++ ...rable_key_storage_on_linux_os_crypto.patch | 290 ++++++++++++++++++ shell/browser/browser_process_impl.cc | 29 +- shell/browser/electron_browser_main_parts.cc | 12 + shell/browser/net/network_context_service.cc | 6 +- .../net/system_network_context_manager.cc | 44 +++ 12 files changed, 524 insertions(+), 11 deletions(-) rename build/fuses/{fuses.json => fuses.json5} (84%) create mode 100644 patches/chromium/make_keychain_service_account_optionally_configurable_at_runtime.patch create mode 100644 patches/chromium/support_runtime_configurable_key_storage_on_linux_os_crypto.patch diff --git a/BUILD.gn b/BUILD.gn index 0f59b9c14cfba..28ad68664eb5a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,6 +1,7 @@ import("//build/config/locales.gni") import("//build/config/ui.gni") import("//build/config/win/manifest.gni") +import("//components/os_crypt/features.gni") import("//components/spellcheck/spellcheck_build_features.gni") import("//content/public/app/mac_helpers.gni") import("//extensions/buildflags/buildflags.gni") @@ -292,7 +293,7 @@ templated_file("electron_version_header") { action("electron_fuses") { script = "build/fuses/build.py" - inputs = [ "build/fuses/fuses.json" ] + inputs = [ "build/fuses/fuses.json5" ] outputs = [ "$target_gen_dir/fuses.h", @@ -331,6 +332,7 @@ source_set("electron_lib") { "//components/network_hints/common:mojo_bindings", "//components/network_hints/renderer", "//components/network_session_configurator/common", + "//components/os_crypt", "//components/pref_registry", "//components/prefs", "//components/upload_list", @@ -678,6 +680,10 @@ source_set("electron_lib") { ] libs += [ "uxtheme.lib" ] } + + if (allow_runtime_configurable_key_storage) { + defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ] + } } electron_paks("packed_resources") { diff --git a/build/args/all.gn b/build/args/all.gn index 9ac1f938c4402..e18315dd14bb6 100644 --- a/build/args/all.gn +++ b/build/args/all.gn @@ -28,3 +28,6 @@ libcxx_abi_unstable = false enable_pseudolocales = false is_cfi = false + +# Make application name configurable at runtime for cookie crypto +allow_runtime_configurable_key_storage = true diff --git a/build/fuses/build.py b/build/fuses/build.py index f17c08fdb8451..6c56dceb091df 100755 --- a/build/fuses/build.py +++ b/build/fuses/build.py @@ -49,8 +49,8 @@ } """ -with open(os.path.join(dir_path, "fuses.json"), 'r') as f: - fuse_defaults = json.load(f) +with open(os.path.join(dir_path, "fuses.json5"), 'r') as f: + fuse_defaults = json.loads(''.join(line for line in f.readlines() if not line.strip()[0] == "/")) fuse_version = fuse_defaults['_version'] del fuse_defaults['_version'] diff --git a/build/fuses/fuses.json b/build/fuses/fuses.json5 similarity index 84% rename from build/fuses/fuses.json rename to build/fuses/fuses.json5 index 9ff211adb43e3..dfc518ffa36e7 100644 --- a/build/fuses/fuses.json +++ b/build/fuses/fuses.json5 @@ -2,5 +2,6 @@ "_comment": "Modifying the fuse schema in any breaking way should result in the _version prop being incremented. NEVER remove a fuse or change its meaning, instead mark it as removed with 'r'", "_schema": "0 == off, 1 == on, r == removed fuse", "_version": 1, - "run_as_node": "1" + "run_as_node": "1", + "cookie_encryption": "0" } diff --git a/docs/tutorial/fuses.md b/docs/tutorial/fuses.md index 76141ca314402..90a039a530f7b 100644 --- a/docs/tutorial/fuses.md +++ b/docs/tutorial/fuses.md @@ -51,4 +51,4 @@ Somewhere in the Electron binary there will be a sequence of bytes that look lik To flip a fuse you find its position in the fuse wire and change it to "0" or "1" depending on the state you'd like. -You can view the current schema [here](https://github.com/electron/electron/blob/master/build/fuses/fuses.json). +You can view the current schema [here](https://github.com/electron/electron/blob/master/build/fuses/fuses.json5). diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 8e2d84985171f..5b2fe7858beb8 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -110,3 +110,5 @@ add_setter_for_browsermainloop_result_code.patch cherry-pick-8089dbfc616f.patch x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch build_libc_as_static_library.patch +support_runtime_configurable_key_storage_on_linux_os_crypto.patch +make_keychain_service_account_optionally_configurable_at_runtime.patch diff --git a/patches/chromium/make_keychain_service_account_optionally_configurable_at_runtime.patch b/patches/chromium/make_keychain_service_account_optionally_configurable_at_runtime.patch new file mode 100644 index 0000000000000..83379001d47d6 --- /dev/null +++ b/patches/chromium/make_keychain_service_account_optionally_configurable_at_runtime.patch @@ -0,0 +1,132 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Fri, 7 May 2021 00:35:57 +0000 +Subject: Make keychain service/account optionally configurable at runtime + +This flag allows embedders to customize the service/account_name used +for cookie crypto at runtime. Currently these values are hardcoded +to Chromium/Chrome and the only way to change them is to patch this +file as part of the build process. Making these non-const and +assignable allows embedders to easily avoid colliding with the +"Chrome Safe Storage" keychain value. The const vs non-const change +is done behind a build flag so that the normal Chrome and Chromium +builds are unaffected. + +I intend to follow this up with a similar CL for changes to the +linux crypto files too. + +Change-Id: Id2f9456a8dfc71a004a2dd405bed46518c559ac4 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2861286 +Reviewed-by: Lei Zhang +Commit-Queue: Jeremy Rose +Cr-Commit-Position: refs/heads/master@{#880168} + +diff --git a/components/os_crypt/BUILD.gn b/components/os_crypt/BUILD.gn +index 79f9744a94d79e155958ee28e93eeb2b4e65d112..b9c1cf2bd914812697b161217e09ea693d2c6e65 100644 +--- a/components/os_crypt/BUILD.gn ++++ b/components/os_crypt/BUILD.gn +@@ -51,6 +51,10 @@ component("os_crypt") { + + defines = [ "IS_OS_CRYPT_IMPL" ] + ++ if (allow_runtime_configurable_key_storage) { ++ defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ] ++ } ++ + if ((is_posix || is_fuchsia) && !is_apple && + (!(is_linux || is_chromeos_lacros) || is_chromecast)) { + sources += [ "os_crypt_posix.cc" ] +diff --git a/components/os_crypt/features.gni b/components/os_crypt/features.gni +index a145e0530d3cdc2a04d471f98d72c3dd30e437d9..73b6e8703298b342ba94b977c9b98da0e1739bbd 100644 +--- a/components/os_crypt/features.gni ++++ b/components/os_crypt/features.gni +@@ -9,4 +9,10 @@ declare_args() { + # Whether to use libgnome-keyring (deprecated by libsecret). + # See http://crbug.com/466975 and http://crbug.com/355223. + use_gnome_keyring = (is_linux || is_chromeos_lacros) && use_glib ++ ++ # Whether to make account and service names for the crypto key storage ++ # configurable at runtime for embedders. ++ # ++ # Currently only has an effect on macOS via KeychainPassword ++ allow_runtime_configurable_key_storage = false + } +diff --git a/components/os_crypt/keychain_password_mac.h b/components/os_crypt/keychain_password_mac.h +index 6fda0244667c4eb5d1abb973f4b72d9c59ed2165..40b2522b87912124c106a7c28853399d31238b81 100644 +--- a/components/os_crypt/keychain_password_mac.h ++++ b/components/os_crypt/keychain_password_mac.h +@@ -9,6 +9,7 @@ + + #include "base/component_export.h" + #include "base/macros.h" ++#include "base/no_destructor.h" + + namespace crypto { + class AppleKeychain; +@@ -16,6 +17,12 @@ class AppleKeychain; + + class COMPONENT_EXPORT(OS_CRYPT) KeychainPassword { + public: ++#if defined(ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE) ++ using KeychainNameType = base::NoDestructor; ++#else ++ using KeychainNameType = const base::NoDestructor; ++#endif ++ + KeychainPassword(const crypto::AppleKeychain& keychain); + ~KeychainPassword(); + +@@ -28,8 +35,8 @@ class COMPONENT_EXPORT(OS_CRYPT) KeychainPassword { + std::string GetPassword() const; + + // The service and account names used in Chrome's Safe Storage keychain item. +- static COMPONENT_EXPORT(OS_CRYPT) const char service_name[]; +- static COMPONENT_EXPORT(OS_CRYPT) const char account_name[]; ++ static COMPONENT_EXPORT(OS_CRYPT) KeychainNameType service_name; ++ static COMPONENT_EXPORT(OS_CRYPT) KeychainNameType account_name; + + private: + const crypto::AppleKeychain& keychain_; +diff --git a/components/os_crypt/keychain_password_mac.mm b/components/os_crypt/keychain_password_mac.mm +index 6654c46eb0af784a3a2ff64569d5d1931b9fae30..f8973f5ed0e213c0d242d2141091607017824ec3 100644 +--- a/components/os_crypt/keychain_password_mac.mm ++++ b/components/os_crypt/keychain_password_mac.mm +@@ -48,11 +48,11 @@ + // the encryption keyword. So as to not lose encrypted data when system + // locale changes we DO NOT LOCALIZE. + #if BUILDFLAG(GOOGLE_CHROME_BRANDING) +-const char KeychainPassword::service_name[] = "Chrome Safe Storage"; +-const char KeychainPassword::account_name[] = "Chrome"; ++KeychainPassword::KeychainNameType KeychainPassword::service_name("Chrome Safe Storage"); ++KeychainPassword::KeychainNameType KeychainPassword::account_name("Chrome"); + #else +-const char KeychainPassword::service_name[] = "Chromium Safe Storage"; +-const char KeychainPassword::account_name[] = "Chromium"; ++KeychainPassword::KeychainNameType KeychainPassword::service_name("Chromium Safe Storage"); ++KeychainPassword::KeychainNameType KeychainPassword::account_name("Chromium"); + #endif + + KeychainPassword::KeychainPassword(const AppleKeychain& keychain) +@@ -64,8 +64,9 @@ + UInt32 password_length = 0; + void* password_data = nullptr; + OSStatus error = keychain_.FindGenericPassword( +- strlen(service_name), service_name, strlen(account_name), account_name, +- &password_length, &password_data, nullptr); ++ service_name->size(), service_name->c_str(), ++ account_name->size(), account_name->c_str(), &password_length, ++ &password_data, nullptr); + + if (error == noErr) { + std::string password = +@@ -75,8 +76,8 @@ + } + + if (error == errSecItemNotFound) { +- std::string password = +- AddRandomPasswordToKeychain(keychain_, service_name, account_name); ++ std::string password = AddRandomPasswordToKeychain( ++ keychain_, *service_name, *account_name); + return password; + } + diff --git a/patches/chromium/support_runtime_configurable_key_storage_on_linux_os_crypto.patch b/patches/chromium/support_runtime_configurable_key_storage_on_linux_os_crypto.patch new file mode 100644 index 0000000000000..5674d039def48 --- /dev/null +++ b/patches/chromium/support_runtime_configurable_key_storage_on_linux_os_crypto.patch @@ -0,0 +1,290 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Mon, 10 May 2021 17:10:25 -0700 +Subject: support runtime configurable key storage on linux (os_crypto) + +This modifies the OsCrypt::Config struct used on linux to support +runtime configurable application names which are used in the Keyring and +LibSecret implementations of os_crypt on linux. + +Change-Id: Ifc287b589f118da8fcd5afaf39e5ba7ffe46f5fd + +diff --git a/components/os_crypt/key_storage_config_linux.h b/components/os_crypt/key_storage_config_linux.h +index a856604756aa65c52171a9eff84ba2b316d8609c..72c16682e5df615ab84f67af66cc36c2b76c30e3 100644 +--- a/components/os_crypt/key_storage_config_linux.h ++++ b/components/os_crypt/key_storage_config_linux.h +@@ -26,6 +26,12 @@ struct COMPONENT_EXPORT(OS_CRYPT) Config { + std::string store; + // The product name to use for permission prompts. + std::string product_name; ++ // The application name to store the key under. For Chromium/Chrome builds ++ // leave this unset and it will default correctly. This config option is ++ // for embedders to provide their application name in place of "Chromium". ++ // Only used when the allow_runtime_configurable_key_storage feature is ++ // enabled. ++ std::string application_name; + // A runner on the main thread for gnome-keyring to be called from. + // TODO(crbug/466975): Libsecret and KWallet don't need this. We can remove + // this when we stop supporting keyring. +diff --git a/components/os_crypt/key_storage_keyring.cc b/components/os_crypt/key_storage_keyring.cc +index 409bd27cbc0877634d7d9809575cfa5f60ba04c2..75141308a18e5c3c083439621796f0b49b78db6b 100644 +--- a/components/os_crypt/key_storage_keyring.cc ++++ b/components/os_crypt/key_storage_keyring.cc +@@ -15,12 +15,6 @@ + + namespace { + +-#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +-const char kApplicationName[] = "chrome"; +-#else +-const char kApplicationName[] = "chromium"; +-#endif +- + const GnomeKeyringPasswordSchema kSchema = { + GNOME_KEYRING_ITEM_GENERIC_SECRET, + {{"application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING}, {nullptr}}}; +@@ -28,8 +22,10 @@ const GnomeKeyringPasswordSchema kSchema = { + } // namespace + + KeyStorageKeyring::KeyStorageKeyring( +- scoped_refptr main_thread_runner) +- : main_thread_runner_(main_thread_runner) {} ++ scoped_refptr main_thread_runner, ++ std::string application_name) ++ : main_thread_runner_(main_thread_runner), ++ application_name_(std::move(application_name)) {} + + KeyStorageKeyring::~KeyStorageKeyring() {} + +@@ -49,7 +45,8 @@ base::Optional KeyStorageKeyring::GetKeyImpl() { + gchar* password_c = nullptr; + GnomeKeyringResult result = + GnomeKeyringLoader::gnome_keyring_find_password_sync_ptr( +- &kSchema, &password_c, "application", kApplicationName, nullptr); ++ &kSchema, &password_c, "application", application_name_.c_str(), ++ nullptr); + if (result == GNOME_KEYRING_RESULT_OK) { + password = password_c; + GnomeKeyringLoader::gnome_keyring_free_password_ptr(password_c); +@@ -71,7 +68,7 @@ base::Optional KeyStorageKeyring::AddRandomPasswordInKeyring() { + GnomeKeyringResult result = + GnomeKeyringLoader::gnome_keyring_store_password_sync_ptr( + &kSchema, nullptr /* default keyring */, KeyStorageLinux::kKey, +- password.c_str(), "application", kApplicationName, nullptr); ++ password.c_str(), "application", application_name_.c_str(), nullptr); + if (result != GNOME_KEYRING_RESULT_OK) { + VLOG(1) << "OSCrypt failed to store generated password to gnome-keyring"; + return base::nullopt; +diff --git a/components/os_crypt/key_storage_keyring.h b/components/os_crypt/key_storage_keyring.h +index 6406f2825997e0166defd7dd1457c734aa5ee6c4..fb8283f91aebe549c46deae97a483218cf4f57d7 100644 +--- a/components/os_crypt/key_storage_keyring.h ++++ b/components/os_crypt/key_storage_keyring.h +@@ -20,8 +20,9 @@ class SingleThreadTaskRunner; + // Specialisation of KeyStorageLinux that uses Libsecret. + class COMPONENT_EXPORT(OS_CRYPT) KeyStorageKeyring : public KeyStorageLinux { + public: +- explicit KeyStorageKeyring( +- scoped_refptr main_thread_runner); ++ KeyStorageKeyring( ++ scoped_refptr main_thread_runner, ++ std::string application_name); + ~KeyStorageKeyring() override; + + protected: +@@ -37,6 +38,8 @@ class COMPONENT_EXPORT(OS_CRYPT) KeyStorageKeyring : public KeyStorageLinux { + // Keyring calls need to originate from the main thread. + scoped_refptr main_thread_runner_; + ++ const std::string application_name_; ++ + DISALLOW_COPY_AND_ASSIGN(KeyStorageKeyring); + }; + +diff --git a/components/os_crypt/key_storage_keyring_unittest.cc b/components/os_crypt/key_storage_keyring_unittest.cc +index 010febfe974a2bdd2efb52c78b1fc16c5d248768..0cecd45b78b871c15ed7caf1025aca8710f113f9 100644 +--- a/components/os_crypt/key_storage_keyring_unittest.cc ++++ b/components/os_crypt/key_storage_keyring_unittest.cc +@@ -130,7 +130,7 @@ class GnomeKeyringTest : public testing::Test { + }; + + GnomeKeyringTest::GnomeKeyringTest() +- : task_runner_(new base::TestSimpleTaskRunner()), keyring_(task_runner_) { ++ : task_runner_(new base::TestSimpleTaskRunner()), keyring_(task_runner_, "chromium") { + MockGnomeKeyringLoader::ResetForOSCrypt(); + } + +diff --git a/components/os_crypt/key_storage_libsecret.cc b/components/os_crypt/key_storage_libsecret.cc +index 312570612ccb6ec4480a6f8d4a74accf5ba79ff8..f97ae381cd8e838b0150ef77d42500f66403cd11 100644 +--- a/components/os_crypt/key_storage_libsecret.cc ++++ b/components/os_crypt/key_storage_libsecret.cc +@@ -14,12 +14,6 @@ + + namespace { + +-#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +-const char kApplicationName[] = "chrome"; +-#else +-const char kApplicationName[] = "chromium"; +-#endif +- + const SecretSchema kKeystoreSchemaV2 = { + "chrome_libsecret_os_crypt_password_v2", + SECRET_SCHEMA_DONT_MATCH_NAME, +@@ -64,6 +58,9 @@ void AnalyseKeyHistory(GList* secret_items) { + + } // namespace + ++KeyStorageLibsecret::KeyStorageLibsecret(std::string application_name) ++ : application_name_(std::move(application_name)) {} ++ + base::Optional + KeyStorageLibsecret::AddRandomPasswordInLibsecret() { + std::string password; +@@ -71,7 +68,7 @@ KeyStorageLibsecret::AddRandomPasswordInLibsecret() { + GError* error = nullptr; + bool success = LibsecretLoader::secret_password_store_sync( + &kKeystoreSchemaV2, nullptr, KeyStorageLinux::kKey, password.c_str(), +- nullptr, &error, "application", kApplicationName, nullptr); ++ nullptr, &error, "application", application_name_.c_str(), nullptr); + if (error) { + VLOG(1) << "Libsecret lookup failed: " << error->message; + g_error_free(error); +@@ -88,7 +85,7 @@ KeyStorageLibsecret::AddRandomPasswordInLibsecret() { + + base::Optional KeyStorageLibsecret::GetKeyImpl() { + LibsecretAttributesBuilder attrs; +- attrs.Append("application", kApplicationName); ++ attrs.Append("application", application_name_); + + LibsecretLoader::SearchHelper helper; + helper.Search(&kKeystoreSchemaV2, attrs.Get(), +diff --git a/components/os_crypt/key_storage_libsecret.h b/components/os_crypt/key_storage_libsecret.h +index e59a2a1b5a776010556613ad63391c000ef977a4..1e889f00406cdf4eb40a3807df89432c2d57e4e1 100644 +--- a/components/os_crypt/key_storage_libsecret.h ++++ b/components/os_crypt/key_storage_libsecret.h +@@ -15,7 +15,7 @@ + // Specialisation of KeyStorageLinux that uses Libsecret. + class COMPONENT_EXPORT(OS_CRYPT) KeyStorageLibsecret : public KeyStorageLinux { + public: +- KeyStorageLibsecret() = default; ++ explicit KeyStorageLibsecret(std::string application_name); + ~KeyStorageLibsecret() override = default; + + protected: +@@ -26,6 +26,8 @@ class COMPONENT_EXPORT(OS_CRYPT) KeyStorageLibsecret : public KeyStorageLinux { + private: + base::Optional AddRandomPasswordInLibsecret(); + ++ const std::string application_name_; ++ + DISALLOW_COPY_AND_ASSIGN(KeyStorageLibsecret); + }; + +diff --git a/components/os_crypt/key_storage_libsecret_unittest.cc b/components/os_crypt/key_storage_libsecret_unittest.cc +index ca54c3f27b42a685dd0d695922d340f580bac57b..8988ffb928a97186f429ea96e543003a4175aacb 100644 +--- a/components/os_crypt/key_storage_libsecret_unittest.cc ++++ b/components/os_crypt/key_storage_libsecret_unittest.cc +@@ -236,7 +236,7 @@ class LibsecretTest : public testing::Test { + }; + + TEST_F(LibsecretTest, LibsecretRepeats) { +- KeyStorageLibsecret libsecret; ++ KeyStorageLibsecret libsecret("chromium"); + MockLibsecretLoader::ResetForOSCrypt(); + g_password_store.Pointer()->SetPassword("initial password"); + base::Optional password = libsecret.GetKey(); +@@ -248,7 +248,7 @@ TEST_F(LibsecretTest, LibsecretRepeats) { + } + + TEST_F(LibsecretTest, LibsecretCreatesRandomised) { +- KeyStorageLibsecret libsecret; ++ KeyStorageLibsecret libsecret("chromium"); + MockLibsecretLoader::ResetForOSCrypt(); + base::Optional password = libsecret.GetKey(); + MockLibsecretLoader::ResetForOSCrypt(); +diff --git a/components/os_crypt/key_storage_linux.cc b/components/os_crypt/key_storage_linux.cc +index 33fed0fa438776e3da4de6a3589745ad8b8304e9..4f50a360fcb9cc8624de92fa2ca37a5115705271 100644 +--- a/components/os_crypt/key_storage_linux.cc ++++ b/components/os_crypt/key_storage_linux.cc +@@ -9,6 +9,7 @@ + #include "base/logging.h" + #include "base/metrics/histogram_macros.h" + #include "base/nix/xdg_util.h" ++#include "base/no_destructor.h" + #include "base/sequenced_task_runner.h" + #include "base/synchronization/waitable_event.h" + #include "base/task_runner_util.h" +@@ -145,12 +146,29 @@ std::unique_ptr KeyStorageLinux::CreateService( + std::unique_ptr KeyStorageLinux::CreateServiceInternal( + os_crypt::SelectedLinuxBackend selected_backend, + const os_crypt::Config& config) { ++#if BUILDFLAG(GOOGLE_CHROME_BRANDING) ++ static const base::NoDestructor kDefaultApplicationName("chrome"); ++#else ++ static const base::NoDestructor kDefaultApplicationName("chromium"); ++#endif ++ + std::unique_ptr key_storage; + ++#if defined(USE_LIBSECRET) || defined(USE_KEYRING) ++#if defined(ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE) ++ std::string application_name = config.application_name; ++ if (application_name.empty()) { ++ application_name = *kDefaultApplicationName; ++ } ++#else ++ std::string application_name = *kDefaultApplicationName; ++#endif ++#endif ++ + #if defined(USE_LIBSECRET) + if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY || + selected_backend == os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) { +- key_storage.reset(new KeyStorageLibsecret()); ++ key_storage.reset(new KeyStorageLibsecret(application_name)); + if (key_storage->WaitForInitOnTaskRunner()) { + VLOG(1) << "OSCrypt using Libsecret as backend."; + return key_storage; +@@ -162,11 +180,7 @@ std::unique_ptr KeyStorageLinux::CreateServiceInternal( + #if defined(USE_KEYRING) + if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY || + selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING) { +- key_storage.reset(new KeyStorageKeyring(config.main_thread_runner)); +- if (key_storage->WaitForInitOnTaskRunner()) { +- VLOG(1) << "OSCrypt using Keyring as backend."; +- return key_storage; +- } ++ key_storage.reset(new KeyStorageKeyring(config.main_thread_runner, application_name)); + LOG(WARNING) << "OSCrypt tried Keyring but couldn't initialise."; + } + #endif // defined(USE_KEYRING) +diff --git a/services/network/network_service.cc b/services/network/network_service.cc +index 92d25979693ae8ecd34b9abc4afd24d274ae6921..87ef037d5c7aec1b8106051d7e3f082468e23452 100644 +--- a/services/network/network_service.cc ++++ b/services/network/network_service.cc +@@ -621,6 +621,7 @@ void NetworkService::SetCryptConfig(mojom::CryptConfigPtr crypt_config) { + auto config = std::make_unique(); + config->store = crypt_config->store; + config->product_name = crypt_config->product_name; ++ config->application_name = crypt_config->application_name; + config->main_thread_runner = base::ThreadTaskRunnerHandle::Get(); + config->should_use_preference = crypt_config->should_use_preference; + config->user_data_path = crypt_config->user_data_path; +diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom +index 864ca071874e54842fe418e3b84d0b8877e3e1b7..b4b12d0de2203041791cde1b1f8ef808cee23a9f 100644 +--- a/services/network/public/mojom/network_service.mojom ++++ b/services/network/public/mojom/network_service.mojom +@@ -98,6 +98,13 @@ struct CryptConfig { + // The product name to use for permission prompts. + string product_name; + ++ // The application name to store the crypto key against. For Chromium/Chrome ++ // builds leave this unset and it will default correctly. This config option ++ // is for embedders to provide their application name in place of "Chromium". ++ // Only used when the allow_runtime_configurable_key_storage feature is ++ // enabled ++ string application_name; ++ + // Controls whether preference on using or ignoring backends is used. + bool should_use_preference; + diff --git a/shell/browser/browser_process_impl.cc b/shell/browser/browser_process_impl.cc index 9aa7488c0fc57..a7765b27c3e33 100644 --- a/shell/browser/browser_process_impl.cc +++ b/shell/browser/browser_process_impl.cc @@ -9,8 +9,12 @@ #include #include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/path_service.h" #include "chrome/common/chrome_switches.h" +#include "components/os_crypt/os_crypt.h" #include "components/prefs/in_memory_pref_store.h" +#include "components/prefs/json_pref_store.h" #include "components/prefs/overlay_user_pref_store.h" #include "components/prefs/pref_registry.h" #include "components/prefs/pref_registry_simple.h" @@ -20,11 +24,13 @@ #include "components/proxy_config/proxy_config_pref_names.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/common/content_switches.h" +#include "electron/fuses.h" #include "extensions/common/constants.h" #include "net/proxy_resolution/proxy_config.h" #include "net/proxy_resolution/proxy_config_service.h" #include "net/proxy_resolution/proxy_config_with_annotation.h" #include "services/network/public/cpp/network_switches.h" +#include "shell/common/electron_paths.h" #if BUILDFLAG(ENABLE_PRINTING) #include "chrome/browser/printing/print_job_manager.h" @@ -83,15 +89,32 @@ BuildState* BrowserProcessImpl::GetBuildState() { } void BrowserProcessImpl::PostEarlyInitialization() { - // Mock user prefs, as we only need to track changes for a - // in memory pref store. There are no persistent preferences PrefServiceFactory prefs_factory; auto pref_registry = base::MakeRefCounted(); PrefProxyConfigTrackerImpl::RegisterPrefs(pref_registry.get()); +#if defined(OS_WIN) + OSCrypt::RegisterLocalPrefs(pref_registry.get()); +#endif + auto pref_store = base::MakeRefCounted(); ApplyProxyModeFromCommandLine(pref_store.get()); prefs_factory.set_command_line_prefs(std::move(pref_store)); - prefs_factory.set_user_prefs(new OverlayUserPrefStore(new InMemoryPrefStore)); + + // Only use a persistent prefs store when cookie encryption is enabled as that + // is the only key that needs it + if (electron::fuses::IsCookieEncryptionEnabled()) { + base::FilePath prefs_path; + CHECK(base::PathService::Get(electron::DIR_USER_DATA, &prefs_path)); + prefs_path = prefs_path.Append(FILE_PATH_LITERAL("Local State")); + base::ThreadRestrictions::ScopedAllowIO allow_io; + scoped_refptr user_pref_store = + base::MakeRefCounted(prefs_path); + user_pref_store->ReadPrefs(); + prefs_factory.set_user_prefs(user_pref_store); + } else { + prefs_factory.set_user_prefs( + new OverlayUserPrefStore(new InMemoryPrefStore)); + } local_state_ = prefs_factory.Create(std::move(pref_registry)); } diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index e551a9baadd3d..bd861b3973374 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -17,6 +17,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/icon_manager.h" +#include "components/os_crypt/os_crypt.h" #include "content/browser/browser_main_loop.h" // nogncheck #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" @@ -26,6 +27,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/result_codes.h" #include "electron/buildflags/buildflags.h" +#include "electron/fuses.h" #include "media/base/localized_strings.h" #include "services/network/public/cpp/features.h" #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" @@ -549,6 +551,16 @@ void ElectronBrowserMainParts::PreMainMessageLoopStartCommon() { RegisterURLHandler(); #endif media::SetLocalizedStringProvider(MediaStringProvider); + +#if defined(OS_WIN) + if (electron::fuses::IsCookieEncryptionEnabled()) { + auto* local_state = g_browser_process->local_state(); + DCHECK(local_state); + + bool os_crypt_init = OSCrypt::Init(local_state); + DCHECK(os_crypt_init); + } +#endif } device::mojom::GeolocationControl* diff --git a/shell/browser/net/network_context_service.cc b/shell/browser/net/network_context_service.cc index a1ee1d2dc381b..aeaab36f1f5d9 100644 --- a/shell/browser/net/network_context_service.cc +++ b/shell/browser/net/network_context_service.cc @@ -9,6 +9,7 @@ #include "chrome/common/chrome_constants.h" #include "content/public/browser/network_service_instance.h" #include "content/public/browser/shared_cors_origin_access_list.h" +#include "electron/fuses.h" #include "net/net_buildflags.h" #include "services/network/network_service.h" #include "services/network/public/cpp/cors/origin_access_list.h" @@ -77,9 +78,8 @@ void NetworkContextService::ConfigureNetworkContextParams( network_context_params->restore_old_session_cookies = false; network_context_params->persist_session_cookies = false; - // TODO(deepak1556): Matches the existing behavior https://git.io/fxHMl, - // enable encryption as a followup. - network_context_params->enable_encrypted_cookies = false; + network_context_params->enable_encrypted_cookies = + electron::fuses::IsCookieEncryptionEnabled(); network_context_params->transport_security_persister_path = path; } diff --git a/shell/browser/net/system_network_context_manager.cc b/shell/browser/net/system_network_context_manager.cc index f6890b7e9a11d..c6c404c833895 100644 --- a/shell/browser/net/system_network_context_manager.cc +++ b/shell/browser/net/system_network_context_manager.cc @@ -8,11 +8,16 @@ #include #include "base/command_line.h" +#include "base/path_service.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" +#include "chrome/common/chrome_switches.h" +#include "components/os_crypt/os_crypt.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/network_service_instance.h" #include "content/public/common/content_features.h" +#include "content/public/common/network_service_util.h" +#include "electron/fuses.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "net/net_buildflags.h" #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" @@ -21,11 +26,17 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/mojom/network_context.mojom.h" +#include "shell/browser/browser.h" #include "shell/browser/electron_browser_client.h" #include "shell/common/application_info.h" +#include "shell/common/electron_paths.h" #include "shell/common/options_switches.h" #include "url/gurl.h" +#if defined(OS_MAC) +#include "components/os_crypt/keychain_password_mac.h" +#endif + namespace { // The global instance of the SystemNetworkContextmanager. @@ -218,6 +229,39 @@ void SystemNetworkContextManager::OnNetworkServiceCreated( network_service->CreateNetworkContext( network_context_.BindNewPipeAndPassReceiver(), CreateNetworkContextParams()); + + if (electron::fuses::IsCookieEncryptionEnabled()) { + std::string app_name = electron::Browser::Get()->GetName(); +#if defined(OS_MAC) + *KeychainPassword::service_name = app_name + " Safe Storage"; + *KeychainPassword::account_name = app_name; +#endif + // The OSCrypt keys are process bound, so if network service is out of + // process, send it the required key. + if (content::IsOutOfProcessNetworkService()) { +#if defined(OS_LINUX) + // c.f. + // https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/net/system_network_context_manager.cc;l=515;drc=9d82515060b9b75fa941986f5db7390299669ef1;bpv=1;bpt=1 + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + network::mojom::CryptConfigPtr config = + network::mojom::CryptConfig::New(); + config->application_name = app_name; + config->product_name = app_name; + // c.f. + // https://source.chromium.org/chromium/chromium/src/+/master:chrome/common/chrome_switches.cc;l=689;drc=9d82515060b9b75fa941986f5db7390299669ef1 + config->store = + command_line.GetSwitchValueASCII(::switches::kPasswordStore); + config->should_use_preference = + command_line.HasSwitch(::switches::kEnableEncryptionSelection); + base::PathService::Get(electron::DIR_USER_DATA, &config->user_data_path); + network_service->SetCryptConfig(std::move(config)); +#else + network_service->SetEncryptionKey(OSCrypt::GetRawEncryptionKey()); +#endif + } + } } network::mojom::NetworkContextParamsPtr From 0c19db01d9d13908c507e0bd9d82dc3d75eb6854 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Wed, 2 Jun 2021 18:06:33 -0700 Subject: [PATCH 30/79] chore: cherry-pick 3299d70b7d0f from chromium (#29494) * chore: cherry-pick 3299d70b7d0f from chromium * chore: update patches Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Electron Bot --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-3299d70b7d0f.patch | 337 ++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 patches/chromium/cherry-pick-3299d70b7d0f.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 5b2fe7858beb8..792d7d4f27389 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -110,5 +110,6 @@ add_setter_for_browsermainloop_result_code.patch cherry-pick-8089dbfc616f.patch x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch build_libc_as_static_library.patch +cherry-pick-3299d70b7d0f.patch support_runtime_configurable_key_storage_on_linux_os_crypto.patch make_keychain_service_account_optionally_configurable_at_runtime.patch diff --git a/patches/chromium/cherry-pick-3299d70b7d0f.patch b/patches/chromium/cherry-pick-3299d70b7d0f.patch new file mode 100644 index 0000000000000..c8ceaea76607b --- /dev/null +++ b/patches/chromium/cherry-pick-3299d70b7d0f.patch @@ -0,0 +1,337 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Hongchan Choi +Date: Wed, 21 Apr 2021 01:58:33 +0000 +Subject: Make PushPullFIFO adapt to irregular audio callbacks from the browser + process +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For background and rationale: +go/webaudio-worklet-glitch-aaudio (Googler only) + +The current implementation of PushPullFIFO doesn’t dynamically increase +or decrease the amount frames to keep in the FIFO. So it is vulnerable +to callback bursts from the device thread and it does not recover from +subsequent callbacks when underrun happens. + +The proposed solution is to implement the “earmark” counter in +PushPullFIFO, so it can dynamically adapt when the buffer +underrun happens by increasing the frame amount to keep. This design is +inspired by the buffer optimization technique [1] from the AAudio +reference. + +[1] https://developer.android.com/ndk/guides/audio/aaudio/aaudio#optimizing-performance + +Tested on: +https://googlechromelabs.github.io/web-audio-samples/audio-worklet/ [Android, MacOS] +https://learningsynths.ableton.com/en/playground [Android, MacOS] +https://mfcc64.github.io/0000001/audio-worklet-test.html [Android, MacOS] +https://dev.anthum.com/audio-worklet/chrome-android-debug [Android, MacOS] + +Potentially this patch might be able to fix: +825823,1198540,1188901,1188104 + +Bug: 1090441,1173656,1181434,1188117 +Change-Id: I69e5f100caf2896e268234d41e4e18c3b1648719 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2797664 +Commit-Queue: Hongchan Choi +Reviewed-by: Raymond Toy +Cr-Commit-Position: refs/heads/master@{#874535} + +diff --git a/third_party/blink/renderer/platform/audio/audio_destination.cc b/third_party/blink/renderer/platform/audio/audio_destination.cc +index 126783eb5a6e4f738555d49803dc908c39d101ed..a16975a4ece7a8e8fd8e9870360e815efddcc920 100644 +--- a/third_party/blink/renderer/platform/audio/audio_destination.cc ++++ b/third_party/blink/renderer/platform/audio/audio_destination.cc +@@ -212,10 +212,10 @@ void AudioDestination::Render(const WebVector& destination_data, + for (unsigned i = 0; i < number_of_output_channels_; ++i) + output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); + +- size_t frames_to_render = fifo_->Pull(output_bus_.get(), number_of_frames); +- +- // Use the dual-thread rendering model if the AudioWorklet is activated. + if (worklet_task_runner_) { ++ // Use the dual-thread rendering if the AudioWorklet is activated. ++ size_t frames_to_render = ++ fifo_->PullAndUpdateEarmark(output_bus_.get(), number_of_frames); + PostCrossThreadTask( + *worklet_task_runner_, FROM_HERE, + CrossThreadBindOnce(&AudioDestination::RequestRender, +@@ -223,7 +223,8 @@ void AudioDestination::Render(const WebVector& destination_data, + frames_to_render, delay, delay_timestamp, + prior_frames_skipped)); + } else { +- // Otherwise use the single-thread rendering with AudioDeviceThread. ++ // Otherwise use the single-thread rendering. ++ size_t frames_to_render = fifo_->Pull(output_bus_.get(), number_of_frames); + RequestRender(number_of_frames, frames_to_render, delay, + delay_timestamp, prior_frames_skipped); + } +@@ -316,6 +317,11 @@ void AudioDestination::StartWithWorkletTaskRunner( + + if (device_state_ != DeviceState::kStopped) + return; ++ ++ // The dual-thread rendering kicks off, so updates the earmark frames ++ // accordingly. ++ fifo_->SetEarmarkFrames(callback_buffer_size_); ++ + worklet_task_runner_ = std::move(worklet_task_runner); + web_audio_device_->Start(); + SetDeviceState(DeviceState::kRunning); +diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo.cc +index f89e51ee5544d2c265d81c1247c4a16723d7e153..0dfc6d8707da99f6ceb9a58a78d67bcb1af10ee1 100644 +--- a/third_party/blink/renderer/platform/audio/push_pull_fifo.cc ++++ b/third_party/blink/renderer/platform/audio/push_pull_fifo.cc +@@ -210,6 +210,99 @@ size_t PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) { + : 0; + } + ++size_t PushPullFIFO::PullAndUpdateEarmark(AudioBus* output_bus, ++ size_t frames_requested) { ++ TRACE_EVENT2("webaudio", ++ "PushPullFIFO::PullAndUpdateEarmark", ++ "frames_requested", frames_requested, ++ "pull_count_", pull_count_); ++ ++ CHECK(output_bus); ++ SECURITY_CHECK(frames_requested <= output_bus->length()); ++ ++ MutexLocker locker(lock_); ++ TRACE_EVENT0("webaudio", "PushPullFIFO::PullAndUpdateEarmark (under lock)"); ++ ++ SECURITY_CHECK(frames_requested <= fifo_length_); ++ SECURITY_CHECK(index_read_ < fifo_length_); ++ ++ // The frames available was not enough to fulfill |frames_requested|. Fill ++ // the output buffer with silence and update |earmark_frames_|. ++ if (frames_requested > frames_available_) { ++ const size_t missing_frames = frames_requested - frames_available_; ++ ++ if (underflow_count_++ < kMaxMessagesToLog) { ++ LOG(WARNING) << "PushPullFIFO::PullAndUpdateEarmark" ++ << "underflow while pulling (" ++ << "underflowCount=" << underflow_count_ ++ << ", availableFrames=" << frames_available_ ++ << ", requestedFrames=" << frames_requested ++ << ", fifoLength=" << fifo_length_ << ")"; ++ } ++ ++ TRACE_EVENT2("webaudio", ++ "PushPullFIFO::PullAndUpdateEarmark (underrun)", ++ "missing frames", missing_frames, ++ "underflow_count_", underflow_count_); ++ ++ // We assume that the next |frames_requested| from |AudioOutputDevice| will ++ // be the same. ++ earmark_frames_ += frames_requested; ++ ++ // |earmark_frames_| can't be bigger than the half of the FIFO size. ++ if (earmark_frames_ > fifo_length_ * 0.5) { ++ earmark_frames_ = fifo_length_ * 0.5; ++ } ++ ++ // Note that it silences when underrun happens now, and ship the remaining ++ // frames in subsequent callbacks without silence in between. ++ for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) { ++ float* output_bus_channel = output_bus->Channel(i)->MutableData(); ++ memset(output_bus_channel, 0, ++ frames_requested * sizeof(*output_bus_channel)); ++ } ++ ++ // The producer (WebAudio) needs to prepare the next pull plus what's ++ // missing. ++ return frames_requested + missing_frames; ++ } ++ ++ const size_t remainder = fifo_length_ - index_read_; ++ const size_t frames_to_fill = std::min(frames_available_, frames_requested); ++ ++ for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) { ++ const float* fifo_bus_channel = fifo_bus_->Channel(i)->Data(); ++ float* output_bus_channel = output_bus->Channel(i)->MutableData(); ++ ++ // Fill up the output bus with the available frames first. ++ if (remainder >= frames_to_fill) { ++ // The remainder is big enough for the frames to pull. ++ memcpy(output_bus_channel, fifo_bus_channel + index_read_, ++ frames_to_fill * sizeof(*fifo_bus_channel)); ++ } else { ++ // The frames to pull is bigger than the remainder size. ++ // Wrap around the index. ++ memcpy(output_bus_channel, fifo_bus_channel + index_read_, ++ remainder * sizeof(*fifo_bus_channel)); ++ memcpy(output_bus_channel + remainder, fifo_bus_channel, ++ (frames_to_fill - remainder) * sizeof(*fifo_bus_channel)); ++ } ++ } ++ ++ // Update the read index; wrap it around if necessary. ++ index_read_ = (index_read_ + frames_to_fill) % fifo_length_; ++ ++ // Update the number of frames in FIFO. ++ frames_available_ -= frames_to_fill; ++ DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_); ++ ++ pull_count_++; ++ ++ // Ask the producer to fill the FIFO up to |earmark_frames_|. ++ return earmark_frames_ > frames_available_ ++ ? earmark_frames_ - frames_available_ : 0; ++} ++ + const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() { + MutexLocker locker(lock_); + return {length(), NumberOfChannels(), frames_available_, index_read_, +diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo.h b/third_party/blink/renderer/platform/audio/push_pull_fifo.h +index 8b822b17f66bdc27a61d09bc272631846ea82dcb..ad34a8b0fc39cf566c971323a8dade13a53a1420 100644 +--- a/third_party/blink/renderer/platform/audio/push_pull_fifo.h ++++ b/third_party/blink/renderer/platform/audio/push_pull_fifo.h +@@ -71,18 +71,33 @@ class PLATFORM_EXPORT PushPullFIFO { + // the request from the consumer without causing error, but with a glitch. + size_t Pull(AudioBus* output_bus, size_t frames_requested); + ++ // Pull and update |ear_mark_frames_| to make the dual thread rendering mode ++ // (i.e. AudioWorklet) more smooth. The single thread rendering does not need ++ // this treatment. ++ size_t PullAndUpdateEarmark(AudioBus* output_bus, size_t frames_requested); ++ ++ void SetEarmarkFrames(size_t earmark_frames) { ++ DCHECK(IsMainThread()); ++ MutexLocker locker(lock_); ++ earmark_frames_ = earmark_frames; ++ } ++ + size_t length() const { return fifo_length_; } + unsigned NumberOfChannels() const { + lock_.AssertAcquired(); + return fifo_bus_->NumberOfChannels(); + } + +- // TODO(hongchan): For single thread unit test only. Consider refactoring. + AudioBus* GetFIFOBusForTest() { + MutexLocker locker(lock_); + return fifo_bus_.get(); + } + ++ size_t GetEarmarkFramesForTest() { ++ MutexLocker locker(lock_); ++ return earmark_frames_; ++ } ++ + // For single thread unit test only. Get the current configuration that + // consists of FIFO length, number of channels, read/write index position and + // under/overflow count. +@@ -101,6 +116,11 @@ class PLATFORM_EXPORT PushPullFIFO { + unsigned underflow_count_ = 0; + + Mutex lock_; ++ ++ // To adapt the unstable callback timing. Every buffer underrun from ++ // PullAndUpdateEarmark() will increase this number. ++ size_t earmark_frames_ GUARDED_BY(lock_) = 0; ++ + // The number of frames in the FIFO actually available for pulling. + size_t frames_available_ GUARDED_BY(lock_) = 0; + size_t index_read_ GUARDED_BY(lock_) = 0; +diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc +index 13c6a4d5078e6e1970cd976a3c5829aa7c915460..7abc6af203e7e84e3acda71440c035dfc1b66226 100644 +--- a/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc ++++ b/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc +@@ -364,6 +364,96 @@ INSTANTIATE_TEST_SUITE_P(PushPullFIFOFeatureTest, + PushPullFIFOFeatureTest, + testing::ValuesIn(g_feature_test_params)); + ++ ++struct FIFOEarmarkTestParam { ++ FIFOTestSetup setup; ++ size_t callback_buffer_size; ++ size_t expected_earmark_frames; ++}; ++ ++class PushPullFIFOEarmarkFramesTest ++ : public testing::TestWithParam {}; ++ ++TEST_P(PushPullFIFOEarmarkFramesTest, FeatureTests) { ++ const FIFOTestSetup setup = GetParam().setup; ++ const size_t callback_buffer_size = GetParam().callback_buffer_size; ++ const size_t expected_earmark_frames = GetParam().expected_earmark_frames; ++ ++ // Create a FIFO with a specified configuration. ++ std::unique_ptr fifo = std::make_unique( ++ setup.number_of_channels, setup.fifo_length); ++ fifo->SetEarmarkFrames(callback_buffer_size); ++ ++ scoped_refptr output_bus; ++ ++ // Iterate all the scheduled push/pull actions. ++ size_t frame_counter = 0; ++ for (const auto& action : setup.fifo_actions) { ++ if (strcmp(action.action, "PUSH") == 0) { ++ scoped_refptr input_bus = ++ AudioBus::Create(setup.number_of_channels, action.number_of_frames); ++ frame_counter = FillBusWithLinearRamp(input_bus.get(), frame_counter); ++ fifo->Push(input_bus.get()); ++ LOG(INFO) << "PUSH " << action.number_of_frames ++ << " frames (frameCounter=" << frame_counter << ")"; ++ } else if (strcmp(action.action, "PULL_EARMARK") == 0) { ++ output_bus = ++ AudioBus::Create(setup.number_of_channels, action.number_of_frames); ++ fifo->PullAndUpdateEarmark(output_bus.get(), action.number_of_frames); ++ LOG(INFO) << "PULL_EARMARK " << action.number_of_frames << " frames"; ++ } else { ++ NOTREACHED(); ++ } ++ } ++ ++ // Test the earmark frames. ++ const size_t actual_earmark_frames = fifo->GetEarmarkFramesForTest(); ++ EXPECT_EQ(expected_earmark_frames, actual_earmark_frames); ++} ++ ++FIFOEarmarkTestParam g_earmark_test_params[] = { ++ // When there's no underrun, the earmark is equal to the callback size. ++ {{8192, 2, { ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 256}, ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 256} ++ }}, 256, 256}, ++ // The first underrun increases the earmark by the callback size. ++ {{8192, 2, { ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 384}, // udnerrun; updating earmark and skipping pull. ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 384} // OK ++ }}, 384, 768}, ++ // Simulating "bursty and irregular" callbacks. ++ {{8192, 2, { ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 480}, // OK ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 480}, // underrun; updating earmark and skipping pull. ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 480}, // OK ++ {"PUSH", 128}, ++ {"PULL_EARMARK", 480} // underrun; updating earmark and skipping pull. ++ }}, 480, 1440} ++}; ++ ++INSTANTIATE_TEST_SUITE_P(PushPullFIFOEarmarkFramesTest, ++ PushPullFIFOEarmarkFramesTest, ++ testing::ValuesIn(g_earmark_test_params)); ++ + } // namespace + + } // namespace blink From 1873d8ec81cd1b60a77ca492904a857a723036e1 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Wed, 2 Jun 2021 18:07:41 -0700 Subject: [PATCH 31/79] Bump v13.1.0 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 5923531dee787..03c39bfe83dc4 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.0.1 \ No newline at end of file +13.1.0 \ No newline at end of file diff --git a/package.json b/package.json index 57442663d2eb2..fa8357503ece6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.0.1", + "version": "13.1.0", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index a973c64cfb816..8445ad94c9599 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,0,1,0 - PRODUCTVERSION 13,0,1,0 + FILEVERSION 13,1,0,0 + PRODUCTVERSION 13,1,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "13.0.1" + VALUE "FileVersion", "13.1.0" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "13.0.1" + VALUE "ProductVersion", "13.1.0" VALUE "SquirrelAwareVersion", "1" END END From d84b7fc4d488227b80de65943b8ae19e3183e3c3 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Thu, 3 Jun 2021 07:43:50 +0200 Subject: [PATCH 32/79] test: add spec for --require filtering in NODE_OPTIONS (#29507) --- shell/common/node_bindings.cc | 4 ++++ spec-main/node-spec.ts | 23 +++++++++++++++++++++++ spec/fixtures/module/fail.js | 1 + 3 files changed, 28 insertions(+) create mode 100644 spec/fixtures/module/fail.js diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index a6fb17e343c0e..8d3b263078946 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -131,6 +131,10 @@ void stop_and_close_uv_loop(uv_loop_t* loop) { bool g_is_initialized = false; bool IsPackagedApp() { + auto env = base::Environment::Create(); + if (env->HasVar("ELECTRON_FORCE_IS_PACKAGED")) + return true; + base::FilePath exe_path; base::PathService::Get(base::FILE_EXE, &exe_path); base::FilePath::StringType base_name = diff --git a/spec-main/node-spec.ts b/spec-main/node-spec.ts index 063d71287aafc..c9fd2cfbd7e3e 100644 --- a/spec-main/node-spec.ts +++ b/spec-main/node-spec.ts @@ -132,6 +132,29 @@ describe('node feature', () => { child.stderr.on('data', listener); child.stdout.on('data', listener); }); + + it('does allow --require in non-packaged apps', async () => { + const appPath = path.join(fixtures, 'module', 'noop.js'); + const env = Object.assign({}, process.env, { + NODE_OPTIONS: `--require=${path.join(fixtures, 'module', 'fail.js')}` + }); + // App should exit with code 1. + const child = childProcess.spawn(process.execPath, [appPath], { env }); + const [code] = await emittedOnce(child, 'exit'); + expect(code).to.equal(1); + }); + + it('does not allow --require in packaged apps', async () => { + const appPath = path.join(fixtures, 'module', 'noop.js'); + const env = Object.assign({}, process.env, { + ELECTRON_FORCE_IS_PACKAGED: 'true', + NODE_OPTIONS: `--require=${path.join(fixtures, 'module', 'fail.js')}` + }); + // App should exit with code 0. + const child = childProcess.spawn(process.execPath, [appPath], { env }); + const [code] = await emittedOnce(child, 'exit'); + expect(code).to.equal(0); + }); }); ifdescribe(features.isRunAsNodeEnabled())('Node.js cli flags', () => { diff --git a/spec/fixtures/module/fail.js b/spec/fixtures/module/fail.js new file mode 100644 index 0000000000000..6cee2e1e79a14 --- /dev/null +++ b/spec/fixtures/module/fail.js @@ -0,0 +1 @@ +process.exit(1); From 5860b701896c1ec393974594196a14c0812cfd34 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Jun 2021 16:19:49 +0900 Subject: [PATCH 33/79] docs: link to IncomingMessage (#29516) Co-authored-by: David Sanders --- docs/api/client-request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/client-request.md b/docs/api/client-request.md index 865f733d78474..d2c018b7e34cb 100644 --- a/docs/api/client-request.md +++ b/docs/api/client-request.md @@ -71,7 +71,7 @@ const request = net.request({ Returns: -* `response` IncomingMessage - An object representing the HTTP response message. +* `response` [IncomingMessage](incoming-message.md) - An object representing the HTTP response message. #### Event: 'login' From ff2a0a4bd314b34b9cc54609a4bc6047ec9c5d29 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 4 Jun 2021 13:19:31 +0900 Subject: [PATCH 34/79] fix: change ASAR archive cache to per-process to fix leak (#29535) * fix: change ASAR archive cache to per-process to fix leak (#29292) * chore: address code review comments * chore: tighten up thread-safety * chore: better address code review comments * chore: more code review changes Co-authored-by: David Sanders --- shell/browser/electron_browser_main_parts.cc | 5 +-- shell/common/asar/archive.cc | 21 +++++++--- shell/common/asar/archive.h | 17 ++++---- shell/common/asar/asar_util.cc | 42 ++++++++++++++------ shell/common/asar/asar_util.h | 2 +- shell/renderer/electron_renderer_client.cc | 5 +-- shell/renderer/web_worker_observer.cc | 2 - 7 files changed, 58 insertions(+), 36 deletions(-) diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index bd861b3973374..a62f80161ae99 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -44,7 +44,6 @@ #include "shell/browser/ui/devtools_manager_delegate.h" #include "shell/common/api/electron_bindings.h" #include "shell/common/application_info.h" -#include "shell/common/asar/asar_util.h" #include "shell/common/electron_paths.h" #include "shell/common/gin_helper/trackable_object.h" #include "shell/common/node_bindings.h" @@ -211,9 +210,7 @@ ElectronBrowserMainParts::ElectronBrowserMainParts( self_ = this; } -ElectronBrowserMainParts::~ElectronBrowserMainParts() { - asar::ClearArchives(); -} +ElectronBrowserMainParts::~ElectronBrowserMainParts() = default; // static ElectronBrowserMainParts* ElectronBrowserMainParts::Get() { diff --git a/shell/common/asar/archive.cc b/shell/common/asar/archive.cc index f6c6b5e3ebbb8..739931a28222d 100644 --- a/shell/common/asar/archive.cc +++ b/shell/common/asar/archive.cc @@ -8,6 +8,7 @@ #include #include +#include "base/check.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" @@ -118,7 +119,7 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, } // namespace Archive::Archive(const base::FilePath& path) - : path_(path), file_(base::File::FILE_OK) { + : initialized_(false), path_(path), file_(base::File::FILE_OK) { base::ThreadRestrictions::ScopedAllowIO allow_io; file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ); #if defined(OS_WIN) @@ -141,6 +142,10 @@ Archive::~Archive() { } bool Archive::Init() { + // Should only be initialized once + CHECK(!initialized_); + initialized_ = true; + if (!file_.IsValid()) { if (file_.error_details() != base::File::FILE_ERROR_NOT_FOUND) { LOG(WARNING) << "Opening " << path_.value() << ": " @@ -198,7 +203,7 @@ bool Archive::Init() { return true; } -bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) { +bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) const { if (!header_) return false; @@ -213,7 +218,7 @@ bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) { return FillFileInfoWithNode(info, header_size_, node); } -bool Archive::Stat(const base::FilePath& path, Stats* stats) { +bool Archive::Stat(const base::FilePath& path, Stats* stats) const { if (!header_) return false; @@ -237,7 +242,7 @@ bool Archive::Stat(const base::FilePath& path, Stats* stats) { } bool Archive::Readdir(const base::FilePath& path, - std::vector* files) { + std::vector* files) const { if (!header_) return false; @@ -257,7 +262,8 @@ bool Archive::Readdir(const base::FilePath& path, return true; } -bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) { +bool Archive::Realpath(const base::FilePath& path, + base::FilePath* realpath) const { if (!header_) return false; @@ -276,6 +282,11 @@ bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) { } bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { + if (!header_) + return false; + + base::AutoLock auto_lock(external_files_lock_); + auto it = external_files_.find(path.value()); if (it != external_files_.end()) { *out = it->second->path(); diff --git a/shell/common/asar/archive.h b/shell/common/asar/archive.h index d97cb03cc781a..a25ae4d4de300 100644 --- a/shell/common/asar/archive.h +++ b/shell/common/asar/archive.h @@ -11,6 +11,7 @@ #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/synchronization/lock.h" namespace base { class DictionaryValue; @@ -21,7 +22,7 @@ namespace asar { class ScopedTemporaryFile; // This class represents an asar package, and provides methods to read -// information from it. +// information from it. It is thread-safe after |Init| has been called. class Archive { public: struct FileInfo { @@ -46,16 +47,17 @@ class Archive { bool Init(); // Get the info of a file. - bool GetFileInfo(const base::FilePath& path, FileInfo* info); + bool GetFileInfo(const base::FilePath& path, FileInfo* info) const; // Fs.stat(path). - bool Stat(const base::FilePath& path, Stats* stats); + bool Stat(const base::FilePath& path, Stats* stats) const; // Fs.readdir(path). - bool Readdir(const base::FilePath& path, std::vector* files); + bool Readdir(const base::FilePath& path, + std::vector* files) const; // Fs.realpath(path). - bool Realpath(const base::FilePath& path, base::FilePath* realpath); + bool Realpath(const base::FilePath& path, base::FilePath* realpath) const; // Copy the file into a temporary file, and return the new path. // For unpacked file, this method will return its real path. @@ -65,16 +67,17 @@ class Archive { int GetFD() const; base::FilePath path() const { return path_; } - base::DictionaryValue* header() const { return header_.get(); } private: - base::FilePath path_; + bool initialized_; + const base::FilePath path_; base::File file_; int fd_ = -1; uint32_t header_size_ = 0; std::unique_ptr header_; // Cached external temporary files. + base::Lock external_files_lock_; std::unordered_map> external_files_; diff --git a/shell/common/asar/asar_util.cc b/shell/common/asar/asar_util.cc index 65ddbf2843dc6..4800fb13421ab 100644 --- a/shell/common/asar/asar_util.cc +++ b/shell/common/asar/asar_util.cc @@ -6,11 +6,14 @@ #include #include +#include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/stl_util.h" +#include "base/synchronization/lock.h" #include "base/threading/thread_local.h" #include "base/threading/thread_restrictions.h" #include "shell/common/asar/archive.h" @@ -19,30 +22,41 @@ namespace asar { namespace { -// The global instance of ArchiveMap, will be destroyed on exit. typedef std::map> ArchiveMap; -base::LazyInstance>::Leaky - g_archive_map_tls = LAZY_INSTANCE_INITIALIZER; const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar"); -std::map g_is_directory_cache; - bool IsDirectoryCached(const base::FilePath& path) { - auto it = g_is_directory_cache.find(path); - if (it != g_is_directory_cache.end()) { + static base::NoDestructor> + s_is_directory_cache; + static base::NoDestructor lock; + + base::AutoLock auto_lock(*lock); + auto& is_directory_cache = *s_is_directory_cache; + + auto it = is_directory_cache.find(path); + if (it != is_directory_cache.end()) { return it->second; } base::ThreadRestrictions::ScopedAllowIO allow_io; - return g_is_directory_cache[path] = base::DirectoryExists(path); + return is_directory_cache[path] = base::DirectoryExists(path); } } // namespace +ArchiveMap& GetArchiveCache() { + static base::NoDestructor s_archive_map; + return *s_archive_map; +} + +base::Lock& GetArchiveCacheLock() { + static base::NoDestructor lock; + return *lock; +} + std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { - if (!g_archive_map_tls.Pointer()->Get()) - g_archive_map_tls.Pointer()->Set(new ArchiveMap); - ArchiveMap& map = *g_archive_map_tls.Pointer()->Get(); + base::AutoLock auto_lock(GetArchiveCacheLock()); + ArchiveMap& map = GetArchiveCache(); // if we have it, return it const auto lower = map.lower_bound(path); @@ -61,8 +75,10 @@ std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { } void ClearArchives() { - if (g_archive_map_tls.Pointer()->Get()) - delete g_archive_map_tls.Pointer()->Get(); + base::AutoLock auto_lock(GetArchiveCacheLock()); + ArchiveMap& map = GetArchiveCache(); + + map.clear(); } bool GetAsarArchivePath(const base::FilePath& full_path, diff --git a/shell/common/asar/asar_util.h b/shell/common/asar/asar_util.h index c48336eb2c61f..efeaf59a16bb3 100644 --- a/shell/common/asar/asar_util.h +++ b/shell/common/asar/asar_util.h @@ -16,7 +16,7 @@ namespace asar { class Archive; -// Gets or creates a new Archive from the path. +// Gets or creates and caches a new Archive from the path. std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path); // Destroy cached Archive objects. diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index b04e1c4fcb173..ce52466f4661b 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -11,7 +11,6 @@ #include "content/public/renderer/render_frame.h" #include "electron/buildflags/buildflags.h" #include "shell/common/api/electron_bindings.h" -#include "shell/common/asar/asar_util.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_bindings.h" @@ -40,9 +39,7 @@ ElectronRendererClient::ElectronRendererClient() NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)), electron_bindings_(new ElectronBindings(node_bindings_->uv_loop())) {} -ElectronRendererClient::~ElectronRendererClient() { - asar::ClearArchives(); -} +ElectronRendererClient::~ElectronRendererClient() = default; void ElectronRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index b4b5aa30b066e..705734347a1a0 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -7,7 +7,6 @@ #include "base/lazy_instance.h" #include "base/threading/thread_local.h" #include "shell/common/api/electron_bindings.h" -#include "shell/common/asar/asar_util.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_bindings.h" #include "shell/common/node_includes.h" @@ -39,7 +38,6 @@ WebWorkerObserver::~WebWorkerObserver() { lazy_tls.Pointer()->Set(nullptr); node::FreeEnvironment(node_bindings_->uv_env()); node::FreeIsolateData(node_bindings_->isolate_data()); - asar::ClearArchives(); } void WebWorkerObserver::WorkerScriptReadyForEvaluation( From 0753e4911c173c5afbfb2d25c7a85e0bd92b2d46 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Fri, 4 Jun 2021 10:27:55 -0700 Subject: [PATCH 35/79] fix: disable CET as v8 deoptimization is incompatible with it (#29546) --- build/args/all.gn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/args/all.gn b/build/args/all.gn index e18315dd14bb6..86200887efe7c 100644 --- a/build/args/all.gn +++ b/build/args/all.gn @@ -31,3 +31,5 @@ is_cfi = false # Make application name configurable at runtime for cookie crypto allow_runtime_configurable_key_storage = true + +enable_cet_shadow_stack = false From 0dcc6607947b5fea2df467bb2617a1b5b0a5fa1b Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Fri, 4 Jun 2021 10:28:20 -0700 Subject: [PATCH 36/79] Revert "fix: change ASAR archive cache to per-process to fix leak (#29535)" This reverts commit ff2a0a4bd314b34b9cc54609a4bc6047ec9c5d29. --- shell/browser/electron_browser_main_parts.cc | 5 ++- shell/common/asar/archive.cc | 21 +++------- shell/common/asar/archive.h | 17 ++++---- shell/common/asar/asar_util.cc | 42 ++++++-------------- shell/common/asar/asar_util.h | 2 +- shell/renderer/electron_renderer_client.cc | 5 ++- shell/renderer/web_worker_observer.cc | 2 + 7 files changed, 36 insertions(+), 58 deletions(-) diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index a62f80161ae99..bd861b3973374 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -44,6 +44,7 @@ #include "shell/browser/ui/devtools_manager_delegate.h" #include "shell/common/api/electron_bindings.h" #include "shell/common/application_info.h" +#include "shell/common/asar/asar_util.h" #include "shell/common/electron_paths.h" #include "shell/common/gin_helper/trackable_object.h" #include "shell/common/node_bindings.h" @@ -210,7 +211,9 @@ ElectronBrowserMainParts::ElectronBrowserMainParts( self_ = this; } -ElectronBrowserMainParts::~ElectronBrowserMainParts() = default; +ElectronBrowserMainParts::~ElectronBrowserMainParts() { + asar::ClearArchives(); +} // static ElectronBrowserMainParts* ElectronBrowserMainParts::Get() { diff --git a/shell/common/asar/archive.cc b/shell/common/asar/archive.cc index 739931a28222d..f6c6b5e3ebbb8 100644 --- a/shell/common/asar/archive.cc +++ b/shell/common/asar/archive.cc @@ -8,7 +8,6 @@ #include #include -#include "base/check.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" @@ -119,7 +118,7 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, } // namespace Archive::Archive(const base::FilePath& path) - : initialized_(false), path_(path), file_(base::File::FILE_OK) { + : path_(path), file_(base::File::FILE_OK) { base::ThreadRestrictions::ScopedAllowIO allow_io; file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ); #if defined(OS_WIN) @@ -142,10 +141,6 @@ Archive::~Archive() { } bool Archive::Init() { - // Should only be initialized once - CHECK(!initialized_); - initialized_ = true; - if (!file_.IsValid()) { if (file_.error_details() != base::File::FILE_ERROR_NOT_FOUND) { LOG(WARNING) << "Opening " << path_.value() << ": " @@ -203,7 +198,7 @@ bool Archive::Init() { return true; } -bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) const { +bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) { if (!header_) return false; @@ -218,7 +213,7 @@ bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) const { return FillFileInfoWithNode(info, header_size_, node); } -bool Archive::Stat(const base::FilePath& path, Stats* stats) const { +bool Archive::Stat(const base::FilePath& path, Stats* stats) { if (!header_) return false; @@ -242,7 +237,7 @@ bool Archive::Stat(const base::FilePath& path, Stats* stats) const { } bool Archive::Readdir(const base::FilePath& path, - std::vector* files) const { + std::vector* files) { if (!header_) return false; @@ -262,8 +257,7 @@ bool Archive::Readdir(const base::FilePath& path, return true; } -bool Archive::Realpath(const base::FilePath& path, - base::FilePath* realpath) const { +bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) { if (!header_) return false; @@ -282,11 +276,6 @@ bool Archive::Realpath(const base::FilePath& path, } bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { - if (!header_) - return false; - - base::AutoLock auto_lock(external_files_lock_); - auto it = external_files_.find(path.value()); if (it != external_files_.end()) { *out = it->second->path(); diff --git a/shell/common/asar/archive.h b/shell/common/asar/archive.h index a25ae4d4de300..d97cb03cc781a 100644 --- a/shell/common/asar/archive.h +++ b/shell/common/asar/archive.h @@ -11,7 +11,6 @@ #include "base/files/file.h" #include "base/files/file_path.h" -#include "base/synchronization/lock.h" namespace base { class DictionaryValue; @@ -22,7 +21,7 @@ namespace asar { class ScopedTemporaryFile; // This class represents an asar package, and provides methods to read -// information from it. It is thread-safe after |Init| has been called. +// information from it. class Archive { public: struct FileInfo { @@ -47,17 +46,16 @@ class Archive { bool Init(); // Get the info of a file. - bool GetFileInfo(const base::FilePath& path, FileInfo* info) const; + bool GetFileInfo(const base::FilePath& path, FileInfo* info); // Fs.stat(path). - bool Stat(const base::FilePath& path, Stats* stats) const; + bool Stat(const base::FilePath& path, Stats* stats); // Fs.readdir(path). - bool Readdir(const base::FilePath& path, - std::vector* files) const; + bool Readdir(const base::FilePath& path, std::vector* files); // Fs.realpath(path). - bool Realpath(const base::FilePath& path, base::FilePath* realpath) const; + bool Realpath(const base::FilePath& path, base::FilePath* realpath); // Copy the file into a temporary file, and return the new path. // For unpacked file, this method will return its real path. @@ -67,17 +65,16 @@ class Archive { int GetFD() const; base::FilePath path() const { return path_; } + base::DictionaryValue* header() const { return header_.get(); } private: - bool initialized_; - const base::FilePath path_; + base::FilePath path_; base::File file_; int fd_ = -1; uint32_t header_size_ = 0; std::unique_ptr header_; // Cached external temporary files. - base::Lock external_files_lock_; std::unordered_map> external_files_; diff --git a/shell/common/asar/asar_util.cc b/shell/common/asar/asar_util.cc index 4800fb13421ab..65ddbf2843dc6 100644 --- a/shell/common/asar/asar_util.cc +++ b/shell/common/asar/asar_util.cc @@ -6,14 +6,11 @@ #include #include -#include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/lazy_instance.h" -#include "base/no_destructor.h" #include "base/stl_util.h" -#include "base/synchronization/lock.h" #include "base/threading/thread_local.h" #include "base/threading/thread_restrictions.h" #include "shell/common/asar/archive.h" @@ -22,41 +19,30 @@ namespace asar { namespace { +// The global instance of ArchiveMap, will be destroyed on exit. typedef std::map> ArchiveMap; +base::LazyInstance>::Leaky + g_archive_map_tls = LAZY_INSTANCE_INITIALIZER; const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar"); -bool IsDirectoryCached(const base::FilePath& path) { - static base::NoDestructor> - s_is_directory_cache; - static base::NoDestructor lock; - - base::AutoLock auto_lock(*lock); - auto& is_directory_cache = *s_is_directory_cache; +std::map g_is_directory_cache; - auto it = is_directory_cache.find(path); - if (it != is_directory_cache.end()) { +bool IsDirectoryCached(const base::FilePath& path) { + auto it = g_is_directory_cache.find(path); + if (it != g_is_directory_cache.end()) { return it->second; } base::ThreadRestrictions::ScopedAllowIO allow_io; - return is_directory_cache[path] = base::DirectoryExists(path); + return g_is_directory_cache[path] = base::DirectoryExists(path); } } // namespace -ArchiveMap& GetArchiveCache() { - static base::NoDestructor s_archive_map; - return *s_archive_map; -} - -base::Lock& GetArchiveCacheLock() { - static base::NoDestructor lock; - return *lock; -} - std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { - base::AutoLock auto_lock(GetArchiveCacheLock()); - ArchiveMap& map = GetArchiveCache(); + if (!g_archive_map_tls.Pointer()->Get()) + g_archive_map_tls.Pointer()->Set(new ArchiveMap); + ArchiveMap& map = *g_archive_map_tls.Pointer()->Get(); // if we have it, return it const auto lower = map.lower_bound(path); @@ -75,10 +61,8 @@ std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { } void ClearArchives() { - base::AutoLock auto_lock(GetArchiveCacheLock()); - ArchiveMap& map = GetArchiveCache(); - - map.clear(); + if (g_archive_map_tls.Pointer()->Get()) + delete g_archive_map_tls.Pointer()->Get(); } bool GetAsarArchivePath(const base::FilePath& full_path, diff --git a/shell/common/asar/asar_util.h b/shell/common/asar/asar_util.h index efeaf59a16bb3..c48336eb2c61f 100644 --- a/shell/common/asar/asar_util.h +++ b/shell/common/asar/asar_util.h @@ -16,7 +16,7 @@ namespace asar { class Archive; -// Gets or creates and caches a new Archive from the path. +// Gets or creates a new Archive from the path. std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path); // Destroy cached Archive objects. diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index ce52466f4661b..b04e1c4fcb173 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -11,6 +11,7 @@ #include "content/public/renderer/render_frame.h" #include "electron/buildflags/buildflags.h" #include "shell/common/api/electron_bindings.h" +#include "shell/common/asar/asar_util.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_bindings.h" @@ -39,7 +40,9 @@ ElectronRendererClient::ElectronRendererClient() NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)), electron_bindings_(new ElectronBindings(node_bindings_->uv_loop())) {} -ElectronRendererClient::~ElectronRendererClient() = default; +ElectronRendererClient::~ElectronRendererClient() { + asar::ClearArchives(); +} void ElectronRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index 705734347a1a0..b4b5aa30b066e 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -7,6 +7,7 @@ #include "base/lazy_instance.h" #include "base/threading/thread_local.h" #include "shell/common/api/electron_bindings.h" +#include "shell/common/asar/asar_util.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_bindings.h" #include "shell/common/node_includes.h" @@ -38,6 +39,7 @@ WebWorkerObserver::~WebWorkerObserver() { lazy_tls.Pointer()->Set(nullptr); node::FreeEnvironment(node_bindings_->uv_env()); node::FreeIsolateData(node_bindings_->isolate_data()); + asar::ClearArchives(); } void WebWorkerObserver::WorkerScriptReadyForEvaluation( From b5c79632efefe299a8f0203de4c7eee714f64f42 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Fri, 4 Jun 2021 10:29:05 -0700 Subject: [PATCH 37/79] Bump v13.1.1 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 03c39bfe83dc4..2e7dfda226a97 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.1.0 \ No newline at end of file +13.1.1 \ No newline at end of file diff --git a/package.json b/package.json index fa8357503ece6..8bac5d8b31ef8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.1.0", + "version": "13.1.1", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 8445ad94c9599..47b852671b2a3 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,1,0,0 - PRODUCTVERSION 13,1,0,0 + FILEVERSION 13,1,1,0 + PRODUCTVERSION 13,1,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "13.1.0" + VALUE "FileVersion", "13.1.1" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "13.1.0" + VALUE "ProductVersion", "13.1.1" VALUE "SquirrelAwareVersion", "1" END END From 314e627df1a26a3e9faf3bbf21513cfbd6fc0bbf Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Fri, 4 Jun 2021 10:29:42 -0700 Subject: [PATCH 38/79] Revert "Revert "fix: change ASAR archive cache to per-process to fix leak (#29535)"" This reverts commit 0dcc6607947b5fea2df467bb2617a1b5b0a5fa1b. --- shell/browser/electron_browser_main_parts.cc | 5 +-- shell/common/asar/archive.cc | 21 +++++++--- shell/common/asar/archive.h | 17 ++++---- shell/common/asar/asar_util.cc | 42 ++++++++++++++------ shell/common/asar/asar_util.h | 2 +- shell/renderer/electron_renderer_client.cc | 5 +-- shell/renderer/web_worker_observer.cc | 2 - 7 files changed, 58 insertions(+), 36 deletions(-) diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index bd861b3973374..a62f80161ae99 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -44,7 +44,6 @@ #include "shell/browser/ui/devtools_manager_delegate.h" #include "shell/common/api/electron_bindings.h" #include "shell/common/application_info.h" -#include "shell/common/asar/asar_util.h" #include "shell/common/electron_paths.h" #include "shell/common/gin_helper/trackable_object.h" #include "shell/common/node_bindings.h" @@ -211,9 +210,7 @@ ElectronBrowserMainParts::ElectronBrowserMainParts( self_ = this; } -ElectronBrowserMainParts::~ElectronBrowserMainParts() { - asar::ClearArchives(); -} +ElectronBrowserMainParts::~ElectronBrowserMainParts() = default; // static ElectronBrowserMainParts* ElectronBrowserMainParts::Get() { diff --git a/shell/common/asar/archive.cc b/shell/common/asar/archive.cc index f6c6b5e3ebbb8..739931a28222d 100644 --- a/shell/common/asar/archive.cc +++ b/shell/common/asar/archive.cc @@ -8,6 +8,7 @@ #include #include +#include "base/check.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" @@ -118,7 +119,7 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, } // namespace Archive::Archive(const base::FilePath& path) - : path_(path), file_(base::File::FILE_OK) { + : initialized_(false), path_(path), file_(base::File::FILE_OK) { base::ThreadRestrictions::ScopedAllowIO allow_io; file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ); #if defined(OS_WIN) @@ -141,6 +142,10 @@ Archive::~Archive() { } bool Archive::Init() { + // Should only be initialized once + CHECK(!initialized_); + initialized_ = true; + if (!file_.IsValid()) { if (file_.error_details() != base::File::FILE_ERROR_NOT_FOUND) { LOG(WARNING) << "Opening " << path_.value() << ": " @@ -198,7 +203,7 @@ bool Archive::Init() { return true; } -bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) { +bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) const { if (!header_) return false; @@ -213,7 +218,7 @@ bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) { return FillFileInfoWithNode(info, header_size_, node); } -bool Archive::Stat(const base::FilePath& path, Stats* stats) { +bool Archive::Stat(const base::FilePath& path, Stats* stats) const { if (!header_) return false; @@ -237,7 +242,7 @@ bool Archive::Stat(const base::FilePath& path, Stats* stats) { } bool Archive::Readdir(const base::FilePath& path, - std::vector* files) { + std::vector* files) const { if (!header_) return false; @@ -257,7 +262,8 @@ bool Archive::Readdir(const base::FilePath& path, return true; } -bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) { +bool Archive::Realpath(const base::FilePath& path, + base::FilePath* realpath) const { if (!header_) return false; @@ -276,6 +282,11 @@ bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) { } bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { + if (!header_) + return false; + + base::AutoLock auto_lock(external_files_lock_); + auto it = external_files_.find(path.value()); if (it != external_files_.end()) { *out = it->second->path(); diff --git a/shell/common/asar/archive.h b/shell/common/asar/archive.h index d97cb03cc781a..a25ae4d4de300 100644 --- a/shell/common/asar/archive.h +++ b/shell/common/asar/archive.h @@ -11,6 +11,7 @@ #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/synchronization/lock.h" namespace base { class DictionaryValue; @@ -21,7 +22,7 @@ namespace asar { class ScopedTemporaryFile; // This class represents an asar package, and provides methods to read -// information from it. +// information from it. It is thread-safe after |Init| has been called. class Archive { public: struct FileInfo { @@ -46,16 +47,17 @@ class Archive { bool Init(); // Get the info of a file. - bool GetFileInfo(const base::FilePath& path, FileInfo* info); + bool GetFileInfo(const base::FilePath& path, FileInfo* info) const; // Fs.stat(path). - bool Stat(const base::FilePath& path, Stats* stats); + bool Stat(const base::FilePath& path, Stats* stats) const; // Fs.readdir(path). - bool Readdir(const base::FilePath& path, std::vector* files); + bool Readdir(const base::FilePath& path, + std::vector* files) const; // Fs.realpath(path). - bool Realpath(const base::FilePath& path, base::FilePath* realpath); + bool Realpath(const base::FilePath& path, base::FilePath* realpath) const; // Copy the file into a temporary file, and return the new path. // For unpacked file, this method will return its real path. @@ -65,16 +67,17 @@ class Archive { int GetFD() const; base::FilePath path() const { return path_; } - base::DictionaryValue* header() const { return header_.get(); } private: - base::FilePath path_; + bool initialized_; + const base::FilePath path_; base::File file_; int fd_ = -1; uint32_t header_size_ = 0; std::unique_ptr header_; // Cached external temporary files. + base::Lock external_files_lock_; std::unordered_map> external_files_; diff --git a/shell/common/asar/asar_util.cc b/shell/common/asar/asar_util.cc index 65ddbf2843dc6..4800fb13421ab 100644 --- a/shell/common/asar/asar_util.cc +++ b/shell/common/asar/asar_util.cc @@ -6,11 +6,14 @@ #include #include +#include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/stl_util.h" +#include "base/synchronization/lock.h" #include "base/threading/thread_local.h" #include "base/threading/thread_restrictions.h" #include "shell/common/asar/archive.h" @@ -19,30 +22,41 @@ namespace asar { namespace { -// The global instance of ArchiveMap, will be destroyed on exit. typedef std::map> ArchiveMap; -base::LazyInstance>::Leaky - g_archive_map_tls = LAZY_INSTANCE_INITIALIZER; const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar"); -std::map g_is_directory_cache; - bool IsDirectoryCached(const base::FilePath& path) { - auto it = g_is_directory_cache.find(path); - if (it != g_is_directory_cache.end()) { + static base::NoDestructor> + s_is_directory_cache; + static base::NoDestructor lock; + + base::AutoLock auto_lock(*lock); + auto& is_directory_cache = *s_is_directory_cache; + + auto it = is_directory_cache.find(path); + if (it != is_directory_cache.end()) { return it->second; } base::ThreadRestrictions::ScopedAllowIO allow_io; - return g_is_directory_cache[path] = base::DirectoryExists(path); + return is_directory_cache[path] = base::DirectoryExists(path); } } // namespace +ArchiveMap& GetArchiveCache() { + static base::NoDestructor s_archive_map; + return *s_archive_map; +} + +base::Lock& GetArchiveCacheLock() { + static base::NoDestructor lock; + return *lock; +} + std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { - if (!g_archive_map_tls.Pointer()->Get()) - g_archive_map_tls.Pointer()->Set(new ArchiveMap); - ArchiveMap& map = *g_archive_map_tls.Pointer()->Get(); + base::AutoLock auto_lock(GetArchiveCacheLock()); + ArchiveMap& map = GetArchiveCache(); // if we have it, return it const auto lower = map.lower_bound(path); @@ -61,8 +75,10 @@ std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { } void ClearArchives() { - if (g_archive_map_tls.Pointer()->Get()) - delete g_archive_map_tls.Pointer()->Get(); + base::AutoLock auto_lock(GetArchiveCacheLock()); + ArchiveMap& map = GetArchiveCache(); + + map.clear(); } bool GetAsarArchivePath(const base::FilePath& full_path, diff --git a/shell/common/asar/asar_util.h b/shell/common/asar/asar_util.h index c48336eb2c61f..efeaf59a16bb3 100644 --- a/shell/common/asar/asar_util.h +++ b/shell/common/asar/asar_util.h @@ -16,7 +16,7 @@ namespace asar { class Archive; -// Gets or creates a new Archive from the path. +// Gets or creates and caches a new Archive from the path. std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path); // Destroy cached Archive objects. diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index b04e1c4fcb173..ce52466f4661b 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -11,7 +11,6 @@ #include "content/public/renderer/render_frame.h" #include "electron/buildflags/buildflags.h" #include "shell/common/api/electron_bindings.h" -#include "shell/common/asar/asar_util.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_bindings.h" @@ -40,9 +39,7 @@ ElectronRendererClient::ElectronRendererClient() NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)), electron_bindings_(new ElectronBindings(node_bindings_->uv_loop())) {} -ElectronRendererClient::~ElectronRendererClient() { - asar::ClearArchives(); -} +ElectronRendererClient::~ElectronRendererClient() = default; void ElectronRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index b4b5aa30b066e..705734347a1a0 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -7,7 +7,6 @@ #include "base/lazy_instance.h" #include "base/threading/thread_local.h" #include "shell/common/api/electron_bindings.h" -#include "shell/common/asar/asar_util.h" #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_bindings.h" #include "shell/common/node_includes.h" @@ -39,7 +38,6 @@ WebWorkerObserver::~WebWorkerObserver() { lazy_tls.Pointer()->Set(nullptr); node::FreeEnvironment(node_bindings_->uv_env()); node::FreeIsolateData(node_bindings_->isolate_data()); - asar::ClearArchives(); } void WebWorkerObserver::WorkerScriptReadyForEvaluation( From f0a9e5674178fbc8185bfcb4217ea2b9edf3cb25 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:17:15 +0900 Subject: [PATCH 39/79] chore: return early on promise rejection (#29538) Co-authored-by: David Sanders --- shell/browser/api/electron_api_cookies.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/browser/api/electron_api_cookies.cc b/shell/browser/api/electron_api_cookies.cc index 7b4654a9e8214..e9706eff4d035 100644 --- a/shell/browser/api/electron_api_cookies.cc +++ b/shell/browser/api/electron_api_cookies.cc @@ -293,6 +293,7 @@ v8::Local Cookies::Set(v8::Isolate* isolate, const std::string* url_string = details.FindStringKey("url"); if (!url_string) { promise.RejectWithErrorMessage("Missing required option 'url'"); + return handle; } const std::string* name = details.FindStringKey("name"); const std::string* value = details.FindStringKey("value"); From 2d18829b43ea2498ded22fdc62cc49aa03fae3ce Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:23:51 +0900 Subject: [PATCH 40/79] build: Improve squirrel.mac BUILD.gn xcrun_action error (#29513) Right now, if executing `xcrun` fails, then the error message prints the second argument to the `xcrun.py` script, which is the first argument to the tool that `xcrun` is executing, making the whole error message quite confusing. Consider the following error: ``` python ../../third_party/squirrel.mac/build/xcrun.py dtrace -h -s /private/tmp/20210531211008-def376dc/src/third_party/squirrel.mac/vendor/ReactiveObjC/ReactiveObjC/RACSignalProvider.d -o /private/tmp/20210531211008-def376dc/src/out/release/gen/third_party/squirrel.mac/dtrace/RACSignalProvider.h xcrun script '-h' failed with code '71': xcrun: error: can't exec '/tmp/20210531211008-def376dc/dtrace' (errno=Permission denied) ``` The command that `xcrun` is executing is `dtrace`, but the error just mentions the `-h` flag. Notes: none Signed-off-by: Juan Cruz Viotti Co-authored-by: Juan Cruz Viotti --- patches/squirrel.mac/build_add_gn_config.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/squirrel.mac/build_add_gn_config.patch b/patches/squirrel.mac/build_add_gn_config.patch index 486d2fa655612..67cb585f8ba0c 100644 --- a/patches/squirrel.mac/build_add_gn_config.patch +++ b/patches/squirrel.mac/build_add_gn_config.patch @@ -508,7 +508,7 @@ index 0000000000000000000000000000000000000000..bdfaf95f3eca65b3e0831db1b66f651d +} diff --git a/build/xcrun.py b/build/xcrun.py new file mode 100644 -index 0000000000000000000000000000000000000000..20d0cdb51cc933f56b7a7193c195457e82500870 +index 0000000000000000000000000000000000000000..18ac587f80441106405d00fafd9ee1f25b147772 --- /dev/null +++ b/build/xcrun.py @@ -0,0 +1,14 @@ @@ -524,7 +524,7 @@ index 0000000000000000000000000000000000000000..20d0cdb51cc933f56b7a7193c195457e +try: + subprocess.check_output(args, stderr=subprocess.STDOUT) +except subprocess.CalledProcessError as e: -+ print("xcrun script '" + sys.argv[2] + "' failed with code '" + str(e.returncode) + "':\n" + e.output) ++ print("xcrun script '" + ' '.join(sys.argv[1:]) + "' failed with code '" + str(e.returncode) + "':\n" + e.output) + sys.exit(e.returncode) diff --git a/filenames.gni b/filenames.gni new file mode 100644 From 07b8b2ffdd4d111ea51816f5692727621b62f8ee Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 14:42:58 +0900 Subject: [PATCH 41/79] docs: Updated "recent documents" fiddle tutorial (#29561) * Port recent-documents fiddle to 12-x-y. * Update recent-documents tutorial. * update for review comments Co-authored-by: Kevin Hartman Co-authored-by: Ethan Arrowood --- .../features/recent-documents/index.html | 9 ++- .../fiddles/features/recent-documents/main.js | 8 +-- docs/images/recent-documents.png | Bin 46587 -> 38277 bytes docs/tutorial/recent-documents.md | 65 ++++++++++++------ 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/docs/fiddles/features/recent-documents/index.html b/docs/fiddles/features/recent-documents/index.html index a3855d2640d8a..62aae8f8a25c3 100644 --- a/docs/fiddles/features/recent-documents/index.html +++ b/docs/fiddles/features/recent-documents/index.html @@ -2,15 +2,14 @@ - Hello World! + Recent Documents -

Hello World!

+

Recent Documents

- We are using node , - Chrome , - and Electron . + Right click on the app icon to see recent documents. + You should see `recently-used.md` added to the list of recent files

diff --git a/docs/fiddles/features/recent-documents/main.js b/docs/fiddles/features/recent-documents/main.js index 60ba1f814dbe7..d11a5bcc6705a 100644 --- a/docs/fiddles/features/recent-documents/main.js +++ b/docs/fiddles/features/recent-documents/main.js @@ -5,17 +5,15 @@ const path = require('path') function createWindow () { const win = new BrowserWindow({ width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } + height: 600 }) win.loadFile('index.html') } + const fileName = 'recently-used.md' fs.writeFile(fileName, 'Lorem Ipsum', () => { - app.addRecentDocument(path.join(process.cwd(), `${fileName}`)) + app.addRecentDocument(path.join(__dirname, fileName)) }) app.whenReady().then(createWindow) diff --git a/docs/images/recent-documents.png b/docs/images/recent-documents.png index 331466f3b1b35150d07393babbc4fe0a12ed3fb4..3542c1315e34ab8cc00e04b11fa67aeecd12a44c 100644 GIT binary patch delta 31830 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

Wn{E$FsD?3BpeNmM)#bv4n(Cwr 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 V*o&lhC=37q002ovPDHLkV1k>$F`fVb diff --git a/docs/tutorial/recent-documents.md b/docs/tutorial/recent-documents.md index c26f7964cc343..2d75246bae3d8 100644 --- a/docs/tutorial/recent-documents.md +++ b/docs/tutorial/recent-documents.md @@ -13,39 +13,62 @@ __Application dock menu:__ ![macOS Dock Menu][dock-menu-image] -To add a file to recent documents, you need to use the -[app.addRecentDocument][addrecentdocument] API. - ## Example -### Add an item to recent documents - -Starting with a working application from the -[Quick Start Guide](quick-start.md), add the following lines to the -`main.js` file: +### Managing recent documents ```javascript fiddle='docs/fiddles/features/recent-documents' -const { app } = require('electron') +const { app, BrowserWindow } = require('electron') +const fs = require('fs') +const path = require('path') + +function createWindow () { + const win = new BrowserWindow({ + width: 800, + height: 600 + }) + + win.loadFile('index.html') +} + +const fileName = 'recently-used.md' +fs.writeFile(fileName, 'Lorem Ipsum', () => { + app.addRecentDocument(path.join(__dirname, fileName)) +}) -app.addRecentDocument('/Users/USERNAME/Desktop/work.type') +app.whenReady().then(createWindow) + +app.on('window-all-closed', () => { + app.clearRecentDocuments() + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } +}) ``` -After launching the Electron application, right click the application icon. -You should see the item you just added. In this guide, the item is a Markdown -file located in the root of the project: +#### Adding a recent document -![Recent document](../images/recent-documents.png) +To add a file to recent documents, use the +[app.addRecentDocument][addrecentdocument] API. -### Clear the list of recent documents +After launching the Electron application, right click the application icon. +In this guide, the item is a Markdown file located in the root of the project. +You should see `recently-used.md` added to the list of recent files: -To clear the list of recent documents, you need to use -[app.clearRecentDocuments][clearrecentdocuments] API in the `main.js` file: +![Recent document](../images/recent-documents.png) -```javascript -const { app } = require('electron') +#### Clearing the list of recent documents -app.clearRecentDocuments() -``` +To clear the list of recent documents, use the +[app.clearRecentDocuments][clearrecentdocuments] API. +In this guide, the list of documents is cleared once all windows have been +closed. ## Additional information From 78569e9b91b556d8687fd50244b618edd86d89a3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 14:43:42 +0900 Subject: [PATCH 42/79] docs: Update notifications (renderer) docs (#29564) * remove version information from html * change format for readability * clarify which console the message should appear in * minor changes to renderer.md * update UI on click instead of developer console * remove node-integration and fix md * update content * chore: remove **** Co-authored-by: Jeremy Foster Co-authored-by: Ethan Arrowood Co-authored-by: Cheng Zhao --- .../notifications/renderer/index.html | 8 +++----- .../features/notifications/renderer/main.js | 5 +---- .../notifications/renderer/renderer.js | 11 +++++----- docs/tutorial/notifications.md | 20 ++++++++----------- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/docs/fiddles/features/notifications/renderer/index.html b/docs/fiddles/features/notifications/renderer/index.html index 4d6de7642e14d..206eadb3a3dd6 100644 --- a/docs/fiddles/features/notifications/renderer/index.html +++ b/docs/fiddles/features/notifications/renderer/index.html @@ -7,11 +7,9 @@

Hello World!

-

- We are using node , - Chrome , - and Electron . -

+

After launching this application, you should see the system notification.

+

Click it to see the effect in this interface.

+ diff --git a/docs/fiddles/features/notifications/renderer/main.js b/docs/fiddles/features/notifications/renderer/main.js index 4502be6814764..e24a66dd52b8f 100644 --- a/docs/fiddles/features/notifications/renderer/main.js +++ b/docs/fiddles/features/notifications/renderer/main.js @@ -3,10 +3,7 @@ const { app, BrowserWindow } = require('electron') function createWindow () { const win = new BrowserWindow({ width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } + height: 600 }) win.loadFile('index.html') diff --git a/docs/fiddles/features/notifications/renderer/renderer.js b/docs/fiddles/features/notifications/renderer/renderer.js index 7ea004cba1523..a6c88f9a79464 100644 --- a/docs/fiddles/features/notifications/renderer/renderer.js +++ b/docs/fiddles/features/notifications/renderer/renderer.js @@ -1,7 +1,6 @@ -const myNotification = new Notification('Title', { - body: 'Notification from the Renderer process' -}) +const NOTIFICATION_TITLE = 'Title' +const NOTIFICATION_BODY = 'Notification from the Renderer process. Click to log to console.' +const CLICK_MESSAGE = 'Notification clicked!' -myNotification.onclick = () => { - console.log('Notification clicked') -} +new Notification(NOTIFICATION_TITLE, { body: NOTIFICATION_BODY }) + .onclick = () => document.getElementById("output").innerText = CLICK_MESSAGE diff --git a/docs/tutorial/notifications.md b/docs/tutorial/notifications.md index 2ba5abb2b7a52..f6f4f28a48626 100644 --- a/docs/tutorial/notifications.md +++ b/docs/tutorial/notifications.md @@ -18,7 +18,7 @@ To show notifications in the Main process, you need to use the ### Show notifications in the Renderer process -Assuming you have a working Electron application from the +Starting with a working application from the [Quick Start Guide](quick-start.md), add the following line to the `index.html` file before the closing `` tag: @@ -26,26 +26,22 @@ Assuming you have a working Electron application from the ``` -and add the `renderer.js` file: +...and add the `renderer.js` file: ```javascript fiddle='docs/fiddles/features/notifications/renderer' -const myNotification = new Notification('Title', { - body: 'Notification from the Renderer process' -}) +const NOTIFICATION_TITLE = 'Title' +const NOTIFICATION_BODY = 'Notification from the Renderer process. Click to log to console.' +const CLICK_MESSAGE = 'Notification clicked' -myNotification.onclick = () => { - console.log('Notification clicked') -} +new Notification(NOTIFICATION_TITLE, { body: NOTIFICATION_BODY }) + .onclick = () => console.log(CLICK_MESSAGE) ``` After launching the Electron application, you should see the notification: ![Notification in the Renderer process](../images/notification-renderer.png) -If you open the Console and then click the notification, you will see the -message that was generated after triggering the `onclick` event: - -![Onclick message for the notification](../images/message-notification-renderer.png) +Additionally, if you click on the notification, the DOM will update to show "Notification clicked!". ### Show notifications in the Main process From 9059a50f93e7287972adfc5422c3108376b2021f Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 7 Jun 2021 23:25:35 +0900 Subject: [PATCH 43/79] fix: keep shifted character in menu accelerator (#29482) * fix: keep shifted character in menu accelerator * chore: update patches Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> --- patches/chromium/accelerator.patch | 89 +++++++++---------- shell/browser/api/electron_api_menu.cc | 8 +- shell/browser/api/electron_api_menu.h | 4 +- shell/browser/api/electron_api_menu_mac.h | 5 +- shell/browser/api/electron_api_menu_mac.mm | 38 ++++++++ shell/browser/ui/accelerator_util.cc | 7 +- .../ui/cocoa/electron_menu_controller.mm | 32 +++++-- .../common/gin_converters/blink_converter.cc | 6 +- shell/common/keyboard_util.cc | 23 +++-- shell/common/keyboard_util.h | 7 +- spec-main/api-menu-item-spec.ts | 32 ++++--- 11 files changed, 168 insertions(+), 83 deletions(-) diff --git a/patches/chromium/accelerator.patch b/patches/chromium/accelerator.patch index bf5de4e6846a4..b56b5b202fd32 100644 --- a/patches/chromium/accelerator.patch +++ b/patches/chromium/accelerator.patch @@ -1,16 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 4 Oct 2018 14:57:02 -0700 -Subject: accelerator.patch +Subject: fix: improve shortcut text of Accelerator This patch makes three changes to Accelerator::GetShortcutText to improve shortcut display text in menus: 1. Ctrl-Alt- accelerators show as Ctrl-Alt- instead of as Ctrl- 2. F2-F24 accelerators show up as such -3. Ctrl-Shift-= should show as Ctrl-+ +3. Ctrl-Shift-= and Ctrl-Plus show up as such diff --git a/ui/base/accelerators/accelerator.cc b/ui/base/accelerators/accelerator.cc -index c44f3d3752025bd3f11db790a97a48e8ba856034..8e0c1446315823a391614b19aa2c4ba2e5faed0d 100644 +index c44f3d3752025bd3f11db790a97a48e8ba856034..8b1859b18aa7ebcecf4c2a90b4ced0186d258ddc 100644 --- a/ui/base/accelerators/accelerator.cc +++ b/ui/base/accelerators/accelerator.cc @@ -11,6 +11,7 @@ @@ -21,61 +21,39 @@ index c44f3d3752025bd3f11db790a97a48e8ba856034..8e0c1446315823a391614b19aa2c4ba2 #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" -@@ -27,9 +28,7 @@ - #include +@@ -209,6 +210,11 @@ std::u16string Accelerator::GetShortcutText() const { #endif --#if !defined(OS_WIN) && (defined(USE_AURA) || defined(OS_MAC)) - #include "ui/events/keycodes/keyboard_code_conversion.h" --#endif - - #if BUILDFLAG(IS_CHROMEOS_ASH) - #include "ui/base/ui_base_features.h" -@@ -208,7 +207,15 @@ std::u16string Accelerator::GetShortcutText() const { - shortcut = KeyCodeToName(); - #endif - -+ unsigned int flags = 0; if (shortcut.empty()) { -+ const uint16_t c = DomCodeToUsLayoutCharacter( -+ UsLayoutKeyboardCodeToDomCode(key_code_), flags); -+ if (c != 0) { -+ shortcut = -+ static_cast( -+ base::ToUpperASCII(static_cast(c))); -+ } ++ // When a shifted char is explicitly specified, for example Ctrl+Plus, ++ // use the shifted char directly. ++ if (shifted_char) { ++ shortcut += *shifted_char; ++ } else { #if defined(OS_WIN) // Our fallback is to try translate the key code to a regular character // unless it is one of digits (VK_0 to VK_9). Some keyboard -@@ -217,21 +224,14 @@ std::u16string Accelerator::GetShortcutText() const { - // accent' for '0'). For display in the menu (e.g. Ctrl-0 for the - // default zoom level), we leave VK_[0-9] alone without translation. - wchar_t key; -- if (base::IsAsciiDigit(key_code_)) -+ if (base::IsAsciiDigit(key_code_)) { - key = static_cast(key_code_); -- else -- key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR)); -- // If there is no translation for the given |key_code_| (e.g. -- // VKEY_UNKNOWN), |::MapVirtualKeyW| returns 0. -- if (key != 0) -- shortcut += key; --#elif defined(USE_AURA) || defined(OS_MAC) || defined(OS_ANDROID) -- const uint16_t c = DomCodeToUsLayoutCharacter( -- UsLayoutKeyboardCodeToDomCode(key_code_), false); -- if (c != 0) -- shortcut += -- static_cast(base::ToUpperASCII(c)); -+ shortcut = key; -+ } +@@ -232,6 +238,10 @@ std::u16string Accelerator::GetShortcutText() const { + shortcut += + static_cast(base::ToUpperASCII(c)); #endif ++ } + if (key_code_ > VKEY_F1 && key_code_ <= VKEY_F24) + shortcut = base::UTF8ToUTF16( + base::StringPrintf("F%d", key_code_ - VKEY_F1 + 1)); } #if defined(OS_MAC) -@@ -427,7 +427,7 @@ std::u16string Accelerator::ApplyLongFormModifiers( +@@ -419,7 +429,7 @@ std::u16string Accelerator::ApplyLongFormModifiers( + const std::u16string& shortcut) const { + std::u16string result = shortcut; + +- if (IsShiftDown()) ++ if (!shifted_char && IsShiftDown()) + result = ApplyModifierToAcceleratorString(result, IDS_APP_SHIFT_KEY); + + // Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut. +@@ -427,7 +437,7 @@ std::u16string Accelerator::ApplyLongFormModifiers( // more information. if (IsCtrlDown()) result = ApplyModifierToAcceleratorString(result, IDS_APP_CTRL_KEY); @@ -84,3 +62,24 @@ index c44f3d3752025bd3f11db790a97a48e8ba856034..8e0c1446315823a391614b19aa2c4ba2 result = ApplyModifierToAcceleratorString(result, IDS_APP_ALT_KEY); if (IsCmdDown()) { +diff --git a/ui/base/accelerators/accelerator.h b/ui/base/accelerators/accelerator.h +index 62eacbe23f949cf62b86a775d331459ae3934048..008b753809f34b72d0c1de46916c0d36eb90a8f5 100644 +--- a/ui/base/accelerators/accelerator.h ++++ b/ui/base/accelerators/accelerator.h +@@ -16,6 +16,7 @@ + #include + + #include "base/component_export.h" ++#include "base/optional.h" + #include "base/time/time.h" + #include "build/build_config.h" + #include "ui/events/event_constants.h" +@@ -100,6 +101,8 @@ class COMPONENT_EXPORT(UI_BASE) Accelerator { + return interrupted_by_mouse_event_; + } + ++ base::Optional shifted_char; ++ + private: + std::u16string ApplyLongFormModifiers(const std::u16string& shortcut) const; + std::u16string ApplyShortFormModifiers(const std::u16string& shortcut) const; diff --git a/shell/browser/api/electron_api_menu.cc b/shell/browser/api/electron_api_menu.cc index c992168e77893..a5806167f37bd 100644 --- a/shell/browser/api/electron_api_menu.cc +++ b/shell/browser/api/electron_api_menu.cc @@ -236,11 +236,13 @@ std::u16string Menu::GetToolTipAt(int index) const { return model_->GetToolTipAt(index); } -std::u16string Menu::GetAcceleratorTextAt(int index) const { +#ifdef DCHECK_IS_ON +std::u16string Menu::GetAcceleratorTextAtForTesting(int index) const { ui::Accelerator accelerator; model_->GetAcceleratorAtWithParams(index, true, &accelerator); return accelerator.GetShortcutText(); } +#endif bool Menu::IsItemCheckedAt(int index) const { return model_->IsItemCheckedAt(index); @@ -289,13 +291,15 @@ v8::Local Menu::FillObjectTemplate( .SetMethod("getLabelAt", &Menu::GetLabelAt) .SetMethod("getSublabelAt", &Menu::GetSublabelAt) .SetMethod("getToolTipAt", &Menu::GetToolTipAt) - .SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAt) .SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt) .SetMethod("isEnabledAt", &Menu::IsEnabledAt) .SetMethod("worksWhenHiddenAt", &Menu::WorksWhenHiddenAt) .SetMethod("isVisibleAt", &Menu::IsVisibleAt) .SetMethod("popupAt", &Menu::PopupAt) .SetMethod("closePopupAt", &Menu::ClosePopupAt) +#ifdef DCHECK_IS_ON + .SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAtForTesting) +#endif .Build(); } diff --git a/shell/browser/api/electron_api_menu.h b/shell/browser/api/electron_api_menu.h index 89150f5bd341a..3fd2be0c30069 100644 --- a/shell/browser/api/electron_api_menu.h +++ b/shell/browser/api/electron_api_menu.h @@ -78,6 +78,9 @@ class Menu : public gin::Wrappable
, int positioning_item, base::OnceClosure callback) = 0; virtual void ClosePopupAt(int32_t window_id) = 0; +#ifdef DCHECK_IS_ON + virtual std::u16string GetAcceleratorTextAtForTesting(int index) const; +#endif std::unique_ptr model_; Menu* parent_ = nullptr; @@ -111,7 +114,6 @@ class Menu : public gin::Wrappable, std::u16string GetLabelAt(int index) const; std::u16string GetSublabelAt(int index) const; std::u16string GetToolTipAt(int index) const; - std::u16string GetAcceleratorTextAt(int index) const; bool IsItemCheckedAt(int index) const; bool IsEnabledAt(int index) const; bool IsVisibleAt(int index) const; diff --git a/shell/browser/api/electron_api_menu_mac.h b/shell/browser/api/electron_api_menu_mac.h index 5a1802563b28f..dc52e2c45b803 100644 --- a/shell/browser/api/electron_api_menu_mac.h +++ b/shell/browser/api/electron_api_menu_mac.h @@ -35,11 +35,14 @@ class MenuMac : public Menu { int positioning_item, base::OnceClosure callback); void ClosePopupAt(int32_t window_id) override; - void ClosePopupOnUI(int32_t window_id); +#ifdef DCHECK_IS_ON + std::u16string GetAcceleratorTextAtForTesting(int index) const override; +#endif private: friend class Menu; + void ClosePopupOnUI(int32_t window_id); void OnClosed(int32_t window_id, base::OnceClosure callback); scoped_nsobject menu_controller_; diff --git a/shell/browser/api/electron_api_menu_mac.mm b/shell/browser/api/electron_api_menu_mac.mm index 49d7934187680..0d3d5eff41e7b 100644 --- a/shell/browser/api/electron_api_menu_mac.mm +++ b/shell/browser/api/electron_api_menu_mac.mm @@ -127,6 +127,44 @@ std::move(close_popup)); } +#ifdef DCHECK_IS_ON +std::u16string MenuMac::GetAcceleratorTextAtForTesting(int index) const { + // A least effort to get the real shortcut text of NSMenuItem, the code does + // not need to be perfect since it is test only. + base::scoped_nsobject controller( + [[ElectronMenuController alloc] initWithModel:model() + useDefaultAccelerator:NO]); + NSMenuItem* item = [[controller menu] itemAtIndex:index]; + std::u16string text; + NSEventModifierFlags modifiers = [item keyEquivalentModifierMask]; + if (modifiers & NSEventModifierFlagControl) + text += u"Ctrl"; + if (modifiers & NSEventModifierFlagShift) { + if (!text.empty()) + text += u"+"; + text += u"Shift"; + } + if (modifiers & NSEventModifierFlagOption) { + if (!text.empty()) + text += u"+"; + text += u"Alt"; + } + if (modifiers & NSEventModifierFlagCommand) { + if (!text.empty()) + text += u"+"; + text += u"Command"; + } + if (!text.empty()) + text += u"+"; + auto key = base::ToUpperASCII(base::SysNSStringToUTF16([item keyEquivalent])); + if (key == u"\t") + text += u"Tab"; + else + text += key; + return text; +} +#endif + void MenuMac::ClosePopupOnUI(int32_t window_id) { auto controller = popup_controllers_.find(window_id); if (controller != popup_controllers_.end()) { diff --git a/shell/browser/ui/accelerator_util.cc b/shell/browser/ui/accelerator_util.cc index 05ec3dafe1cab..89f431f01f505 100644 --- a/shell/browser/ui/accelerator_util.cc +++ b/shell/browser/ui/accelerator_util.cc @@ -31,10 +31,10 @@ bool StringToAccelerator(const std::string& shortcut, // Now, parse it into an accelerator. int modifiers = ui::EF_NONE; ui::KeyboardCode key = ui::VKEY_UNKNOWN; + base::Optional shifted_char; for (const auto& token : tokens) { - bool shifted = false; - ui::KeyboardCode code = electron::KeyboardCodeFromStr(token, &shifted); - if (shifted) + ui::KeyboardCode code = electron::KeyboardCodeFromStr(token, &shifted_char); + if (shifted_char) modifiers |= ui::EF_SHIFT_DOWN; switch (code) { // The token can be a modifier. @@ -65,6 +65,7 @@ bool StringToAccelerator(const std::string& shortcut, } *accelerator = ui::Accelerator(key, modifiers); + accelerator->shifted_char = shifted_char; return true; } diff --git a/shell/browser/ui/cocoa/electron_menu_controller.mm b/shell/browser/ui/cocoa/electron_menu_controller.mm index d543c27295f78..2f80201365c18 100644 --- a/shell/browser/ui/cocoa/electron_menu_controller.mm +++ b/shell/browser/ui/cocoa/electron_menu_controller.mm @@ -21,9 +21,9 @@ #include "shell/browser/ui/electron_menu_model.h" #include "shell/browser/window_list.h" #include "ui/base/accelerators/accelerator.h" -#include "ui/base/accelerators/platform_accelerator_cocoa.h" #include "ui/base/l10n/l10n_util_mac.h" #include "ui/events/cocoa/cocoa_event_utils.h" +#include "ui/events/keycodes/keyboard_code_conversion_mac.h" #include "ui/gfx/image/image.h" #include "ui/strings/grit/ui_strings.h" @@ -394,11 +394,31 @@ - (void)addItemToMenu:(NSMenu*)menu ui::Accelerator accelerator; if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_, &accelerator)) { - NSString* key_equivalent; - NSUInteger modifier_mask; - GetKeyEquivalentAndModifierMaskFromAccelerator( - accelerator, &key_equivalent, &modifier_mask); - [item setKeyEquivalent:key_equivalent]; + // Note that we are not using Chromium's + // GetKeyEquivalentAndModifierMaskFromAccelerator API, + // because it will convert Shift+Character to ShiftedCharacter, for + // example Shift+/ would be converted to ?, which is against macOS HIG. + // See also https://github.com/electron/electron/issues/21790. + NSUInteger modifier_mask = 0; + if (accelerator.IsCtrlDown()) + modifier_mask |= NSEventModifierFlagControl; + if (accelerator.IsAltDown()) + modifier_mask |= NSEventModifierFlagOption; + if (accelerator.IsCmdDown()) + modifier_mask |= NSEventModifierFlagCommand; + unichar character; + if (accelerator.shifted_char) { + // When a shifted char is explicitly specified, for example Ctrl+Plus, + // use the shifted char directly. + character = static_cast(*accelerator.shifted_char); + } else { + // Otherwise use the unshifted combinations, for example Ctrl+Shift+=. + if (accelerator.IsShiftDown()) + modifier_mask |= NSEventModifierFlagShift; + ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), modifier_mask, + nullptr, &character); + } + [item setKeyEquivalent:[NSString stringWithFormat:@"%C", character]]; [item setKeyEquivalentModifierMask:modifier_mask]; } diff --git a/shell/common/gin_converters/blink_converter.cc b/shell/common/gin_converters/blink_converter.cc index 2e6b49f0b6938..94ce709b3e70e 100644 --- a/shell/common/gin_converters/blink_converter.cc +++ b/shell/common/gin_converters/blink_converter.cc @@ -187,10 +187,10 @@ bool Converter::FromV8(v8::Isolate* isolate, if (!dict.Get("keyCode", &str)) return false; - bool shifted = false; - ui::KeyboardCode keyCode = electron::KeyboardCodeFromStr(str, &shifted); + base::Optional shifted_char; + ui::KeyboardCode keyCode = electron::KeyboardCodeFromStr(str, &shifted_char); out->windows_key_code = keyCode; - if (shifted) + if (shifted_char) out->SetModifiers(out->GetModifiers() | blink::WebInputEvent::Modifiers::kShiftKey); diff --git a/shell/common/keyboard_util.cc b/shell/common/keyboard_util.cc index c5dc82c540dcd..597a10f040810 100644 --- a/shell/common/keyboard_util.cc +++ b/shell/common/keyboard_util.cc @@ -15,8 +15,9 @@ namespace electron { namespace { // Return key code represented by |str|. -ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s, - bool* shifted) { +ui::KeyboardCode KeyboardCodeFromKeyIdentifier( + const std::string& s, + base::Optional* shifted_char) { std::string str = base::ToLowerASCII(s); if (str == "ctrl" || str == "control") { return ui::VKEY_CONTROL; @@ -36,7 +37,7 @@ ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s, } else if (str == "altgr") { return ui::VKEY_ALTGR; } else if (str == "plus") { - *shifted = true; + shifted_char->emplace('+'); return ui::VKEY_OEM_PLUS; } else if (str == "capslock") { return ui::VKEY_CAPITAL; @@ -319,11 +320,17 @@ ui::KeyboardCode KeyboardCodeFromCharCode(char16_t c, bool* shifted) { } } -ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted) { - if (str.size() == 1) - return KeyboardCodeFromCharCode(str[0], shifted); - else - return KeyboardCodeFromKeyIdentifier(str, shifted); +ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, + base::Optional* shifted_char) { + if (str.size() == 1) { + bool shifted = false; + auto ret = KeyboardCodeFromCharCode(str[0], &shifted); + if (shifted) + shifted_char->emplace(str[0]); + return ret; + } else { + return KeyboardCodeFromKeyIdentifier(str, shifted_char); + } } } // namespace electron diff --git a/shell/common/keyboard_util.h b/shell/common/keyboard_util.h index 28f2a69637aad..29b740152e808 100644 --- a/shell/common/keyboard_util.h +++ b/shell/common/keyboard_util.h @@ -7,6 +7,7 @@ #include +#include "base/optional.h" #include "ui/events/keycodes/keyboard_codes.h" namespace electron { @@ -15,9 +16,11 @@ namespace electron { // pressed. ui::KeyboardCode KeyboardCodeFromCharCode(char16_t c, bool* shifted); -// Return key code of the |str|, and also determine whether the SHIFT key is +// Return key code of the |str|, if the original key is a shifted character, +// for example + and /, set it in |shifted_char|. // pressed. -ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted); +ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, + base::Optional* shifted_char); } // namespace electron diff --git a/spec-main/api-menu-item-spec.ts b/spec-main/api-menu-item-spec.ts index afdd368b09923..9039cf855f0ac 100644 --- a/spec-main/api-menu-item-spec.ts +++ b/spec-main/api-menu-item-spec.ts @@ -462,9 +462,9 @@ describe('MenuItems', () => { { label: 'text', accelerator: 'Alt+A' } ]); - expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌘A' : 'Ctrl+A'); - expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧A' : 'Shift+A'); - expect(menu.getAcceleratorTextAt(2)).to.equal(isDarwin() ? '⌥A' : 'Alt+A'); + expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? 'Command+A' : 'Ctrl+A'); + expect(menu.getAcceleratorTextAt(1)).to.equal('Shift+A'); + expect(menu.getAcceleratorTextAt(2)).to.equal('Alt+A'); }); it('should display modifiers correctly for special keys', () => { @@ -474,9 +474,9 @@ describe('MenuItems', () => { { label: 'text', accelerator: 'Alt+Tab' } ]); - expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌘⇥' : 'Ctrl+Tab'); - expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥' : 'Shift+Tab'); - expect(menu.getAcceleratorTextAt(2)).to.equal(isDarwin() ? '⌥⇥' : 'Alt+Tab'); + expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? 'Command+Tab' : 'Ctrl+Tab'); + expect(menu.getAcceleratorTextAt(1)).to.equal('Shift+Tab'); + expect(menu.getAcceleratorTextAt(2)).to.equal('Alt+Tab'); }); it('should not display modifiers twice', () => { @@ -485,18 +485,26 @@ describe('MenuItems', () => { { label: 'text', accelerator: 'Shift+Shift+Tab' } ]); - expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⇧A' : 'Shift+A'); - expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥' : 'Shift+Tab'); + expect(menu.getAcceleratorTextAt(0)).to.equal('Shift+A'); + expect(menu.getAcceleratorTextAt(1)).to.equal('Shift+Tab'); }); - it('should display correctly for edge cases', () => { + it('should display correctly for shifted keys', () => { const menu = Menu.buildFromTemplate([ { label: 'text', accelerator: 'Control+Shift+=' }, - { label: 'text', accelerator: 'Control+Plus' } + { label: 'text', accelerator: 'Control+Plus' }, + { label: 'text', accelerator: 'Control+Shift+3' }, + { label: 'text', accelerator: 'Control+#' }, + { label: 'text', accelerator: 'Control+Shift+/' }, + { label: 'text', accelerator: 'Control+?' } ]); - expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌃⇧=' : 'Ctrl+Shift+='); - expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⌃⇧=' : 'Ctrl+Shift+='); + expect(menu.getAcceleratorTextAt(0)).to.equal('Ctrl+Shift+='); + expect(menu.getAcceleratorTextAt(1)).to.equal('Ctrl++'); + expect(menu.getAcceleratorTextAt(2)).to.equal('Ctrl+Shift+3'); + expect(menu.getAcceleratorTextAt(3)).to.equal('Ctrl+#'); + expect(menu.getAcceleratorTextAt(4)).to.equal('Ctrl+Shift+/'); + expect(menu.getAcceleratorTextAt(5)).to.equal('Ctrl+?'); }); }); }); From 30921548ed50e0238d559ea14d91c302bd702f8a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 11:40:09 +0900 Subject: [PATCH 44/79] fix: make intermediates work with 'select-client-certificate' (#29569) Co-authored-by: David Sanders --- shell/browser/api/electron_api_app.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/shell/browser/api/electron_api_app.cc b/shell/browser/api/electron_api_app.cc index e64fc7f685f00..d3dad9539dea1 100644 --- a/shell/browser/api/electron_api_app.cc +++ b/shell/browser/api/electron_api_app.cc @@ -536,10 +536,16 @@ void OnClientCertificateSelected( if (!certs.empty()) { scoped_refptr cert(certs[0].get()); for (auto& identity : *identities) { - if (cert->EqualsExcludingChain(identity->certificate())) { + scoped_refptr identity_cert = + identity->certificate(); + // Since |cert| was recreated from |data|, it won't include any + // intermediates. That's fine for checking equality, but once a match is + // found then |identity_cert| should be used since it will include the + // intermediates which would otherwise be lost. + if (cert->EqualsExcludingChain(identity_cert.get())) { net::ClientCertIdentity::SelfOwningAcquirePrivateKey( - std::move(identity), - base::BindRepeating(&GotPrivateKey, delegate, std::move(cert))); + std::move(identity), base::BindRepeating(&GotPrivateKey, delegate, + std::move(identity_cert))); break; } } From d7736e59247030afa4a61ba3f389f79fd4df81f2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 10:31:27 +0200 Subject: [PATCH 45/79] fix: improper wrapping of fs.promises.readFile (#29576) --- lib/asar/fs-wrapper.ts | 110 ++++++++++++++++------------ spec-main/fixtures/dogs-running.txt | 1 + spec-main/node-spec.ts | 12 +++ 3 files changed, 78 insertions(+), 45 deletions(-) create mode 100644 spec-main/fixtures/dogs-running.txt diff --git a/lib/asar/fs-wrapper.ts b/lib/asar/fs-wrapper.ts index 5f0d0587a31f5..1e7150c45bd1e 100644 --- a/lib/asar/fs-wrapper.ts +++ b/lib/asar/fs-wrapper.ts @@ -490,63 +490,83 @@ export const wrapFsWithAsar = (fs: Record) => { } }; - const { readFile } = fs; - fs.readFile = function (pathArgument: string, options: any, callback: any) { + function fsReadFileAsar (pathArgument: string, options: any, callback: any) { const pathInfo = splitPath(pathArgument); - if (!pathInfo.isAsar) return readFile.apply(this, arguments); - const { asarPath, filePath } = pathInfo; + if (pathInfo.isAsar) { + const { asarPath, filePath } = pathInfo; - if (typeof options === 'function') { - callback = options; - options = { encoding: null }; - } else if (typeof options === 'string') { - options = { encoding: options }; - } else if (options === null || options === undefined) { - options = { encoding: null }; - } else if (typeof options !== 'object') { - throw new TypeError('Bad arguments'); - } + if (typeof options === 'function') { + callback = options; + options = { encoding: null }; + } else if (typeof options === 'string') { + options = { encoding: options }; + } else if (options === null || options === undefined) { + options = { encoding: null }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); + } - const { encoding } = options; - const archive = getOrCreateArchive(asarPath); - if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); - nextTick(callback, [error]); - return; - } + const { encoding } = options; + const archive = getOrCreateArchive(asarPath); + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; + } - const info = archive.getFileInfo(filePath); - if (!info) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); - nextTick(callback, [error]); - return; - } + const info = archive.getFileInfo(filePath); + if (!info) { + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; + } - if (info.size === 0) { - nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]); - return; - } + if (info.size === 0) { + nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]); + return; + } - if (info.unpacked) { - const realPath = archive.copyFileOut(filePath); - return fs.readFile(realPath, options, callback); + if (info.unpacked) { + const realPath = archive.copyFileOut(filePath); + return fs.readFile(realPath, options, callback); + } + + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); + if (!(fd >= 0)) { + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; + } + + logASARAccess(asarPath, filePath, info.offset); + fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => { + callback(error, encoding ? buffer.toString(encoding) : buffer); + }); } + } - const buffer = Buffer.alloc(info.size); - const fd = archive.getFd(); - if (!(fd >= 0)) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); - nextTick(callback, [error]); - return; + const { readFile } = fs; + fs.readFile = function (pathArgument: string, options: any, callback: any) { + const pathInfo = splitPath(pathArgument); + if (!pathInfo.isAsar) { + return readFile.apply(this, arguments); } - logASARAccess(asarPath, filePath, info.offset); - fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => { - callback(error, encoding ? buffer.toString(encoding) : buffer); - }); + return fsReadFileAsar(pathArgument, options, callback); }; - fs.promises.readFile = util.promisify(fs.readFile); + const { readFile: readFilePromise } = fs.promises; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + fs.promises.readFile = function (pathArgument: string, options: any) { + const pathInfo = splitPath(pathArgument); + if (!pathInfo.isAsar) { + return readFilePromise.apply(this, arguments); + } + + const p = util.promisify(fsReadFileAsar); + return p(pathArgument, options); + }; const { readFileSync } = fs; fs.readFileSync = function (pathArgument: string, options: any) { diff --git a/spec-main/fixtures/dogs-running.txt b/spec-main/fixtures/dogs-running.txt new file mode 100644 index 0000000000000..66d80ebc6def2 --- /dev/null +++ b/spec-main/fixtures/dogs-running.txt @@ -0,0 +1 @@ +Dogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs runningDogs running \ No newline at end of file diff --git a/spec-main/node-spec.ts b/spec-main/node-spec.ts index c9fd2cfbd7e3e..c667603e3b4c3 100644 --- a/spec-main/node-spec.ts +++ b/spec-main/node-spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import * as childProcess from 'child_process'; +import * as fs from 'fs'; import * as path from 'path'; import * as util from 'util'; import { emittedOnce } from './events-helpers'; @@ -199,6 +200,17 @@ describe('node feature', () => { }); }); + describe('fs.readFile', () => { + it('can accept a FileHandle as the Path argument', async () => { + const filePathForHandle = path.resolve(mainFixturesPath, 'dogs-running.txt'); + const fileHandle = await fs.promises.open(filePathForHandle, 'r'); + + const file = await fs.promises.readFile(fileHandle, { encoding: 'utf8' }); + expect(file).to.not.be.empty(); + await fileHandle.close(); + }); + }); + ifdescribe(features.isRunAsNodeEnabled())('inspector', () => { let child: childProcess.ChildProcessWithoutNullStreams; let exitPromise: Promise; From 05ae55d131889b82be01d2630a1e4f33b44cd7c2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 09:26:51 -0700 Subject: [PATCH 46/79] fix: ensure fuse order is read in a stable way (#29615) Co-authored-by: Samuel Attard --- build/fuses/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/fuses/build.py b/build/fuses/build.py index 6c56dceb091df..c82e6cfba02af 100755 --- a/build/fuses/build.py +++ b/build/fuses/build.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from collections import OrderedDict import json import os import sys @@ -50,7 +51,7 @@ """ with open(os.path.join(dir_path, "fuses.json5"), 'r') as f: - fuse_defaults = json.loads(''.join(line for line in f.readlines() if not line.strip()[0] == "/")) + fuse_defaults = json.loads(''.join(line for line in f.readlines() if not line.strip()[0] == "/"), object_pairs_hook=OrderedDict) fuse_version = fuse_defaults['_version'] del fuse_defaults['_version'] From 839dcbbbdb98fae9b391ff0a948299a8503c5e49 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Wed, 9 Jun 2021 09:39:07 -0700 Subject: [PATCH 47/79] Bump v13.1.2 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 2e7dfda226a97..72ec0bba138c7 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.1.1 \ No newline at end of file +13.1.2 \ No newline at end of file diff --git a/package.json b/package.json index 8bac5d8b31ef8..912760da02a39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.1.1", + "version": "13.1.2", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 47b852671b2a3..c0e20e24cde5f 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,1,1,0 - PRODUCTVERSION 13,1,1,0 + FILEVERSION 13,1,2,0 + PRODUCTVERSION 13,1,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "13.1.1" + VALUE "FileVersion", "13.1.2" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "13.1.1" + VALUE "ProductVersion", "13.1.2" VALUE "SquirrelAwareVersion", "1" END END From 6c4c66d2cd5a37a8d863b056864028cf2ddd1d62 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 12:48:41 -0400 Subject: [PATCH 48/79] fix: select-bluetooth-device on Windows (#29611) Co-authored-by: John Kleinschmidt --- shell/browser/lib/bluetooth_chooser.cc | 3 ++- shell/browser/lib/bluetooth_chooser.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/browser/lib/bluetooth_chooser.cc b/shell/browser/lib/bluetooth_chooser.cc index 4aad929d91118..3dec4d19be391 100644 --- a/shell/browser/lib/bluetooth_chooser.cc +++ b/shell/browser/lib/bluetooth_chooser.cc @@ -60,6 +60,7 @@ void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) { event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, ""); break; case AdapterPresence::POWERED_ON: + rescan_ = true; break; } } @@ -92,7 +93,7 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) { case DiscoveryState::DISCOVERING: // The first time this state fires is due to a rescan triggering so set a // flag to ignore devices - if (!refreshing_) { + if (rescan_ && !refreshing_) { refreshing_ = true; } else { // The second time this state fires we are now safe to pick a device diff --git a/shell/browser/lib/bluetooth_chooser.h b/shell/browser/lib/bluetooth_chooser.h index e7ada17fc7a34..346cf986779d1 100644 --- a/shell/browser/lib/bluetooth_chooser.h +++ b/shell/browser/lib/bluetooth_chooser.h @@ -42,6 +42,7 @@ class BluetoothChooser : public content::BluetoothChooser { EventHandler event_handler_; int num_retries_ = 0; bool refreshing_ = false; + bool rescan_ = false; DISALLOW_COPY_AND_ASSIGN(BluetoothChooser); }; From 54de33464daa99f9df5c1699c6ee23f3dc80e8fd Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 15:59:28 -0700 Subject: [PATCH 49/79] chore: bump chromium to 91.0.4472.101 (13-x-y) (#29629) * chore: bump chromium in DEPS to 91.0.4472.101 * chore: update patches Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> --- DEPS | 2 +- patches/chromium/can_create_window.patch | 4 ++-- patches/v8/dcheck.patch | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index c746bfc74d933..845aed978b868 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ gclient_gn_args = [ vars = { 'chromium_version': - '91.0.4472.77', + '91.0.4472.101', 'node_version': 'v14.16.0', 'nan_version': diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index a303c956b158f..ad118ac048d8c 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 08a3eb686360e7a53cca75437cbe5f4d15cb9a17..78d3287ef7a708f44bd8ef0290618ef781f09d9c 100644 +index 7f7e6adf57128f1e8b0890aa14102fe4db452cb2..59d855ec17efe9117ee9bca5074b1ab28dfb40fa 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -5440,6 +5440,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -5430,6 +5430,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, diff --git a/patches/v8/dcheck.patch b/patches/v8/dcheck.patch index 28d4b401ee28c..af94496ef7897 100644 --- a/patches/v8/dcheck.patch +++ b/patches/v8/dcheck.patch @@ -19,10 +19,10 @@ index 50173de3f145b4febcc29c03080306c137918527..1ac347ede7e51559917f09442ac4824f isolate->default_microtask_queue()->PerformCheckpoint(this); } diff --git a/src/heap/heap.cc b/src/heap/heap.cc -index 823d37fd02d0169f0cb2785f9aa3395c5970711e..db6c29b72dd45f625b9876dab0e68ee56c9fae66 100644 +index af55137e1f6026f5e97f7b496ee71b78ddeb665b..582fc6302bddc736d71cb650d45d6bf9deafa9b2 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc -@@ -5600,9 +5600,9 @@ void Heap::TearDown() { +@@ -5607,9 +5607,9 @@ void Heap::TearDown() { void Heap::AddGCPrologueCallback(v8::Isolate::GCCallbackWithData callback, GCType gc_type, void* data) { DCHECK_NOT_NULL(callback); From 38fce95417c4107f3cc1fbd1c2a513d79d45d09d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 09:33:53 +0900 Subject: [PATCH 50/79] docs: fix image links in `performance.md` (#29631) * docs: fix image links in performance.md Fixes https://github.com/electron/electron/issues/29580 Signed-off-by: Darshan Sen * Apply suggestions from code review Co-authored-by: David Sanders Co-authored-by: Darshan Sen Co-authored-by: David Sanders --- docs/tutorial/performance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/performance.md b/docs/tutorial/performance.md index c920f2e8dc1bd..4f1e805400204 100644 --- a/docs/tutorial/performance.md +++ b/docs/tutorial/performance.md @@ -120,9 +120,9 @@ file in the directory you executed it in. Both files can be analyzed using the Chrome Developer Tools, using the `Performance` and `Memory` tabs respectively. -![performance-cpu-prof] +![Performance CPU Profile][performance-cpu-prof] -![performance-heap-prof] +![Performance Heap Memory Profile][performance-heap-prof] In this example, on the author's machine, we saw that loading `request` took almost half a second, whereas `node-fetch` took dramatically less memory From 72733bae8a6ba345d2353ed1b04282ca4a08a56e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 09:35:07 +0900 Subject: [PATCH 51/79] docs: fix typo (#29582) * Typo fix * Update main.js Co-authored-by: ZReC --- docs/fiddles/features/macos-dark-mode/main.js | 2 +- docs/tutorial/dark-mode.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/fiddles/features/macos-dark-mode/main.js b/docs/fiddles/features/macos-dark-mode/main.js index 4f0254e95bd8c..9503efb5f9a92 100644 --- a/docs/fiddles/features/macos-dark-mode/main.js +++ b/docs/fiddles/features/macos-dark-mode/main.js @@ -22,7 +22,7 @@ function createWindow () { }) ipcMain.handle('dark-mode:system', () => { - nativeTheme.themeSouce = 'system' + nativeTheme.themeSource = 'system' }) } diff --git a/docs/tutorial/dark-mode.md b/docs/tutorial/dark-mode.md index 5ac69241ebae2..bbb2f6381435f 100644 --- a/docs/tutorial/dark-mode.md +++ b/docs/tutorial/dark-mode.md @@ -159,7 +159,7 @@ function createWindow () { }) ipcMain.handle('dark-mode:system', () => { - nativeTheme.themeSouce = 'system' + nativeTheme.themeSource = 'system' }) } From 46e7511af96f15e26026c743bbd3694d3f8674cb Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Mon, 14 Jun 2021 02:36:15 +0200 Subject: [PATCH 52/79] fix: ensure custom traffic lights float to top (#29663) * fix: ensure custom traffic lights float to top * chore: split into separate function --- shell/browser/native_window_mac.h | 3 +++ shell/browser/native_window_mac.mm | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index c2b2c76e7ba00..f2d89402d987b 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -152,6 +152,9 @@ class NativeWindowMac : public NativeWindow, void NotifyWindowWillEnterFullScreen(); void NotifyWindowWillLeaveFullScreen(); + // Ensure the buttons view are always floated on the top. + void ReorderButtonsView(); + // Cleanup observers when window is getting closed. Note that the destructor // can be called much later after window gets closed, so we should not do // cleanup in destructor. diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 3df4687514657..270fa17a718c2 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -458,11 +458,8 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { set_content_view(view); root_view->AddChildView(content_view()); - if (buttons_view_) { - // Ensure the buttons view are always floated on the top. - [buttons_view_ removeFromSuperview]; - [[window_ contentView] addSubview:buttons_view_]; - } + if (buttons_view_) + ReorderButtonsView(); root_view->Layout(); } @@ -1143,6 +1140,8 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } [CATransaction commit]; + + ReorderButtonsView(); } void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) { @@ -1184,6 +1183,8 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } [CATransaction commit]; + + ReorderButtonsView(); } void NativeWindowMac::SetParentWindow(NativeWindow* parent) { @@ -1636,6 +1637,13 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { UpdateVibrancyRadii(false); } +void NativeWindowMac::ReorderButtonsView() { + if (buttons_view_) { + [buttons_view_ removeFromSuperview]; + [[window_ contentView] addSubview:buttons_view_]; + } +} + void NativeWindowMac::Cleanup() { DCHECK(!IsClosed()); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); From 4cecc87c557c4bd384061c48124db70a04f645bf Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 17:47:17 +0900 Subject: [PATCH 53/79] fix: copy received data in URLPipeLoader to prevent corruption (#29669) Co-authored-by: David Sanders --- shell/browser/net/url_pipe_loader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/net/url_pipe_loader.cc b/shell/browser/net/url_pipe_loader.cc index 39a4152e6477c..0968af2d2bbe3 100644 --- a/shell/browser/net/url_pipe_loader.cc +++ b/shell/browser/net/url_pipe_loader.cc @@ -85,7 +85,7 @@ void URLPipeLoader::OnDataReceived(base::StringPiece string_piece, producer_->Write( std::make_unique( string_piece, mojo::StringDataSource::AsyncWritingMode:: - STRING_STAYS_VALID_UNTIL_COMPLETION), + STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION), base::BindOnce(&URLPipeLoader::OnWrite, weak_factory_.GetWeakPtr(), std::move(resume))); } From e0e7b14bdc6c96b7e569edbd0d1feefc4a2e3b2b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 20:59:05 +0900 Subject: [PATCH 54/79] fix: use correct spelling of attachment with Content-Disposition header (#29672) Co-authored-by: David Sanders --- shell/browser/api/electron_api_web_request.cc | 2 +- spec-main/api-web-request-spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/api/electron_api_web_request.cc b/shell/browser/api/electron_api_web_request.cc index 2bec3aafd6462..e81c0eeb91861 100644 --- a/shell/browser/api/electron_api_web_request.cc +++ b/shell/browser/api/electron_api_web_request.cc @@ -129,7 +129,7 @@ v8::Local HttpResponseHeadersToV8( !value.empty()) { net::HttpContentDisposition header(value, std::string()); std::string decodedFilename = - header.is_attachment() ? " attachement" : " inline"; + header.is_attachment() ? " attachment" : " inline"; decodedFilename += "; filename=" + header.filename(); value = decodedFilename; } diff --git a/spec-main/api-web-request-spec.ts b/spec-main/api-web-request-spec.ts index 7bf6f32d0d74a..4376a735a37b5 100644 --- a/spec-main/api-web-request-spec.ts +++ b/spec-main/api-web-request-spec.ts @@ -18,7 +18,7 @@ describe('webRequest module', () => { res.setHeader('Location', 'http://' + req.rawHeaders[1]); res.end(); } else if (req.url === '/contentDisposition') { - res.setHeader('content-disposition', [' attachement; filename=aa%E4%B8%ADaa.txt']); + res.setHeader('content-disposition', [' attachment; filename=aa%E4%B8%ADaa.txt']); const content = req.url; res.end(content); } else { @@ -306,11 +306,11 @@ describe('webRequest module', () => { it('does not change content-disposition header by default', async () => { ses.webRequest.onHeadersReceived((details, callback) => { - expect(details.responseHeaders!['content-disposition']).to.deep.equal([' attachement; filename=aa中aa.txt']); + expect(details.responseHeaders!['content-disposition']).to.deep.equal([' attachment; filename=aa中aa.txt']); callback({}); }); const { data, headers } = await ajax(defaultURL + 'contentDisposition'); - expect(headers).to.match(/^content-disposition: attachement; filename=aa%E4%B8%ADaa.txt$/m); + expect(headers).to.match(/^content-disposition: attachment; filename=aa%E4%B8%ADaa.txt$/m); expect(data).to.equal('/contentDisposition'); }); From 192fc3ee2c9e6da42f1380b9644882ca8d023a62 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 21:00:20 +0900 Subject: [PATCH 55/79] fix: check DCHECK_IS_ON() instead of #ifdef DCHECK_IS_ON (#29674) Co-authored-by: Jeremy Rose --- shell/browser/api/electron_api_menu.cc | 4 ++-- shell/browser/api/electron_api_menu.h | 2 +- shell/browser/api/electron_api_menu_mac.h | 2 +- shell/browser/api/electron_api_menu_mac.mm | 2 +- shell/common/api/electron_api_v8_util.cc | 4 ++-- shell/renderer/api/electron_api_context_bridge.cc | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/shell/browser/api/electron_api_menu.cc b/shell/browser/api/electron_api_menu.cc index a5806167f37bd..87650266a4c1a 100644 --- a/shell/browser/api/electron_api_menu.cc +++ b/shell/browser/api/electron_api_menu.cc @@ -236,7 +236,7 @@ std::u16string Menu::GetToolTipAt(int index) const { return model_->GetToolTipAt(index); } -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() std::u16string Menu::GetAcceleratorTextAtForTesting(int index) const { ui::Accelerator accelerator; model_->GetAcceleratorAtWithParams(index, true, &accelerator); @@ -297,7 +297,7 @@ v8::Local Menu::FillObjectTemplate( .SetMethod("isVisibleAt", &Menu::IsVisibleAt) .SetMethod("popupAt", &Menu::PopupAt) .SetMethod("closePopupAt", &Menu::ClosePopupAt) -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() .SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAtForTesting) #endif .Build(); diff --git a/shell/browser/api/electron_api_menu.h b/shell/browser/api/electron_api_menu.h index 3fd2be0c30069..1efdad158cdd1 100644 --- a/shell/browser/api/electron_api_menu.h +++ b/shell/browser/api/electron_api_menu.h @@ -78,7 +78,7 @@ class Menu : public gin::Wrappable, int positioning_item, base::OnceClosure callback) = 0; virtual void ClosePopupAt(int32_t window_id) = 0; -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() virtual std::u16string GetAcceleratorTextAtForTesting(int index) const; #endif diff --git a/shell/browser/api/electron_api_menu_mac.h b/shell/browser/api/electron_api_menu_mac.h index dc52e2c45b803..00ca7ccf5fcb8 100644 --- a/shell/browser/api/electron_api_menu_mac.h +++ b/shell/browser/api/electron_api_menu_mac.h @@ -35,7 +35,7 @@ class MenuMac : public Menu { int positioning_item, base::OnceClosure callback); void ClosePopupAt(int32_t window_id) override; -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() std::u16string GetAcceleratorTextAtForTesting(int index) const override; #endif diff --git a/shell/browser/api/electron_api_menu_mac.mm b/shell/browser/api/electron_api_menu_mac.mm index 0d3d5eff41e7b..ab34ff799504a 100644 --- a/shell/browser/api/electron_api_menu_mac.mm +++ b/shell/browser/api/electron_api_menu_mac.mm @@ -127,7 +127,7 @@ std::move(close_popup)); } -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() std::u16string MenuMac::GetAcceleratorTextAtForTesting(int index) const { // A least effort to get the real shortcut text of NSMenuItem, the code does // not need to be perfect since it is test only. diff --git a/shell/common/api/electron_api_v8_util.cc b/shell/common/api/electron_api_v8_util.cc index a0133ec525848..1c69e8ac7af6d 100644 --- a/shell/common/api/electron_api_v8_util.cc +++ b/shell/common/api/electron_api_v8_util.cc @@ -107,7 +107,7 @@ bool IsSameOrigin(const GURL& l, const GURL& r) { return url::Origin::Create(l).IsSameOriginWith(url::Origin::Create(r)); } -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() std::vector> weakly_tracked_values; void WeaklyTrackValue(v8::Isolate* isolate, v8::Local value) { @@ -157,7 +157,7 @@ void Initialize(v8::Local exports, dict.SetMethod("requestGarbageCollectionForTesting", &RequestGarbageCollectionForTesting); dict.SetMethod("isSameOrigin", &IsSameOrigin); -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() dict.SetMethod("triggerFatalErrorForTesting", &TriggerFatalErrorForTesting); dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues); dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues); diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 2c2737f4cf4b2..c6f2b44ce27d0 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -688,7 +688,7 @@ void Initialize(v8::Local exports, &electron::api::OverrideGlobalPropertyFromIsolatedWorld); dict.SetMethod("_isCalledFromMainWorld", &electron::api::IsCalledFromMainWorld); -#ifdef DCHECK_IS_ON +#if DCHECK_IS_ON() dict.Set("_isDebug", true); #endif } From 3e90aff6fd6ce088549d9c31c91937bf90c142fb Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 10:12:35 -0700 Subject: [PATCH 56/79] docs: fix file mode of versioning-sketch-2.png (#29681) Unlike the other files, this file had its executable bit set in its file mode. This change removes the executable bit to align its file mode with the rest of the files. Signed-off-by: Darshan Sen Co-authored-by: Darshan Sen --- docs/images/versioning-sketch-2.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 docs/images/versioning-sketch-2.png diff --git a/docs/images/versioning-sketch-2.png b/docs/images/versioning-sketch-2.png old mode 100755 new mode 100644 From 7958f3efbb624c904b88cbb31572cb6dfc695b8e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 10:12:58 -0700 Subject: [PATCH 57/79] docs: fix typo in process-model.md (#29683) Co-authored-by: Luke Ingalls <45518011+lukeingalls@users.noreply.github.com> --- docs/tutorial/process-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/process-model.md b/docs/tutorial/process-model.md index 0438058babbec..44ff022045c55 100644 --- a/docs/tutorial/process-model.md +++ b/docs/tutorial/process-model.md @@ -138,7 +138,7 @@ way to import Electron's content scripts. Preload scripts contain code that executes in a renderer process before its web content -begins loading. These scripts runs within the renderer context, but are granted more +begins loading. These scripts run within the renderer context, but are granted more privileges by having access to Node.js APIs. A preload script can be attached to the main process in the `BrowserWindow` constructor's From 7b2a4c99844806c2ce89e38e7325fe2abf776f43 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 11:36:57 +0900 Subject: [PATCH 58/79] docs: Update represented-file fiddle tutorial (#29693) * Update represented-file fiddle. * add index and code back to guide Co-authored-by: Kevin Hartman Co-authored-by: Ethan Arrowood --- .../features/represented-file/index.html | 7 +++-- .../fiddles/features/represented-file/main.js | 5 +-- docs/images/represented-file.png | Bin 23293 -> 33176 bytes docs/tutorial/represented-file.md | 29 ++++++++++++++---- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/docs/fiddles/features/represented-file/index.html b/docs/fiddles/features/represented-file/index.html index a3855d2640d8a..67583b9d9ddd0 100644 --- a/docs/fiddles/features/represented-file/index.html +++ b/docs/fiddles/features/represented-file/index.html @@ -4,13 +4,14 @@ Hello World! +

Hello World!

- We are using node , - Chrome , - and Electron . + Click on the title with the

Command
or
Control
key pressed. + You should see a popup with the represented file at the top.

+ diff --git a/docs/fiddles/features/represented-file/main.js b/docs/fiddles/features/represented-file/main.js index 61b6ef3316beb..204a3fc4586eb 100644 --- a/docs/fiddles/features/represented-file/main.js +++ b/docs/fiddles/features/represented-file/main.js @@ -4,10 +4,7 @@ const os = require('os'); function createWindow () { const win = new BrowserWindow({ width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } + height: 600 }) win.loadFile('index.html') diff --git a/docs/images/represented-file.png b/docs/images/represented-file.png index a2d3a3cdd7707230debb6bcc2df463df52ba1b43..8ccb477ab7e5afd6502ebdf46fd1a898351db424 100644 GIT binary patch delta 26640 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 U#<})z;0 { const win = new BrowserWindow() - win.setRepresentedFilename('/etc/passwd') + win.setRepresentedFilename(os.homedir()) win.setDocumentEdited(true) }) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } +}) ``` After launching the Electron application, click on the title with `Command` or -`Control` key pressed. You should see a popup with the file you just defined: +`Control` key pressed. You should see a popup with the represented file at the top. +In this guide, this is the current user's home directory: ![Represented file](../images/represented-file.png) From 62402870fded96b753ca10b3213cdb4d3102e5dc Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Tue, 15 Jun 2021 12:02:03 +0200 Subject: [PATCH 59/79] fix: warnAboutRemoteModuleWithRemoteContent (#29691) Co-authored-by: Milan Burda --- lib/renderer/security-warnings.ts | 8 ++++---- spec-main/security-warnings-spec.ts | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/renderer/security-warnings.ts b/lib/renderer/security-warnings.ts index 23f538fb1d36a..4d3cf8ad5a0c4 100644 --- a/lib/renderer/security-warnings.ts +++ b/lib/renderer/security-warnings.ts @@ -270,9 +270,7 @@ const warnAboutAllowedPopups = function () { // Logs a warning message about the remote module const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || isLocalhost()) return; - const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true; - if (!remoteModuleEnabled) return; + if (!webPreferences || !webPreferences.enableRemoteModule || isLocalhost()) return; if (getIsRemoteProtocol()) { const warning = `This renderer process has "enableRemoteModule" enabled @@ -298,7 +296,9 @@ const logSecurityWarnings = function ( warnAboutEnableBlinkFeatures(webPreferences); warnAboutInsecureCSP(); warnAboutAllowedPopups(); - warnAboutRemoteModuleWithRemoteContent(webPreferences); + if (BUILDFLAG(ENABLE_REMOTE_MODULE)) { + warnAboutRemoteModuleWithRemoteContent(webPreferences); + } }; const getWebPreferences = async function () { diff --git a/spec-main/security-warnings-spec.ts b/spec-main/security-warnings-spec.ts index b1081202052aa..4dc21839a5061 100644 --- a/spec-main/security-warnings-spec.ts +++ b/spec-main/security-warnings-spec.ts @@ -9,6 +9,9 @@ import { BrowserWindow, WebPreferences } from 'electron/main'; import { closeWindow } from './window-helpers'; import { AddressInfo } from 'net'; import { emittedUntil } from './events-helpers'; +import { ifit } from './spec-helpers'; + +const features = process._linkedBinding('electron_common_features'); const messageContainsSecurityWarning = (event: Event, level: number, message: string) => { return message.indexOf('Electron Security Warning') > -1; @@ -226,10 +229,13 @@ describe('security warnings', () => { expect(message).to.not.include('insecure-resources.html'); }); - it('should warn about enabled remote module with remote content', async () => { + ifit(features.isRemoteModuleEnabled())('should warn about enabled remote module with remote content', async () => { w = new BrowserWindow({ show: false, - webPreferences + webPreferences: { + enableRemoteModule: true, + ...webPreferences + } }); w.loadURL(`${serverUrl}/base-page-security.html`); @@ -237,10 +243,13 @@ describe('security warnings', () => { expect(message).to.include('enableRemoteModule'); }); - it('should not warn about enabled remote module with remote content from localhost', async () => { + ifit(features.isRemoteModuleEnabled())('should not warn about enabled remote module with remote content from localhost', async () => { w = new BrowserWindow({ show: false, - webPreferences + webPreferences: { + enableRemoteModule: true, + ...webPreferences + } }); w.loadURL(`${serverUrl}/base-page-security-onload-message.html`); const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded); From 2ef55e1e9e4ec16cd63911c7b411f5fc4e081400 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 12:49:52 -0700 Subject: [PATCH 60/79] chore: disable default async spellchecker on Windows (#29706) * chore: disable default async spellchecker on Windows * chore: disable kWinRetrieveSuggestionsOnlyOnDemand in feature list * chore: remove incorrectly backported patches Co-authored-by: VerteDinde Co-authored-by: VerteDinde Co-authored-by: Keeley Hammond --- shell/browser/feature_list.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shell/browser/feature_list.cc b/shell/browser/feature_list.cc index 618c9b77ad5bb..366eec41403f4 100644 --- a/shell/browser/feature_list.cc +++ b/shell/browser/feature_list.cc @@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/metrics/field_trial.h" +#include "components/spellcheck/common/spellcheck_features.h" #include "content/public/common/content_features.h" #include "electron/buildflags/buildflags.h" #include "media/base/media_switches.h" @@ -41,6 +42,13 @@ void InitializeFeatureList() { #if !BUILDFLAG(ENABLE_PICTURE_IN_PICTURE) disable_features += std::string(",") + media::kPictureInPicture.name; #endif + +#if defined(OS_WIN) + // Disable async spellchecker suggestions for Windows, which causes + // an empty suggestions list to be returned + disable_features += + std::string(",") + spellcheck::kWinRetrieveSuggestionsOnlyOnDemand.name; +#endif base::FeatureList::InitializeInstance(enable_features, disable_features); } From f313d00083d7588f0ec281ed6dacdcd8aeda741a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 16:15:51 -0400 Subject: [PATCH 61/79] docs: update WebPreferences default values for Electron 12 (#29710) Updates the values for `contextIsolation` and `worldSafeExecuteJavaScript` for Electron 12. Co-authored-by: Erick Zhao --- docs/api/browser-window.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 5e88e5addc578..b772be7f76a20 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -339,7 +339,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. more details. * `contextIsolation` Boolean (optional) - Whether to run Electron APIs and the specified `preload` script in a separate JavaScript context. Defaults - to `false`. The context that the `preload` script runs in will only have + to `true`. The context that the `preload` script runs in will only have access to its own dedicated `document` and `window` globals, as well as its own set of JavaScript builtins (`Array`, `Object`, `JSON`, etc.), which are all invisible to the loaded content. The Electron API will only @@ -351,8 +351,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. context in the dev tools by selecting the 'Electron Isolated Context' entry in the combo box at the top of the Console tab. * `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values - can't unsafely cross between worlds when using `contextIsolation`. The default - is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_ + can't unsafely cross between worlds when using `contextIsolation`. Defaults to `true`. _Deprecated_ * `nativeWindowOpen` Boolean (optional) - Whether to use native `window.open()`. Defaults to `false`. Child windows will always have node integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently From d6c51bc456ebe1856e98b96175d507a17de4b1d1 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 13:23:07 -0700 Subject: [PATCH 62/79] chore: bump chromium in DEPS to 91.0.4472.106 (#29700) Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 845aed978b868..9d863be2dc485 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ gclient_gn_args = [ vars = { 'chromium_version': - '91.0.4472.101', + '91.0.4472.106', 'node_version': 'v14.16.0', 'nan_version': From ad2f34491c9629e1e83a0db19eb34ffd5c176be5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 16 Jun 2021 10:13:02 -0700 Subject: [PATCH 63/79] fix setWindowOpenHandler call syntax (#29726) Co-authored-by: kdau --- docs/api/window-open.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/api/window-open.md b/docs/api/window-open.md index c235deea7925b..a4a4639456b02 100644 --- a/docs/api/window-open.md +++ b/docs/api/window-open.md @@ -117,15 +117,18 @@ const mainWindow = new BrowserWindow({ mainWindow.webContents.setWindowOpenHandler(({ url }) => { if (url === 'about:blank') { return { - frame: false, - fullscreenable: false, - backgroundColor: 'black', - webPreferences: { - preload: 'my-child-window-preload-script.js' + action: 'allow', + overrideBrowserWindowOptions: { + frame: false, + fullscreenable: false, + backgroundColor: 'black', + webPreferences: { + preload: 'my-child-window-preload-script.js' + } } } } - return false + return { action: 'deny' } }) ``` From 10c238c0ea1d33075bc59ba6baf236cdb71d5a81 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jun 2021 15:20:05 +0900 Subject: [PATCH 64/79] fix: potential crash when setting vibrancy (#29722) Co-authored-by: Shelley Vohr --- shell/browser/native_window_mac.mm | 8 ++++---- spec-main/api-browser-window-spec.ts | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 270fa17a718c2..96c533e1bc947 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1353,8 +1353,6 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return; } - vibrancy_type_ = type; - NSVisualEffectView* effect_view = (NSVisualEffectView*)vibrant_view; if (effect_view == nil) { effect_view = [[[NSVisualEffectView alloc] @@ -1383,7 +1381,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { node::Environment* env = node::Environment::GetCurrent(JavascriptEnvironment::GetIsolate()); - NSVisualEffectMaterial vibrancyType; + NSVisualEffectMaterial vibrancyType{}; if (type == "appearance-based") { EmitWarning(env, "NSVisualEffectMaterialAppearanceBased" + dep_warn, "electron"); @@ -1440,8 +1438,10 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } } - if (vibrancyType) + if (vibrancyType) { + vibrancy_type_ = type; [effect_view setMaterial:vibrancyType]; + } } void NativeWindowMac::SetWindowButtonVisibility(bool visible) { diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 1632832ff998c..284a15e16158a 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -1597,6 +1597,13 @@ describe('BrowserWindow module', () => { w.setVibrancy('' as any); }).to.not.throw(); }); + + it('does not crash if vibrancy is set to an invalid value', () => { + const w = new BrowserWindow({ show: false }); + expect(() => { + w.setVibrancy('i-am-not-a-valid-vibrancy-type' as any); + }).to.not.throw(); + }); }); ifdescribe(process.platform === 'darwin')('trafficLightPosition', () => { From b143042c7fe9e4da667eecd2fc8451fb84893cfa Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jun 2021 15:21:27 +0900 Subject: [PATCH 65/79] fix: draggable regions with devtools open (#29734) Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_browser_window.cc | 4 ++++ shell/browser/api/electron_api_browser_window.h | 4 ---- .../api/electron_api_browser_window_mac.mm | 4 ---- .../api/electron_api_browser_window_views.cc | 15 ++++++++++++++- .../views/inspectable_web_contents_view_views.cc | 7 +++++++ 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index 70acd9527b4e1..d9df95019a6d0 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -393,6 +393,10 @@ void BrowserWindow::ResetBrowserViews() { #endif } +void BrowserWindow::OnDevToolsResized() { + UpdateDraggableRegions(draggable_regions_); +} + void BrowserWindow::SetVibrancy(v8::Isolate* isolate, v8::Local value) { std::string type = gin::V8ToString(isolate, value); diff --git a/shell/browser/api/electron_api_browser_window.h b/shell/browser/api/electron_api_browser_window.h index 6dc0764696f46..8d8c4ff2ad526 100644 --- a/shell/browser/api/electron_api_browser_window.h +++ b/shell/browser/api/electron_api_browser_window.h @@ -63,9 +63,7 @@ class BrowserWindow : public BaseWindow, void OnActivateContents() override; void OnPageTitleUpdated(const std::u16string& title, bool explicit_set) override; -#if defined(OS_MAC) void OnDevToolsResized() override; -#endif // NativeWindowObserver: void RequestPreferredWidth(int* width) override; @@ -121,9 +119,7 @@ class BrowserWindow : public BaseWindow, // it should be cancelled when we can prove that the window is responsive. base::CancelableClosure window_unresponsive_closure_; -#if defined(OS_MAC) std::vector draggable_regions_; -#endif v8::Global web_contents_; base::WeakPtr api_web_contents_; diff --git a/shell/browser/api/electron_api_browser_window_mac.mm b/shell/browser/api/electron_api_browser_window_mac.mm index 73a7da2c90eaf..fa8e2919e0817 100644 --- a/shell/browser/api/electron_api_browser_window_mac.mm +++ b/shell/browser/api/electron_api_browser_window_mac.mm @@ -37,10 +37,6 @@ [contentView viewDidMoveToWindow]; } -void BrowserWindow::OnDevToolsResized() { - UpdateDraggableRegions(draggable_regions_); -} - void BrowserWindow::UpdateDraggableRegions( const std::vector& regions) { if (window_->has_frame()) diff --git a/shell/browser/api/electron_api_browser_window_views.cc b/shell/browser/api/electron_api_browser_window_views.cc index 1b50f7f0fe46b..436c2dad716be 100644 --- a/shell/browser/api/electron_api_browser_window_views.cc +++ b/shell/browser/api/electron_api_browser_window_views.cc @@ -5,6 +5,7 @@ #include "shell/browser/api/electron_api_browser_window.h" #include "shell/browser/native_window_views.h" +#include "ui/aura/window.h" namespace electron { @@ -14,8 +15,20 @@ void BrowserWindow::UpdateDraggableRegions( const std::vector& regions) { if (window_->has_frame()) return; + + if (&draggable_regions_ != ®ions) { + auto const offset = + web_contents()->GetNativeView()->GetBoundsInRootWindow(); + auto snapped_regions = mojo::Clone(regions); + for (auto& snapped_region : snapped_regions) { + snapped_region->bounds.Offset(offset.x(), offset.y()); + } + + draggable_regions_ = mojo::Clone(snapped_regions); + } + static_cast(window_.get()) - ->UpdateDraggableRegions(regions); + ->UpdateDraggableRegions(draggable_regions_); } } // namespace api diff --git a/shell/browser/ui/views/inspectable_web_contents_view_views.cc b/shell/browser/ui/views/inspectable_web_contents_view_views.cc index 74b1f706cf425..dbc356fdeaefd 100644 --- a/shell/browser/ui/views/inspectable_web_contents_view_views.cc +++ b/shell/browser/ui/views/inspectable_web_contents_view_views.cc @@ -128,6 +128,10 @@ void InspectableWebContentsViewViews::ShowDevTools(bool activate) { } else { devtools_window_->ShowInactive(); } + + // Update draggable regions to account for the new dock position. + if (GetDelegate()) + GetDelegate()->DevToolsResized(); } else { devtools_web_view_->SetVisible(true); devtools_web_view_->SetWebContents( @@ -228,6 +232,9 @@ void InspectableWebContentsViewViews::Layout() { devtools_web_view_->SetBoundsRect(new_devtools_bounds); contents_web_view_->SetBoundsRect(new_contents_bounds); + + if (GetDelegate()) + GetDelegate()->DevToolsResized(); } } // namespace electron From b35f904e588cf885a6a5aca1acc6fae725ba480b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jun 2021 15:29:27 +0900 Subject: [PATCH 66/79] fix: ensure detached devtools are not always draggable (#29737) Co-authored-by: Samuel Attard --- .../electron_inspectable_web_contents_view.mm | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm index cdd10716c0ee8..acd226d047a6a 100644 --- a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm +++ b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm @@ -160,13 +160,12 @@ - (void)setIsDocked:(BOOL)docked activate:(BOOL)activate { // Switch to new state. devtools_docked_ = docked; + auto* inspectable_web_contents = + inspectableWebContentsView_->inspectable_web_contents(); + auto* devToolsWebContents = + inspectable_web_contents->GetDevToolsWebContents(); + auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView(); if (!docked) { - auto* inspectable_web_contents = - inspectableWebContentsView_->inspectable_web_contents(); - auto* devToolsWebContents = - inspectable_web_contents->GetDevToolsWebContents(); - auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView(); - auto styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSMiniaturizableWindowMask | NSWindowStyleMaskResizable | NSTexturedBackgroundWindowMask | @@ -189,6 +188,9 @@ - (void)setIsDocked:(BOOL)docked activate:(BOOL)activate { devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [contentView addSubview:devToolsView]; + [devToolsView setMouseDownCanMoveWindow:NO]; + } else { + [devToolsView setMouseDownCanMoveWindow:YES]; } [self setDevToolsVisible:YES activate:activate]; } From c94ab729dd9d9bdec69cbca2d263079e7eb3cf23 Mon Sep 17 00:00:00 2001 From: George Xu <33054982+georgexu99@users.noreply.github.com> Date: Thu, 17 Jun 2021 14:03:07 -0700 Subject: [PATCH 67/79] docs: added guide and updated docs for Tray (#29385) (#29762) * docs: added guide and updated docs for Tray * docs: improve clarity --- docs/fiddles/native-ui/tray/index.html | 83 +----------------------- docs/fiddles/native-ui/tray/main.js | 85 ++++--------------------- docs/fiddles/native-ui/tray/renderer.js | 35 ---------- docs/tutorial/tray.md | 83 ++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 188 deletions(-) delete mode 100644 docs/fiddles/native-ui/tray/renderer.js create mode 100644 docs/tutorial/tray.md diff --git a/docs/fiddles/native-ui/tray/index.html b/docs/fiddles/native-ui/tray/index.html index 2a330342f2df0..81a25e1a864ef 100644 --- a/docs/fiddles/native-ui/tray/index.html +++ b/docs/fiddles/native-ui/tray/index.html @@ -16,91 +16,12 @@

Open the - full API documentation (opens in new window) + full API documentation in your browser.

- -
-
-
- - -
-

- The demo button sends a message to the main process using the - ipc module. In the main process the app is told to - place an icon, with a context menu, in the tray. -

- -

- In this example the tray icon can be removed by clicking 'Remove' in - the context menu or selecting the demo button again. -

-
Main Process
-
-              
-const path = require('path')
-const {ipcMain, app, Menu, Tray} = require('electron')
-
-let appIcon = null
-
-ipcMain.on('put-in-tray', (event) => {
-  const iconName = process.platform === 'win32' ? 'windows-icon.png' : 'iconTemplate.png'
-  const iconPath = path.join(__dirname, iconName)
-  appIcon = new Tray(iconPath)
-
-  const contextMenu = Menu.buildFromTemplate([{
-    label: 'Remove',
-    click: () => {
-      event.sender.send('tray-removed')
-    }
-  }])
-
-  appIcon.setToolTip('Electron Demo in the tray.')
-  appIcon.setContextMenu(contextMenu)
-})
-
-ipcMain.on('remove-tray', () => {
-  appIcon.destroy()
-})
-
-app.on('window-all-closed', () => {
-  if (appIcon) appIcon.destroy()
-})
-              
-            
-
Renderer Process
-
-              
-const ipc = require('electron').ipcRenderer
-
-const trayBtn = document.getElementById('put-in-tray')
-let trayOn = false
-
-trayBtn.addEventListener('click', function (event) {
-  if (trayOn) {
-    trayOn = false
-    document.getElementById('tray-countdown').innerHTML = ''
-    ipc.send('remove-tray')
-  } else {
-    trayOn = true
-    const message = 'Click demo again to remove.'
-    document.getElementById('tray-countdown').innerHTML = message
-    ipc.send('put-in-tray')
-  }
-})
-// Tray removed from context menu on icon
-ipc.on('tray-removed', function () {
-  ipc.send('remove-tray')
-  trayOn = false
-  document.getElementById('tray-countdown').innerHTML = ''
-})                  
-              
-            
-

ProTip

Tray support in Linux. @@ -109,7 +30,7 @@

ProTip

will need to install libappindicator1 to make the tray icon work. See the - full API documentation (opens in new window) + full API documentation for more details about using Tray on Linux.

diff --git a/docs/fiddles/native-ui/tray/main.js b/docs/fiddles/native-ui/tray/main.js index 9a94efe327315..3d5ce65e02a43 100644 --- a/docs/fiddles/native-ui/tray/main.js +++ b/docs/fiddles/native-ui/tray/main.js @@ -1,77 +1,18 @@ -// Modules to control application life and create native browser window -const { ipcMain, app, nativeImage, Menu, Tray, BrowserWindow } = require('electron') +const { app, Tray, Menu, nativeImage } = require('electron') -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. -let mainWindow -let appIcon = null +let tray -function createWindow () { - // Create the browser window. - mainWindow = new BrowserWindow({ - width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } - }) +app.whenReady().then(() => { + const icon = nativeImage.createFromDataURL('data:image/png;base64,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') + tray = new Tray(icon) - // and load the index.html of the app. - mainWindow.loadFile('index.html') + const contextMenu = Menu.buildFromTemplate([ + { label: 'Item1', type: 'radio' }, + { label: 'Item2', type: 'radio' }, + { label: 'Item3', type: 'radio', checked: true }, + { label: 'Item4', type: 'radio' } + ]) - // Open the DevTools. - // mainWindow.webContents.openDevTools() - - // Emitted when the window is closed. - mainWindow.on('closed', function () { - // Dereference the window object, usually you would store windows - // in an array if your app supports multi windows, this is the time - // when you should delete the corresponding element. - mainWindow = null - }) -} - -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.whenReady().then(createWindow) - -// Quit when all windows are closed. -app.on('window-all-closed', function () { - // On macOS it is common for applications and their menu bar - // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== 'darwin') { - app.quit() - } + tray.setToolTip('This is my application.') + tray.setContextMenu(contextMenu) }) - -app.on('activate', function () { - // On macOS it is common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (mainWindow === null) { - createWindow() - } -}) - -ipcMain.on('put-in-tray', (event) => { - const icon = nativeImage.createFromDataURL('data:image/png;base64,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') - - appIcon = new Tray(icon) - - const contextMenu = Menu.buildFromTemplate([{ - label: 'Remove', - click: () => { - event.sender.send('tray-removed') - } - }]) - - appIcon.setToolTip('Electron Demo in the tray.') - appIcon.setContextMenu(contextMenu) -}) - -ipcMain.on('remove-tray', () => { - appIcon.destroy() -}) - -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and require them here. diff --git a/docs/fiddles/native-ui/tray/renderer.js b/docs/fiddles/native-ui/tray/renderer.js deleted file mode 100644 index 40eee8c859bdf..0000000000000 --- a/docs/fiddles/native-ui/tray/renderer.js +++ /dev/null @@ -1,35 +0,0 @@ -const { ipcRenderer, shell } = require('electron') - -const trayBtn = document.getElementById('put-in-tray') -const links = document.querySelectorAll('a[href]') - -let trayOn = false - -trayBtn.addEventListener('click', function (event) { - if (trayOn) { - trayOn = false - document.getElementById('tray-countdown').innerHTML = '' - ipcRenderer.send('remove-tray') - } else { - trayOn = true - const message = 'Click demo again to remove.' - document.getElementById('tray-countdown').innerHTML = message - ipcRenderer.send('put-in-tray') - } -}) -// Tray removed from context menu on icon -ipcRenderer.on('tray-removed', function () { - ipcRenderer.send('remove-tray') - trayOn = false - document.getElementById('tray-countdown').innerHTML = '' -}) - -Array.prototype.forEach.call(links, (link) => { - const url = link.getAttribute('href') - if (url.indexOf('http') === 0) { - link.addEventListener('click', (e) => { - e.preventDefault() - shell.openExternal(url) - }) - } -}) \ No newline at end of file diff --git a/docs/tutorial/tray.md b/docs/tutorial/tray.md new file mode 100644 index 0000000000000..e12d093b53627 --- /dev/null +++ b/docs/tutorial/tray.md @@ -0,0 +1,83 @@ +--- +title: Tray +description: This guide will take you through the process of creating +a Tray icon with its own context menu to the system's notification area. +slug: tray +hide_title: true +--- + +# Tray + +## Overview + + + +This guide will take you through the process of creating a +[Tray](https://www.electronjs.org/docs/api/tray) icon with +its own context menu to the system's notification area. + +On MacOS and Ubuntu, the Tray will be located on the top +right corner of your screen, adjacent to your battery and wifi icons. +On Windows, the Tray will usually be located in the bottom right corner. + +## Example + +### main.js + +First we must import `app`, `Tray`, `Menu`, `nativeImage` from `electron`. + +```js +const { app, Tray, Menu, nativeImage } = require('electron') +``` + +Next we will create our Tray. To do this, we will use a +[`NativeImage`](https://www.electronjs.org/docs/api/native-image) icon, +which can be created through any one of these +[methods](https://www.electronjs.org/docs/api/native-image#methods). +Note that we wrap our Tray creation code within an +[`app.whenReady`](https://www.electronjs.org/docs/api/app#appwhenready) +as we will need to wait for our electron app to finish initializing. + +```js title='main.js' +let tray + +app.whenReady().then(() => { + const icon = nativeImage.createFromPath('path/to/asset.png') + tray = new Tray(icon) + + // note: your contextMenu, Tooltip and Title code will go here! +}) +``` + +Great! Now we can start attaching a context menu to our Tray, like so: + +```js +const contextMenu = Menu.buildFromTemplate([ + { label: 'Item1', type: 'radio' }, + { label: 'Item2', type: 'radio' }, + { label: 'Item3', type: 'radio', checked: true }, + { label: 'Item4', type: 'radio' } +]) + +tray.setContextMenu(contextMenu) +``` + +The code above will create 4 separate radio-type items in the context menu. +To read more about constructing native menus, click +[here](https://www.electronjs.org/docs/api/menu#menubuildfromtemplatetemplate). + +Finally, let's give our tray a tooltip and a title. + +```js +tray.setToolTip('This is my application') +tray.setTitle('This is my title') +``` + +## Conclusion + +After you start your electron app, you should see the Tray residing +in either the top or bottom right of your screen, depending on your +operating system. + +```fiddle docs/fiddles/native-ui/tray +``` From 5d36cdf485817166ca9c5c19cbfa11b9c2123afb Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Fri, 18 Jun 2021 02:34:03 -0700 Subject: [PATCH 68/79] fix: color select eyedropper not working within DevTools (#29729) (#29760) Co-authored-by: Shelley Vohr --- chromium_src/BUILD.gn | 2 ++ .../browser/api/electron_api_web_contents.cc | 24 +++++++++++++++++++ shell/browser/api/electron_api_web_contents.h | 6 +++++ shell/browser/ui/inspectable_web_contents.cc | 5 +++- .../ui/inspectable_web_contents_delegate.h | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index 8a154b448b3e4..42b71245719a7 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -145,6 +145,8 @@ static_library("chrome") { if (enable_color_chooser) { sources += [ + "//chrome/browser/devtools/devtools_eye_dropper.cc", + "//chrome/browser/devtools/devtools_eye_dropper.h", "//chrome/browser/platform_util.cc", "//chrome/browser/platform_util.h", "//chrome/browser/ui/browser_dialogs.h", diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index d46c4d9d82b4a..5c3ce8999bb66 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -3469,6 +3469,30 @@ void WebContents::DevToolsSearchInPath(int request_id, file_system_path)); } +void WebContents::DevToolsSetEyeDropperActive(bool active) { + auto* web_contents = GetWebContents(); + if (!web_contents) + return; + + if (active) { + eye_dropper_ = std::make_unique( + web_contents, base::BindRepeating(&WebContents::ColorPickedInEyeDropper, + base::Unretained(this))); + } else { + eye_dropper_.reset(); + } +} + +void WebContents::ColorPickedInEyeDropper(int r, int g, int b, int a) { + base::DictionaryValue color; + color.SetInteger("r", r); + color.SetInteger("g", g); + color.SetInteger("b", b); + color.SetInteger("a", a); + inspectable_web_contents_->CallClientFunction( + "DevToolsAPI.eyeDropperPickedColor", &color, nullptr, nullptr); +} + #if defined(TOOLKIT_VIEWS) && !defined(OS_MAC) gfx::ImageSkia WebContents::GetDevToolsWindowIcon() { if (!owner_window()) diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 51e1ec79571ab..3d746fc37fdbc 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -14,6 +14,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/observer_list_types.h" +#include "chrome/browser/devtools/devtools_eye_dropper.h" #include "chrome/browser/devtools/devtools_file_system_indexer.h" #include "content/common/cursors/webcursor.h" #include "content/common/frame.mojom.h" @@ -663,6 +664,7 @@ class WebContents : public gin::Wrappable, void DevToolsSearchInPath(int request_id, const std::string& file_system_path, const std::string& query) override; + void DevToolsSetEyeDropperActive(bool active) override; // InspectableWebContentsViewDelegate: #if defined(TOOLKIT_VIEWS) && !defined(OS_MAC) @@ -676,6 +678,8 @@ class WebContents : public gin::Wrappable, // Destroy the managed InspectableWebContents object. void ResetManagedWebContents(bool async); + void ColorPickedInEyeDropper(int r, int g, int b, int a); + // DevTools index event callbacks. void OnDevToolsIndexingWorkCalculated(int request_id, const std::string& file_system_path, @@ -747,6 +751,8 @@ class WebContents : public gin::Wrappable, scoped_refptr devtools_file_system_indexer_; + std::unique_ptr eye_dropper_; + ElectronBrowserContext* browser_context_; // The stored InspectableWebContents object. diff --git a/shell/browser/ui/inspectable_web_contents.cc b/shell/browser/ui/inspectable_web_contents.cc index 4cf9330aca6db..74463ee631eb4 100644 --- a/shell/browser/ui/inspectable_web_contents.cc +++ b/shell/browser/ui/inspectable_web_contents.cc @@ -784,7 +784,10 @@ void InspectableWebContents::SearchInPath(int request_id, void InspectableWebContents::SetWhitelistedShortcuts( const std::string& message) {} -void InspectableWebContents::SetEyeDropperActive(bool active) {} +void InspectableWebContents::SetEyeDropperActive(bool active) { + if (delegate_) + delegate_->DevToolsSetEyeDropperActive(active); +} void InspectableWebContents::ShowCertificateViewer( const std::string& cert_chain) {} diff --git a/shell/browser/ui/inspectable_web_contents_delegate.h b/shell/browser/ui/inspectable_web_contents_delegate.h index 363ef1e202771..fce4344212482 100644 --- a/shell/browser/ui/inspectable_web_contents_delegate.h +++ b/shell/browser/ui/inspectable_web_contents_delegate.h @@ -35,6 +35,7 @@ class InspectableWebContentsDelegate { virtual void DevToolsSearchInPath(int request_id, const std::string& file_system_path, const std::string& query) {} + virtual void DevToolsSetEyeDropperActive(bool active) {} }; } // namespace electron From 301108ece49e6f1a97d299d688f11f5f17079c11 Mon Sep 17 00:00:00 2001 From: George Xu <33054982+georgexu99@users.noreply.github.com> Date: Fri, 18 Jun 2021 11:02:42 -0700 Subject: [PATCH 69/79] docs: Add clarification for Visual Zoom behavior (#28860) (#29764) * Add clarification for visual zoom documentation Co-authored-by: Michael Kozakov --- docs/api/web-frame.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index 9891c5d09ab0c..98fa952c83fbc 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -62,6 +62,11 @@ Sets the maximum and minimum pinch-to-zoom level. > webFrame.setVisualZoomLevelLimits(1, 3) > ``` +> **NOTE**: Visual zoom only applies to pinch-to-zoom behavior. Cmd+/-/0 zoom shortcuts are +> controlled by the 'zoomIn', 'zoomOut', and 'resetZoom' MenuItem roles in the application +> Menu. To disable shortcuts, manually [define the Menu](./menu.md#examples) and omit zoom roles +> from the definition. + ### `webFrame.setSpellCheckProvider(language, provider)` * `language` String From 16b09a676f2c90e8dc32121d0a8ef443f943c350 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 10:10:05 +0900 Subject: [PATCH 70/79] docs: clarify use of ELECTRON_SKIP_BINARY_DOWNLOAD (#29745) Co-authored-by: Erick Zhao --- docs/tutorial/installation.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/tutorial/installation.md b/docs/tutorial/installation.md index a75daa68246c1..87b6623fd7bf4 100644 --- a/docs/tutorial/installation.md +++ b/docs/tutorial/installation.md @@ -135,14 +135,18 @@ a text file. A typical cache might look like this: ## Skip binary download -When installing the `electron` NPM package, it automatically downloads the electron binary. - -This can sometimes be unnecessary, e.g. in a CI environment, when testing another component. - -To prevent the binary from being downloaded when you install all npm dependencies you can set the environment variable `ELECTRON_SKIP_BINARY_DOWNLOAD`. -E.g.: - -```sh +Under the hood, Electron's JavaScript API binds to a binary that contains its +implementations. Because this binary is crucial to the function of any Electron app, +it is downloaded by default in the `postinstall` step every time you install `electron` +from the npm registry. + +However, if you want to install your project's dependencies but don't need to use +Electron functionality, you can set the `ELECTRON_SKIP_BINARY_DOWNLOAD` environment +variable to prevent the binary from being downloaded. For instance, this feature can +be useful in continuous integration environments when running unit tests that mock +out the `electron` module. + +```sh npm2yarn ELECTRON_SKIP_BINARY_DOWNLOAD=1 npm install ``` From fa683ef8b7173ace95de95aa8f5167819914d922 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 13:59:36 +0900 Subject: [PATCH 71/79] fix: fix hover state not clear bug when BrowserWindow is not resizable (#611) (#29800) Co-authored-by: sssooonnnggg --- shell/browser/ui/views/frameless_view.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/shell/browser/ui/views/frameless_view.cc b/shell/browser/ui/views/frameless_view.cc index 6c5ae23ec2f34..734896a66ee53 100644 --- a/shell/browser/ui/views/frameless_view.cc +++ b/shell/browser/ui/views/frameless_view.cc @@ -38,6 +38,13 @@ int FramelessView::ResizingBorderHitTest(const gfx::Point& point) { bool can_ever_resize = frame_->widget_delegate() ? frame_->widget_delegate()->CanResize() : false; + + // https://github.com/electron/electron/issues/611 + // If window isn't resizable, we should always return HTCLIENT, otherwise the + // hover state of DOM will not be cleared probably. + if (!can_ever_resize) + return HTCLIENT; + // Don't allow overlapping resize handles when the window is maximized or // fullscreen, as it can't be resized in those states. int resize_border = frame_->IsMaximized() || frame_->IsFullscreen() From df27597c0588aeaef142ee2cd1a364da1003303c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 14:00:25 +0900 Subject: [PATCH 72/79] docs: fix frontmatter for Tray tutorial (#29803) Co-authored-by: Erick Zhao --- docs/tutorial/tray.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/tray.md b/docs/tutorial/tray.md index e12d093b53627..1f0c8e6b6fc88 100644 --- a/docs/tutorial/tray.md +++ b/docs/tutorial/tray.md @@ -1,7 +1,7 @@ --- title: Tray description: This guide will take you through the process of creating -a Tray icon with its own context menu to the system's notification area. + a Tray icon with its own context menu to the system's notification area. slug: tray hide_title: true --- From 252805ddb456e47cfc5178d1c9f81e6638acd525 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 15:49:17 +0900 Subject: [PATCH 73/79] fix: microtasks policy in CreateEnvironment (#29808) * fix: microtasks policy in CreateEnvironment Microtasks policy should not be updated for the renderer because `NodeBindings::CreateEnvironment` might be entered with or without `UvRunOnce()` on stack. One of the examples of such calls is `window.open()` which is possible to invoke while `uv_run()` is still running (e.g. with `setImmediate()`). All in all, it doesn't matter that much which policy we use since `v8::MicrotasksScope` has a check for the policy in its destructor and no commits will be made if the policy is `kExplicit`. It is important, however, to not change the policy in the middle of `UvRunOnce()` so we should respect whatever we currently have and move on. Fix: #29463 * Move test to a better place * Update spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.html Co-authored-by: Jeremy Rose * Update spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.html Co-authored-by: Jeremy Rose * simplify crash-case * comment * fix comment Co-authored-by: Fedor Indutny Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Co-authored-by: Jeremy Rose Co-authored-by: Fedor Indutny --- shell/common/node_bindings.cc | 10 ++++++--- .../setimmediate-window-open-crash/index.html | 21 +++++++++++++++++++ .../setimmediate-window-open-crash/index.js | 21 +++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.html create mode 100644 spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.js diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 8d3b263078946..d86c2dcc971a0 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -489,9 +489,13 @@ node::Environment* NodeBindings::CreateEnvironment( // Node.js requires that microtask checkpoints be explicitly invoked. is.policy = v8::MicrotasksPolicy::kExplicit; } else { - // Match Blink's behavior by allowing microtasks invocation to be controlled - // by MicrotasksScope objects. - is.policy = v8::MicrotasksPolicy::kScoped; + // Blink expects the microtasks policy to be kScoped, but Node.js expects it + // to be kExplicit. In the renderer, there can be many contexts within the + // same isolate, so we don't want to change the existing policy here, which + // could be either kExplicit or kScoped depending on whether we're executing + // from within a Node.js or a Blink entrypoint. Instead, the policy is + // toggled to kExplicit when entering Node.js through UvRunOnce. + is.policy = context->GetIsolate()->GetMicrotasksPolicy(); // We do not want to use Node.js' message listener as it interferes with // Blink's. diff --git a/spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.html b/spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.html new file mode 100644 index 0000000000000..9bb131aef4548 --- /dev/null +++ b/spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.html @@ -0,0 +1,21 @@ + + + + + diff --git a/spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.js b/spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.js new file mode 100644 index 0000000000000..2032f096c11c0 --- /dev/null +++ b/spec-main/fixtures/crash-cases/setimmediate-window-open-crash/index.js @@ -0,0 +1,21 @@ +const { app, BrowserWindow } = require('electron'); + +function createWindow () { + const mainWindow = new BrowserWindow({ + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + nativeWindowOpen: true + } + }); + + mainWindow.on('close', () => { + app.quit(); + }); + + mainWindow.loadFile('index.html'); +} + +app.whenReady().then(() => { + createWindow(); +}); From f39ac0764d1b46fb9644eeef56ff75375b09736b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 19:06:06 +0900 Subject: [PATCH 74/79] fix: update Squirel.Mac to fix CPU spin during update (#29805) * fix: update Squirel.Mac to fix CPU spin during update Refs: https://github.com/Squirrel/Squirrel.Mac/pull/259 Closes: #29119 * chore: update patches Co-authored-by: Samuel Attard Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> --- DEPS | 2 +- patches/squirrel.mac/build_add_gn_config.patch | 2 +- ...hat_self_is_retained_until_the_racsignal_is_complete.patch | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 9d863be2dc485..75a3bf772a573 100644 --- a/DEPS +++ b/DEPS @@ -20,7 +20,7 @@ vars = { 'nan_version': 'v2.14.2', 'squirrel.mac_version': - 'cdc0729c8bf8576bfef18629186e1e9ecf1b0d9f', + '0e5d146ba13101a1302d59ea6e6e0b3cace4ae38', 'pyyaml_version': '3.12', diff --git a/patches/squirrel.mac/build_add_gn_config.patch b/patches/squirrel.mac/build_add_gn_config.patch index 67cb585f8ba0c..c5093de623021 100644 --- a/patches/squirrel.mac/build_add_gn_config.patch +++ b/patches/squirrel.mac/build_add_gn_config.patch @@ -449,7 +449,7 @@ index 85073b2f5d58d3e071fb6ef30598973b4d00eda8..c81c820d61da3c7d1cfd2c516147c954 NSString * const SQRLUpdaterErrorDomain = @"SQRLUpdaterErrorDomain"; diff --git a/Squirrel/SQRLZipArchiver.m b/Squirrel/SQRLZipArchiver.m -index f84127f642516078249925953e97621909265deb..478509cdd528db4fcfa340c6f93fa58a446957e6 100644 +index cbc8fb61c66184c1d76e8b9f52a89292b4e9939c..d0f20f022a4ff392b596bcf0be96ab3e3c835285 100644 --- a/Squirrel/SQRLZipArchiver.m +++ b/Squirrel/SQRLZipArchiver.m @@ -7,8 +7,8 @@ diff --git a/patches/squirrel.mac/fix_ensure_that_self_is_retained_until_the_racsignal_is_complete.patch b/patches/squirrel.mac/fix_ensure_that_self_is_retained_until_the_racsignal_is_complete.patch index e511986871993..5bc4554cfb904 100644 --- a/patches/squirrel.mac/fix_ensure_that_self_is_retained_until_the_racsignal_is_complete.patch +++ b/patches/squirrel.mac/fix_ensure_that_self_is_retained_until_the_racsignal_is_complete.patch @@ -6,10 +6,10 @@ Subject: fix: ensure that self is retained until the RACSignal is complete Looks like the clang that Chromium uses is slightly smarter with ARC than whatever Squirrel was built with previously. We now need to keep a reference to self to keep it alive into the "then" of the RACSignal. diff --git a/Squirrel/SQRLZipArchiver.m b/Squirrel/SQRLZipArchiver.m -index 478509cdd528db4fcfa340c6f93fa58a446957e6..7c279bf73c368453bff4f922d76908c06dc378cd 100644 +index d0f20f022a4ff392b596bcf0be96ab3e3c835285..68f5dac8e553638f41306956df9d38eeda18f8f2 100644 --- a/Squirrel/SQRLZipArchiver.m +++ b/Squirrel/SQRLZipArchiver.m -@@ -134,7 +134,7 @@ - (RACSignal *)launchWithArguments:(NSArray *)arguments { +@@ -135,7 +135,7 @@ - (RACSignal *)launchWithArguments:(NSArray *)arguments { return [RACSignal zip:@[ self.taskTerminated, self.standardErrorData ] reduce:^(NSNumber *exitStatus, NSData *errorData) { From 5c1e1ee5ec3186fbfc5a420c62815ac4e02f9307 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 21:20:05 +0900 Subject: [PATCH 75/79] fix: do not cancel CORS preflight request on proxy auth. (#29811) * fix: do not cancel CORS preflight request on proxy auth. (#29266) * fix: do not cancel CORS preflight request on proxy auth. If connecting via proxy, preflight request can receive 407 header response from proxy. This does not mean request was finished even though it received headers (from proxy, not the destination server), so prevent "completing" and most importantly deleting it, which causes request to be canceled in network layer. Just continue to monitor it and await proper response from server. Also add circut breaker to cancel request if proxy auth failed 3 times (for example user keeps cancelling auth). This behavior happens only when app registered WebRequest api listeners. * Port chromium webrequest changes to electron code. Move relevant parts of chromium WebRequestProxyingURLLoaderFactory from https://chromium-review.googlesource.com/c/chromium/src/+/2011781 into electron ProxyingURLLoaderFactory. * Update code to upstreamed version and remove retyr count failsafe. Co-authored-by: Milan Burda * chore: add required header Co-authored-by: marekharanczyk <48673767+marekharanczyk@users.noreply.github.com> Co-authored-by: Milan Burda Co-authored-by: Cheng Zhao --- .../net/proxying_url_loader_factory.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index 5f5f9026b9bd3..d252c00eac074 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -13,6 +13,7 @@ #include "extensions/browser/extension_navigation_ui_data.h" #include "net/base/completion_repeating_callback.h" #include "net/base/load_flags.h" +#include "net/http/http_status_code.h" #include "net/http/http_util.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/cpp/features.h" @@ -313,6 +314,14 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnComplete( void ProxyingURLLoaderFactory::InProgressRequest::OnLoaderCreated( mojo::PendingReceiver receiver) { + // When CORS is involved there may be multiple network::URLLoader associated + // with this InProgressRequest, because CorsURLLoader may create a new + // network::URLLoader for the same request id in redirect handling - see + // CorsURLLoader::FollowRedirect. In such a case the old network::URLLoader + // is going to be detached fairly soon, so we don't need to take care of it. + // We need this explicit reset to avoid a DCHECK failure in mojo::Receiver. + header_client_receiver_.reset(); + header_client_receiver_.Bind(std::move(receiver)); if (for_cors_preflight_) { // In this case we don't have |target_loader_| and @@ -555,13 +564,17 @@ void ProxyingURLLoaderFactory::InProgressRequest:: override_headers_ = nullptr; if (for_cors_preflight_) { - // If this is for CORS preflight, there is no associated client. We notify - // the completion here, and deletes |this|. + // If this is for CORS preflight, there is no associated client. info_->AddResponseInfoFromResourceResponse(*current_response_); + // Do not finish proxied preflight requests that require proxy auth. + // The request is not finished yet, give control back to network service + // which will start authentication process. + if (info_->response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) + return; + // We notify the completion here, and delete |this|. factory_->web_request_api()->OnResponseStarted(&info_.value(), request_); factory_->web_request_api()->OnCompleted(&info_.value(), request_, net::OK); - // Deletes |this|. factory_->RemoveRequest(network_service_request_id_, request_id_); return; } From 6504c4bbc88eed40eaab46218ec9c1eff884bec4 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Mon, 21 Jun 2021 10:03:02 -0700 Subject: [PATCH 76/79] Bump v13.1.3 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 72ec0bba138c7..68ac8a21f037b 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.1.2 \ No newline at end of file +13.1.3 \ No newline at end of file diff --git a/package.json b/package.json index 912760da02a39..cde65cba11baa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.1.2", + "version": "13.1.3", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index c0e20e24cde5f..1ab8ffab930f3 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,1,2,0 - PRODUCTVERSION 13,1,2,0 + FILEVERSION 13,1,3,0 + PRODUCTVERSION 13,1,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "13.1.2" + VALUE "FileVersion", "13.1.3" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "13.1.2" + VALUE "ProductVersion", "13.1.3" VALUE "SquirrelAwareVersion", "1" END END From 69493ac51e6123afb6280824e74b574c697f5c1f Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Tue, 22 Jun 2021 01:58:07 -0400 Subject: [PATCH 77/79] ci: run linux arm tests on CircleCI (#29766) * ci: run linux arm tests on CircleCI * cleanup electron dirs after testing (cherry picked from commit 1c0a6045fbae8f9111fdd92730e5a81652e5a911) * use start-stop-daemon to kill Xvfb (cherry picked from commit 1d10a68c31eda061f3804d4f08d91e77f2647ab8) --- .circleci/config.yml | 83 ++++++++++++++++++++++------------ spec-main/version-bump-spec.ts | 2 +- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d8c47f323af12..b16a4f3c946d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,6 +57,16 @@ parameters: type: boolean default: false +# Executors +executors: + linux-arm: + resource_class: electronjs/linux-arm + machine: true + + linux-arm64: + resource_class: electronjs/linux-arm64 + machine: true + # The config expects the following environment variables to be set: # - "SLACK_WEBHOOK" Slack hook URL to send notifications. # @@ -239,7 +249,14 @@ step-maybe-cleanup-arm64-mac: &step-maybe-cleanup-arm64-mac killall Safari || echo "No Safari processes left running" rm -rf ~/Library/Application\ Support/Electron* rm -rf ~/Library/Application\ Support/electron* + elif [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then + XVFB=/usr/bin/Xvfb + /sbin/start-stop-daemon --stop --exec $XVFB || echo "Xvfb not running" + pkill electron || echo "electron not running" + rm -rf ~/.config/Electron* + rm -rf ~/.config/electron* fi + when: always step-checkout-electron: &step-checkout-electron @@ -763,7 +780,7 @@ step-verify-mksnapshot: &step-verify-mksnapshot command: | if [ "$IS_ASAN" != "1" ]; then cd src - if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then + if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/cross-arch-snapshots else python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default @@ -908,28 +925,6 @@ step-maybe-cross-arch-snapshot-store: &step-maybe-cross-arch-snapshot-store path: src/cross-arch-snapshots destination: cross-arch-snapshots -step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test - run: - name: Trigger an arm test on VSTS if applicable - command: | - cd src - # Only run for non-fork prs - if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then - #Trigger VSTS job, passing along CircleCI job number and branch to build - if [ "`uname`" == "Darwin" ]; then - if [ x"$MAS_BUILD" == x"true" ]; then - export DEVOPS_BUILD="electron-mas-arm64-testing" - else - export DEVOPS_BUILD="electron-osx-arm64-testing" - fi - echo "Triggering $DEVOPS_BUILD build on Azure DevOps" - node electron/script/release/ci-release-build.js --job=$DEVOPS_BUILD --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH - else - echo "Triggering electron-$TARGET_ARCH-testing build on VSTS" - node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH - fi - fi - step-maybe-generate-typescript-defs: &step-maybe-generate-typescript-defs run: name: Generate type declarations @@ -1344,7 +1339,7 @@ steps-tests: &steps-tests (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split)) 2>&1 | $ASAN_SYMBOLIZE (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split)) 2>&1 | $ASAN_SYMBOLIZE else - if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then + if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then export ELECTRON_SKIP_NATIVE_MODULE_TESTS=true (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging) (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging) @@ -1635,9 +1630,6 @@ commands: steps: - *step-save-out-cache - # Trigger tests on arm hardware if needed - - *step-maybe-trigger-arm-test - - *step-maybe-notify-slack-failure electron-publish: @@ -1981,7 +1973,7 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - electron-build: - persist: false + persist: true checkout: true use-out-cache: false @@ -2040,7 +2032,7 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - electron-build: - persist: false + persist: true checkout: true use-out-cache: false @@ -2437,6 +2429,24 @@ jobs: <<: *env-send-slack-notifications <<: *steps-verify-ffmpeg + linux-arm-testing-tests: + executor: linux-arm + environment: + <<: *env-arm + <<: *env-global + <<: *env-headless-testing + <<: *env-stack-dumping + <<: *steps-tests + + linux-arm64-testing-tests: + executor: linux-arm64 + environment: + <<: *env-arm64 + <<: *env-global + <<: *env-headless-testing + <<: *env-stack-dumping + <<: *steps-tests + osx-testing-x64-tests: <<: *machine-mac-large environment: @@ -2689,8 +2699,23 @@ workflows: - linux-ia32-testing - linux-arm-testing + - linux-arm-testing-tests: + filters: + branches: + # Do not run this on forked pull requests + ignore: /pull\/[0-9]+/ + requires: + - linux-arm-testing - linux-arm64-testing + - linux-arm64-testing-tests: + filters: + branches: + # Do not run this on forked pull requests + ignore: /pull\/[0-9]+/ + requires: + - linux-arm64-testing + - linux-arm64-testing-gn-check: requires: - linux-checkout-fast diff --git a/spec-main/version-bump-spec.ts b/spec-main/version-bump-spec.ts index aff7cd28b53f6..247f960b06f36 100644 --- a/spec-main/version-bump-spec.ts +++ b/spec-main/version-bump-spec.ts @@ -43,7 +43,7 @@ describe('version-bumper', () => { // On macOS Circle CI we don't have a real git environment due to running // gclient sync on a linux machine. These tests therefore don't run as expected. - ifdescribe(!(process.platform === 'linux' && process.arch === 'arm') && process.platform !== 'darwin')('nextVersion', () => { + ifdescribe(!(process.platform === 'linux' && process.arch.indexOf('arm') === 0) && process.platform !== 'darwin')('nextVersion', () => { const nightlyPattern = /[0-9.]*(-nightly.(\d{4})(\d{2})(\d{2}))$/g; const betaPattern = /[0-9.]*(-beta[0-9.]*)/g; From 2bf125f82a3c15c0f74ab95661b3e04f82da9ca7 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 14:59:06 +0900 Subject: [PATCH 78/79] fix: allow ppapi processes access to resource bundle on all platforms (#29830) * wip: debug resource bundle failure * fix: include ppapi subprocesses for windows resource bundle * fix: allow ppapi plugin processes access to resource bundle on all platforms. Aligns with chrome_main_delegate here: https://chromium-review.googlesource.com/c/chromium/src/+/2619003 * chore: remove incorrectly backported patches Co-authored-by: VerteDinde Co-authored-by: VerteDinde Co-authored-by: Keeley Hammond --- shell/app/electron_main_delegate.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/app/electron_main_delegate.cc b/shell/app/electron_main_delegate.cc index 91ffc49cd39c4..5d8da65aaf527 100644 --- a/shell/app/electron_main_delegate.cc +++ b/shell/app/electron_main_delegate.cc @@ -88,9 +88,9 @@ bool SubprocessNeedsResourceBundle(const std::string& process_type) { #if defined(OS_MAC) // Mac needs them too for scrollbar related images and for sandbox // profiles. - process_type == ::switches::kPpapiPluginProcess || process_type == ::switches::kGpuProcess || #endif + process_type == ::switches::kPpapiPluginProcess || process_type == ::switches::kRendererProcess || process_type == ::switches::kUtilityProcess; } From 9942b2ba8014f9025b4c8cb3e894476b58cc8f86 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Tue, 22 Jun 2021 10:20:05 -0700 Subject: [PATCH 79/79] Bump v13.1.4 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 68ac8a21f037b..5443013375b35 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.1.3 \ No newline at end of file +13.1.4 \ No newline at end of file diff --git a/package.json b/package.json index cde65cba11baa..b54662516c2ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.1.3", + "version": "13.1.4", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 1ab8ffab930f3..c1f10c6f462de 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 13,1,3,0 - PRODUCTVERSION 13,1,3,0 + FILEVERSION 13,1,4,0 + PRODUCTVERSION 13,1,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "13.1.3" + VALUE "FileVersion", "13.1.4" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "13.1.3" + VALUE "ProductVersion", "13.1.4" VALUE "SquirrelAwareVersion", "1" END END