diff --git a/.circleci/config.yml b/.circleci/config.yml index 08da988ba843f..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 @@ -505,6 +522,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: @@ -530,6 +548,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 @@ -687,6 +706,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: @@ -756,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 @@ -837,6 +861,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 @@ -890,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 @@ -1326,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) @@ -1617,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: @@ -1687,6 +1697,9 @@ commands: - *step-hunspell-build - *step-hunspell-store + # libcxx + - *step-maybe-generate-libcxx + # typescript defs - *step-maybe-generate-typescript-defs @@ -1960,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 @@ -2019,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 @@ -2416,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: @@ -2668,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/BUILD.gn b/BUILD.gn index 992ef531d7615..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") @@ -25,6 +26,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") @@ -290,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", @@ -329,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", @@ -676,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") { @@ -1292,13 +1300,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 +1402,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/DEPS b/DEPS index 6e7c5d1b51ccd..75a3bf772a573 100644 --- a/DEPS +++ b/DEPS @@ -14,13 +14,13 @@ gclient_gn_args = [ vars = { 'chromium_version': - '91.0.4472.69', + '91.0.4472.106', 'node_version': 'v14.16.0', 'nan_version': 'v2.14.2', 'squirrel.mac_version': - 'cdc0729c8bf8576bfef18629186e1e9ecf1b0d9f', + '0e5d146ba13101a1302d59ea6e6e0b3cace4ae38', 'pyyaml_version': '3.12', diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index e32b8a9446335..5443013375b35 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -13.0.0-beta.28 \ No newline at end of file +13.1.4 \ No newline at end of file diff --git a/build/args/all.gn b/build/args/all.gn index 9ac1f938c4402..86200887efe7c 100644 --- a/build/args/all.gn +++ b/build/args/all.gn @@ -28,3 +28,8 @@ 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 + +enable_cet_shadow_stack = false diff --git a/build/fuses/build.py b/build/fuses/build.py index f17c08fdb8451..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 @@ -49,8 +50,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] == "/"), object_pairs_hook=OrderedDict) 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/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/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/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 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' 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 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' } }) ``` 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) 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. 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/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/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/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/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/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/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/images/recent-documents.png b/docs/images/recent-documents.png index 331466f3b1b35..3542c1315e34a 100644 Binary files a/docs/images/recent-documents.png and b/docs/images/recent-documents.png differ diff --git a/docs/images/represented-file.png b/docs/images/represented-file.png index a2d3a3cdd7707..8ccb477ab7e5a 100644 Binary files a/docs/images/represented-file.png and b/docs/images/represented-file.png differ diff --git a/docs/images/versioning-sketch-2.png b/docs/images/versioning-sketch-2.png old mode 100755 new mode 100644 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' }) } 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/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/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 ``` 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 diff --git a/docs/tutorial/notifications.md b/docs/tutorial/notifications.md index 072e3de9aef9d..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 @@ -55,18 +51,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) 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 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 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) diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 6cfdbf9c939c7..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') @@ -440,7 +442,7 @@ window.addEventListener('DOMContentLoaded', () => { ``` -```fiddle docs/fiddles/quickstart +```fiddle docs/fiddles/quick-start ``` To summarize all the steps we've done: 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 diff --git a/docs/tutorial/represented-file.md b/docs/tutorial/represented-file.md index c59c0039a2bc2..7e681f52fa309 100644 --- a/docs/tutorial/represented-file.md +++ b/docs/tutorial/represented-file.md @@ -20,23 +20,40 @@ To set the represented file of window, you can use the ## Example -Starting with a working application from the -[Quick Start Guide](quick-start.md), add the following lines to the -`main.js` file: - ```javascript fiddle='docs/fiddles/features/represented-file' const { app, BrowserWindow } = require('electron') +const os = require('os'); + +function createWindow () { + const win = new BrowserWindow({ + width: 800, + height: 600 + }) +} app.whenReady().then(() => { 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) diff --git a/docs/tutorial/support.md b/docs/tutorial/support.md index 06878301f2efc..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)* @@ -64,9 +63,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 diff --git a/docs/tutorial/tray.md b/docs/tutorial/tray.md new file mode 100644 index 0000000000000..1f0c8e6b6fc88 --- /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 +``` 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/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/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/package.json b/package.json index b10bde8d3b81a..b54662516c2ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "13.0.0-beta.28", + "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/patches/chromium/.patches b/patches/chromium/.patches index de3d43644b94e..792d7d4f27389 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -109,3 +109,7 @@ 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 +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/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/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/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/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 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/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/patches/squirrel.mac/build_add_gn_config.patch b/patches/squirrel.mac/build_add_gn_config.patch index 486d2fa655612..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 @@ @@ -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 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) { 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); 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/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', 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); 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/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'); 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); 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; 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) 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) { 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; } 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; } } 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_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index 14f9ffac0c014..9817bb70b8dbd 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -394,6 +394,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/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"); 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_menu.cc b/shell/browser/api/electron_api_menu.cc index c992168e77893..87650266a4c1a 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 { +#if 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) +#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 89150f5bd341a..1efdad158cdd1 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; +#if 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..00ca7ccf5fcb8 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); +#if 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..ab34ff799504a 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)); } +#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. + 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/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_protocol.cc b/shell/browser/api/electron_api_protocol.cc index 952e32d0d3566..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)); @@ -89,6 +88,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 +134,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/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/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index bf73c0396a2cb..0c204f980f840 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2034,7 +2034,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 @@ -3485,6 +3486,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 17f6854c0ed32..e05b532fcede6 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" @@ -664,6 +665,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) @@ -677,6 +679,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, @@ -746,6 +750,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/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/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/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..a62f80161ae99 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" @@ -42,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" @@ -209,9 +210,7 @@ ElectronBrowserMainParts::ElectronBrowserMainParts( self_ = this; } -ElectronBrowserMainParts::~ElectronBrowserMainParts() { - asar::ClearArchives(); -} +ElectronBrowserMainParts::~ElectronBrowserMainParts() = default; // static ElectronBrowserMainParts* ElectronBrowserMainParts::Get() { @@ -549,6 +548,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/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/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); } 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/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); }; diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 52f6007199743..f3688688e012d 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 b38b4c8b8b461..15d4e3b33aa18 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; @@ -154,6 +154,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 e58621439e873..203af46b4a915 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) { @@ -1352,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] @@ -1382,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"); @@ -1439,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) { @@ -1461,7 +1462,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]; } } @@ -1646,6 +1647,13 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return is_active_; } +void NativeWindowMac::ReorderButtonsView() { + if (buttons_view_) { + [buttons_view_ removeFromSuperview]; + [[window_ contentView] addSubview:buttons_view_]; + } +} + void NativeWindowMac::Cleanup() { DCHECK(!IsClosed()); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); 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(); 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/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; } 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 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))); } 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/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 483a17cde6172..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,0,0,28 - PRODUCTVERSION 13,0,0,28 + 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.0.0" + 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.0.0" + VALUE "ProductVersion", "13.1.4" VALUE "SquirrelAwareVersion", "1" END 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/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/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]; } 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/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 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() 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 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; } 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/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/common/asar/archive.cc b/shell/common/asar/archive.cc index 89c5a8d418c90..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* list) { + std::vector* files) const { if (!header_) return false; @@ -245,19 +250,20 @@ 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; } -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/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/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index a6fb17e343c0e..d86c2dcc971a0 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 = @@ -485,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/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 } 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/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/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/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, 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( 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', () => { 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+?'); }); }); }); 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'); }); diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index fac8fca1ee3ae..bf02f30fd36a7 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-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(); +}); 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 063d71287aafc..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'; @@ -132,6 +133,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', () => { @@ -176,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; 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); 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; 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); 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 @@ +