Compare commits

...

96 Commits

Author SHA1 Message Date
Electron Bot
09677ab592 Bump v9.3.2 2020-10-05 09:18:53 -07:00
Milan Burda
2cd8848b97 chore: cherry-pick 52dceba66599 from chromium (#25656)
Co-authored-by: Milan Burda <miburda@microsoft.com>
2020-10-05 09:58:07 +09:00
trop[bot]
82151b4f0d fix: crash when application launched from UNUserNotificationCenter notification (#25739)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2020-10-02 13:57:41 -07:00
trop[bot]
37ece8b71a build: move to ACR for docker image storage (#25733)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-10-02 13:28:52 -07:00
Shelley Vohr
e4c2be03b2 fix: close window when leave fs crash (#25605) 2020-09-30 10:49:24 -04:00
Milan Burda
d1735ee2f3 chore: cherry-pick 814a27f8522b from chromium (#25643)
Co-authored-by: Milan Burda <miburda@microsoft.com>
2020-09-29 16:06:22 -07:00
trop[bot]
4c8eb34bab fix: submenu should be autoreleased (#25689)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-29 15:48:23 -04:00
Milan Burda
14ded68b75 chore: cherry-pick 0e61c69ebd47 from chromium (#25658)
Co-authored-by: Milan Burda <miburda@microsoft.com>
2020-09-28 10:04:13 -07:00
trop[bot]
220b356233 refactor: add a wrapper for wrangling uv handles. (#25663) 2020-09-27 18:52:01 -07:00
Michaela Laurencin
858d74bc36 fix: prevent destroyed view references from causing crashes (#25609) 2020-09-25 09:42:58 -07:00
trop[bot]
c32cb09a68 chore: improve renderer crash logging (#25620)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2020-09-24 12:55:54 -07:00
Shelley Vohr
194edd0270 fix: check printer list when no default printers (#25607) 2020-09-23 15:44:23 -07:00
trop[bot]
6c3c9b5a51 fix: unsubscribe from observers when window is closing (#25586)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-23 10:02:19 -04:00
Samuel Maddock
a43d4cacde fix(extensions): devtools now open for background pages (#25567) 2020-09-22 07:12:32 -07:00
trop[bot]
a7372e8f53 fix: app.importCertificate crash on Linux (#25538)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-09-19 15:24:14 -06:00
Milan Burda
3aa207c2dc build: fix build with enable_pdf_viewer=false (#25494) (#25521) 2020-09-17 20:23:53 -06:00
trop[bot]
f96153a4d1 fix: disable CORS when webSecurity is disabled (#25505)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-17 14:55:56 -06:00
Shelley Vohr
1aeba811cc chore: add V8 crash information to crashReporter (#24864) 2020-09-17 12:08:24 -06:00
trop[bot]
f6dced63b5 fix: ensure ready-to-show event is fired (#25490)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-17 09:42:24 +09:00
Jeremy Rose
489aafc1f9 fix: call node::Stop on exit (#25502) 2020-09-17 09:39:35 +09:00
trop[bot]
a6ed6480d8 fix: update node certdata to NSS 3.56 (#25364)
* fix: update node certdata to NSS 3.56

* Update .patches

Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-09-15 21:52:36 -06:00
trop[bot]
81c1a6ed29 chore: log hint on renderer crash (#25473)
* chore: log hint on renderer crash

* Address feedback from review

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-09-15 14:01:31 -06:00
Electron Bot
fe3378db92 Bump v9.3.1 2020-09-14 13:10:19 -07:00
Samuel Attard
fe85a94035 fix: handle electron script errors better (#25331) (#25453)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
2020-09-14 11:21:23 -07:00
trop[bot]
48779feb91 fix: Ensure electron delay loads the same modules as chromium (#25437)
* Ensure electron delay loads the appropriate modules as chromium on windows

This change adds the same module delay load list that chromium uses for electron.  Some modules were already getting delay loaded from other build files in chromium but not the main list via //build/config/win:delayloads.  We do not include the list of delay loads in delayloads_not_for_child_dll as those have issues being loaded in sandboxes processes.  This will reduce the overall reference set impact of the electron processes.

* fix: Ensure win modules are properly delayloaded

* chore: fix linting

Co-authored-by: Chris Davis <chrisdavis@outlook.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-09-14 09:05:27 -06:00
Cheng Zhao
5e289cee04 chore: cherry-pick 1283160e334f 0dc563cbbca5 from chromium (#25391) 2020-09-14 15:28:53 +09:00
Cheng Zhao
5f4ccec09a chore: cherry-pick c1a7439efcc7 from chromium (#25390) 2020-09-14 11:03:20 +09:00
Cheng Zhao
20f58fddf1 chore: cherry-pick 5e61913985df from chromium (#25389) 2020-09-14 09:50:03 +09:00
Jeremy Rose
75f193a089 fix: memory leak in net.request (#25382) 2020-09-10 11:40:53 +09:00
trop[bot]
7de2539399 fix: bind fake mojo service for badging (#25371)
* fix: bind fake mojo service for badging

* Add a test

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-09-10 09:14:34 +09:00
Charles Kerr
a90397f348 chore: sync 9-x-y release notes script to master (#25305)
* adds the '(Also in N-x-y)' annotations
* handle sublists in release notes (#25279)
* has prepare-release.js catch thrown exceptions (#24923)
* syncs related tests
2020-09-08 20:18:51 +09:00
Eryk Rakowski
6e734570ac feat(extensions): add support for some chrome.management APIs (#25344)
* feat(extensions): add support for some chrome.management APIs (#25098)

* fix: initialize management policy

* fix(extensions): crash when using chrome.management

* test: add tests

* docs: add a note about chrome.management

* fix: lint errors

* fix: lint errors

* fix: remove favicon_service include

* fix: add missing management permission

* docs: more supported apis

* fix: extensions.md line endings

* feat(extensions): add support for some chrome.management APIs (#25098)

* fix: initialize management policy

* fix(extensions): crash when using chrome.management

* test: add tests

* docs: add a note about chrome.management

* fix: lint errors

* fix: lint errors

* fix: remove favicon_service include

* fix: add missing management permission

* docs: more supported apis

* fix: extensions.md line endings

* Update electron_extensions_api_client.cc
2020-09-08 20:12:57 +09:00
Robo
79c1c8d21f fix: avoid creating client_id file for empty DIR_CRASH_DUMPS (#25316) 2020-09-03 18:46:29 -07:00
Cheng Zhao
35b348a7d7 fix: multiple dock icons when calling dock.show/hide (#25301) 2020-09-03 13:06:39 -07:00
Jeremy Rose
213f2fc838 chore: cherry-pick f6cb89728f04 from chromium (#25289) 2020-09-03 11:02:01 -07:00
Electron Bot
fb03807cd2 Bump v9.3.0 2020-09-02 18:08:24 -07:00
Electron Bot
89523b2504 Revert "Bump v9.3.0"
This reverts commit 243dbda31c.
2020-09-02 16:06:01 -07:00
Electron Bot
243dbda31c Bump v9.3.0 2020-09-02 15:02:59 -07:00
Electron Bot
587157e933 Revert "Bump v9.3.0"
This reverts commit 15f9300a64.
2020-09-02 14:51:38 -07:00
Electron Bot
15f9300a64 Bump v9.3.0 2020-09-02 14:23:38 -07:00
Jeremy Rose
88898acfef chore: cherry-pick 72ee7c437c88 from chromium (#25242) 2020-09-02 14:00:58 -07:00
Jeremy Rose
8a69889048 chore: cherry-pick 9ad8c9610d0a from chromium (#25240) 2020-09-02 12:02:49 -07:00
Jeremy Rose
b032c415c1 chore: cherry-pick 0dc563cbbca5 from chromium (#25238) 2020-09-02 11:56:47 -07:00
Jeremy Rose
e8643c1144 chore: cherry-pick 138b748dd0a4 from chromium (#25231) 2020-09-02 11:52:56 -07:00
Jeremy Rose
f8af08c279 chore: cherry-pick 9d100199c92b from chromium (#25229) 2020-09-02 10:31:34 -07:00
Jeremy Rose
713ff4c471 chore: cherry-pick c4f3b713800f from swiftshader (#25265) 2020-09-02 09:48:19 -07:00
Jeremy Rose
99abcba062 chore: cherry-pick af5b008a9546 from angle (#25263) 2020-09-02 09:48:03 -07:00
Jeremy Rose
086086b4e5 chore: cherry-pick bee371eeaf66 from chromium (#25226)
* chore: cherry-pick bee371eeaf66 from chromium

* update patches

Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-09-02 10:00:49 +09:00
Jeremy Rose
6e7425164f chore: cherry-pick e50772b2a25e from skia (#25245) 2020-09-01 14:21:06 -07:00
Jeremy Rose
4a03aa5bd2 chore: cherry-pick 44bb4d7abee6 from angle (#25233) 2020-09-01 14:19:58 -07:00
Jeremy Rose
99b4fd1ce0 chore: cherry-pick 9746a4cde14a from chromium (#25225)
* chore: cherry-pick 9746a4cde14a from chromium

* update patches

Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-09-01 14:17:47 -07:00
Jeremy Rose
a60211333c chore: cherry-pick 6b9a07d9eb9b from chromium (#25223) 2020-09-01 14:17:13 -07:00
trop[bot]
732ce44e61 chore: force source code and markdown files to use LF line ending (#25177)
* chore: force source code and markdown files to use LF line ending

* chore: replace CRLF with LF

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-01 12:48:07 +09:00
Robo
0f70d096fc fix: crash when using v8 cpu_profiler with wasm in the renderer (#25220)
* fix: crash when using v8 cpu_profiler with wasm in the renderer

* update patches

Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-08-31 13:15:11 -07:00
Robo
56cde790e1 fix: Avoid loading DRI via GBM when GpuMemoryBuffers are disabled (#25221) 2020-08-31 13:04:31 -07:00
Robo
7dc7b3627f fix: client area inset calculation when maximized for framless windows (#25052) (#25218)
* adopt per monitor scale factor

* fix: client area inset calculation when maximized

* address review feedback

* pass correct glass insets to GetDwmFrameInsetsInPixels

* remove unused code

* Windows 8 and 10 use the same DWM frame calculation

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 11:22:25 -07:00
trop[bot]
123f4087ca docs: fix supported platforms of powerMonitor (#25215)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 10:15:14 -07:00
Shelley Vohr
b5720bb2b8 feat: reinvigorate visibleOnFullscreen option (#25126) 2020-08-28 11:41:43 -07:00
trop[bot]
84f05ee0a4 fix: do not reset process_id in URLLoaderFactoryParams (#25179)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-28 11:37:27 -07:00
trop[bot]
694e8d8e66 fix: save dialog extensions should be deterministic (#25194)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-28 10:34:33 -07:00
Milan Burda
1529bf7ce5 fix: register for connected standby changes (#25076) (#25165)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-28 10:17:00 +09:00
Markus Olsson
d688b77187 fix: make shell.moveItemToTrash return false on Windows when move is unsuccessful (#25169)
* test: add tests for shell.moveItemToTrash (#25113)

* fix: make shell.moveItemToTrash return false on Windows when move unsuccessful (#25124)

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-08-27 13:07:19 -07:00
trop[bot]
7651036bce feat: expose currencyCode on Product struct in IAP module (#25085)
Co-authored-by: Shur Singh <ssingh@evernote.com>
2020-08-27 11:46:49 -07:00
Shelley Vohr
294825837c fix: save normal window bounds when maximizing (#25133)
* fix: save normal window bounds when maximizing

* fix: prevent maximize being emitted twice

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-25 22:08:26 -07:00
Samuel Attard
945defd47e refactor: wire will-navigate up to a navigation throttle instead of OpenURL (#25109)
* refactor: wire will-navigate up to a navigation throttle instead of OpenURL (#25065)

* refactor: wire will-navigate up to a navigation throttle instead of OpenURL

* spec: add test for x-site _top navigation

* chore: old code be old
2020-08-25 19:22:13 +09:00
trop[bot]
cbd91b1fba fix: reply notifs sometimes destroyed too early (#25101)
* fix: reply notifs sometimes destroyed too early

* Fix windows build

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-24 08:47:30 -07:00
Eryk Rakowski
1aebbe9538 fix(extensions): enable WebSQL in background pages (#25070)
* fix(extensions): enable WebSQL in background pages

* fix: remove trailing spaces
2020-08-21 10:40:05 -07:00
trop[bot]
508d2cb1f5 fix: calculate frame when setting window placement (#25045)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-20 08:46:12 -07:00
trop[bot]
f769da64ad fix: enable TLS renegotiation in node (#25041)
* fix: enable TLS renegotiation in node

* Update .patches

* update patches

Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-08-19 19:35:47 -07:00
Jeremy Rose
e16decd6a1 chore: cherry-pick 70579363ce7b from chromium (#25024)
* chore: cherry-pick 70579363ce7b from chromium

* update patches

Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-08-19 12:38:54 -07:00
Electron Bot
03c7a54dc5 Bump v9.2.1 2020-08-18 10:36:50 -07:00
Robo
eefcbda641 fix: [a11y] Allow focus to move into an editable combobox's listbox (#25004) 2020-08-18 10:33:08 -07:00
Eryk Rakowski
ea118c3984 fix(extensions): bypass cors in requests made from background pages (#24915) 2020-08-18 16:42:21 +09:00
trop[bot]
b204da6603 build: upload windows breakpad symbols (#25007)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-08-17 22:27:28 -07:00
Daniel White
cf321a9b55 chore: cherry-pick fix from chromium issue 1113227 (#24997) 2020-08-17 21:58:19 -07:00
trop[bot]
358d6e1116 fix: pdf download not working (#24996)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-17 19:49:37 -07:00
Jeremy Rose
12934c911f fix: browser contexts live forever (#25002) 2020-08-17 19:49:17 -07:00
trop[bot]
e2c4e341cc fix: loading dedicated/shared worker scripts over custom protocol (#24750)
* fix: add patch to avoid crash in worker with nodeintegration enabled

[worker_global_scope.cc(111)] Check failed: url_.IsValid().

* update patches

* ProtocolRegistry does not exist yet

* use custom partition for sw tests

Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: Electron Bot <anonymous@electronjs.org>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-17 14:41:57 -04:00
trop[bot]
79f6ba4b18 fix: use non-symbols in isURLInstance check (#24862) 2020-08-17 08:55:34 -07:00
Pedro Pontes
3e835c1247 chore: cherry-pick c7c412a36c from webrtc (#24887) 2020-08-10 21:07:09 -07:00
Pedro Pontes
91849fccbe chore: cherry-pick 26df3fdc25 from v8 (#24885) 2020-08-10 20:24:16 -07:00
trop[bot]
8dc38cb973 fix: send guid with linux crashes (#24898) 2020-08-10 13:45:24 -07:00
trop[bot]
f799b6eb37 build: ensure symbol files are named lowercase on disk so that boto can find them (#24858)
* build: ensure symbol files are named lowercase on disk so that boto can find them

* build: only do the lower case symbol copy on case sensitive file systems (#24876)

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
2020-08-07 13:51:49 -07:00
trop[bot]
7063ba73df fix: do not render inactive titlebar as active on Windows (#24873)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-06 20:46:59 -07:00
Jeremy Rose
f01bb5f43b fix: increase max crash key value length (#24854) 2020-08-06 08:02:46 -07:00
Electron Bot
0c2cb59b62 Bump v9.2.0 2020-08-05 11:36:24 -07:00
Electron Bot
310495212a Revert "Bump v9.2.0"
This reverts commit c7e00b933f.
2020-08-05 11:35:26 -07:00
Electron Bot
c7e00b933f Bump v9.2.0 2020-08-05 10:28:20 -07:00
trop[bot]
81d26ac1b7 fix: duplicate suspend/resume events (#24845)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-05 10:25:47 -07:00
trop[bot]
a5795824ef feat: add worldSafe flag for executeJS results (#24712)
* feat: add worldSafe flag for executeJS results

* chore: do not log warning for webContents.executeJS

* Apply suggestions from code review

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

* chore: apply PR feedback

* chore: split logic a bit

* chore: allow primitives through the world safe checl

* chore: clean up per PR feedback

* chore: flip boolean logic

* chore: update per PR feedback

* chore: fix typo

* chore: fix spec

* Update web-frame.ts

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-08-05 12:08:03 -04:00
Robo
0107cab3d1 fix: save crash reports locally when uploadToServer: false on linux (#24778) (#24788)
* fix: generate dumps under crashDumps folder in linux

* Update spec-main/api-crash-reporter-spec.ts

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2020-07-30 23:20:52 -07:00
trop[bot]
f65756bd4f build: upload sentry src bundles on windows as well (#24784)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-07-29 13:59:18 -07:00
Samuel Attard
05343a8972 fix: ensure that errors thrown in the context bridge are created in the correct context (#24747) 2020-07-28 22:26:46 -07:00
Jeremy Rose
72d921e453 fix: wrap OnWindowMessage w/ handlescope (#24769)
* fix: wrap OnWindowMessage w/ handlescope (#24716)

* Update electron_api_top_level_window.cc
2020-07-28 20:34:26 -07:00
Robo
7564453101 chore: cherry-pick 2469d759e3fe from chromium (#24765)
* chore: cherry-pick 2469d759e3fe from chromium

* update patches

Co-authored-by: Electron Bot <anonymous@electronjs.org>
2020-07-28 17:53:36 -07:00
trop[bot]
720e49d73d fix: crash when navigating from a page with webview that has inherited zoom level (#24764)
* fix: cleanup webview zoom level observers on navigation

* add spec

* webview should be on same partition

* wait for webview to finish loading

Co-authored-by: deepak1556 <hop2deep@gmail.com>
2020-07-28 15:52:36 -07:00
229 changed files with 10256 additions and 1888 deletions

View File

@@ -61,7 +61,7 @@ parameters:
# Build machines configs.
docker-image: &docker-image
docker:
- image: electronjs/build:d09fd95029bd8c1c73069888231b29688ef385ed
- image: electron.azurecr.io/build:4cec2c5ab66765caa724e37bae2bffb9b29722a5
machine-linux-medium: &machine-linux-medium
<<: *docker-image
@@ -702,8 +702,6 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
if [ "$GENERATE_SYMBOLS" == "true" ]; then
cd src
ninja -C out/Default electron:electron_symbols
cd out/Default/breakpad_symbols
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
fi
step-maybe-zip-symbols: &step-maybe-zip-symbols

10
.gitattributes vendored
View File

@@ -2,3 +2,13 @@
# files to be checked out with LF endings even if core.autocrlf is true.
*.patch text eol=lf
patches/**/.patches merge=union
# Source code and markdown files should always use LF as line ending.
*.cc text eol=lf
*.mm text eol=lf
*.h text eol=lf
*.js text eol=lf
*.ts text eol=lf
*.py text eol=lf
*.ps1 text eol=lf
*.md text eol=lf

View File

@@ -1115,20 +1115,17 @@ if (is_mac) {
"//components/crash/core/app:run_as_crashpad_handler",
]
ldflags = []
libs = [
"comctl32.lib",
"uiautomationcore.lib",
"wtsapi32.lib",
]
configs += [ "//build/config/win:windowed" ]
ldflags = [
# Windows 7 doesn't have these DLLs.
# TODO: are there other DLLs we need to list here to be win7
# compatible?
"/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll",
"/DELAYLOAD:api-ms-win-core-winrt-string-l1-1-0.dll",
configs += [
"//build/config/win:windowed",
"//build/config/win:delayloads",
]
# This is to support renaming of electron.exe. node-gyp has hard-coded

View File

@@ -1 +1 @@
9.1.2
9.3.2

View File

@@ -10,10 +10,9 @@ config.output = {
filename: path.basename(outPath)
}
const { wrapInitWithProfilingTimeout } = config;
delete config.wrapInitWithProfilingTimeout;
const { wrapInitWithProfilingTimeout, wrapInitWithTryCatch, ...webpackConfig } = config;
webpack(config, (err, stats) => {
webpack(webpackConfig, (err, stats) => {
if (err) {
console.error(err)
process.exit(1)
@@ -21,9 +20,17 @@ webpack(config, (err, stats) => {
console.error(stats.toString('normal'))
process.exit(1)
} else {
let contents = fs.readFileSync(outPath, 'utf8');
if (wrapInitWithTryCatch) {
contents = `try {
${contents}
} catch (err) {
console.error('Electron ${webpackConfig.output.filename} script failed to run');
console.error(err);
}`;
}
if (wrapInitWithProfilingTimeout) {
const contents = fs.readFileSync(outPath, 'utf8');
const newContents = `function ___electron_webpack_init__() {
contents = `function ___electron_webpack_init__() {
${contents}
};
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {
@@ -31,8 +38,8 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
} else {
___electron_webpack_init__();
}`;
fs.writeFileSync(outPath, newContents);
}
fs.writeFileSync(outPath, contents)
process.exit(0)
}
})

View File

@@ -86,7 +86,8 @@ module.exports = ({
loadElectronFromAlternateTarget,
targetDeletesNodeGlobals,
target,
wrapInitWithProfilingTimeout
wrapInitWithProfilingTimeout,
wrapInitWithTryCatch
}) => {
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts')
if (!fs.existsSync(entry)) {
@@ -102,6 +103,7 @@ module.exports = ({
filename: `${target}.bundle.js`
},
wrapInitWithProfilingTimeout,
wrapInitWithTryCatch,
resolve: {
alias: {
'@electron/internal': path.resolve(electronRoot, 'lib'),

View File

@@ -1,4 +1,5 @@
module.exports = require('./webpack.config.base')({
target: 'isolated_renderer',
alwaysHasNode: false
alwaysHasNode: false,
wrapInitWithTryCatch: true
})

View File

@@ -2,5 +2,6 @@ module.exports = require('./webpack.config.base')({
target: 'renderer',
alwaysHasNode: true,
targetDeletesNodeGlobals: true,
wrapInitWithProfilingTimeout: true
wrapInitWithProfilingTimeout: true,
wrapInitWithTryCatch: true
})

View File

@@ -2,4 +2,5 @@ module.exports = require('./webpack.config.base')({
target: 'sandboxed_renderer',
alwaysHasNode: false,
wrapInitWithProfilingTimeout: true,
wrapInitWithTryCatch: true
})

View File

@@ -2,5 +2,6 @@ module.exports = require('./webpack.config.base')({
target: 'worker',
loadElectronFromAlternateTarget: 'renderer',
alwaysHasNode: true,
targetDeletesNodeGlobals: true
targetDeletesNodeGlobals: true,
wrapInitWithTryCatch: true
})

View File

@@ -36,6 +36,7 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
// Linux has only a single persistent slot compared to ChromeOS's separate
// public and private slot.
// Redirect any slot usage to this persistent slot on Linux.
crypto::EnsureNSSInit();
g_nss_cert_database = new net::NSSCertDatabase(
crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* public slot */,
crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* private slot */);

View File

@@ -348,6 +348,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
You can access this 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_
* `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
@@ -1666,9 +1669,12 @@ Sets whether the menu bar should be visible. If the menu bar is auto-hide, users
Returns `Boolean` - Whether the menu bar is visible.
#### `win.setVisibleOnAllWorkspaces(visible)`
#### `win.setVisibleOnAllWorkspaces(visible[, options])`
* `visible` Boolean
* `options` Object (optional)
* `visibleOnFullScreen` Boolean (optional) _macOS_ - Sets whether
the window should be visible above fullscreen windows
Sets whether the window should be visible on all workspaces.

View File

@@ -157,10 +157,16 @@ parameters in a renderer process will not result in those parameters being sent
with crashes that occur in other renderer processes or in the main process.
**Note:** Parameters have limits on the length of the keys and values. Key
names must be no longer than 39 bytes, and values must be no longer than 127
names must be no longer than 39 bytes, and values must be no longer than 20320
bytes. Keys with names longer than the maximum will be silently ignored. Key
values longer than the maximum length will be truncated.
**Note:** On linux values that are longer than 127 bytes will be chunked into
multiple keys, each 127 bytes in length. E.g. `addExtraParameter('foo', 'a'.repeat(130))`
will result in two chunked keys `foo__1` and `foo__2`, the first will contain
the first 127 bytes and the second will contain the remaining 3 bytes. On
your crash reporting backend you should stitch together keys in this format.
### `crashReporter.removeExtraParameter(key)`
* `key` String - Parameter key, must be no longer than 39 bytes.

View File

@@ -28,6 +28,9 @@ session.loadExtension('path/to/unpacked/extension').then(({ id }) => {
Loaded extensions will not be automatically remembered across exits; if you do
not call `loadExtension` when the app runs, the extension will not be loaded.
Note that loading extensions is only supported in persistent sessions.
Attempting to load an extension into an in-memory session will throw an error.
See the [`session`](session.md) documentation for more information about
loading, unloading, and querying active extensions.
@@ -99,3 +102,15 @@ The following methods of `chrome.tabs` are supported:
> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
> supported and will raise an error.
### `chrome.management`
The following methods of `chrome.management` are supported:
- `chrome.management.getAll`
- `chrome.management.get`
- `chrome.management.getSelf`
- `chrome.management.getPermissionWarningsById`
- `chrome.management.getPermissionWarningsByManifest`
- `chrome.management.onEnabled`
- `chrome.management.onDisabled`

View File

@@ -24,19 +24,19 @@ app.whenReady().then(() => {
The `powerMonitor` module emits the following events:
### Event: 'suspend' _Linux_ _Windows_
### Event: 'suspend' _macOS_ _Windows_
Emitted when the system is suspending.
### Event: 'resume' _Linux_ _Windows_
### Event: 'resume' _macOS_ _Windows_
Emitted when system is resuming.
### Event: 'on-ac' _Windows_
### Event: 'on-ac' _macOS_ _Windows_
Emitted when the system changes to AC power.
### Event: 'on-battery' _Windows_
### Event: 'on-battery' _macOS_ _Windows_
Emitted when system changes to battery power.

View File

@@ -7,4 +7,5 @@
* `contentLengths` Number[] - The total size of the content, in bytes.
* `price` Number - The cost of the product in the local currency.
* `formattedPrice` String - The locale formatted price of the product.
* `currencyCode` String - 3 character code presenting a product's currency based on the ISO 4217 standard.
* `isDownloadable` Boolean - A Boolean value that indicates whether the App Store has downloadable content for this product. `true` if at least one file has been associated with the product.

View File

@@ -1,95 +1,95 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('open-error-dialog', event => {
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
})
ipcMain.on('open-information-dialog', event => {
const options = {
type: 'info',
title: 'Information',
message: "This is an information dialog. Isn't it nice?",
buttons: ['Yes', 'No']
}
dialog.showMessageBox(options, index => {
event.sender.send('information-dialog-selection', index)
})
})
ipcMain.on('open-file-dialog', event => {
dialog.showOpenDialog(
{
properties: ['openFile', 'openDirectory']
},
files => {
if (files) {
event.sender.send('selected-directory', files)
}
}
)
})
ipcMain.on('save-dialog', event => {
const options = {
title: 'Save an Image',
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
}
dialog.showSaveDialog(options, filename => {
event.sender.send('saved-file', filename)
})
})
// 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.
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('open-error-dialog', event => {
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
})
ipcMain.on('open-information-dialog', event => {
const options = {
type: 'info',
title: 'Information',
message: "This is an information dialog. Isn't it nice?",
buttons: ['Yes', 'No']
}
dialog.showMessageBox(options, index => {
event.sender.send('information-dialog-selection', index)
})
})
ipcMain.on('open-file-dialog', event => {
dialog.showOpenDialog(
{
properties: ['openFile', 'openDirectory']
},
files => {
if (files) {
event.sender.send('selected-directory', files)
}
}
)
})
ipcMain.on('save-dialog', event => {
const options = {
title: 'Save an Image',
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
}
dialog.showSaveDialog(options, filename => {
event.sender.send('saved-file', filename)
})
})
// 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.

View File

@@ -1,18 +1,18 @@
const { ipcRenderer, shell } = require('electron')
const links = document.querySelectorAll('a[href]')
const errorBtn = document.getElementById('error-dialog')
errorBtn.addEventListener('click', event => {
ipcRenderer.send('open-error-dialog')
})
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)
})
}
const { ipcRenderer, shell } = require('electron')
const links = document.querySelectorAll('a[href]')
const errorBtn = document.getElementById('error-dialog')
errorBtn.addEventListener('click', event => {
ipcRenderer.send('open-error-dialog')
})
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)
})
}
})

View File

@@ -1,70 +1,70 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('open-information-dialog', event => {
const options = {
type: 'info',
title: 'Information',
message: "This is an information dialog. Isn't it nice?",
buttons: ['Yes', 'No']
}
dialog.showMessageBox(options, index => {
event.sender.send('information-dialog-selection', index)
})
})
// 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.
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('open-information-dialog', event => {
const options = {
type: 'info',
title: 'Information',
message: "This is an information dialog. Isn't it nice?",
buttons: ['Yes', 'No']
}
dialog.showMessageBox(options, index => {
event.sender.send('information-dialog-selection', index)
})
})
// 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.

View File

@@ -1,25 +1,25 @@
const { ipcRenderer, shell } = require('electron')
const informationBtn = document.getElementById('information-dialog')
const links = document.querySelectorAll('a[href]')
informationBtn.addEventListener('click', event => {
ipcRenderer.send('open-information-dialog')
})
ipcRenderer.on('information-dialog-selection', (event, index) => {
let message = 'You selected '
if (index === 0) message += 'yes.'
else message += 'no.'
document.getElementById('info-selection').innerHTML = message
})
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)
})
}
const { ipcRenderer, shell } = require('electron')
const informationBtn = document.getElementById('information-dialog')
const links = document.querySelectorAll('a[href]')
informationBtn.addEventListener('click', event => {
ipcRenderer.send('open-information-dialog')
})
ipcRenderer.on('information-dialog-selection', (event, index) => {
let message = 'You selected '
if (index === 0) message += 'yes.'
else message += 'no.'
document.getElementById('info-selection').innerHTML = message
})
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)
})
}
})

View File

@@ -1,70 +1,70 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('open-file-dialog', event => {
dialog.showOpenDialog(
{
properties: ['openFile', 'openDirectory']
},
files => {
if (files) {
event.sender.send('selected-directory', files)
}
}
)
})
// 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.
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('open-file-dialog', event => {
dialog.showOpenDialog(
{
properties: ['openFile', 'openDirectory']
},
files => {
if (files) {
event.sender.send('selected-directory', files)
}
}
)
})
// 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.

View File

@@ -1,22 +1,22 @@
const { ipcRenderer, shell } = require('electron')
const selectDirBtn = document.getElementById('select-directory')
const links = document.querySelectorAll('a[href]')
selectDirBtn.addEventListener('click', event => {
ipcRenderer.send('open-file-dialog')
})
ipcRenderer.on('selected-directory', (event, path) => {
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
})
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)
})
}
})
const { ipcRenderer, shell } = require('electron')
const selectDirBtn = document.getElementById('select-directory')
const links = document.querySelectorAll('a[href]')
selectDirBtn.addEventListener('click', event => {
ipcRenderer.send('open-file-dialog')
})
ipcRenderer.on('selected-directory', (event, path) => {
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
})
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)
})
}
})

View File

@@ -1,66 +1,66 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('save-dialog', event => {
const options = {
title: 'Save an Image',
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
}
dialog.showSaveDialog(options, filename => {
event.sender.send('saved-file', filename)
})
})
// 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.
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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('save-dialog', event => {
const options = {
title: 'Save an Image',
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
}
dialog.showSaveDialog(options, filename => {
event.sender.send('saved-file', filename)
})
})
// 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.

View File

@@ -1,23 +1,23 @@
const { ipcRenderer, shell } = require('electron')
const saveBtn = document.getElementById('save-dialog')
const links = document.querySelectorAll('a[href]')
saveBtn.addEventListener('click', event => {
ipcRenderer.send('save-dialog')
})
ipcRenderer.on('saved-file', (event, path) => {
if (!path) path = 'No path'
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
})
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)
})
}
const { ipcRenderer, shell } = require('electron')
const saveBtn = document.getElementById('save-dialog')
const links = document.querySelectorAll('a[href]')
saveBtn.addEventListener('click', event => {
ipcRenderer.send('save-dialog')
})
ipcRenderer.on('saved-file', (event, path) => {
if (!path) path = 'No path'
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
})
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)
})
}
})

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// In this file you can include the rest of your app's specific main process
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// 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.

View File

@@ -1,13 +1,13 @@
const { shell } = require('electron')
const os = require('os')
const exLinksBtn = document.getElementById('open-ex-links')
const fileManagerBtn = document.getElementById('open-file-manager')
fileManagerBtn.addEventListener('click', (event) => {
shell.showItemInFolder(os.homedir())
})
exLinksBtn.addEventListener('click', (event) => {
shell.openExternal('http://electron.atom.io')
const { shell } = require('electron')
const os = require('os')
const exLinksBtn = document.getElementById('open-ex-links')
const fileManagerBtn = document.getElementById('open-file-manager')
fileManagerBtn.addEventListener('click', (event) => {
shell.showItemInFolder(os.homedir())
})
exLinksBtn.addEventListener('click', (event) => {
shell.openExternal('http://electron.atom.io')
})

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// In this file you can include the rest of your app's specific main process
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// 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.

View File

@@ -1,29 +1,29 @@
const basicNotification = {
title: 'Basic Notification',
body: 'Short message part'
}
const notification = {
title: 'Notification with image',
body: 'Short message plus a custom image',
icon: 'https://via.placeholder.com/150'
}
const basicNotificationButton = document.getElementById('basic-noti')
const notificationButton = document.getElementById('advanced-noti')
notificationButton.addEventListener('click', () => {
const myNotification = new window.Notification(notification.title, notification)
myNotification.onclick = () => {
console.log('Notification clicked')
}
})
basicNotificationButton.addEventListener('click', () => {
const myNotification = new window.Notification(basicNotification.title, basicNotification)
myNotification.onclick = () => {
console.log('Notification clicked')
}
})
const basicNotification = {
title: 'Basic Notification',
body: 'Short message part'
}
const notification = {
title: 'Notification with image',
body: 'Short message plus a custom image',
icon: 'https://via.placeholder.com/150'
}
const basicNotificationButton = document.getElementById('basic-noti')
const notificationButton = document.getElementById('advanced-noti')
notificationButton.addEventListener('click', () => {
const myNotification = new window.Notification(notification.title, notification)
myNotification.onclick = () => {
console.log('Notification clicked')
}
})
basicNotificationButton.addEventListener('click', () => {
const myNotification = new window.Notification(basicNotification.title, basicNotification)
myNotification.onclick = () => {
console.log('Notification clicked')
}
})

File diff suppressed because one or more lines are too long

View File

@@ -1,35 +1,35 @@
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)
})
}
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)
})
}
})

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// In this file you can include the rest of your app's specific main process
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// 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.

View File

@@ -1,25 +1,25 @@
const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell
const framelessWindowBtn = document.getElementById('frameless-window')
const links = document.querySelectorAll('a[href]')
framelessWindowBtn.addEventListener('click', (event) => {
const modalPath = 'https://electronjs.org'
let win = new BrowserWindow({ frame: false })
win.on('close', () => { win = null })
win.loadURL(modalPath)
win.show()
})
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)
})
}
})
const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell
const framelessWindowBtn = document.getElementById('frameless-window')
const links = document.querySelectorAll('a[href]')
framelessWindowBtn.addEventListener('click', (event) => {
const modalPath = 'https://electronjs.org'
let win = new BrowserWindow({ frame: false })
win.on('close', () => { win = null })
win.loadURL(modalPath)
win.show()
})
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)
})
}
})

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// In this file you can include the rest of your app's specific main process
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// 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.

View File

@@ -1,35 +1,35 @@
const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell
const manageWindowBtn = document.getElementById('manage-window')
const links = document.querySelectorAll('a[href]')
let win
manageWindowBtn.addEventListener('click', (event) => {
const modalPath = 'https://electronjs.org'
win = new BrowserWindow({ width: 400, height: 275 })
win.on('resize', updateReply)
win.on('move', updateReply)
win.on('close', () => { win = null })
win.loadURL(modalPath)
win.show()
function updateReply () {
const manageWindowReply = document.getElementById('manage-window-reply')
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
manageWindowReply.innerText = message
}
})
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)
})
}
})
const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell
const manageWindowBtn = document.getElementById('manage-window')
const links = document.querySelectorAll('a[href]')
let win
manageWindowBtn.addEventListener('click', (event) => {
const modalPath = 'https://electronjs.org'
win = new BrowserWindow({ width: 400, height: 275 })
win.on('resize', updateReply)
win.on('move', updateReply)
win.on('close', () => { win = null })
win.loadURL(modalPath)
win.show()
function updateReply () {
const manageWindowReply = document.getElementById('manage-window-reply')
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
manageWindowReply.innerText = message
}
})
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)
})
}
})

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// In this file you can include the rest of your app's specific main process
// Modules to control application life and create native browser window
const { app, BrowserWindow } = 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
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// 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()
}
})
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()
}
})
// 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.

View File

@@ -1,48 +1,48 @@
const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell
const listenToWindowBtn = document.getElementById('listen-to-window')
const focusModalBtn = document.getElementById('focus-on-modal-window')
const links = document.querySelectorAll('a[href]')
let win
listenToWindowBtn.addEventListener('click', () => {
const modalPath = 'https://electronjs.org'
win = new BrowserWindow({ width: 600, height: 400 })
const hideFocusBtn = () => {
focusModalBtn.classList.add('disappear')
focusModalBtn.classList.remove('smooth-appear')
focusModalBtn.removeEventListener('click', clickHandler)
}
const showFocusBtn = (btn) => {
if (!win) return
focusModalBtn.classList.add('smooth-appear')
focusModalBtn.classList.remove('disappear')
focusModalBtn.addEventListener('click', clickHandler)
}
win.on('focus', hideFocusBtn)
win.on('blur', showFocusBtn)
win.on('close', () => {
hideFocusBtn()
win = null
})
win.loadURL(modalPath)
win.show()
const clickHandler = () => { win.focus() }
})
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)
})
}
})
const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell
const listenToWindowBtn = document.getElementById('listen-to-window')
const focusModalBtn = document.getElementById('focus-on-modal-window')
const links = document.querySelectorAll('a[href]')
let win
listenToWindowBtn.addEventListener('click', () => {
const modalPath = 'https://electronjs.org'
win = new BrowserWindow({ width: 600, height: 400 })
const hideFocusBtn = () => {
focusModalBtn.classList.add('disappear')
focusModalBtn.classList.remove('smooth-appear')
focusModalBtn.removeEventListener('click', clickHandler)
}
const showFocusBtn = (btn) => {
if (!win) return
focusModalBtn.classList.add('smooth-appear')
focusModalBtn.classList.remove('disappear')
focusModalBtn.addEventListener('click', clickHandler)
}
win.on('focus', hideFocusBtn)
win.on('blur', showFocusBtn)
win.on('close', () => {
hideFocusBtn()
win = null
})
win.loadURL(modalPath)
win.show()
const clickHandler = () => { win.focus() }
})
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)
})
}
})

View File

@@ -614,6 +614,8 @@ filenames = {
"shell/browser/extensions/api/resources_private/resources_private_api.h",
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
"shell/browser/extensions/api/management/electron_management_api_delegate.cc",
"shell/browser/extensions/api/management/electron_management_api_delegate.h",
"shell/browser/extensions/api/tabs/tabs_api.cc",
"shell/browser/extensions/api/tabs/tabs_api.h",
"shell/browser/extensions/api/streams_private/streams_private_api.cc",

View File

@@ -459,6 +459,10 @@ const addReturnValueToEvent = (event) => {
});
};
const loggingEnabled = () => {
return process.env.ELECTRON_ENABLE_LOGGING || app.commandLine.hasSwitch('enable-logging');
};
// Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () {
// The navigation controller.
@@ -527,8 +531,13 @@ WebContents.prototype._init = function () {
app.emit('renderer-process-crashed', event, this, ...args);
});
this.on('render-process-gone', (event, ...args) => {
app.emit('render-process-gone', event, this, ...args);
this.on('render-process-gone', (event, details) => {
app.emit('render-process-gone', event, this, details);
// Log out a hint to help users better debug renderer crashes.
if (loggingEnabled()) {
console.info(`Renderer process ${details.reason} - see https://www.electronjs.org/docs/tutorial/application-debugging for potential debugging information.`);
}
});
// The devtools requests the webContents to reload.

View File

@@ -1,4 +1,5 @@
import { EventEmitter } from 'events';
import deprecate from '@electron/internal/common/api/deprecate';
const binding = process.electronBinding('web_frame');
@@ -47,14 +48,26 @@ class WebFrame extends EventEmitter {
}
}
const { hasSwitch } = process.electronBinding('command_line');
const worldSafeJS = hasSwitch('world-safe-execute-javascript') && hasSwitch('context-isolation');
// Populate the methods.
for (const name in binding) {
if (!name.startsWith('_')) { // some methods are manually populated above
// TODO(felixrieseberg): Once we can type web_frame natives, we could
// use a neat `keyof` here
(WebFrame as any).prototype[name] = function (...args: Array<any>) {
if (!worldSafeJS && name.startsWith('executeJavaScript')) {
deprecate.log(`Security Warning: webFrame.${name} was called without worldSafeExecuteJavaScript enabled. This is considered unsafe. worldSafeExecuteJavaScript will be enabled by default in Electron 12.`);
}
return binding[name](this.context, ...args);
};
// TODO(MarshallOfSound): Remove once the above deprecation is removed
if (name.startsWith('executeJavaScript')) {
(WebFrame as any).prototype[`_${name}`] = function (...args: Array<any>) {
return binding[name](this.context, ...args);
};
}
}
}

View File

@@ -12,6 +12,11 @@ export const webFrameInit = () => {
ipcRendererUtils.handle('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (
event, method: keyof WebFrameMethod, ...args: any[]
) => {
// TODO(MarshallOfSound): Remove once the world-safe-execute-javascript deprecation warning is removed
if (method.startsWith('executeJavaScript')) {
return (webFrame as any)[`_${method}`](...args);
}
// The TypeScript compiler cannot handle the sheer number of
// call signatures here and simply gives up. Incorrect invocations
// will be caught by "keyof WebFrameMethod" though.

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "9.1.2",
"version": "9.3.2",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

2
patches/angle/.patches Normal file
View File

@@ -0,0 +1,2 @@
fix_stale_validation_cache_on_buffer_deletion.patch
d3d11_fix_bug_with_static_vertex_attributes.patch

View File

@@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jamie Madill <jmadill@chromium.org>
Date: Wed, 22 Jul 2020 13:58:50 -0400
Subject: D3D11: Fix bug with static vertex attributes.
In some specific cases after binding a zero size buffer we could end
up trying to use a buffer storage that was no longer valid. Fix this
by ensuring we don't flush dirty bits when we have an early exit due
to a zero size buffer.
Also adds a regression test.
Bug: chromium:1107433
Change-Id: I9db560e8dd3699abed2bb7fe6d91060148ba1817
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2314216
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp b/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
index 5f834b5c8dbc63d3bcc374eca15a17ee442d77e3..b0e27c9f0c6c316fe347a44776a6926b093a81c4 100644
--- a/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
@@ -250,8 +250,6 @@ angle::Result VertexArray11::updateDirtyAttribs(const gl::Context *context,
for (size_t dirtyAttribIndex : activeDirtyAttribs)
{
- mAttribsToTranslate.reset(dirtyAttribIndex);
-
auto *translatedAttrib = &mTranslatedAttribs[dirtyAttribIndex];
const auto &currentValue = glState.getVertexAttribCurrentValue(dirtyAttribIndex);
@@ -279,6 +277,9 @@ angle::Result VertexArray11::updateDirtyAttribs(const gl::Context *context,
UNREACHABLE();
break;
}
+
+ // Make sure we reset the dirty bit after the switch because STATIC can early exit.
+ mAttribsToTranslate.reset(dirtyAttribIndex);
}
return angle::Result::Continue;

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jamie Madill <jmadill@chromium.org>
Date: Tue, 14 Jul 2020 17:20:18 -0400
Subject: Fix stale validation cache on buffer deletion.
When we would delete the currently bound element array buffer we
would neglect to invalidate a specific validation cache variable.
This incorrectly would let us skip buffer size validation and lead
to internal invalid memory accesses.
Bug: chromium:1105202
Change-Id: I23ab28ccd3ac6b5d461cb8745b930f4d42d53b35
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2323644
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index d8bccaf1e920e261c45c777993b77b8c40a011ad..3233d12dd233786b0988f299ace57d932e0d0fe6 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -8468,6 +8468,7 @@ void StateCache::onVertexArrayStateChange(Context *context)
updateActiveAttribsMask(context);
updateVertexElementLimits(context);
updateBasicDrawStatesError();
+ updateBasicDrawElementsError();
}
void StateCache::onVertexArrayBufferStateChange(Context *context)
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index 201010f9f5555faa79cab480a34caf63bf5ffbf2..06eeff3b94c937067e674fc127afdeab34e63f21 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -202,6 +202,7 @@ class StateCache final : angle::NonCopyable
// 1. onActiveTransformFeedbackChange.
// 2. onVertexArrayBufferStateChange.
// 3. onBufferBindingChange.
+ // 4. onVertexArrayStateChange.
intptr_t getBasicDrawElementsError(const Context *context) const
{
if (mCachedBasicDrawElementsError != kInvalidPointer)

View File

@@ -116,3 +116,25 @@ backport_1073409.patch
backport_1074340.patch
backport_1016278.patch
backport_1042986.patch
a11y_axplatformnodebase_getindexinparent_returns_base_optional.patch
worker_feat_add_hook_to_notify_script_ready.patch
reconnect_p2p_socket_dispatcher_if_network_service_dies.patch
allow_focus_to_move_into_an_editable_combobox_s_listbox.patch
cherry-pick-70579363ce7b.patch
cherry-pick-138b748dd0a4.patch
cherry-pick-9d100199c92b.patch
cherry-pick-bee371eeaf66.patch
cherry-pick-9746a4cde14a.patch
avoid_loading_dri_via_gbm_when_gpumemorybuffers_are_disabled.patch
cherry-pick-72ee7c437c88.patch
cherry-pick-9ad8c9610d0a.patch
indexeddb_fix_crash_in_webidbgetdbnamescallbacksimpl.patch
indexeddb_reset_async_tasks_in_webidbgetdbnamescallbacksimpl.patch
reland_fix_uaf_in_selecttype.patch
cherry-pick-f6cb89728f04.patch
backport_1081874.patch
backport_1122684.patch
backport_1111737.patch
cherry-pick-0e61c69ebd47.patch
cherry-pick-814a27f8522b.patch
cherry-pick-52dceba66599.patch

View File

@@ -0,0 +1,257 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Julie Jeongeun Kim <jkim@igalia.com>
Date: Wed, 8 Apr 2020 11:18:34 +0000
Subject: a11y: AXPlatformNodeBase::GetIndexInParent() returns base::Optional
This fixes the regression issue from the CL[1] which moved
the logic of GetIndexInParent to AXPlatformNodeBase.
Before the CL[1], atk_object::GetIndexInParent returns -1 as an
invalid value but AXPlatformNodeBase::GetIndexInParent returns
positive numbers or 0 except the case when it's not implemented
or doesn't have |delegate_|. AXPlatformNodeBase::GetIndexInParent
is also used for the internal logic in AXPlatformNodeBase with
the assumption that the return value is not less than 0.
The CL[1] moved the logic to AXPlatformNodeBase::GetIndexInParent
with keeping the rule that the return value is not less than 0.
The problem is that screen reader such as Orca expects that it
returns -1 when it's invalid.
With this CL, AXPlatformNodeBase::GetIndexInParent() returns
base::Optional and the caller has -1 if GetIndexInParent() returns
base::nullopt. It also uses GetFirstChild and GetNextSibling for
children iteration in GetHypertextOffsetFromChild() and
GetHypertextOffsetFromEndpoint() instead of GetIndexInParent and
GetChildAt.
[1]https://crrev.com/c/2087234
Bug: 1065534
Change-Id: I00e2ed421ed263cf379ba22f55049fd52d32df9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2129667
Commit-Queue: Julie Kim <jkim@igalia.com>
Reviewed-by: Nektarios Paisios <nektar@chromium.org>
Reviewed-by: Martin Robinson <mrobinson@igalia.com>
Reviewed-by: Joanmarie Diggs <jdiggs@igalia.com>
Cr-Commit-Position: refs/heads/master@{#757381}
diff --git a/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt b/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt
index c68cfe98572b9b2edf3658053be3dea2040524e4..483d5bb7eb7be40fa0dbd81cb336fe41fe665efe 100644
--- a/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt
+++ b/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt
@@ -1,3 +1,3 @@
-CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED index:-1 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
NAME-CHANGED:new role=ROLE_STATIC name='new' ENABLED,SENSITIVE,SHOWING,VISIBLE
STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
diff --git a/content/test/data/accessibility/event/text-changed-expected-auralinux.txt b/content/test/data/accessibility/event/text-changed-expected-auralinux.txt
index b1573d5a3e84bc168403ad989632333473a5c0a8..0fe6700c90e82846aa862cc09a567d5ad36bccc6 100644
--- a/content/test/data/accessibility/event/text-changed-expected-auralinux.txt
+++ b/content/test/data/accessibility/event/text-changed-expected-auralinux.txt
@@ -1,7 +1,7 @@
+CHILDREN-CHANGED index:-1 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED index:-1 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_PARAGRAPH ENABLED,SENSITIVE,SHOWING,VISIBLE
CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_PARAGRAPH ENABLED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
NAME-CHANGED:Modified Div role=ROLE_STATIC name='Modified Div' ENABLED,SENSITIVE,SHOWING,VISIBLE
NAME-CHANGED:Modified Heading role=ROLE_STATIC name='Modified Heading' ENABLED,SENSITIVE,SHOWING,VISIBLE
STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 2f150dbbae32f9faae0f8b55a65133fb333aa5e5..f3e9ab6b2bb8ab0c4a688fe84861fe9d7e766a5a 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2077,7 +2077,7 @@ gint GetIndexInParent(AtkObject* atk_object) {
if (!obj)
return -1;
- return obj->GetIndexInParent();
+ return obj->GetIndexInParent().value_or(-1);
}
gint AtkGetIndexInParent(AtkObject* atk_object) {
@@ -3774,7 +3774,7 @@ void AXPlatformNodeAuraLinux::OnSubtreeCreated() {
return;
g_signal_emit_by_name(GetParent(), "children-changed::add",
- GetIndexInParent(), atk_object);
+ GetIndexInParent().value_or(-1), atk_object);
}
void AXPlatformNodeAuraLinux::OnSubtreeWillBeDeleted() {
@@ -3788,7 +3788,7 @@ void AXPlatformNodeAuraLinux::OnSubtreeWillBeDeleted() {
return;
g_signal_emit_by_name(GetParent(), "children-changed::remove",
- GetIndexInParent(), atk_object);
+ GetIndexInParent().value_or(-1), atk_object);
}
void AXPlatformNodeAuraLinux::OnParentChanged() {
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 12ec1674a79bdc1f28d5d2b1bb4792b81cd8608f..d946b07612f55efebf85615063411d262552944b 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -149,15 +149,16 @@ base::string16 AXPlatformNodeBase::GetNameAsString16() const {
return base::UTF8ToUTF16(name);
}
-int AXPlatformNodeBase::GetIndexInParent() {
+base::Optional<int> AXPlatformNodeBase::GetIndexInParent() {
AXPlatformNodeBase* parent = FromNativeViewAccessible(GetParent());
if (!parent)
- return 0;
+ return base::nullopt;
int child_count = parent->GetChildCount();
if (child_count == 0) {
- // |child_count| could be 0 if the node is PlatformIsLeaf.
- return 0;
+ // |child_count| could be 0 if the parent is IsLeaf.
+ DCHECK(parent->IsLeaf());
+ return base::nullopt;
}
// Ask the delegate for the index in parent, and return it if it's plausible.
@@ -176,7 +177,9 @@ int AXPlatformNodeBase::GetIndexInParent() {
if (parent->ChildAtIndex(i) == current)
return i;
}
- return -1;
+ NOTREACHED()
+ << "Unable to find the child in the list of its parent's children.";
+ return base::nullopt;
}
// AXPlatformNode overrides.
@@ -1429,12 +1432,8 @@ int32_t AXPlatformNodeBase::GetHypertextOffsetFromChild(
// cross-tree traversal is necessary.
if (child->IsTextOnlyObject()) {
int32_t hypertext_offset = 0;
- int32_t index_in_parent = child->GetIndexInParent();
- DCHECK_GE(index_in_parent, 0);
- DCHECK_LT(index_in_parent, static_cast<int32_t>(GetChildCount()));
- for (uint32_t i = 0; i < static_cast<uint32_t>(index_in_parent); ++i) {
- auto* sibling = static_cast<AXPlatformNodeBase*>(
- FromNativeViewAccessible(ChildAtIndex(i)));
+ for (AXPlatformNodeBase* sibling = GetFirstChild(); sibling != child;
+ sibling = sibling->GetNextSibling()) {
DCHECK(sibling);
if (sibling->IsTextOnlyObject()) {
hypertext_offset += (int32_t)sibling->GetHypertext().size();
@@ -1510,7 +1509,7 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
}
AXPlatformNodeBase* common_parent = this;
- int32_t index_in_common_parent = GetIndexInParent();
+ base::Optional<int> index_in_common_parent = GetIndexInParent();
while (common_parent && !endpoint_object->IsDescendantOf(common_parent)) {
index_in_common_parent = common_parent->GetIndexInParent();
common_parent = static_cast<AXPlatformNodeBase*>(
@@ -1519,7 +1518,6 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
if (!common_parent)
return -1;
- DCHECK_GE(index_in_common_parent, 0);
DCHECK(!(common_parent->IsTextOnlyObject()));
// Case 2. Is the selection endpoint inside a descendant of this object?
@@ -1547,17 +1545,14 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
//
// We can safely assume that the endpoint is in another part of the tree or
// at common parent, and that this object is a descendant of common parent.
- int32_t endpoint_index_in_common_parent = -1;
- for (int i = 0; i < common_parent->GetDelegate()->GetChildCount(); ++i) {
- auto* child = static_cast<AXPlatformNodeBase*>(FromNativeViewAccessible(
- common_parent->GetDelegate()->ChildAtIndex(i)));
- DCHECK(child);
+ base::Optional<int> endpoint_index_in_common_parent;
+ for (AXPlatformNodeBase* child = common_parent->GetFirstChild();
+ child != nullptr; child = child->GetNextSibling()) {
if (endpoint_object->IsDescendantOf(child)) {
endpoint_index_in_common_parent = child->GetIndexInParent();
break;
}
}
- DCHECK_GE(endpoint_index_in_common_parent, 0);
if (endpoint_index_in_common_parent < index_in_common_parent)
return 0;
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 0355f281a600979f59a25086fd7400201693bf89..9cb3364d43cb93f5e612a3a076161b4f2e11adaf 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -69,8 +69,9 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
std::string GetName() const;
base::string16 GetNameAsString16() const;
- // This returns 0 if there's no parent.
- virtual int GetIndexInParent();
+ // This returns nullopt if there's no parent, it's unable to find the child in
+ // the list of its parent's children, or its parent doesn't have children.
+ virtual base::Optional<int> GetIndexInParent();
// AXPlatformNode.
void Destroy() override;
diff --git a/ui/accessibility/platform/ax_platform_node_mac.h b/ui/accessibility/platform/ax_platform_node_mac.h
index a5624cb789a7b686c1b139d1845127114befd7e2..920f0a0536364d26b99dbe54cf372abaf7d34439 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.h
+++ b/ui/accessibility/platform/ax_platform_node_mac.h
@@ -27,7 +27,6 @@ class AXPlatformNodeMac : public AXPlatformNodeBase {
// AXPlatformNodeBase.
void Destroy() override;
- int GetIndexInParent() override;
protected:
void AddAttributeToList(const char* name,
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 0454164364c6a4a1b1c11011603af5978d2b8ef5..bd9e17f1e21bd17a68988ee3c9ab08f2a1d99b2e 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -1257,11 +1257,6 @@ void AXPlatformNodeMac::AnnounceText(const base::string16& text) {
[native_node_ AXWindow], false);
}
-int AXPlatformNodeMac::GetIndexInParent() {
- // TODO(dmazzoni): implement this. http://crbug.com/396137
- return -1;
-}
-
bool IsNameExposedInAXValueForRole(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kListBoxOption:
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index a61d4121d9eeb336e72dbb6a3e2bf884a9b4e799..222c9358ca8e9958d6e89faf8779e706862e1f8c 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -1370,10 +1370,11 @@ IFACEMETHODIMP AXPlatformNodeWin::get_attributes(BSTR* attributes) {
IFACEMETHODIMP AXPlatformNodeWin::get_indexInParent(LONG* index_in_parent) {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_INDEX_IN_PARENT);
COM_OBJECT_VALIDATE_1_ARG(index_in_parent);
- *index_in_parent = GetIndexInParent();
- if (*index_in_parent < 0)
+ base::Optional<int> index = GetIndexInParent();
+ if (!index.has_value())
return E_FAIL;
+ *index_in_parent = index.value();
return S_OK;
}
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 2092d5c263dafa452c4977d53678ad81aa6575e1..8e2cf3d13fd4f936dd7c1e0a21bd245b589857d0 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -1149,8 +1149,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessible2IndexInParent) {
ComPtr<IAccessible2> right_iaccessible2 = ToIAccessible2(right_iaccessible);
LONG index;
- EXPECT_EQ(S_OK, root_iaccessible2->get_indexInParent(&index));
- EXPECT_EQ(0, index);
+ EXPECT_EQ(E_FAIL, root_iaccessible2->get_indexInParent(&index));
EXPECT_EQ(S_OK, left_iaccessible2->get_indexInParent(&index));
EXPECT_EQ(0, index);

View File

@@ -0,0 +1,115 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aaron Leventhal <aleventhal@chromium.org>
Date: Tue, 21 Jul 2020 19:41:11 +0000
Subject: Allow focus to move into an editable combobox's listbox
In bug 593646 (https://codereview.chromium.org/2024053003/) a rule was
added, apparently similar to a rule in WebKit, that prevents
aria-activedescendant on an editable combobox field from moving focus
into its listbox. However, removing this condition fixes an issue where
the first item is not read. It also improves the verbalization,
providing the user with the positional info, e.g. "2 of 5".
Removing this line also does not seem to break the example attached to
bug 593646.
Bug: 1082865
Change-Id: I4250fb152f4b06f3c57b300ebe7ef5549c58d624
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2303789
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790502}
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 33324bbca5953f6f2f9d829e4c7b5d7daa3f49ea..a27cacfd303706ef3ee637107d2de0c8839bfa20 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -143,11 +143,6 @@ BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus() const {
if (!focus)
return nullptr;
- // For editable combo boxes, focus should stay on the combo box so the user
- // will not be taken out of the combo box while typing.
- if (focus->GetRole() == ax::mojom::Role::kTextFieldWithComboBox)
- return focus;
-
// Otherwise, follow the active descendant.
return GetActiveDescendant(focus);
}
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt b/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
index d28f2235dedeedab23bf23db0475087c3d3ec73e..9dd9803232490edb95c80abcb2f084b8c42ceb7e 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
@@ -1,7 +1,7 @@
AXWebArea
++AXGroup
++++AXStaticText AXValue='State'
-++AXComboBox AXTitle='State' AXAutocompleteValue='list' AXFocused='1'
+++AXComboBox AXTitle='State' AXAutocompleteValue='list'
++AXList
++++AXStaticText AXValue='Alabama'
-++++AXStaticText AXValue='Alaska'
\ No newline at end of file
+++++AXStaticText AXFocused='1' AXValue='Alaska'
diff --git a/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt
index 97c3c417f30812abecdde273f130088df86bd4ae..93ef79e311977bb4277842fa5332f6097faba837 100644
--- a/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt
@@ -1,2 +1,3 @@
AXExpandedChanged on AXComboBox
-AXSelectedChildrenChanged on AXComboBox
+AXFocusedUIElementChanged on AXComboBox
+AXSelectedChildrenChanged on AXComboBox
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt
index 47ff72a2689baa23600ededfe38d79fd6b5bedcf..f7d4c30f49fe3f3f62132cddf59942181b8127b8 100644
--- a/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt
@@ -1 +1,2 @@
-AXSelectedChildrenChanged on AXComboBox
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
+AXSelectedChildrenChanged on AXComboBox
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt
index 47ff72a2689baa23600ededfe38d79fd6b5bedcf..ab757bb0687ff49affcc67076eddd52e18b0eacc 100644
--- a/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt
@@ -1 +1,2 @@
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
AXSelectedChildrenChanged on AXComboBox
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
index 97cdb8b8b67d46e0a952d22765c823bd346aef3a..343feae3d79a48981cc1e27015ba2a93423eedc0 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
@@ -1,3 +1,4 @@
AXExpandedChanged on AXComboBox
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
AXSelectedChildrenChanged on AXComboBox
-AXSelectedChildrenChanged on AXList
+AXSelectedChildrenChanged on AXList
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt
index 2bfc70f5fecea2c2a5e7268cef641d6d0e7d4a47..ad5e2bf2c8029185c51eecc94cac1dbe7608c99e 100644
--- a/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt
@@ -1,3 +1,3 @@
-AXFocusedUIElementChanged on AXComboBox
-AXSelectedTextChanged on AXComboBox
-AXSelectedTextChanged on AXWebArea
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple not selected"
+AXSelectedTextChanged on AXStaticText AXValue="Apple not selected"
+AXSelectedTextChanged on AXWebArea
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt
index d5f21183c3d0a1c24cb6665194a93c3299dbfd56..9e7d0c0aaeb1c52dc1f1b3afed36f287851b89ff 100644
--- a/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt
@@ -1,5 +1,7 @@
+AXFocusedUIElementChanged on AXStaticText AXValue="Orange"
AXSelectedChildrenChanged on AXComboBox
AXSelectedChildrenChanged on AXList
=== Start Continuation ===
+AXFocusedUIElementChanged on AXStaticText AXValue="Banana"
AXSelectedChildrenChanged on AXComboBox
-AXSelectedChildrenChanged on AXList
+AXSelectedChildrenChanged on AXList
\ No newline at end of file

View File

@@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tom Anderson <thomasanderson@chromium.org>
Date: Thu, 21 May 2020 01:20:33 +0000
Subject: Avoid loading DRI via GBM when GpuMemoryBuffers are disabled
We haven't yet whitelisted the necessary dri files in the GPU sandbox,
which is leading to issues like 1077609 and 1077626. Since GMBs are
not yet supported, avoid loading GBM unless
--enable-native-gpu-memory-buffers is passed.
Bug: 1077609, 1077626, 1031269
Change-Id: Ic052d2e89330c6558da86a91b77637229808102f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2211120
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#770878}
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 9b22493abbd81c2f592e75fe32f7ab7efbb281d9..d650cef5e081da9cd00896db7d59f00635f13279 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -366,11 +366,13 @@ int GpuMain(const MainFunctionParams& parameters) {
#if defined(USE_X11)
// ui::GbmDevice() takes >50ms with amdgpu, so kick off
// GpuMemoryBufferSupportX11 creation on another thread now.
- base::PostTask(
- FROM_HERE, base::BindOnce([]() {
- SCOPED_UMA_HISTOGRAM_TIMER("Linux.X11.GbmSupportX11CreationTime");
- ui::GpuMemoryBufferSupportX11::GetInstance();
- }));
+ if (gpu_preferences.enable_native_gpu_memory_buffers) {
+ base::PostTask(
+ FROM_HERE, base::BindOnce([]() {
+ SCOPED_UMA_HISTOGRAM_TIMER("Linux.X11.GbmSupportX11CreationTime");
+ ui::GpuMemoryBufferSupportX11::GetInstance();
+ }));
+ }
#endif
if (client)

View File

@@ -0,0 +1,549 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Thu, 4 Oct 2018 14:57:02 -0700
Subject: fix: data race on NodeChannel destruction
[1081874] [High] [CVE-2020-6575]: Double free on NodeChannel
Backport https://chromium.googlesource.com/chromium/src/+/5e61913985df0cc621bf72f7fa75e76759ffde15.
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn
index e6fcb96256f6fdb867a362588547a7829f28efe8..6282eed9158c691a9ca49d1ec9a57cedc4392741 100644
--- a/mojo/core/BUILD.gn
+++ b/mojo/core/BUILD.gn
@@ -18,6 +18,7 @@ component("embedder_internal") {
":test_sources",
"//mojo:*",
"//mojo/core/embedder",
+ "//mojo/core/test:test_support",
]
}
@@ -57,6 +58,7 @@ template("core_impl_source_set") {
"handle_table.h",
"invitation_dispatcher.h",
"message_pipe_dispatcher.h",
+ "node_channel.h",
"node_controller.h",
"options_validation.h",
"platform_handle_dispatcher.h",
@@ -86,7 +88,6 @@ template("core_impl_source_set") {
"invitation_dispatcher.cc",
"message_pipe_dispatcher.cc",
"node_channel.cc",
- "node_channel.h",
"node_controller.cc",
"platform_handle_dispatcher.cc",
"platform_handle_in_transit.cc",
@@ -289,6 +290,7 @@ source_set("test_sources") {
"handle_table_unittest.cc",
"message_pipe_unittest.cc",
"message_unittest.cc",
+ "node_channel_unittest.cc",
"options_validation_unittest.cc",
"platform_handle_dispatcher_unittest.cc",
"quota_unittest.cc",
@@ -387,6 +389,7 @@ fuzzer_test("mojo_core_node_channel_fuzzer") {
deps = [
":core_impl_for_fuzzers",
"//base",
+ "//mojo/core/test:test_support",
"//mojo/public/cpp/platform",
]
}
diff --git a/mojo/core/embedder/embedder.cc b/mojo/core/embedder/embedder.cc
index ec68e37d09888f672759f79961e3e6e4d18f0c5c..f70c73be4df529cce09cb503dfce28f2a04e7e1a 100644
--- a/mojo/core/embedder/embedder.cc
+++ b/mojo/core/embedder/embedder.cc
@@ -35,7 +35,7 @@ void SetDefaultProcessErrorCallback(ProcessErrorCallback callback) {
Core::Get()->SetDefaultProcessErrorCallback(std::move(callback));
}
-scoped_refptr<base::TaskRunner> GetIOTaskRunner() {
+scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() {
return Core::Get()->GetNodeController()->io_task_runner();
}
diff --git a/mojo/core/embedder/embedder.h b/mojo/core/embedder/embedder.h
index 5d65400987728940a296ba2a84d40158f52d6d34..96dc44c0a78e0e6cb6e5aa511bd2fd4f1252cf16 100644
--- a/mojo/core/embedder/embedder.h
+++ b/mojo/core/embedder/embedder.h
@@ -13,7 +13,7 @@
#include "base/component_export.h"
#include "base/memory/ref_counted.h"
#include "base/process/process_handle.h"
-#include "base/task_runner.h"
+#include "base/single_thread_task_runner.h"
#include "build/build_config.h"
#include "mojo/core/embedder/configuration.h"
@@ -42,9 +42,10 @@ void SetDefaultProcessErrorCallback(ProcessErrorCallback callback);
// Initialialization/shutdown for interprocess communication (IPC) -------------
-// Retrieves the TaskRunner used for IPC I/O, as set by ScopedIPCSupport.
+// Retrieves the SequencedTaskRunner used for IPC I/O, as set by
+// ScopedIPCSupport.
COMPONENT_EXPORT(MOJO_CORE_EMBEDDER)
-scoped_refptr<base::TaskRunner> GetIOTaskRunner();
+scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner();
} // namespace core
} // namespace mojo
diff --git a/mojo/core/node_channel.cc b/mojo/core/node_channel.cc
index e898b044286e019b5e423b941502030ce3094582..061ea1026e95d1b1f80a762ce377aebdd97e1b42 100644
--- a/mojo/core/node_channel.cc
+++ b/mojo/core/node_channel.cc
@@ -228,7 +228,7 @@ void NodeChannel::NotifyBadMessage(const std::string& error) {
}
void NodeChannel::SetRemoteProcessHandle(ScopedProcessHandle process_handle) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
{
base::AutoLock lock(channel_lock_);
if (channel_)
@@ -253,7 +253,7 @@ ScopedProcessHandle NodeChannel::CloneRemoteProcessHandle() {
}
void NodeChannel::SetRemoteNodeName(const ports::NodeName& name) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
remote_node_name_ = name;
}
@@ -468,15 +468,15 @@ NodeChannel::NodeChannel(
Channel::HandlePolicy channel_handle_policy,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
const ProcessErrorCallback& process_error_callback)
- : delegate_(delegate),
- io_task_runner_(io_task_runner),
+ : base::RefCountedDeleteOnSequence<NodeChannel>(io_task_runner),
+ delegate_(delegate),
process_error_callback_(process_error_callback)
#if !defined(OS_NACL_SFI)
,
channel_(Channel::Create(this,
std::move(connection_params),
channel_handle_policy,
- io_task_runner_))
+ std::move(io_task_runner)))
#endif
{
}
@@ -499,15 +499,10 @@ void NodeChannel::CreateAndBindLocalBrokerHost(
void NodeChannel::OnChannelMessage(const void* payload,
size_t payload_size,
std::vector<PlatformHandle> handles) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
RequestContext request_context(RequestContext::Source::SYSTEM);
- // Ensure this NodeChannel stays alive through the extent of this method. The
- // delegate may have the only other reference to this object and it may choose
- // to drop it here in response to, e.g., a malformed message.
- scoped_refptr<NodeChannel> keepalive = this;
-
if (payload_size <= sizeof(Header)) {
delegate_->OnChannelError(remote_node_name_, this);
return;
@@ -739,7 +734,7 @@ void NodeChannel::OnChannelMessage(const void* payload,
}
void NodeChannel::OnChannelError(Channel::Error error) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
RequestContext request_context(RequestContext::Source::SYSTEM);
diff --git a/mojo/core/node_channel.h b/mojo/core/node_channel.h
index ea91f927049befa258884b13e7f7966c09518687..04501da0fb6cd36df1b332135282104dd442b41e 100644
--- a/mojo/core/node_channel.h
+++ b/mojo/core/node_channel.h
@@ -11,7 +11,7 @@
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/process/process_handle.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
@@ -21,13 +21,15 @@
#include "mojo/core/embedder/process_error_callback.h"
#include "mojo/core/ports/name.h"
#include "mojo/core/scoped_process_handle.h"
+#include "mojo/core/system_impl_export.h"
namespace mojo {
namespace core {
// Wraps a Channel to send and receive Node control messages.
-class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
- public Channel::Delegate {
+class MOJO_SYSTEM_IMPL_EXPORT NodeChannel
+ : public base::RefCountedDeleteOnSequence<NodeChannel>,
+ public Channel::Delegate {
public:
class Delegate {
public:
@@ -92,8 +94,6 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
void** data,
size_t* num_data_bytes);
- Channel* channel() const { return channel_.get(); }
-
// Start receiving messages.
void Start();
@@ -155,7 +155,8 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
#endif
private:
- friend class base::RefCountedThreadSafe<NodeChannel>;
+ friend class base::RefCountedDeleteOnSequence<NodeChannel>;
+ friend class base::DeleteHelper<NodeChannel>;
using PendingMessageQueue = base::queue<Channel::MessagePtr>;
using PendingRelayMessageQueue =
@@ -181,13 +182,12 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
void WriteChannelMessage(Channel::MessagePtr message);
Delegate* const delegate_;
- const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
const ProcessErrorCallback process_error_callback_;
base::Lock channel_lock_;
- scoped_refptr<Channel> channel_;
+ scoped_refptr<Channel> channel_ GUARDED_BY(channel_lock_);
- // Must only be accessed from |io_task_runner_|'s thread.
+ // Must only be accessed from the owning task runner's thread.
ports::NodeName remote_node_name_;
base::Lock remote_process_handle_lock_;
diff --git a/mojo/core/node_channel_fuzzer.cc b/mojo/core/node_channel_fuzzer.cc
index 99047c000dbe6be90f1d28b4b6817e608f9853a6..54fe757e0dec8aa138af96aa1a0afe596563c26c 100644
--- a/mojo/core/node_channel_fuzzer.cc
+++ b/mojo/core/node_channel_fuzzer.cc
@@ -14,6 +14,7 @@
#include "mojo/core/connection_params.h"
#include "mojo/core/entrypoints.h"
#include "mojo/core/node_channel.h" // nogncheck
+#include "mojo/core/test/mock_node_channel_delegate.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#if defined(OS_WIN)
@@ -24,60 +25,6 @@ using mojo::core::Channel;
using mojo::core::ConnectionParams;
using mojo::core::ports::NodeName;
-// Implementation of NodeChannel::Delegate which does nothing. All of the
-// interesting NodeChannel control message message parsing is done by
-// NodeChannel by the time any of the delegate methods are invoked, so there's
-// no need for this to do any work.
-class FakeNodeChannelDelegate : public mojo::core::NodeChannel::Delegate {
- public:
- FakeNodeChannelDelegate() = default;
- ~FakeNodeChannelDelegate() override = default;
-
- void OnAcceptInvitee(const NodeName& from_node,
- const NodeName& inviter_name,
- const NodeName& token) override {}
- void OnAcceptInvitation(const NodeName& from_node,
- const NodeName& token,
- const NodeName& invitee_name) override {}
- void OnAddBrokerClient(const NodeName& from_node,
- const NodeName& client_name,
- base::ProcessHandle process_handle) override {}
- void OnBrokerClientAdded(const NodeName& from_node,
- const NodeName& client_name,
- mojo::PlatformHandle broker_channel) override {}
- void OnAcceptBrokerClient(const NodeName& from_node,
- const NodeName& broker_name,
- mojo::PlatformHandle broker_channel) override {}
- void OnEventMessage(const NodeName& from_node,
- Channel::MessagePtr message) override {}
- void OnRequestPortMerge(
- const NodeName& from_node,
- const mojo::core::ports::PortName& connector_port_name,
- const std::string& token) override {}
- void OnRequestIntroduction(const NodeName& from_node,
- const NodeName& name) override {}
- void OnIntroduce(const NodeName& from_node,
- const NodeName& name,
- mojo::PlatformHandle channel_handle) override {}
- void OnBroadcast(const NodeName& from_node,
- Channel::MessagePtr message) override {}
-#if defined(OS_WIN)
- void OnRelayEventMessage(const NodeName& from_node,
- base::ProcessHandle from_process,
- const NodeName& destination,
- Channel::MessagePtr message) override {}
- void OnEventMessageFromRelay(const NodeName& from_node,
- const NodeName& source_node,
- Channel::MessagePtr message) override {}
-#endif
- void OnAcceptPeer(const NodeName& from_node,
- const NodeName& token,
- const NodeName& peer_name,
- const mojo::core::ports::PortName& port_name) override {}
- void OnChannelError(const NodeName& node,
- mojo::core::NodeChannel* channel) override {}
-};
-
// A fake delegate for the sending Channel endpoint. The sending Channel is not
// being fuzzed and won't receive any interesting messages, so this doesn't need
// to do anything.
@@ -109,7 +56,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
// used to carry messages between processes.
mojo::PlatformChannel channel;
- FakeNodeChannelDelegate receiver_delegate;
+ mojo::core::MockNodeChannelDelegate receiver_delegate;
auto receiver = mojo::core::NodeChannel::Create(
&receiver_delegate, ConnectionParams(channel.TakeLocalEndpoint()),
Channel::HandlePolicy::kRejectHandles,
diff --git a/mojo/core/node_channel_unittest.cc b/mojo/core/node_channel_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..13c46f13fea6342316534a7a843debbf7586108d
--- /dev/null
+++ b/mojo/core/node_channel_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/core/node_channel.h"
+
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/message_loop/message_pump_type.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "mojo/core/embedder/embedder.h"
+#include "mojo/core/test/mock_node_channel_delegate.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace core {
+namespace {
+
+using NodeChannelTest = testing::Test;
+using ports::NodeName;
+
+scoped_refptr<NodeChannel> CreateNodeChannel(NodeChannel::Delegate* delegate,
+ PlatformChannelEndpoint endpoint) {
+ return NodeChannel::Create(delegate, ConnectionParams(std::move(endpoint)),
+ Channel::HandlePolicy::kAcceptHandles,
+ GetIOTaskRunner(), base::NullCallback());
+}
+
+TEST_F(NodeChannelTest, DestructionIsSafe) {
+ // Regression test for https://crbug.com/1081874.
+ base::test::TaskEnvironment task_environment;
+
+ PlatformChannel channel;
+ MockNodeChannelDelegate local_delegate;
+ auto local_channel =
+ CreateNodeChannel(&local_delegate, channel.TakeLocalEndpoint());
+ local_channel->Start();
+ MockNodeChannelDelegate remote_delegate;
+ auto remote_channel =
+ CreateNodeChannel(&remote_delegate, channel.TakeRemoteEndpoint());
+ remote_channel->Start();
+
+ // Verify end-to-end operation
+ const NodeName kRemoteNodeName{123, 456};
+ const NodeName kToken{987, 654};
+ base::RunLoop loop;
+ EXPECT_CALL(local_delegate,
+ OnAcceptInvitee(ports::kInvalidNodeName, kRemoteNodeName, kToken))
+ .WillRepeatedly([&] { loop.Quit(); });
+ remote_channel->AcceptInvitee(kRemoteNodeName, kToken);
+ loop.Run();
+
+ // Now send another message to the local endpoint but tear it down
+ // immediately. This will race with the message being received on the IO
+ // thread, and although the corresponding delegate call may or may not
+ // dispatch as a result, the race should still be memory-safe.
+ remote_channel->AcceptInvitee(kRemoteNodeName, kToken);
+
+ base::RunLoop error_loop;
+ EXPECT_CALL(remote_delegate, OnChannelError).WillOnce([&] {
+ error_loop.Quit();
+ });
+ local_channel.reset();
+ error_loop.Run();
+}
+
+} // namespace
+} // namespace core
+} // namespace mojo
diff --git a/mojo/core/test/BUILD.gn b/mojo/core/test/BUILD.gn
index 1abadfc503d5176d2af1dcecfde153f17488546d..9429c61853059e10ca2430ad79ec8d9a39d90906 100644
--- a/mojo/core/test/BUILD.gn
+++ b/mojo/core/test/BUILD.gn
@@ -7,6 +7,8 @@ import("//third_party/protobuf/proto_library.gni")
static_library("test_support") {
testonly = true
sources = [
+ "mock_node_channel_delegate.cc",
+ "mock_node_channel_delegate.h",
"mojo_test_base.cc",
"mojo_test_base.h",
"test_utils.h",
@@ -27,8 +29,10 @@ static_library("test_support") {
public_deps = [
"//base",
"//base/test:test_support",
+ "//mojo/core:embedder_internal",
"//mojo/core/embedder",
"//mojo/public/cpp/system",
+ "//testing/gmock",
"//testing/gtest",
]
}
diff --git a/mojo/core/test/mock_node_channel_delegate.cc b/mojo/core/test/mock_node_channel_delegate.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d257c3e1dc03857f91e94807328a0dc176f332f4
--- /dev/null
+++ b/mojo/core/test/mock_node_channel_delegate.cc
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/core/test/mock_node_channel_delegate.h"
+
+namespace mojo {
+namespace core {
+
+MockNodeChannelDelegate::MockNodeChannelDelegate() = default;
+
+MockNodeChannelDelegate::~MockNodeChannelDelegate() = default;
+
+} // namespace core
+} // namespace mojo
diff --git a/mojo/core/test/mock_node_channel_delegate.h b/mojo/core/test/mock_node_channel_delegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..06ca96857b9472682d577a802c68a56a7af0aacd
--- /dev/null
+++ b/mojo/core/test/mock_node_channel_delegate.h
@@ -0,0 +1,114 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_CORE_TEST_MOCK_NODE_CHANNEL_DELEGATE_H_
+#define MOJO_CORE_TEST_MOCK_NODE_CHANNEL_DELEGATE_H_
+
+#include "build/build_config.h"
+#include "mojo/core/node_channel.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace mojo {
+namespace core {
+
+// A NodeChannel Delegate implementation which can be used by NodeChannel unit
+// tests and fuzzers.
+class MockNodeChannelDelegate
+ : public testing::NiceMock<NodeChannel::Delegate> {
+ public:
+ using NodeName = ports::NodeName;
+ using PortName = ports::PortName;
+
+ MockNodeChannelDelegate();
+ MockNodeChannelDelegate(const MockNodeChannelDelegate&) = delete;
+ MockNodeChannelDelegate& operator=(const MockNodeChannelDelegate&) = delete;
+ ~MockNodeChannelDelegate() override;
+
+ // testing::NiceMock<NodeChannel::Delegate> implementation:
+ MOCK_METHOD(void,
+ OnAcceptInvitee,
+ (const NodeName& from_node,
+ const NodeName& inviter_name,
+ const NodeName& token),
+ (override));
+ MOCK_METHOD(void,
+ OnAcceptInvitation,
+ (const NodeName& from_node,
+ const NodeName& token,
+ const NodeName& invitee_name),
+ (override));
+ MOCK_METHOD(void,
+ OnAddBrokerClient,
+ (const NodeName& from_node,
+ const NodeName& client_name,
+ base::ProcessHandle process_handle),
+ (override));
+ MOCK_METHOD(void,
+ OnBrokerClientAdded,
+ (const NodeName& from_node,
+ const NodeName& client_name,
+ PlatformHandle broker_channel),
+ (override));
+ MOCK_METHOD(void,
+ OnAcceptBrokerClient,
+ (const NodeName& from_node,
+ const NodeName& broker_name,
+ PlatformHandle broker_channel),
+ (override));
+ MOCK_METHOD(void,
+ OnEventMessage,
+ (const NodeName& from_node, Channel::MessagePtr message),
+ (override));
+ MOCK_METHOD(void,
+ OnRequestPortMerge,
+ (const NodeName& from_node,
+ const PortName& connector_port_name,
+ const std::string& token),
+ (override));
+ MOCK_METHOD(void,
+ OnRequestIntroduction,
+ (const NodeName& from_node, const NodeName& name),
+ (override));
+ MOCK_METHOD(void,
+ OnIntroduce,
+ (const NodeName& from_node,
+ const NodeName& name,
+ PlatformHandle channel_handle),
+ (override));
+ MOCK_METHOD(void,
+ OnBroadcast,
+ (const NodeName& from_node, Channel::MessagePtr message),
+ (override));
+#if defined(OS_WIN)
+ MOCK_METHOD(void,
+ OnRelayEventMessage,
+ (const NodeName& from_node,
+ base::ProcessHandle from_process,
+ const NodeName& destination,
+ Channel::MessagePtr message),
+ (override));
+ MOCK_METHOD(void,
+ OnEventMessageFromRelay,
+ (const NodeName& from_node,
+ const NodeName& source_node,
+ Channel::MessagePtr message),
+ (override));
+#endif
+ MOCK_METHOD(void,
+ OnAcceptPeer,
+ (const NodeName& from_node,
+ const NodeName& token,
+ const NodeName& peer_name,
+ const PortName& port_name),
+ (override));
+ MOCK_METHOD(void,
+ OnChannelError,
+ (const NodeName& node, NodeChannel* channel),
+ (override));
+};
+
+} // namespace core
+} // namespace mojo
+
+#endif // MOJO_CORE_TEST_MOCK_NODE_CHANNEL_DELEGATE_H_

View File

@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Thu, 4 Oct 2018 14:57:02 -0700
Subject: fix: remove references to launched device before it is reset
[1111737] [High] [CVE-2020-6576]: Security: OffscreenCanvas - Use After Free in OffscreenCanvasRenderingContext2D::DrawTextInternal()
Backport https://chromium.googlesource.com/chromium/src/+/1283160e334f78c5eed4668d95e04f2ed2e2a4a3.
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index 8a3f1c3be3d804e3879a8a6aa5921bee99b0879e..85ed13b0008c58ba45cc28be36441234d928ed30 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -709,6 +709,10 @@ void VideoCaptureController::ReleaseDeviceAsync(base::OnceClosure done_cb) {
device_launcher_->AbortLaunch();
return;
}
+ // |buffer_contexts_| contain references to |launched_device_| as observers.
+ // Clear those observer references prior to resetting |launced_device_|.
+ for (auto& entry : buffer_contexts_)
+ entry.set_consumer_feedback_observer(nullptr);
launched_device_.reset();
}

View File

@@ -0,0 +1,149 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Thu, 4 Oct 2018 14:57:02 -0700
Subject: fix: elide headers in QuicHttp3Logger
[1122684] [High] [CVE-2020-15959]: Insufficient policy enforcement in networking.
Backport https://chromium.googlesource.com/chromium/src/+/c1a7439efcc7626c34d3e38503c974e4c215c489.
diff --git a/net/quic/quic_http3_logger.cc b/net/quic/quic_http3_logger.cc
index 8240046a419ed41fa340c106f45d6e9468ec7bf9..4cebe54bce4df8daaffe1a31f07ffe431a96bff6 100644
--- a/net/quic/quic_http3_logger.cc
+++ b/net/quic/quic_http3_logger.cc
@@ -9,10 +9,13 @@
#include <vector>
#include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
+#include "net/http/http_log_util.h"
#include "net/log/net_log_capture_mode.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_values.h"
+#include "net/spdy/spdy_log_util.h"
namespace net {
@@ -64,20 +67,19 @@ base::Value NetLogThreeIntParams(base::StringPiece name1,
return dict;
}
-base::Value NetLogHeadersToDict(const quic::QuicHeaderList& headers) {
- base::Value dict(base::Value::Type::DICTIONARY);
- for (auto header : headers) {
- dict.SetStringKey(header.first, header.second);
- }
- return dict;
-}
-
-base::Value NetLogHeadersToDict(const spdy::SpdyHeaderBlock& headers) {
- base::Value dict(base::Value::Type::DICTIONARY);
- for (auto header : headers) {
- dict.SetStringKey(header.first, header.second);
+base::ListValue ElideQuicHeaderListForNetLog(
+ const quic::QuicHeaderList& headers,
+ NetLogCaptureMode capture_mode) {
+ base::ListValue headers_list;
+ for (const auto& header : headers) {
+ base::StringPiece key = header.first;
+ base::StringPiece value = header.second;
+ headers_list.Append(NetLogStringValue(
+ base::StrCat({key, ": ",
+ ElideHeaderValueForNetLog(capture_mode, key.as_string(),
+ value.as_string())})));
}
- return dict;
+ return headers_list;
}
} // namespace
@@ -235,11 +237,13 @@ void QuicHttp3Logger::OnHeadersDecoded(quic::QuicStreamId stream_id,
return;
}
net_log_.AddEvent(
- NetLogEventType::HTTP3_HEADERS_DECODED, [stream_id, &headers] {
+ NetLogEventType::HTTP3_HEADERS_DECODED,
+ [stream_id, &headers](NetLogCaptureMode capture_mode) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("stream_id",
NetLogNumberValue(static_cast<uint64_t>(stream_id)));
- dict.SetKey("headers", NetLogHeadersToDict(headers));
+ dict.SetKey("headers",
+ ElideQuicHeaderListForNetLog(headers, capture_mode));
return dict;
});
}
@@ -266,16 +270,18 @@ void QuicHttp3Logger::OnPushPromiseDecoded(quic::QuicStreamId stream_id,
if (!net_log_.IsCapturing()) {
return;
}
- net_log_.AddEvent(NetLogEventType::HTTP3_PUSH_PROMISE_DECODED, [stream_id,
- push_id,
- &headers] {
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetKey("stream_id",
- NetLogNumberValue(static_cast<uint64_t>(stream_id)));
- dict.SetKey("push_id", NetLogNumberValue(static_cast<uint64_t>(push_id)));
- dict.SetKey("headers", NetLogHeadersToDict(headers));
- return dict;
- });
+ net_log_.AddEvent(
+ NetLogEventType::HTTP3_PUSH_PROMISE_DECODED,
+ [stream_id, push_id, &headers](NetLogCaptureMode capture_mode) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey("stream_id",
+ NetLogNumberValue(static_cast<uint64_t>(stream_id)));
+ dict.SetKey("push_id",
+ NetLogNumberValue(static_cast<uint64_t>(push_id)));
+ dict.SetKey("headers",
+ ElideQuicHeaderListForNetLog(headers, capture_mode));
+ return dict;
+ });
}
void QuicHttp3Logger::OnUnknownFrameReceived(
@@ -344,11 +350,13 @@ void QuicHttp3Logger::OnHeadersFrameSent(
return;
}
net_log_.AddEvent(
- NetLogEventType::HTTP3_HEADERS_SENT, [stream_id, &header_block] {
+ NetLogEventType::HTTP3_HEADERS_SENT,
+ [stream_id, &header_block](NetLogCaptureMode capture_mode) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("stream_id",
NetLogNumberValue(static_cast<uint64_t>(stream_id)));
- dict.SetKey("headers", NetLogHeadersToDict(header_block));
+ dict.SetKey("headers",
+ ElideSpdyHeaderBlockForNetLog(header_block, capture_mode));
return dict;
});
}
@@ -360,16 +368,18 @@ void QuicHttp3Logger::OnPushPromiseFrameSent(
if (!net_log_.IsCapturing()) {
return;
}
- net_log_.AddEvent(NetLogEventType::HTTP3_PUSH_PROMISE_SENT, [stream_id,
- push_id,
- &header_block] {
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetKey("stream_id",
- NetLogNumberValue(static_cast<uint64_t>(stream_id)));
- dict.SetKey("push_id", NetLogNumberValue(static_cast<uint64_t>(push_id)));
- dict.SetKey("headers", NetLogHeadersToDict(header_block));
- return dict;
- });
+ net_log_.AddEvent(
+ NetLogEventType::HTTP3_PUSH_PROMISE_SENT,
+ [stream_id, push_id, &header_block](NetLogCaptureMode capture_mode) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey("stream_id",
+ NetLogNumberValue(static_cast<uint64_t>(stream_id)));
+ dict.SetKey("push_id",
+ NetLogNumberValue(static_cast<uint64_t>(push_id)));
+ dict.SetKey("headers",
+ ElideSpdyHeaderBlockForNetLog(header_block, capture_mode));
+ return dict;
+ });
}
} // namespace net

View File

@@ -0,0 +1,750 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ramin Halavati <rhalavati@chromium.org>
Date: Wed, 9 Sep 2020 05:10:19 +0000
Subject: Reland Run ObfuscatedFileUtilMemoryDelegate entirely on TaskRunner.
MemoryFileStreamWriter called some ObfuscatedFileUtilMemoryDelegate
functions through IO thread while other functions in OFUMD are called
on a threadpool sequence. This could result in races in updating
directory structure.
To fix the issue, MemoryFileStreamWriter and MemoryFileStreamReader are
updated to call all OFUMD on the default task runner of the file system
context.
This CL was landed in crrev.com/c/2308721 and reverted due to flakiness.
The flaky crashes are believed to be because the buffer passed to
MemoryFileStreamReader::Read and MemoryFileStreamWrite::Write are not
thread safe.
Patchset1 is a copy of the previous CL and the issue is fixed in the
next patchsets.
Bug: 1100136
Change-Id: I619b82c2f4d23a020e9ce7e5e6c16980907b501b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2398701
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Ramin Halavati <rhalavati@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805198}
(cherry picked from commit 0e61c69ebd476e5b688f341f8d0bf69fe814c515)
diff --git a/storage/browser/file_system/file_stream_reader.h b/storage/browser/file_system/file_stream_reader.h
index b3cfc7f751be2e8654ba6d5e51849a4f35863d7a..d9ac296c94a4df765417d71b0c80b798ff7c888c 100644
--- a/storage/browser/file_system/file_stream_reader.h
+++ b/storage/browser/file_system/file_stream_reader.h
@@ -60,6 +60,7 @@ class FileStreamReader {
// ERR_UPLOAD_FILE_CHANGED error.
COMPONENT_EXPORT(STORAGE_BROWSER)
static std::unique_ptr<FileStreamReader> CreateForMemoryFile(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset,
diff --git a/storage/browser/file_system/file_stream_test_utils.cc b/storage/browser/file_system/file_stream_test_utils.cc
index 835a423c9c913ed46e8b68e7a9d3b323b3263695..e66dfc716aae031cc9c6e00d660753d4487cb60a 100644
--- a/storage/browser/file_system/file_stream_test_utils.cc
+++ b/storage/browser/file_system/file_stream_test_utils.cc
@@ -40,6 +40,14 @@ void ReadFromReader(FileStreamReader* reader,
}
}
+int64_t GetLengthFromReader(FileStreamReader* reader) {
+ EXPECT_NE(nullptr, reader);
+ net::TestInt64CompletionCallback callback;
+
+ int rv = reader->GetLength(callback.callback());
+ return callback.GetResult(rv);
+}
+
int WriteStringToWriter(FileStreamWriter* writer, const std::string& data) {
scoped_refptr<net::StringIOBuffer> buffer =
base::MakeRefCounted<net::StringIOBuffer>(data);
diff --git a/storage/browser/file_system/file_stream_test_utils.h b/storage/browser/file_system/file_stream_test_utils.h
index 5714f7a1e7a1f6e91628e9f958a1b13324d7ec8e..d6425f15af6309a0891a10ca54cc092b8c1180f1 100644
--- a/storage/browser/file_system/file_stream_test_utils.h
+++ b/storage/browser/file_system/file_stream_test_utils.h
@@ -20,8 +20,12 @@ void ReadFromReader(FileStreamReader* reader,
size_t size,
int* result);
-// Writes |data| to |writer|, an intialized FileStreamWriter. Returns net::OK if
-// successful, otherwise a net error.
+// Returns the length of the file if it could be successfully retrieved,
+// otherwise a net error.
+int64_t GetLengthFromReader(FileStreamReader* reader);
+
+// Writes |data| to |writer|, an initialized FileStreamWriter. Returns net::OK
+// if successful, otherwise a net error.
int WriteStringToWriter(FileStreamWriter* writer, const std::string& data);
} // namespace storage
diff --git a/storage/browser/file_system/file_stream_writer.h b/storage/browser/file_system/file_stream_writer.h
index 2ddbecc6587d94d16ad547e3b2249c103621ee7e..11ce21c64d9b0f43d761b45ae7a710be60f03316 100644
--- a/storage/browser/file_system/file_stream_writer.h
+++ b/storage/browser/file_system/file_stream_writer.h
@@ -48,10 +48,9 @@ class FileStreamWriter {
// Creates a writer for the existing memory file in the path |file_path|
// starting from |initial_offset|.
- // TODO(mek): Remove or use |open_or_create| field here, as it is not
- // currently used. https://crbug.com/1041048
COMPONENT_EXPORT(STORAGE_BROWSER)
static std::unique_ptr<FileStreamWriter> CreateForMemoryFile(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset);
diff --git a/storage/browser/file_system/file_system_file_stream_reader.cc b/storage/browser/file_system/file_system_file_stream_reader.cc
index 8a3b85166bf85fb9661953c37b2942eddd5a61e1..9d37b8075199fe5c93c058610bb1f9d99730b526 100644
--- a/storage/browser/file_system/file_system_file_stream_reader.cc
+++ b/storage/browser/file_system/file_system_file_stream_reader.cc
@@ -112,6 +112,7 @@ void FileSystemFileStreamReader::DidCreateSnapshot(
file_system_context_->sandbox_delegate()->memory_file_util_delegate();
}
file_reader_ = FileStreamReader::CreateForMemoryFile(
+ file_system_context_->default_file_task_runner(),
memory_file_util_delegate, platform_path, initial_offset_,
expected_modification_time_);
} else {
diff --git a/storage/browser/file_system/memory_file_stream_reader.cc b/storage/browser/file_system/memory_file_stream_reader.cc
index f5d895c6cc97e883024e854395a24f094c797ed4..0ca229bb8e8e853d96710fc5946e7a5d854c2180 100644
--- a/storage/browser/file_system/memory_file_stream_reader.cc
+++ b/storage/browser/file_system/memory_file_stream_reader.cc
@@ -8,68 +8,114 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "base/task_runner_util.h"
+#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
namespace storage {
std::unique_ptr<FileStreamReader> FileStreamReader::CreateForMemoryFile(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time) {
- return base::WrapUnique(
- new MemoryFileStreamReader(std::move(memory_file_util), file_path,
- initial_offset, expected_modification_time));
+ return base::WrapUnique(new MemoryFileStreamReader(
+ std::move(task_runner), std::move(memory_file_util), file_path,
+ initial_offset, expected_modification_time));
}
MemoryFileStreamReader::MemoryFileStreamReader(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time)
: memory_file_util_(std::move(memory_file_util)),
+ task_runner_(std::move(task_runner)),
file_path_(file_path),
expected_modification_time_(expected_modification_time),
offset_(initial_offset) {
- DCHECK(memory_file_util_);
+ DCHECK(memory_file_util_.MaybeValid());
}
MemoryFileStreamReader::~MemoryFileStreamReader() = default;
int MemoryFileStreamReader::Read(net::IOBuffer* buf,
int buf_len,
- net::CompletionOnceCallback /*callback*/) {
- base::File::Info file_info;
- if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
- base::File::FILE_OK) {
- return net::ERR_FILE_NOT_FOUND;
- }
-
- if (!FileStreamReader::VerifySnapshotTime(expected_modification_time_,
- file_info)) {
- return net::ERR_UPLOAD_FILE_CHANGED;
- }
-
- int result = memory_file_util_->ReadFile(file_path_, offset_, buf, buf_len);
+ net::CompletionOnceCallback callback) {
+ task_runner_->PostTaskAndReplyWithResult(
+ FROM_HERE,
+ base::BindOnce(
+ [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
+ const base::FilePath& path, base::Time expected_modification_time,
+ int64_t offset, scoped_refptr<net::IOBuffer> buf,
+ int buf_len) -> int {
+ if (!util)
+ return net::ERR_FILE_NOT_FOUND;
+ base::File::Info file_info;
+ if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK)
+ return net::ERR_FILE_NOT_FOUND;
+
+ if (!FileStreamReader::VerifySnapshotTime(
+ expected_modification_time, file_info)) {
+ return net::ERR_UPLOAD_FILE_CHANGED;
+ }
+
+ return util->ReadFile(path, offset, std::move(buf), buf_len);
+ },
+ memory_file_util_, file_path_, expected_modification_time_, offset_,
+ base::WrapRefCounted(buf), buf_len),
+ base::BindOnce(&MemoryFileStreamReader::OnReadCompleted,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+
+ return net::ERR_IO_PENDING;
+}
+
+void MemoryFileStreamReader::OnReadCompleted(
+ net::CompletionOnceCallback callback,
+ int result) {
if (result > 0)
offset_ += result;
- return result;
+
+ std::move(callback).Run(result);
}
int64_t MemoryFileStreamReader::GetLength(
- net::Int64CompletionOnceCallback /*callback*/) {
- base::File::Info file_info;
- if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
- base::File::FILE_OK) {
- return net::ERR_FILE_NOT_FOUND;
- }
-
- if (!FileStreamReader::VerifySnapshotTime(expected_modification_time_,
- file_info)) {
- return net::ERR_UPLOAD_FILE_CHANGED;
- }
-
- return file_info.size;
+ net::Int64CompletionOnceCallback callback) {
+ task_runner_->PostTaskAndReplyWithResult(
+ FROM_HERE,
+ base::BindOnce(
+ [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
+ const base::FilePath& path,
+ base::Time expected_modification_time) -> int64_t {
+ if (!util)
+ return net::ERR_FILE_NOT_FOUND;
+ base::File::Info file_info;
+ if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK) {
+ return net::ERR_FILE_NOT_FOUND;
+ }
+
+ if (!FileStreamReader::VerifySnapshotTime(
+ expected_modification_time, file_info)) {
+ return net::ERR_UPLOAD_FILE_CHANGED;
+ }
+
+ return file_info.size;
+ },
+ memory_file_util_, file_path_, expected_modification_time_),
+ // |callback| is not directly used to make sure that it is not called if
+ // stream is deleted while this function is in flight.
+ base::BindOnce(&MemoryFileStreamReader::OnGetLengthCompleted,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+
+ return net::ERR_IO_PENDING;
+}
+
+void MemoryFileStreamReader::OnGetLengthCompleted(
+ net::Int64CompletionOnceCallback callback,
+ int64_t result) {
+ std::move(callback).Run(result);
}
} // namespace storage
diff --git a/storage/browser/file_system/memory_file_stream_reader.h b/storage/browser/file_system/memory_file_stream_reader.h
index 909db6b1178bc329af5e4694538045bba243310b..4f05d450522613e668549e59d58c36552650773e 100644
--- a/storage/browser/file_system/memory_file_stream_reader.h
+++ b/storage/browser/file_system/memory_file_stream_reader.h
@@ -32,17 +32,25 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) MemoryFileStreamReader
friend class FileStreamReader;
MemoryFileStreamReader(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time);
+ void OnReadCompleted(net::CompletionOnceCallback callback, int result);
+ void OnGetLengthCompleted(net::Int64CompletionOnceCallback callback,
+ int64_t result);
+
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
+ const scoped_refptr<base::TaskRunner> task_runner_;
const base::FilePath file_path_;
const base::Time expected_modification_time_;
int64_t offset_;
+ base::WeakPtrFactory<MemoryFileStreamReader> weak_factory_{this};
+
DISALLOW_COPY_AND_ASSIGN(MemoryFileStreamReader);
};
diff --git a/storage/browser/file_system/memory_file_stream_reader_unittest.cc b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
index 7cbaf6e06f82e792a52d549f963a0044a0a5fbd5..99bcfcbeb7e5dcdced47be3df1820f518558a78b 100644
--- a/storage/browser/file_system/memory_file_stream_reader_unittest.cc
+++ b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
@@ -17,6 +17,7 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
+#include "base/test/task_environment.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "storage/browser/file_system/file_stream_reader.h"
@@ -62,9 +63,9 @@ class MemoryFileStreamReaderTest : public testing::Test {
const base::FilePath& path,
int64_t initial_offset,
const base::Time& expected_modification_time) {
- return FileStreamReader::CreateForMemoryFile(file_util_->GetWeakPtr(), path,
- initial_offset,
- expected_modification_time);
+ return FileStreamReader::CreateForMemoryFile(
+ base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
+ initial_offset, expected_modification_time);
}
void TouchTestFile(base::TimeDelta delta) {
@@ -83,6 +84,7 @@ class MemoryFileStreamReaderTest : public testing::Test {
}
private:
+ base::test::TaskEnvironment task_environment_;
base::ScopedTempDir file_system_directory_;
std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
base::Time test_file_modification_time_;
@@ -113,14 +115,14 @@ TEST_F(MemoryFileStreamReaderTest, Empty) {
ASSERT_EQ(net::OK, result);
ASSERT_EQ(0U, data.size());
- int64_t length_result = reader->GetLength(base::DoNothing());
+ int64_t length_result = GetLengthFromReader(reader.get());
ASSERT_EQ(0, length_result);
}
TEST_F(MemoryFileStreamReaderTest, GetLengthNormal) {
std::unique_ptr<FileStreamReader> reader(
CreateFileReader(test_path(), 0, test_file_modification_time()));
- int64_t result = reader->GetLength(base::DoNothing());
+ int64_t result = GetLengthFromReader(reader.get());
ASSERT_EQ(kTestDataSize, result);
}
@@ -131,7 +133,7 @@ TEST_F(MemoryFileStreamReaderTest, GetLengthAfterModified) {
std::unique_ptr<FileStreamReader> reader(
CreateFileReader(test_path(), 0, test_file_modification_time()));
- int64_t result = reader->GetLength(base::DoNothing());
+ int64_t result = GetLengthFromReader(reader.get());
ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
}
@@ -142,14 +144,14 @@ TEST_F(MemoryFileStreamReaderTest, GetLengthAfterModifiedWithNoExpectedTime) {
std::unique_ptr<FileStreamReader> reader(
CreateFileReader(test_path(), 0, base::Time()));
- int64_t result = reader->GetLength(base::DoNothing());
+ int64_t result = GetLengthFromReader(reader.get());
ASSERT_EQ(kTestDataSize, result);
}
TEST_F(MemoryFileStreamReaderTest, GetLengthWithOffset) {
std::unique_ptr<FileStreamReader> reader(
CreateFileReader(test_path(), 3, base::Time()));
- int64_t result = reader->GetLength(base::DoNothing());
+ int64_t result = GetLengthFromReader(reader.get());
// Initial offset does not affect the result of GetLength.
ASSERT_EQ(kTestDataSize, result);
}
diff --git a/storage/browser/file_system/memory_file_stream_writer.cc b/storage/browser/file_system/memory_file_stream_writer.cc
index 9c421145866cd4425f752e343abcfca6260178a2..b36c4b5e4bbb5d7280fba11ab73e8c4a4d39c088 100644
--- a/storage/browser/file_system/memory_file_stream_writer.cc
+++ b/storage/browser/file_system/memory_file_stream_writer.cc
@@ -8,43 +8,68 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "base/task_runner_util.h"
+#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
namespace storage {
std::unique_ptr<FileStreamWriter> FileStreamWriter::CreateForMemoryFile(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset) {
return base::WrapUnique(new MemoryFileStreamWriter(
- std::move(memory_file_util), file_path, initial_offset));
+ std::move(task_runner), std::move(memory_file_util), file_path,
+ initial_offset));
}
MemoryFileStreamWriter::MemoryFileStreamWriter(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset)
: memory_file_util_(std::move(memory_file_util)),
+ task_runner_(std::move(task_runner)),
file_path_(file_path),
offset_(initial_offset) {
- DCHECK(memory_file_util_);
+ DCHECK(memory_file_util_.MaybeValid());
}
MemoryFileStreamWriter::~MemoryFileStreamWriter() = default;
int MemoryFileStreamWriter::Write(net::IOBuffer* buf,
int buf_len,
- net::CompletionOnceCallback /*callback*/) {
- base::File::Info file_info;
- if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
- base::File::FILE_OK) {
- return net::ERR_FILE_NOT_FOUND;
- }
-
- int result = memory_file_util_->WriteFile(file_path_, offset_, buf, buf_len);
+ net::CompletionOnceCallback callback) {
+ task_runner_->PostTaskAndReplyWithResult(
+ FROM_HERE,
+ base::BindOnce(
+ [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
+ const base::FilePath& path, int64_t offset,
+ scoped_refptr<net::IOBuffer> buf, int buf_len) -> int {
+ if (!util)
+ return net::ERR_FILE_NOT_FOUND;
+ base::File::Info file_info;
+ if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK)
+ return net::ERR_FILE_NOT_FOUND;
+
+ return util->WriteFile(path, offset, std::move(buf), buf_len);
+ },
+ memory_file_util_, file_path_, offset_, base::WrapRefCounted(buf),
+ buf_len),
+ base::BindOnce(&MemoryFileStreamWriter::OnWriteCompleted,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+
+ return net::ERR_IO_PENDING;
+}
+
+void MemoryFileStreamWriter::OnWriteCompleted(
+ net::CompletionOnceCallback callback,
+ int result) {
if (result > 0)
offset_ += result;
- return result;
+
+ std::move(callback).Run(result);
}
int MemoryFileStreamWriter::Cancel(net::CompletionOnceCallback /*callback*/) {
diff --git a/storage/browser/file_system/memory_file_stream_writer.h b/storage/browser/file_system/memory_file_stream_writer.h
index fe1c9d17932e2c4398295bc0ed6359f86281be91..74f6213f0f80b619083779184d04a62c0f983835 100644
--- a/storage/browser/file_system/memory_file_stream_writer.h
+++ b/storage/browser/file_system/memory_file_stream_writer.h
@@ -30,15 +30,21 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) MemoryFileStreamWriter
private:
friend class FileStreamWriter;
MemoryFileStreamWriter(
+ scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
const base::FilePath& file_path,
int64_t initial_offset);
+ void OnWriteCompleted(net::CompletionOnceCallback callback, int result);
+
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
+ const scoped_refptr<base::TaskRunner> task_runner_;
const base::FilePath file_path_;
int64_t offset_;
+ base::WeakPtrFactory<MemoryFileStreamWriter> weak_factory_{this};
+
DISALLOW_COPY_AND_ASSIGN(MemoryFileStreamWriter);
};
diff --git a/storage/browser/file_system/memory_file_stream_writer_unittest.cc b/storage/browser/file_system/memory_file_stream_writer_unittest.cc
index 7fcda3dfd5e533e8d501a5b56ae5b2abd9486f2f..44342a33c66f21eed35287a4bb9b94f82de21521 100644
--- a/storage/browser/file_system/memory_file_stream_writer_unittest.cc
+++ b/storage/browser/file_system/memory_file_stream_writer_unittest.cc
@@ -13,6 +13,7 @@
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/test/task_environment.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "storage/browser/file_system/file_stream_test_utils.h"
@@ -59,11 +60,13 @@ class MemoryFileStreamWriterTest : public testing::Test {
std::unique_ptr<FileStreamWriter> CreateWriter(const base::FilePath& path,
int64_t offset) {
- return FileStreamWriter::CreateForMemoryFile(file_util_->GetWeakPtr(), path,
- offset);
+ return FileStreamWriter::CreateForMemoryFile(
+ base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
+ offset);
}
private:
+ base::test::TaskEnvironment task_environment_;
base::ScopedTempDir file_system_directory_;
std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
};
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
index 5919d1f1c2c4c80fd73cfbb0586af0e505d43152..3a3d8563e3c91d3b9ca15a980a709a8fd96e8c6c 100644
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
@@ -56,13 +56,17 @@ struct ObfuscatedFileUtilMemoryDelegate::DecomposedPath {
ObfuscatedFileUtilMemoryDelegate::ObfuscatedFileUtilMemoryDelegate(
const base::FilePath& file_system_directory)
: root_(std::make_unique<Entry>(Entry::kDirectory)) {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
file_system_directory.GetComponents(&root_path_components_);
}
-ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() = default;
+ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
base::Optional<ObfuscatedFileUtilMemoryDelegate::DecomposedPath>
ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DecomposedPath dp;
path.GetComponents(&dp.components);
@@ -118,6 +122,7 @@ ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) {
bool ObfuscatedFileUtilMemoryDelegate::DirectoryExists(
const base::FilePath& path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
return dp && dp->entry && dp->entry->type == Entry::kDirectory;
}
@@ -126,6 +131,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateDirectory(
const base::FilePath& path,
bool exclusive,
bool recursive) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp)
return base::File::FILE_ERROR_NOT_FOUND;
@@ -169,6 +175,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateDirectory(
bool ObfuscatedFileUtilMemoryDelegate::DeleteFileOrDirectory(
const base::FilePath& path,
bool recursive) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp)
return false;
@@ -185,11 +192,13 @@ bool ObfuscatedFileUtilMemoryDelegate::DeleteFileOrDirectory(
}
bool ObfuscatedFileUtilMemoryDelegate::IsLink(const base::FilePath& file_path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// In-memory file system does not support links.
return false;
}
bool ObfuscatedFileUtilMemoryDelegate::PathExists(const base::FilePath& path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
return dp && dp->entry;
}
@@ -197,6 +206,7 @@ bool ObfuscatedFileUtilMemoryDelegate::PathExists(const base::FilePath& path) {
base::File ObfuscatedFileUtilMemoryDelegate::CreateOrOpen(
const base::FilePath& path,
int file_flags) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO:(https://crbug.com/936722): Once the output of this function is
// changed to base::File::Error, it can use CreateOrOpenInternal to perform
// the task and return the result.
@@ -206,6 +216,7 @@ base::File ObfuscatedFileUtilMemoryDelegate::CreateOrOpen(
void ObfuscatedFileUtilMemoryDelegate::CreateOrOpenInternal(
const DecomposedPath& dp,
int file_flags) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!dp.entry) {
dp.parent->directory_content.emplace(dp.components.back(), Entry::kFile);
return;
@@ -221,6 +232,7 @@ void ObfuscatedFileUtilMemoryDelegate::CreateOrOpenInternal(
base::File::Error ObfuscatedFileUtilMemoryDelegate::DeleteFile(
const base::FilePath& path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp || !dp->entry)
return base::File::FILE_ERROR_NOT_FOUND;
@@ -235,6 +247,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::DeleteFile(
base::File::Error ObfuscatedFileUtilMemoryDelegate::EnsureFileExists(
const base::FilePath& path,
bool* created) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
*created = false;
if (!dp || !dp->parent)
@@ -253,6 +266,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::EnsureFileExists(
base::File::Error ObfuscatedFileUtilMemoryDelegate::GetFileInfo(
const base::FilePath& path,
base::File::Info* file_info) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp || !dp->entry)
return base::File::FILE_ERROR_NOT_FOUND;
@@ -272,6 +286,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::Touch(
const base::FilePath& path,
const base::Time& last_access_time,
const base::Time& last_modified_time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp || !dp->entry)
return base::File::FILE_ERROR_FAILED;
@@ -285,6 +300,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::Touch(
base::File::Error ObfuscatedFileUtilMemoryDelegate::Truncate(
const base::FilePath& path,
int64_t length) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
return base::File::FILE_ERROR_NOT_FOUND;
@@ -297,6 +313,7 @@ NativeFileUtil::CopyOrMoveMode
ObfuscatedFileUtilMemoryDelegate::CopyOrMoveModeForDestination(
const FileSystemURL& /*dest_url*/,
bool copy) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return copy ? NativeFileUtil::CopyOrMoveMode::COPY_SYNC
: NativeFileUtil::CopyOrMoveMode::MOVE;
}
@@ -306,6 +323,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile(
const base::FilePath& dest_path,
FileSystemOperation::CopyOrMoveOption option,
NativeFileUtil::CopyOrMoveMode mode) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> src_dp = ParsePath(src_path);
base::Optional<DecomposedPath> dest_dp = ParsePath(dest_path);
@@ -361,6 +379,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile(
bool ObfuscatedFileUtilMemoryDelegate::MoveDirectoryInternal(
const DecomposedPath& src_dp,
const DecomposedPath& dest_dp) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(src_dp.entry->type == Entry::kDirectory);
if (!dest_dp.entry) {
dest_dp.parent->directory_content.insert(
@@ -379,6 +398,7 @@ bool ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFileInternal(
const DecomposedPath& src_dp,
const DecomposedPath& dest_dp,
bool move) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(src_dp.entry->type == Entry::kFile);
if (dest_dp.entry)
dest_dp.parent->directory_content.erase(dest_dp.components.back());
@@ -404,6 +424,7 @@ bool ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFileInternal(
size_t ObfuscatedFileUtilMemoryDelegate::ComputeDirectorySize(
const base::FilePath& path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp || !dp->entry || dp->entry->type != Entry::kDirectory)
return 0;
@@ -427,8 +448,9 @@ size_t ObfuscatedFileUtilMemoryDelegate::ComputeDirectorySize(
int ObfuscatedFileUtilMemoryDelegate::ReadFile(const base::FilePath& path,
int64_t offset,
- net::IOBuffer* buf,
+ scoped_refptr<net::IOBuffer> buf,
int buf_len) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
if (!dp || dp->entry->type != Entry::kFile)
return net::ERR_FILE_NOT_FOUND;
@@ -445,13 +467,15 @@ int ObfuscatedFileUtilMemoryDelegate::ReadFile(const base::FilePath& path,
return buf_len;
}
-int ObfuscatedFileUtilMemoryDelegate::WriteFile(const base::FilePath& path,
- int64_t offset,
- net::IOBuffer* buf,
- int buf_len) {
+int ObfuscatedFileUtilMemoryDelegate::WriteFile(
+ const base::FilePath& path,
+ int64_t offset,
+ scoped_refptr<net::IOBuffer> buf,
+ int buf_len) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || dp->entry->type != Entry::kFile)
+ if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
return net::ERR_FILE_NOT_FOUND;
size_t offset_u = static_cast<size_t>(offset);
@@ -479,6 +503,7 @@ int ObfuscatedFileUtilMemoryDelegate::WriteFile(const base::FilePath& path,
base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateFileForTesting(
const base::FilePath& path,
base::span<const char> content) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool created;
base::File::Error result = EnsureFileExists(path, &created);
if (result != base::File::FILE_OK)
@@ -498,6 +523,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyInForeignFile(
const base::FilePath& dest_path,
FileSystemOperation::CopyOrMoveOption /* option */,
NativeFileUtil::CopyOrMoveMode /* mode */) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Optional<DecomposedPath> dest_dp = ParsePath(dest_path);
if (!dest_dp || !dest_dp->parent)
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.h b/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
index 4dd25b48affa901251ec4ec54dc6221a60626d19..d1240511303860e67603543e5795c893ef0db482 100644
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
@@ -88,7 +88,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilMemoryDelegate
// bytes are returned. Otherwise a net::Error value is returned.
int ReadFile(const base::FilePath& path,
int64_t offset,
- net::IOBuffer* buf,
+ scoped_refptr<net::IOBuffer> buf,
int buf_len);
// Writes |buf_len| bytes to the file at |path|, starting from |offset|.
@@ -96,7 +96,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilMemoryDelegate
// net::Error value is returned.
int WriteFile(const base::FilePath& path,
int64_t offset,
- net::IOBuffer* buf,
+ scoped_refptr<net::IOBuffer> buf,
int buf_len);
base::File::Error CreateFileForTesting(const base::FilePath& path,
@@ -126,6 +126,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilMemoryDelegate
const DecomposedPath& dest_dp,
bool move);
+ SEQUENCE_CHECKER(sequence_checker_);
+
// The root of the directory tree.
std::unique_ptr<Entry> root_;
diff --git a/storage/browser/file_system/sandbox_file_stream_writer.cc b/storage/browser/file_system/sandbox_file_stream_writer.cc
index 21495aee42684fcbe7c79fa5d26ad4f2006da875..d344e8ae71e254c2fc758e6a8f6b219d4b145805 100644
--- a/storage/browser/file_system/sandbox_file_stream_writer.cc
+++ b/storage/browser/file_system/sandbox_file_stream_writer.cc
@@ -155,6 +155,7 @@ void SandboxFileStreamWriter::DidCreateSnapshotFile(
file_system_context_->sandbox_delegate()->memory_file_util_delegate();
}
file_writer_ = FileStreamWriter::CreateForMemoryFile(
+ file_system_context_->default_file_task_runner(),
memory_file_util_delegate, platform_path, initial_offset_);
} else {

View File

@@ -0,0 +1,64 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Cooper <alcooper@chromium.org>
Date: Tue, 4 Aug 2020 00:31:54 +0000
Subject: Update FocusChanged notifiers to operate on a copy
These focus changed calls ultimately trigger javascript events. These
events could potentially run code that would modify the list of items
that the FocusChanged notifiers are notifying, and thus invalidate their
in-use iterators.
Fix this by having these methods iterate over a copy instead of the
member list.
(cherry picked from commit d8f526f4e25c24ed29e60b46b3416bfabd5e8f11)
Fixed: 1107815
Change-Id: I03fa08eeadc60736f3a3fae079253dbd3ee26476
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2314158
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Klaus Weidner <klausw@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#791261}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2335893
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1015}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index 225ff4339c3a9b0bd79b3a188e28cf615e6ed97c..19215d532094c340dd146660b062aeb3293b7bc3 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -1335,7 +1335,12 @@ void FocusController::RegisterFocusChangedObserver(
}
void FocusController::NotifyFocusChangedObservers() const {
- for (const auto& it : focus_changed_observers_)
+ // Since this eventually dispatches an event to the page, the page could add
+ // new observer, which would invalidate our iterators; so iterate over a copy
+ // of the observer list.
+ HeapHashSet<WeakMember<FocusChangedObserver>> observers =
+ focus_changed_observers_;
+ for (const auto& it : observers)
it->FocusedFrameChanged();
}
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index a94164360dcfe0e0686e5d48e64ee8a4dc9ef125..8b22f66e7f0c9e905d9a6503abb557287c6e456b 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -682,7 +682,11 @@ XRSystem::XRSystem(LocalFrame& frame, int64_t ukm_source_id)
void XRSystem::FocusedFrameChanged() {
// Tell all sessions that focus changed.
- for (const auto& session : sessions_) {
+ // Since this eventually dispatches an event to the page, the page could
+ // create a new session which would invalidate our iterators; so iterate over
+ // a copy of the session map.
+ HeapHashSet<WeakMember<XRSession>> processing_sessions = sessions_;
+ for (const auto& session : processing_sessions) {
session->OnFocusChanged();
}

View File

@@ -0,0 +1,115 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Rahul Arakeri <arakeri@microsoft.com>
Date: Tue, 8 Sep 2020 20:36:24 +0000
Subject: Fix for UAF when referencing a deleted scrollbar layer.
This CL fixes an issue where autoscroll may be called on a scrollbar
layer that is deleted. When the pointer is pressed down, an autoscroll
task is scheduled to be executed after ~250ms. The task will execute if
the pointer remains held down. In LayerTreeImpl::UnregisterScrollbar,
DidUnregisterScrollbarLayer only gets called after both scrollbars are
removed. So if you go from 2 to 1 scrollbar while an autoscroll task is
queued, the autoscroll task won't get cancelled. At this point, the
ScrollbarController tries to access the deleted scrollbar and that
leads to an access violation. The fix here is to ensure that the call
to DidUnregisterScrollbarLayer happens for each scrollbar.
Bug: 1106612
Change-Id: I4f1d684830012db8fdd73258c9a9e5231807bcb6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2356809
Reviewed-by: Robert Flack <flackr@chromium.org>
Commit-Queue: Rahul Arakeri <arakeri@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#805057}
(cherry picked from commit 52dceba66599f0892fb649717fc462ff016d2fa2)
diff --git a/cc/input/scrollbar_controller.cc b/cc/input/scrollbar_controller.cc
index b4d2ef531e2f5ccee157babb67522c454c720c9e..be80b5e07f5330ac636946f32f6cf713e0bb77a5 100644
--- a/cc/input/scrollbar_controller.cc
+++ b/cc/input/scrollbar_controller.cc
@@ -433,9 +433,12 @@ void ScrollbarController::ResetState() {
captured_scrollbar_metadata_ = base::nullopt;
}
-void ScrollbarController::DidUnregisterScrollbar(ElementId element_id) {
+void ScrollbarController::DidUnregisterScrollbar(
+ ElementId element_id,
+ ScrollbarOrientation orientation) {
if (captured_scrollbar_metadata_.has_value() &&
- captured_scrollbar_metadata_->scroll_element_id == element_id)
+ captured_scrollbar_metadata_->scroll_element_id == element_id &&
+ captured_scrollbar_metadata_->orientation == orientation)
ResetState();
}
@@ -531,6 +534,7 @@ void ScrollbarController::StartAutoScrollAnimation(
// the same time.
DCHECK(!drag_state_.has_value());
DCHECK_NE(velocity, 0);
+ DCHECK(ScrollbarLayer());
// scroll_node is set up while handling GSB. If there's no node to scroll, we
// don't need to create any animation for it.
diff --git a/cc/input/scrollbar_controller.h b/cc/input/scrollbar_controller.h
index ab93bde692dfc48f3d2e2f95ca56c984e8d0eb88..c9faf8ca505776b1b2764225427e28008ae74c8e 100644
--- a/cc/input/scrollbar_controller.h
+++ b/cc/input/scrollbar_controller.h
@@ -135,7 +135,8 @@ class CC_EXPORT ScrollbarController {
const ScrollbarLayerImplBase* scrollbar,
ScrollbarPart pressed_scrollbar_part);
bool ScrollbarScrollIsActive() { return scrollbar_scroll_is_active_; }
- void DidUnregisterScrollbar(ElementId element_id);
+ void DidUnregisterScrollbar(ElementId element_id,
+ ScrollbarOrientation orientation);
ScrollbarLayerImplBase* ScrollbarLayer();
void WillBeginImplFrame();
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 688a0905c997c86b63bcc607033ec82a74741114..46821befb4f1720a93cb9e4b8e9d33bd45a6292a 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -5326,9 +5326,10 @@ void LayerTreeHostImpl::RegisterScrollbarAnimationController(
}
void LayerTreeHostImpl::DidUnregisterScrollbarLayer(
- ElementId scroll_element_id) {
+ ElementId scroll_element_id,
+ ScrollbarOrientation orientation) {
scrollbar_animation_controllers_.erase(scroll_element_id);
- scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id);
+ scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation);
}
ScrollbarAnimationController*
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 2352d8e17c316542f2d65f6e1c6b075db9cf66bd..f2272d0c760f3301b0b64d81d6372758110798e9 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -464,7 +464,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void RegisterScrollbarAnimationController(ElementId scroll_element_id,
float initial_opacity);
- void DidUnregisterScrollbarLayer(ElementId scroll_element_id);
+ void DidUnregisterScrollbarLayer(ElementId scroll_element_id,
+ ScrollbarOrientation orientation);
ScrollbarAnimationController* ScrollbarAnimationControllerForElementId(
ElementId scroll_element_id) const;
void FlashAllScrollbars(bool did_scroll);
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 45f7e5707a0ab5b983294964369d549e2981bf7d..2b6ea7532e954babe7150386209e958546b9bc40 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1933,9 +1933,11 @@ void LayerTreeImpl::UnregisterScrollbar(
if (scrollbar_ids.horizontal == Layer::INVALID_ID &&
scrollbar_ids.vertical == Layer::INVALID_ID) {
element_id_to_scrollbar_layer_ids_.erase(scroll_element_id);
- if (IsActiveTree()) {
- host_impl_->DidUnregisterScrollbarLayer(scroll_element_id);
- }
+ }
+
+ if (IsActiveTree()) {
+ host_impl_->DidUnregisterScrollbarLayer(scroll_element_id,
+ scrollbar_layer->orientation());
}
}

View File

@@ -0,0 +1,78 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Reilly Grant <reillyg@chromium.org>
Date: Wed, 22 Jul 2020 04:00:16 +0000
Subject: usb: Prevent iterator invalidation during Promise resolution
This change swaps sets of ScriptPromiseResolvers into local variables in
a number of places where it was possible for script to execute during
the call to Resolve() or Reject() and modify the set being iterated
over, thus invalidating the iterator.
(cherry picked from commit dbc6c3c3652680e287c60b3c6551622748543439)
Bug: 1106773
Change-Id: Id4eb0cd444a7dbb5de23038ec80f44fee649cfe4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2304538
Auto-Submit: Reilly Grant <reillyg@chromium.org>
Commit-Queue: James Hollyer <jameshollyer@chromium.org>
Reviewed-by: James Hollyer <jameshollyer@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#790217}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2310964
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/branch-heads/4183@{#842}
Cr-Branched-From: 740e9e8a40505392ba5c8e022a8024b3d018ca65-refs/heads/master@{#782793}
diff --git a/third_party/blink/renderer/modules/webusb/usb.cc b/third_party/blink/renderer/modules/webusb/usb.cc
index de66613d20851dee9a96a8fdcfaec30da9f05b53..e3dc44eb208946aac4a2f836365127ba88c08edd 100644
--- a/third_party/blink/renderer/modules/webusb/usb.cc
+++ b/third_party/blink/renderer/modules/webusb/usb.cc
@@ -254,15 +254,22 @@ void USB::OnDeviceRemoved(UsbDeviceInfoPtr device_info) {
void USB::OnServiceConnectionError() {
service_.reset();
client_receiver_.reset();
- for (ScriptPromiseResolver* resolver : get_devices_requests_)
+
+ // Move the set to a local variable to prevent script execution in Resolve()
+ // from invalidating the iterator used by the loop.
+ HeapHashSet<Member<ScriptPromiseResolver>> get_devices_requests;
+ get_devices_requests.swap(get_devices_requests_);
+ for (auto& resolver : get_devices_requests)
resolver->Resolve(HeapVector<Member<USBDevice>>(0));
- get_devices_requests_.clear();
- for (ScriptPromiseResolver* resolver : get_permission_requests_) {
+ // Move the set to a local variable to prevent script execution in Reject()
+ // from invalidating the iterator used by the loop.
+ HeapHashSet<Member<ScriptPromiseResolver>> get_permission_requests;
+ get_permission_requests.swap(get_permission_requests_);
+ for (auto& resolver : get_permission_requests) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotFoundError, kNoDeviceSelected));
}
- get_permission_requests_.clear();
}
void USB::AddedEventListener(const AtomicString& event_type,
diff --git a/third_party/blink/renderer/modules/webusb/usb_device.cc b/third_party/blink/renderer/modules/webusb/usb_device.cc
index 655212ee5dd6c27498b83b8eb5b9e13a14c38a40..17f52e86a134ceb137923b65d1686ce3cbf3bf85 100644
--- a/third_party/blink/renderer/modules/webusb/usb_device.cc
+++ b/third_party/blink/renderer/modules/webusb/usb_device.cc
@@ -1125,11 +1125,15 @@ void USBDevice::AsyncReset(ScriptPromiseResolver* resolver, bool success) {
void USBDevice::OnConnectionError() {
device_.reset();
opened_ = false;
- for (ScriptPromiseResolver* resolver : device_requests_) {
+
+ // Move the set to a local variable to prevent script execution in Reject()
+ // from invalidating the iterator used by the loop.
+ HeapHashSet<Member<ScriptPromiseResolver>> device_requests;
+ device_requests.swap(device_requests_);
+ for (auto& resolver : device_requests) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotFoundError, kDeviceDisconnected));
}
- device_requests_.clear();
}
bool USBDevice::MarkRequestComplete(ScriptPromiseResolver* resolver) {

View File

@@ -0,0 +1,83 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Hiroki Nakagawa <nhiroki@chromium.org>
Date: Fri, 7 Aug 2020 15:27:06 +0000
Subject: Worker: Fix a race condition on task runner handling
WebSharedWorkerImpl accesses WorkerScheduler from the main thread to
take a task runner, and then dispatches a connect event to
SharedWorkerGlobalScope using the task runner.
This causes a race condition if close() is called on the global scope
on the worker thread while the task runner is being taken on the main
thread: close() call disposes of WorkerScheduler, and accessing the
scheduler after that is not allowed. See the issue for details.
To fix this, this CL makes WebSharedWorkerImpl capture the task runner
between starting a worker thread (initializing WorkerScheduler) and
posting a task to evaluate worker scripts that may call close(). This
ensures that WebSharedWorkerImpl accesses WorkerScheduler before the
scheduler is disposed of.
(cherry picked from commit c7bbec3e595c4359e36e5472b7265c4b6d047f2c)
Bug: 1104046
Change-Id: I145cd39f706019c33220fcb01ed81f76963ffff0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2308550
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#790284}
Tbr: bashi@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342337
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1050}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index 7582cdbd3a1ee5e8a58686715020f304319860d8..9942e60d41f4add60bf28388902e08bc1d071dc7 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -145,11 +145,8 @@ void WebSharedWorkerImpl::Connect(MessagePortChannel web_channel) {
DCHECK(IsMainThread());
if (asked_to_terminate_)
return;
- // The HTML spec requires to queue a connect event using the DOM manipulation
- // task source.
- // https://html.spec.whatwg.org/C/#shared-workers-and-the-sharedworker-interface
PostCrossThreadTask(
- *GetWorkerThread()->GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
+ *task_runner_for_connect_event_, FROM_HERE,
CrossThreadBindOnce(&WebSharedWorkerImpl::ConnectTaskOnWorkerThread,
WTF::CrossThreadUnretained(this),
WTF::Passed(std::move(web_channel))));
@@ -269,6 +266,18 @@ void WebSharedWorkerImpl::StartWorkerContext(
GetWorkerThread()->Start(std::move(creation_params), thread_startup_data,
std::move(devtools_params));
+
+ // Capture the task runner for dispatching connect events. This is necessary
+ // for avoiding race condition with WorkerScheduler termination induced by
+ // close() call on SharedWorkerGlobalScope. See https://crbug.com/1104046 for
+ // details.
+ //
+ // The HTML spec requires to queue a connect event using the DOM manipulation
+ // task source.
+ // https://html.spec.whatwg.org/C/#shared-workers-and-the-sharedworker-interface
+ task_runner_for_connect_event_ =
+ GetWorkerThread()->GetTaskRunner(TaskType::kDOMManipulation);
+
switch (script_type) {
case mojom::ScriptType::kClassic:
GetWorkerThread()->FetchAndRunClassicScript(
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index 13006f67bd3e10b8d6c38e504d4bf235aa2f9022..98374fb9031e200a225e6e2982ff2d364fa007f5 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -114,6 +114,8 @@ class CORE_EXPORT WebSharedWorkerImpl final : public WebSharedWorker {
// |client_| owns |this|.
WebSharedWorkerClient* client_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_connect_event_;
+
bool asked_to_terminate_ = false;
base::WeakPtrFactory<WebSharedWorkerImpl> weak_ptr_factory_{this};

View File

@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andrey Kosyakov <caseq@chromium.org>
Date: Fri, 28 Aug 2020 18:55:17 +0000
Subject: Delegate TargetHandler::Session permission checks to the root client
Bug: 1114636
Change-Id: Iba3865206d7e80b363ec69180ac05e20b56aade2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380855
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Reviewed-by: Devlin <rdevlin.cronin@chromium.org>
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#802736}
(cherry picked from commit 814a27f8522b6ccddcce1a8f6a3b8fb37128ecf9)
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index 4614ecae2110e5577539cffba02fcab153af301f..bcd054f2dc388908cf92bae3ff0254422960dc12 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -436,7 +436,7 @@ class TargetHandler::Session : public DevToolsAgentHostClient {
// was introduced. Try a DCHECK instead and possibly remove the check.
if (!handler_->root_session_->HasChildSession(id_))
return;
- handler_->root_session_->GetClient()->DispatchProtocolMessage(
+ GetRootClient()->DispatchProtocolMessage(
handler_->root_session_->GetAgentHost(), message);
return;
}
@@ -454,6 +454,26 @@ class TargetHandler::Session : public DevToolsAgentHostClient {
Detach(true);
}
+ bool MayAttachToURL(const GURL& url, bool is_webui) override {
+ return GetRootClient()->MayAttachToURL(url, is_webui);
+ }
+
+ bool MayAttachToBrowser() override {
+ return GetRootClient()->MayAttachToBrowser();
+ }
+
+ bool MayReadLocalFiles() override {
+ return GetRootClient()->MayReadLocalFiles();
+ }
+
+ bool MayWriteLocalFiles() override {
+ return GetRootClient()->MayWriteLocalFiles();
+ }
+
+ content::DevToolsAgentHostClient* GetRootClient() {
+ return handler_->root_session_->GetClient();
+ }
+
TargetHandler* handler_;
scoped_refptr<DevToolsAgentHost> agent_host_;
std::string id_;

View File

@@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andres Calderon Jaramillo <andrescj@chromium.org>
Date: Mon, 20 Jul 2020 19:49:42 +0000
Subject: blink: Disable WebP YUV image decoding by default.
JPEG YUV image decoding is already disabled by default.
Bug: 1102054,1091347
Test: WebP images in bug 1091347 don't have artifacts on Linux
Change-Id: Icf7f9ef46da97ba80d4e3b4aac4196f789bdf6d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283409
Reviewed-by: Andres Calderon Jaramillo <andrescj@chromium.org>
Commit-Queue: Andres Calderon Jaramillo <andrescj@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#901}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index ad1c48f2d727f5fee33b2f5bd9d39682da2ba085..5f87b90e6cf3a3e3cb5f68e13fcbed06d039b18f 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -257,7 +257,7 @@ const base::Feature kDecodeJpeg420ImagesToYUV{
// Decodes lossy WebP images to YUV instead of RGBX and stores in this format
// in the image decode cache. See crbug.com/900264 for details on the feature.
const base::Feature kDecodeLossyWebPImagesToYUV{
- "DecodeLossyWebPImagesToYUV", base::FEATURE_ENABLED_BY_DEFAULT};
+ "DecodeLossyWebPImagesToYUV", base::FEATURE_DISABLED_BY_DEFAULT};
// Enables cache-aware WebFonts loading. See https://crbug.com/570205.
// The feature is disabled on Android for WebView API issue discussed at

View File

@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Guido Urdaneta <guidou@chromium.org>
Date: Tue, 4 Aug 2020 21:25:10 +0000
Subject: Use copy of source map in
MediaElementElementListener::UpdateSources()
Prior to this CL, this function iterated over a source map that could
be modified by a re-entrant call triggered by JS code.
(cherry picked from commit 292ac9aa5ba263f63f761e03b8214cae21e667c9)
Bug: 1105426
Change-Id: I47e49e4132cba98e12ee7c195720ac9ecc1f485b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2312703
Reviewed-by: Marina Ciocea <marinaciocea@chromium.org>
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#790894}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332823
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1026}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
index 03ba68236c511ba2d2767af3c796b37a90dce476..80f36cb236adee50aefc505ff64f092cbc4e82b9 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
@@ -242,9 +242,14 @@ void MediaElementEventListener::UpdateSources(ExecutionContext* context) {
for (auto track : media_stream_->getTracks())
sources_.insert(track->Component()->Source());
+ // Handling of the ended event in JS triggered by DidStopMediaStreamSource()
+ // may cause a reentrant call to this function, which can modify |sources_|.
+ // Iterate over a copy of |sources_| to avoid invalidation of the iterator
+ // when a reentrant call occurs.
+ auto sources_copy = sources_;
if (!media_element_->currentSrc().IsEmpty() &&
!media_element_->IsMediaDataCorsSameOrigin()) {
- for (auto source : sources_)
+ for (auto source : sources_copy)
DidStopMediaStreamSource(source.Get());
}
}

View File

@@ -0,0 +1,160 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Yutaka Hirano <yhirano@chromium.org>
Date: Fri, 7 Aug 2020 09:06:23 +0000
Subject: Fix UAF in ScriptPromiseProperty caused by reentrant code
v8::Promise::Resolve can run user code synchronously, which caused a UAF
in ScriptPromiseProperty. Fix it.
Bug: 1108518
Change-Id: Ia9baec6eef0887323cd88ceb1d3fa0c14fdb77ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2325499
Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#792661}
(cherry picked from commit 6d18e924b9c426905434cc280d7b602b3a3379ed)
TBR=yhirano@chromium.org
Change-Id: I3b7bfd5e8d932fb59c292159a4526cf70b44c58b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342489
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1049}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_property.h b/third_party/blink/renderer/bindings/core/v8/script_promise_property.h
index 7c83ee35246486cf2a43227bf180904b2d9eb5f3..49ec9edc0d897e6f6e842a425cc2b0a4272702e2 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_property.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_property.h
@@ -102,10 +102,11 @@ class ScriptPromiseProperty final
}
state_ = kResolved;
resolved_ = value;
- for (const Member<ScriptPromiseResolver>& resolver : resolvers_) {
+ HeapVector<Member<ScriptPromiseResolver>> resolvers;
+ resolvers.swap(resolvers_);
+ for (const Member<ScriptPromiseResolver>& resolver : resolvers) {
resolver->Resolve(resolved_);
}
- resolvers_.clear();
}
void ResolveWithUndefined() {
@@ -116,10 +117,11 @@ class ScriptPromiseProperty final
}
state_ = kResolved;
resolved_with_undefined_ = true;
- for (const Member<ScriptPromiseResolver>& resolver : resolvers_) {
+ HeapVector<Member<ScriptPromiseResolver>> resolvers;
+ resolvers.swap(resolvers_);
+ for (const Member<ScriptPromiseResolver>& resolver : resolvers) {
resolver->Resolve();
}
- resolvers_.clear();
}
template <typename PassRejectedType>
@@ -131,10 +133,11 @@ class ScriptPromiseProperty final
}
state_ = kRejected;
rejected_ = value;
- for (const Member<ScriptPromiseResolver>& resolver : resolvers_) {
+ HeapVector<Member<ScriptPromiseResolver>> resolvers;
+ resolvers.swap(resolvers_);
+ for (const Member<ScriptPromiseResolver>& resolver : resolvers) {
resolver->Reject(rejected_);
}
- resolvers_.clear();
}
// Resets this property by unregistering the Promise property from the
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
index c2dd607686e4ef76885036b4a678103c3869241e..95b6b1b83638b30918032fc4f76fec56a781c492 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
@@ -96,6 +96,35 @@ class GarbageCollectedHolder final : public GarbageCollectedScriptWrappable {
Member<Property> property_;
};
+class ScriptPromisePropertyResetter : public ScriptFunction {
+ public:
+ using Property =
+ ScriptPromiseProperty<Member<GarbageCollectedScriptWrappable>,
+ Member<GarbageCollectedScriptWrappable>>;
+ static v8::Local<v8::Function> CreateFunction(ScriptState* script_state,
+ Property* property) {
+ auto* self = MakeGarbageCollected<ScriptPromisePropertyResetter>(
+ script_state, property);
+ return self->BindToV8Function();
+ }
+
+ ScriptPromisePropertyResetter(ScriptState* script_state, Property* property)
+ : ScriptFunction(script_state), property_(property) {}
+
+ void Trace(Visitor* visitor) override {
+ visitor->Trace(property_);
+ ScriptFunction::Trace(visitor);
+ }
+
+ private:
+ ScriptValue Call(ScriptValue arg) override {
+ property_->Reset();
+ return ScriptValue();
+ }
+
+ const Member<Property> property_;
+};
+
class ScriptPromisePropertyTestBase {
public:
ScriptPromisePropertyTestBase()
@@ -520,6 +549,48 @@ TEST_F(ScriptPromisePropertyGarbageCollectedTest, MarkAsHandled) {
}
}
+TEST_F(ScriptPromisePropertyGarbageCollectedTest, SyncResolve) {
+ // Call getters to create resolvers in the property.
+ GetProperty()->Promise(DOMWrapperWorld::MainWorld());
+ GetProperty()->Promise(OtherWorld());
+
+ auto* resolution =
+ MakeGarbageCollected<GarbageCollectedScriptWrappable>("hi");
+ v8::HandleScope handle_scope(GetIsolate());
+ v8::Local<v8::Object> main_v8_resolution;
+ v8::Local<v8::Object> other_v8_resolution;
+ {
+ ScriptState::Scope scope(MainScriptState());
+ main_v8_resolution = ToV8(resolution, MainScriptState()).As<v8::Object>();
+ v8::PropertyDescriptor descriptor(
+ ScriptPromisePropertyResetter::CreateFunction(MainScriptState(),
+ GetProperty()),
+ v8::Undefined(GetIsolate()));
+ ASSERT_EQ(
+ v8::Just(true),
+ main_v8_resolution->DefineProperty(
+ MainScriptState()->GetContext(),
+ v8::String::NewFromUtf8Literal(GetIsolate(), "then"), descriptor));
+ }
+ {
+ ScriptState::Scope scope(OtherScriptState());
+ other_v8_resolution = ToV8(resolution, OtherScriptState()).As<v8::Object>();
+ v8::PropertyDescriptor descriptor(
+ ScriptPromisePropertyResetter::CreateFunction(OtherScriptState(),
+ GetProperty()),
+ v8::Undefined(GetIsolate()));
+ ASSERT_EQ(
+ v8::Just(true),
+ other_v8_resolution->DefineProperty(
+ OtherScriptState()->GetContext(),
+ v8::String::NewFromUtf8Literal(GetIsolate(), "then"), descriptor));
+ }
+
+ // This shouldn't crash.
+ GetProperty()->Resolve(resolution);
+ EXPECT_EQ(GetProperty()->GetState(), Property::State::kPending);
+}
+
TEST_F(ScriptPromisePropertyNonScriptWrappableResolutionTargetTest,
ResolveWithUndefined) {
Test(ToV8UndefinedGenerator(), "undefined", __FILE__, __LINE__);

View File

@@ -0,0 +1,112 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Hongchan Choi <hongchan@chromium.org>
Date: Tue, 4 Aug 2020 16:13:40 +0000
Subject: Use SupportWeakPtr in OfflineAudioDestinationHandler
OfflineAudioDestinationHandler's render thread notifies the
main thread when the rendering state changes. In this process,
the associated audio context can be deleted when a posted task
is performed sometime later in the task runner's queue.
By using WeakPtr, the task runner will not perform a scheduled task
in the queue when the target object is no longer valid.
(cherry picked from commit 4f309b864587890acaefa9da5d580abb21ff9ca0)
Bug: 1095584
Test: Locally confirmed that the repro case does not crash after 30 min.
Change-Id: Ic1814b97f8d9a8d1027ef04f475112874cfa8137
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2285473
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Raymond Toy <rtoy@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#786381}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2335487
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1019}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
index ec610b99c6758bc66cda07bd344f29825c8376a3..2b0d87395cef31793273ef69f61eb540be897fcf 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
@@ -51,17 +51,14 @@ OfflineAudioDestinationHandler::OfflineAudioDestinationHandler(
frames_to_process_(frames_to_process),
is_rendering_started_(false),
number_of_channels_(number_of_channels),
- sample_rate_(sample_rate) {
- channel_count_ = number_of_channels;
+ sample_rate_(sample_rate),
+ main_thread_task_runner_(Context()->GetExecutionContext()->GetTaskRunner(
+ TaskType::kInternalMedia)) {
+ DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
+ channel_count_ = number_of_channels;
SetInternalChannelCountMode(kExplicit);
SetInternalChannelInterpretation(AudioBus::kSpeakers);
-
- if (Context()->GetExecutionContext()) {
- main_thread_task_runner_ = Context()->GetExecutionContext()->GetTaskRunner(
- TaskType::kMiscPlatformAPI);
- DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
- }
}
scoped_refptr<OfflineAudioDestinationHandler>
@@ -218,7 +215,7 @@ void OfflineAudioDestinationHandler::SuspendOfflineRendering() {
PostCrossThreadTask(
*main_thread_task_runner_, FROM_HERE,
CrossThreadBindOnce(&OfflineAudioDestinationHandler::NotifySuspend,
- WrapRefCounted(this),
+ GetWeakPtr(),
Context()->CurrentSampleFrame()));
}
@@ -229,7 +226,7 @@ void OfflineAudioDestinationHandler::FinishOfflineRendering() {
PostCrossThreadTask(
*main_thread_task_runner_, FROM_HERE,
CrossThreadBindOnce(&OfflineAudioDestinationHandler::NotifyComplete,
- WrapRefCounted(this)));
+ GetWeakPtr()));
}
void OfflineAudioDestinationHandler::NotifySuspend(size_t frame) {
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
index e4d2d8bfcd5d90d233ca8c761f07e8f518f83018..07d80e4cff535ee7230ea14d96c292bacaa7c19e 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
@@ -28,6 +28,7 @@
#include <memory>
#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
#include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
#include "third_party/blink/renderer/modules/webaudio/audio_destination_node.h"
#include "third_party/blink/renderer/modules/webaudio/offline_audio_context.h"
@@ -124,6 +125,17 @@ class OfflineAudioDestinationHandler final : public AudioDestinationHandler {
// from AudioWorkletThread will be used until the rendering is finished.
void PrepareTaskRunnerForRendering();
+ // For cross-thread posting, this object uses two different targets.
+ // 1. rendering thread -> main thread: WeakPtr
+ // When the main thread starts deleting this object, a task posted with
+ // a WeakPtr from the rendering thread will be cancelled.
+ // 2. main thread -> rendering thread: scoped_refptr
+ // |render_thread_| is owned by this object, so it is safe to target with
+ // WrapRefCounted() instead of GetWeakPtr().
+ base::WeakPtr<OfflineAudioDestinationHandler> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
// This AudioHandler renders into this SharedAudioBuffer.
std::unique_ptr<SharedAudioBuffer> shared_render_target_;
// Temporary AudioBus for each render quantum.
@@ -148,6 +160,8 @@ class OfflineAudioDestinationHandler final : public AudioDestinationHandler {
scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+ base::WeakPtrFactory<OfflineAudioDestinationHandler> weak_factory_{this};
};
class OfflineAudioDestinationNode final : public AudioDestinationNode {

View File

@@ -0,0 +1,284 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Tue, 28 Jul 2020 22:47:48 +0000
Subject: M85: Correctly retrieve the plugin when printing.
The logic in PrintRenderFrameHelper to retrieve a plugin is out of sync
with the logic in WebLocalFrameImpl::PrintBegin(). If
PrintRenderFrameHelper thinks it is printing a webpage, while
WebLocalFrameImpl thinks it is printing a plugin, bad things happen.
Fix this by adding WebLocalFrame::GetPluginToPrint(), to expose the
plugin finding logic in WebLocalFrameImpl. With GetPluginToPrint()
available, PrintRenderFrameHelper can delete its own GetPlugin() helper,
and switch the GetPlugin() callers to use GetPluginToPrint() instead.
Once synchronized, some use cases for printing Flash now work correctly.
(cherry picked from commit f8d7d428b1549ff1f87e3d34c5ca0b53d6ce4e84)
Tbr: japhet@chromium.org
Bug: 1098860
Change-Id: I9500db9ed2d6da0f87dad84c197f738d3a1e3c84
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2317168
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#791564}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2324664
Reviewed-by: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/branch-heads/4183@{#1009}
Cr-Branched-From: 740e9e8a40505392ba5c8e022a8024b3d018ca65-refs/heads/master@{#782793}
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 8456ec0c453345e5cab96728e501d341053d8cf2..9dd56e9df0d91ccd1d6789e3b3f261a533d373e8 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -61,8 +61,6 @@
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_navigation_control.h"
#include "third_party/blink/public/web/web_plugin.h"
-#include "third_party/blink/public/web/web_plugin_container.h"
-#include "third_party/blink/public/web/web_plugin_document.h"
#include "third_party/blink/public/web/web_print_params.h"
#include "third_party/blink/public/web/web_print_preset_options.h"
#include "third_party/blink/public/web/web_script_source.h"
@@ -343,28 +341,14 @@ void ComputeWebKitPrintParamsInDesiredDpi(
webkit_print_params->pages_per_sheet = print_params.pages_per_sheet;
}
-blink::WebPlugin* GetPlugin(const blink::WebLocalFrame* frame) {
- return frame->GetDocument().IsPluginDocument()
- ? frame->GetDocument().To<blink::WebPluginDocument>().Plugin()
- : nullptr;
-}
-
-bool IsPrintingNodeOrPdfFrame(const blink::WebLocalFrame* frame,
+bool IsPrintingNodeOrPdfFrame(blink::WebLocalFrame* frame,
const blink::WebNode& node) {
- if (!node.IsNull())
- return true;
- blink::WebPlugin* plugin = GetPlugin(frame);
+ blink::WebPlugin* plugin = frame->GetPluginToPrint(node);
return plugin && plugin->SupportsPaginatedPrint();
}
bool IsPrintingPdf(blink::WebLocalFrame* frame, const blink::WebNode& node) {
- blink::WebPlugin* plugin;
- if (node.IsNull()) {
- plugin = GetPlugin(frame);
- } else {
- blink::WebPluginContainer* plugin_container = node.PluginContainer();
- plugin = plugin_container ? plugin_container->Plugin() : nullptr;
- }
+ blink::WebPlugin* plugin = frame->GetPluginToPrint(node);
return plugin && plugin->IsPdfPlugin();
}
@@ -2337,7 +2321,7 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
// 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
// document has been loaded.
is_scripted_preview_delayed_ = true;
- if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
+ if (is_loading_ && print_preview_context_.IsPlugin()) {
// Wait for DidStopLoading. Plugins may not know the correct
// |is_modifiable| value until they are fully loaded, which occurs when
// DidStopLoading() is called. Defer showing the preview until then.
@@ -2364,7 +2348,7 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
// Wait for DidStopLoading. Continuing with this function while
// |is_loading_| is true will cause print preview to hang when try to
// print a PDF document.
- if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
+ if (is_loading_ && print_preview_context_.IsPlugin()) {
on_stop_loading_closure_ =
base::BindOnce(&PrintRenderFrameHelper::RequestPrintPreview,
weak_ptr_factory_.GetWeakPtr(), type);
@@ -2375,12 +2359,12 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
}
case PRINT_PREVIEW_USER_INITIATED_SELECTION: {
DCHECK(has_selection);
- DCHECK(!GetPlugin(print_preview_context_.source_frame()));
+ DCHECK(!print_preview_context_.IsPlugin());
params.selection_only = has_selection;
break;
}
case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
- if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
+ if (is_loading_ && print_preview_context_.IsPlugin()) {
on_stop_loading_closure_ =
base::BindOnce(&PrintRenderFrameHelper::RequestPrintPreview,
weak_ptr_factory_.GetWeakPtr(), type);
@@ -2465,8 +2449,7 @@ void PrintRenderFrameHelper::PrintPreviewContext::InitWithFrame(
state_ = INITIALIZED;
source_frame_.Reset(web_frame);
source_node_.Reset();
- CalculateIsModifiable();
- CalculateIsPdf();
+ CalculatePluginAttributes();
}
void PrintRenderFrameHelper::PrintPreviewContext::InitWithNode(
@@ -2477,8 +2460,7 @@ void PrintRenderFrameHelper::PrintPreviewContext::InitWithNode(
state_ = INITIALIZED;
source_frame_.Reset(web_node.GetDocument().GetFrame());
source_node_ = web_node;
- CalculateIsModifiable();
- CalculateIsPdf();
+ CalculatePluginAttributes();
}
void PrintRenderFrameHelper::PrintPreviewContext::OnPrintPreview() {
@@ -2621,6 +2603,11 @@ bool PrintRenderFrameHelper::PrintPreviewContext::IsForArc() const {
return is_for_arc_;
}
+bool PrintRenderFrameHelper::PrintPreviewContext::IsPlugin() const {
+ DCHECK(state_ != UNINITIALIZED);
+ return is_plugin_;
+}
+
bool PrintRenderFrameHelper::PrintPreviewContext::IsModifiable() const {
DCHECK(state_ != UNINITIALIZED);
return is_modifiable_;
@@ -2713,11 +2700,9 @@ void PrintRenderFrameHelper::PrintPreviewContext::ClearContext() {
error_ = PREVIEW_ERROR_NONE;
}
-void PrintRenderFrameHelper::PrintPreviewContext::CalculateIsModifiable() {
+void PrintRenderFrameHelper::PrintPreviewContext::CalculatePluginAttributes() {
+ is_plugin_ = !!source_frame()->GetPluginToPrint(source_node_);
is_modifiable_ = !IsPrintingNodeOrPdfFrame(source_frame(), source_node_);
-}
-
-void PrintRenderFrameHelper::PrintPreviewContext::CalculateIsPdf() {
is_pdf_ = IsPrintingPdf(source_frame(), source_node_);
}
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index 81df566a5908f1b35b9da5c2aad35ea7dec1dc3b..30cb90cab8a971b1b4eb11bb89e05b09853b7721 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -503,6 +503,7 @@ class PrintRenderFrameHelper
int GetNextPageNumber();
bool IsRendering() const;
bool IsForArc() const;
+ bool IsPlugin() const;
bool IsModifiable() const;
bool IsPdf() const;
bool HasSelection();
@@ -543,9 +544,7 @@ class PrintRenderFrameHelper
// Reset some of the internal rendering context.
void ClearContext();
- void CalculateIsModifiable();
-
- void CalculateIsPdf();
+ void CalculatePluginAttributes();
// Specifies what to render for print preview.
FrameReference source_frame_;
@@ -565,6 +564,9 @@ class PrintRenderFrameHelper
// List of page indices that need to be rendered.
std::vector<int> pages_to_render_;
+ // True, if the document source is a plugin.
+ bool is_plugin_ = false;
+
// True, if the document source is modifiable. e.g. HTML and not PDF.
bool is_modifiable_ = true;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index c5d9cd52989c0d776ac12a5c0b88782d2c40b213..ecb810fecebd4ecb8c93630490cfb8ad5fe856c6 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -59,6 +59,7 @@ class WebLocalFrameClient;
class WebFrameWidget;
class WebInputMethodController;
class WebPerformance;
+class WebPlugin;
class WebRange;
class WebSecurityOrigin;
class WebScriptExecutionCallback;
@@ -655,13 +656,16 @@ class WebLocalFrame : public WebFrame {
// This function should be called before pairs of PrintBegin() and PrintEnd().
virtual void DispatchBeforePrintEvent() = 0;
+ // Get the plugin to print, if any. The |constrain_to_node| parameter is the
+ // same as the one for PrintBegin() below.
+ virtual WebPlugin* GetPluginToPrint(const WebNode& constrain_to_node) = 0;
+
// Reformats the WebFrame for printing. WebPrintParams specifies the printable
// content size, paper size, printable area size, printer DPI and print
- // scaling option. If constrainToNode node is specified, then only the given
+ // scaling option. If |constrain_to_node| is specified, then only the given
// node is printed (for now only plugins are supported), instead of the entire
// frame.
- // Returns the number of pages that can be printed at the given
- // page size.
+ // Returns the number of pages that can be printed at the given page size.
virtual int PrintBegin(const WebPrintParams&,
const WebNode& constrain_to_node = WebNode()) = 0;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 9cb4f3d02fccd6f1098c9b8080f1057642aead5f..52e57b11169ec19c2f890545560a5784af071029 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1537,19 +1537,29 @@ void WebLocalFrameImpl::DispatchPrintEventRecursively(
}
}
-int WebLocalFrameImpl::PrintBegin(const WebPrintParams& print_params,
- const WebNode& constrain_to_node) {
- WebPluginContainerImpl* plugin_container = nullptr;
+WebPluginContainerImpl* WebLocalFrameImpl::GetPluginToPrintHelper(
+ const WebNode& constrain_to_node) {
if (constrain_to_node.IsNull()) {
// If this is a plugin document, check if the plugin supports its own
// printing. If it does, we will delegate all printing to that.
- plugin_container = GetFrame()->GetWebPluginContainer();
- } else {
- // We only support printing plugin nodes for now.
- plugin_container =
- To<WebPluginContainerImpl>(constrain_to_node.PluginContainer());
+ return GetFrame()->GetWebPluginContainer();
}
+ // We only support printing plugin nodes for now.
+ return To<WebPluginContainerImpl>(constrain_to_node.PluginContainer());
+}
+
+WebPlugin* WebLocalFrameImpl::GetPluginToPrint(
+ const WebNode& constrain_to_node) {
+ WebPluginContainerImpl* plugin_container =
+ GetPluginToPrintHelper(constrain_to_node);
+ return plugin_container ? plugin_container->Plugin() : nullptr;
+}
+
+int WebLocalFrameImpl::PrintBegin(const WebPrintParams& print_params,
+ const WebNode& constrain_to_node) {
+ WebPluginContainerImpl* plugin_container =
+ GetPluginToPrintHelper(constrain_to_node);
if (plugin_container && plugin_container->SupportsPaginatedPrint()) {
print_context_ = MakeGarbageCollected<ChromePluginPrintContext>(
GetFrame(), plugin_container, print_params);
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index d7582f9c2f733d4beeb294df34dcbf09ce3970ce..f93ce60b7dd67c49feadc7679f2dc40dab0fb47e 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -279,6 +279,7 @@ class CORE_EXPORT WebLocalFrameImpl final
bool HasVisibleContent() const override;
WebRect VisibleContentRect() const override;
void DispatchBeforePrintEvent() override;
+ WebPlugin* GetPluginToPrint(const WebNode& constrain_to_node) override;
int PrintBegin(const WebPrintParams&,
const WebNode& constrain_to_node) override;
float GetPrintPageShrink(int page) override;
@@ -470,6 +471,9 @@ class CORE_EXPORT WebLocalFrameImpl final
// A helper for DispatchBeforePrintEvent() and DispatchAfterPrintEvent().
void DispatchPrintEventRecursively(const AtomicString& event_type);
+ WebPluginContainerImpl* GetPluginToPrintHelper(
+ const WebNode& constrain_to_node);
+
Node* ContextMenuNodeInner() const;
WebLocalFrameClient* client_;

View File

@@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Adrienne Walker <enne@chromium.org>
Date: Tue, 4 Aug 2020 20:10:23 +0000
Subject: indexeddb: fix crash in WebIDBGetDBNamesCallbacksImpl
Resolve() can end up freeing WebIDBGetDBNamesCallbacksImpl by throwing a
mojo error that deletes the self-owned associated receiver that owns it.
So, don't call any other functions after it.
As the promise resolver can only resolve/reject once, it is safe to
not clear it.
(cherry picked from commit da90fc39f5ca0f8dc1c665fbabad8ec229826f89)
Bug: 1106682
Change-Id: Iea943f3c5c1e57adb6ad399baff49522f54d264b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2311620
Commit-Queue: Daniel Murphy <dmurph@chromium.org>
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
Auto-Submit: enne <enne@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#790857}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2337096
Reviewed-by: enne <enne@chromium.org>
Commit-Queue: enne <enne@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1023}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index 46c5b3432931f6caef98f05dbb6f3543b2711874..af1b4c4234c62671ed56b52d7bd13c3557d397e9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -104,7 +104,6 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"The databases() promise was rejected."));
- promise_resolver_.Clear();
}
void SuccessNamesAndVersionsList(
@@ -128,7 +127,7 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
ExecutionContext::From(promise_resolver_->GetScriptState()),
&async_task_id_, "success");
promise_resolver_->Resolve(name_and_version_list);
- promise_resolver_.Clear();
+ // Note: Resolve may cause |this| to be deleted.
}
void SuccessStringList(const Vector<String>&) override { NOTREACHED(); }

View File

@@ -0,0 +1,62 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Adrienne Walker <enne@chromium.org>
Date: Wed, 5 Aug 2020 00:44:52 +0000
Subject: indexeddb: reset async tasks in ~WebIDBGetDBNamesCallbacksImpl
Since sometimes the WebIDBGetDBNamesCallbacksImpl can be destroyed when
the promise is resolved, make sure that no code that could reference it
is still around. Store the async task as an optional member so it can
be cleared during the destructor.
Followup to:
https://chromium-review.googlesource.com/c/chromium/src/+/2311620
(cherry picked from commit 4422ec665ddca3ac05ad90bac5d5ebee7cfc5536)
Bug: 1106682,1109467
Change-Id: Id6a0ff0a3703fab94e9684e41f16d5a1bac20468
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2321332
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
Commit-Queue: enne <enne@chromium.org>
Auto-Submit: enne <enne@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#792121}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2337110
Reviewed-by: enne <enne@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#1029}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index af1b4c4234c62671ed56b52d7bd13c3557d397e9..d0ea6c191643ad51ab9e1d97e7a81ca9a074092e 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -110,6 +110,7 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) override {
if (!promise_resolver_)
return;
+ DCHECK(!async_task_.has_value());
HeapVector<Member<IDBDatabaseInfo>> name_and_version_list;
name_and_version_list.ReserveInitialCapacity(name_and_version_list.size());
@@ -123,11 +124,12 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
name_and_version_list.push_back(idb_info);
}
- probe::AsyncTask async_task(
+ async_task_.emplace(
ExecutionContext::From(promise_resolver_->GetScriptState()),
&async_task_id_, "success");
promise_resolver_->Resolve(name_and_version_list);
- // Note: Resolve may cause |this| to be deleted.
+ // Note: Resolve may cause |this| to be deleted. async_task_ will be
+ // completed in the destructor.
}
void SuccessStringList(const Vector<String>&) override { NOTREACHED(); }
@@ -189,6 +191,7 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
private:
probe::AsyncTaskId async_task_id_;
+ base::Optional<probe::AsyncTask> async_task_;
Persistent<ScriptPromiseResolver> promise_resolver_;
};

View File

@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taylor Brandstetter <deadbeef@chromium.org>
Date: Wed, 12 Aug 2020 05:24:50 +0000
Subject: Reconnect P2P socket dispatcher if network service dies.
Bug: chromium:1113227
Change-Id: Ifc8e856fde4cf4eee25149f0a1e86a3cad71ea83
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2344747
Commit-Queue: Taylor <deadbeef@chromium.org>
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797125}
diff --git a/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc b/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
index 6c3adda46f77bc15d7d1998871b0daeb34780a77..29884a255f24556f4dc8b41b22304938a4f0d775 100644
--- a/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
+++ b/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
@@ -110,6 +110,18 @@ void P2PSocketDispatcher::RequestNetworkEventsIfNecessary() {
void P2PSocketDispatcher::OnConnectionError() {
base::AutoLock lock(p2p_socket_manager_lock_);
p2p_socket_manager_.reset();
+ // Attempt to reconnect in case the network service crashed in his being
+ // restarted.
+ PostCrossThreadTask(
+ *main_task_runner_.get(), FROM_HERE,
+ CrossThreadBindOnce(&P2PSocketDispatcher::ReconnectP2PSocketManager,
+ scoped_refptr<P2PSocketDispatcher>(this)));
+}
+
+void P2PSocketDispatcher::ReconnectP2PSocketManager() {
+ network_notification_client_receiver_.reset();
+ GetP2PSocketManager()->StartNetworkNotifications(
+ network_notification_client_receiver_.BindNewPipeAndPassRemote());
}
} // namespace blink
diff --git a/third_party/blink/renderer/platform/p2p/socket_dispatcher.h b/third_party/blink/renderer/platform/p2p/socket_dispatcher.h
index de9b6690e0b27d3c4a263e8f908c0a95a675a089..c250d2af99d5291f34a7e3bfdb63fcbb70a5bd73 100644
--- a/third_party/blink/renderer/platform/p2p/socket_dispatcher.h
+++ b/third_party/blink/renderer/platform/p2p/socket_dispatcher.h
@@ -83,6 +83,7 @@ class PLATFORM_EXPORT P2PSocketDispatcher
void RequestNetworkEventsIfNecessary();
void OnConnectionError();
+ void ReconnectP2PSocketManager();
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;

View File

@@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mason Freed <masonfreed@chromium.org>
Date: Mon, 20 Jul 2020 19:55:49 +0000
Subject: Reland "Fix UAF in SelectType"
This is a reland of 72158deaf3751325f1983c87829f65441ee32de3
The only change made here is to add the new test to LeakExpectations,
pointing to crbug.com/1103082. Local testing shows that this leak
is triggered by the new test, both before and after this patch. And
since the patch fixes a UAF security bug, I'd like to land it with the
test, and then work on the leak.
Fixed: 1102408
Bug: 1103082
TBR=masonfreed@chromium.org
Original change's description:
> Fix UAF in SelectType
>
> This fixes the UAF detected by ClusterFuzz in [1], caused by [2].
> The test case added here is a minimized version of the clusterfuzz
> case, and I verified that it crashes (ASAN UAF) before this patch
> and no longer crashes after.
>
> [1] https://clusterfuzz.com/testcase-detail/6224868955193344
> [2] https://chromium-review.googlesource.com/c/chromium/src/+/1912682
>
> Fixed: 1102408
> Change-Id: Ieb6a9582ff5b9676596048920bbcff881fdc2eb2
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283901
> Commit-Queue: Mason Freed <masonfreed@chromium.org>
> Auto-Submit: Mason Freed <masonfreed@chromium.org>
> Reviewed-by: Kent Tamura <tkent@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#785970}
(cherry picked from commit e1c45006a8e5a97778eeed0010a7f57d86e70ca4)
Change-Id: I471cb4abc98a7627803de4e434e0453cb729c15f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2288372
Auto-Submit: Mason Freed <masonfreed@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Commit-Queue: Mason Freed <masonfreed@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#786562}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2307621
Reviewed-by: Mason Freed <masonfreed@chromium.org>
Cr-Commit-Position: refs/branch-heads/4147@{#902}
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index 345cbeb26823c5f111f2bc2b98fc15f99efe3314..b5a7e169964b40a51b073ae13b2922019bee0927 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -709,7 +709,7 @@ bool ListBoxSelectType::DefaultEventHandler(const Event& event) {
if (Page* page = select_->GetDocument().GetPage()) {
page->GetAutoscrollController().StartAutoscrollForSelection(
- layout_object);
+ select_->GetLayoutObject());
}
}
// Mousedown didn't happen in this element.
diff --git a/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash-expected.txt b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash-expected.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cbe81f73ef4a3684565b70d51fe460b22702964f
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash-expected.txt
@@ -0,0 +1 @@
+PASS - this test passes if it does not crash (ASAN)
diff --git a/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash.html b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash.html
new file mode 100644
index 0000000000000000000000000000000000000000..8e9361dd7cd1f954a2c482aab7c182789ddc0701
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash.html
@@ -0,0 +1,17 @@
+<style>
+.c:hover { display: block; }
+</style>
+
+<select id=target autofocus=autofocus size=2 class=c></select>
+
+<script>
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+window.onload = function() {
+ eventSender.beginDragWithFiles( ["resources/file-for-drag-to-navigate.html"]);
+ eventSender.mouseMoveTo(target.offsetLeft + 5, target.offsetTop + 5);
+};
+</script>
+
+<p>PASS - this test passes if it does not crash (ASAN)</p>

View File

@@ -0,0 +1,92 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Thu, 17 Oct 2019 18:00:32 -0700
Subject: feat: add hook to notify script ready from WorkerScriptController
In Off-the-main-thread fetch, the WorkerGlobalScope will be in a half
initialized state until the script is finished downloading.
Doc: https://docs.google.com/document/d/1JCv8TD2nPLNC2iRCp_D1OM4I3uTS0HoEobuTymaMqgw/edit
During this stage if the global object is transformed for ex: copying properties
in DidInitializeWorkerContextOnWorkerThread hook then an access to property like
location will result in a crash WorkerGlobalScope::Url() because the script has
not been set with response URL yet.
This issue cannot happen in chromium with existing usage, but can surface when an
embedder tries to integrate Node.js in the worker. Hence, this new hook is proposed
that clearly establishes the worker script is ready for evaluation with the scope
initialized.
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index a94b2b289eb1b5da820dd3bf620911f2edef7c68..e11d29b43553574425c524e722f31bb8c2996fcb 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -385,6 +385,11 @@ class CONTENT_EXPORT ContentRendererClient {
virtual void DidInitializeWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) {}
+ // Notifies that a worker script has been downloaded, scope initialized and
+ // ready for evaluation. This function is called from the worker thread.
+ virtual void WorkerScriptReadyForEvaluationOnWorkerThread(
+ v8::Local<v8::Context> context) {}
+
// Notifies that a worker context will be destroyed. This function is called
// from the worker thread.
virtual void WillDestroyWorkerContextOnWorkerThread(
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index a2fe4cc09ffdcdf93199c01c316a4173a6f84d32..f8360eb68629a5d6b56471ef714312e1c5826025 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -876,6 +876,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated(
worker);
}
+void RendererBlinkPlatformImpl::WorkerScriptReadyForEvaluation(
+ const v8::Local<v8::Context>& worker) {
+ GetContentClient()->renderer()->WorkerScriptReadyForEvaluationOnWorkerThread(
+ worker);
+}
+
bool RendererBlinkPlatformImpl::IsExcludedHeaderForServiceWorkerFetchEvent(
const blink::WebString& header_name) {
return GetContentClient()
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 236894dc0dd7242c1f47caeb982c4f24b784ce95..35f229faa607bc3f06c524619e385d9aa356e2f1 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -182,6 +182,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl {
void DidStartWorkerThread() override;
void WillStopWorkerThread() override;
void WorkerContextCreated(const v8::Local<v8::Context>& worker) override;
+ void WorkerScriptReadyForEvaluation(
+ const v8::Local<v8::Context>& worker) override;
void WorkerContextWillDestroy(const v8::Local<v8::Context>& worker) override;
bool IsExcludedHeaderForServiceWorkerFetchEvent(
const blink::WebString& header_name) override;
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 3edcb4715c2dd8b2cf33f093710f14816b061117..79336bcad4d18e617677b2fb80d898680618e03b 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -615,6 +615,8 @@ class BLINK_PLATFORM_EXPORT Platform {
virtual void DidStartWorkerThread() {}
virtual void WillStopWorkerThread() {}
virtual void WorkerContextCreated(const v8::Local<v8::Context>& worker) {}
+ virtual void WorkerScriptReadyForEvaluation(
+ const v8::Local<v8::Context>& worker) {}
virtual void WorkerContextWillDestroy(const v8::Local<v8::Context>& worker) {}
virtual bool AllowScriptExtensionForServiceWorker(
const WebSecurityOrigin& script_origin) {
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index 83db0d2cba80184ecea9314b5869f836c0e3ea20..a25e2631bfd23bf1c212943d05b5f93a1c0201ed 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -323,6 +323,8 @@ void WorkerOrWorkletScriptController::PrepareForEvaluation() {
wrapper_type_info->InstallConditionalFeatures(
context, *world_, global_object, v8::Local<v8::Object>(),
v8::Local<v8::Function>(), global_interface_template);
+
+ Platform::Current()->WorkerScriptReadyForEvaluation(context);
}
void WorkerOrWorkletScriptController::DisableEvalInternal(

View File

@@ -7,11 +7,15 @@
"src/electron/patches/node": "src/third_party/electron_node",
"src/electron/patches/swiftshader": "src/third_party/swiftshader",
"src/electron/patches/pdfium": "src/third_party/pdfium",
"src/electron/patches/webrtc": "src/third_party/webrtc",
"src/electron/patches/skia": "src/third_party/skia",
"src/electron/patches/angle": "src/third_party/angle",
"src/electron/patches/ffmpeg": "src/third_party/ffmpeg"
}

View File

@@ -41,3 +41,6 @@ win_use_rtlgenrandom_from_advapi32_dll_directly.patch
tools_update_certdata_txt.patch
crypto_update_root_certificates.patch
darwin_work_around_clock_jumping_back_in_time.patch
lib_use_non-symbols_in_isurlinstance_check.patch
fix_enable_tls_renegotiation.patch
crypto_update_certdata_to_nss_3_56.patch

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Rose <nornagon@nornagon.net>
Date: Tue, 18 Aug 2020 09:51:46 -0700
Subject: fix: enable TLS renegotiation
This configures BoringSSL to behave more similarly to OpenSSL.
See https://github.com/electron/electron/issues/18380.
This should be upstreamed.
diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc
index 42b9469e38189f04745732afdeadd59e3ce6ad4c..f664f280d605a32d9f97121ab2816fab0fbe28c9 100644
--- a/src/tls_wrap.cc
+++ b/src/tls_wrap.cc
@@ -125,6 +125,12 @@ void TLSWrap::InitSSL() {
// - https://wiki.openssl.org/index.php/TLS1.3#Non-application_data_records
SSL_set_mode(ssl_.get(), SSL_MODE_AUTO_RETRY);
+#ifdef OPENSSL_IS_BORINGSSL
+ // OpenSSL allows renegotiation by default, but BoringSSL disables it.
+ // Configure BoringSSL to match OpenSSL's behavior.
+ SSL_set_renegotiate_mode(ssl_.get(), ssl_renegotiate_freely);
+#endif
+
SSL_set_app_data(ssl_.get(), this);
// Using InfoCallback isn't how we are supposed to check handshake progress:
// https://github.com/openssl/openssl/issues/7199#issuecomment-420915993

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Tue, 4 Aug 2020 09:17:06 -0700
Subject: lib: use non-symbols in isURLInstance check
This slightly changes the conditional used to determine whether or
not something is a URL instance. Since Node.js adds symbols to the URL
not specified by the WHATWG, those symbols are not present in other
implementations (like Blink's) and therefore can cause false negatives.
This fixes that by slightly changing the check to properties present
in all URL instances as specified in the WHATWG spec.
Upstreamed at: https://github.com/nodejs/node/pull/34622.
diff --git a/lib/internal/url.js b/lib/internal/url.js
index 860fa4d7ad01b391d7d8e4c5ffe432d4d1d5983e..7d557a51930b7f225d128425fcc9db959c13392c 100644
--- a/lib/internal/url.js
+++ b/lib/internal/url.js
@@ -1334,8 +1334,7 @@ function getPathFromURLPosix(url) {
function fileURLToPath(path) {
if (typeof path === 'string')
path = new URL(path);
- else if (path == null || !path[searchParams] ||
- !path[searchParams][searchParams])
+ else if (path == null || !path['origin'] || !path['href'])
throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
if (path.protocol !== 'file:')
throw new ERR_INVALID_URL_SCHEME('file');
@@ -1383,8 +1382,7 @@ function pathToFileURL(filepath) {
}
function toPathIfFileURL(fileURLOrPath) {
- if (fileURLOrPath == null || !fileURLOrPath[searchParams] ||
- !fileURLOrPath[searchParams][searchParams])
+ if (fileURLOrPath == null || !fileURLOrPath['origin'] || !fileURLOrPath['href'])
return fileURLOrPath;
return fileURLToPath(fileURLOrPath);
}

View File

@@ -1 +1,2 @@
backport_1080481.patch
mallocpixelref_should_always_allocate_as_large_as_computebytesize.patch

View File

@@ -0,0 +1,78 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mike Reed <reed@google.com>
Date: Mon, 20 Jul 2020 18:12:00 -0400
Subject: MallocPixelRef should always allocate as large as computeByteSize()
says
Bug: 1103827
Change-Id: I837f92cf10a1a389fe1b0ba55ae1323e7e68f741
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/304416
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Mike Reed <reed@google.com>
(cherry picked from commit c1eb58de32c016eb60e7a46046321ffe351e8222)
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308761
Reviewed-by: Heather Miller <hcm@google.com>
diff --git a/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp
index d998029a2be43b0ecd913ef8e7b2a52f419812dd..02fd9c458012f8b8b7982d2fe3c971e585b54ac2 100644
--- a/src/core/SkMallocPixelRef.cpp
+++ b/src/core/SkMallocPixelRef.cpp
@@ -30,12 +30,9 @@ sk_sp<SkPixelRef> SkMallocPixelRef::MakeAllocate(const SkImageInfo& info, size_t
if (!is_valid(info) || !info.validRowBytes(rowBytes)) {
return nullptr;
}
- size_t size = 0;
- if (!info.isEmpty() && rowBytes) {
- size = info.computeByteSize(rowBytes);
- if (SkImageInfo::ByteSizeOverflowed(size)) {
- return nullptr;
- }
+ size_t size = info.computeByteSize(rowBytes);
+ if (SkImageInfo::ByteSizeOverflowed(size)) {
+ return nullptr;
}
void* addr = sk_calloc_canfail(size);
if (nullptr == addr) {
diff --git a/tests/BitmapTest.cpp b/tests/BitmapTest.cpp
index 94bb5af818c518a1a732679659d85d3ae9d7af26..c9d215cb9b504f0f137ff156cf25950263a0277e 100644
--- a/tests/BitmapTest.cpp
+++ b/tests/BitmapTest.cpp
@@ -398,3 +398,38 @@ DEF_TEST(getalphaf, reporter) {
}
}
}
+
+/* computeByteSize() is documented to return 0 if height is zero, but does not
+ * special-case width==0, so computeByteSize() can return non-zero for that
+ * (since it is defined to return (height-1)*rb + ...
+ *
+ * Test that allocPixels() respects this, and allocates a buffer as large as
+ * computeByteSize()... even though the bitmap is logicallly empty.
+ */
+DEF_TEST(bitmap_zerowidth_crbug_1103827, reporter) {
+ const size_t big_rb = 1 << 16;
+
+ struct {
+ int width, height;
+ size_t rowbytes, expected_size;
+ } rec[] = {
+ { 2, 0, big_rb, 0 }, // zero-height means zero-size
+ { 0, 2, big_rb, big_rb }, // zero-width is computed normally
+ };
+
+ for (const auto& r : rec) {
+ auto info = SkImageInfo::Make(r.width, r.height,
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ size_t size = info.computeByteSize(r.rowbytes);
+ REPORTER_ASSERT(reporter, size == r.expected_size);
+
+ SkBitmap bm;
+ bm.setInfo(info, r.rowbytes);
+ REPORTER_ASSERT(reporter, size == bm.computeByteSize());
+
+ // Be sure we can actually write to that much memory. If the bitmap underallocated
+ // the buffer, this should trash memory and crash (we hope).
+ bm.allocPixels();
+ sk_bzero(bm.getPixels(), size);
+ }
+}

View File

@@ -0,0 +1 @@
fix_copying_cubemap_textures_out_of_bounds.patch

View File

@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexis Hetu <sugoi@google.com>
Date: Wed, 12 Aug 2020 17:43:18 -0400
Subject: Fix copying cubemap textures out of bounds
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Cubemap textures are created with a border, which means that their
size is larger. Their range is also different. Instead of [0, dim-1],
they have a [-1, dim] range. This means that if we copy them starting
at [0, 0] for their full size, we'll overflow at the end, since the
buffer starts at [-1, -1]. This cl prevents going through the fast
copy path when we have a border.
Bug: chromium:1115345
Change-Id: I333acfd6094645231eb111634359d82ed3d5c787
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/47668
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Reviewed-by: Corentin Wallez <cwallez@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
index 0758428fb8c2aa91a21539d533e8b32d78d15e2a..8d80db73d5e43cb7463455f85beabaaf0d773bff 100644
--- a/src/OpenGL/libGLESv2/Device.cpp
+++ b/src/OpenGL/libGLESv2/Device.cpp
@@ -575,7 +575,7 @@ namespace es2
bool fullCopy = (sRect.x0 == 0.0f) && (sRect.y0 == 0.0f) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
(sRect.x1 == (float)sWidth) && (sRect.y1 == (float)sHeight) && (dRect.x1 == dWidth) && (dRect.y1 == dHeight);
bool alpha0xFF = false;
- bool equalSlice = sourceSliceB == destSliceB;
+ bool equalSlice = (sourceSliceB == destSliceB) && (source->getBorder() == 0) && (dest->getBorder() == 0);
bool smallMargin = sourcePitchB <= source->getWidth() * Surface::bytes(source->getInternalFormat()) + 16;
if((source->getInternalFormat() == FORMAT_A8R8G8B8 && dest->getInternalFormat() == FORMAT_X8R8G8B8) ||

View File

@@ -9,3 +9,5 @@ revert_cleanup_switch_offset_of_to_offsetof_where_possible.patch
fix_build_deprecated_attirbute_for_older_msvc_versions.patch
backport_1084820.patch
backport_986051.patch
fix_alreadycalled_checking_in_element_closures.patch
wasm_do_not_log_code_of_functions_whose_module_is_not_fully_loaded.patch

View File

@@ -0,0 +1,110 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shu-yu Guo <syg@chromium.org>
Date: Wed, 15 Jul 2020 17:12:44 -0700
Subject: Fix [[AlreadyCalled]] checking in element closures
Bug: chromium:1105318
Change-Id: I7b1c57b7ff7beaaa53c19a270d5a8c36b11baf17
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2301082
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68903}
diff --git a/src/builtins/promise-all-element-closure.tq b/src/builtins/promise-all-element-closure.tq
index c320b24f036c1cf2afb2fb9d0906690cc90bdb82..bb65760bd16c4d7510e4d1267b6037aef32767e7 100644
--- a/src/builtins/promise-all-element-closure.tq
+++ b/src/builtins/promise-all-element-closure.tq
@@ -81,9 +81,9 @@ namespace promise {
generates 'PropertyArray::HashField::kMax';
transitioning macro PromiseAllResolveElementClosure<F: type>(
- implicit context:
- Context)(value: JSAny, function: JSFunction, wrapResultFunctor: F):
- JSAny {
+ implicit context: Context)(
+ value: JSAny, function: JSFunction, wrapResultFunctor: F,
+ hasResolveAndRejectClosures: constexpr bool): JSAny {
// We use the {function}s context as the marker to remember whether this
// resolve element closure was already called. It points to the resolve
// element context (which is a FunctionContext) until it was called the
@@ -99,10 +99,6 @@ namespace promise {
const nativeContext = LoadNativeContext(context);
function.context = nativeContext;
- // Update the value depending on whether Promise.all or
- // Promise.allSettled is called.
- const updatedValue = wrapResultFunctor.Call(nativeContext, value);
-
// Determine the index from the {function}.
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash =
@@ -117,13 +113,41 @@ namespace promise {
const elements = UnsafeCast<FixedArray>(valuesArray.elements);
const valuesLength = Convert<intptr>(valuesArray.length);
if (index < valuesLength) {
- // The {index} is in bounds of the {values_array},
- // just store the {value} and continue.
+ // The {index} is in bounds of the {values_array}, check if this element has
+ // already been resolved, and store the {value} if not.
+ //
+ // Promise.allSettled, for each input element, has both a resolve and a
+ // reject closure that share an [[AlreadyCalled]] boolean. That is, the
+ // input element can only be settled once: after resolve is called, reject
+ // returns early, and vice versa. Using {function}'s context as the marker
+ // only tracks per-closure instead of per-element. When the second
+ // resolve/reject closure is called on the same index, values.object[index]
+ // will already exist and will not be the hole value. In that case, return
+ // early. Everything up to this point is not yet observable to user code.
+ // This is not a problem for Promise.all since Promise.all has a single
+ // resolve closure (no reject) per element.
+ if (hasResolveAndRejectClosures) {
+ if (elements.objects[index] != TheHole) deferred {
+ return Undefined;
+ }
+ }
+
+ // Update the value depending on whether Promise.all or
+ // Promise.allSettled is called.
+ const updatedValue = wrapResultFunctor.Call(nativeContext, value);
elements.objects[index] = updatedValue;
} else {
// Check if we need to grow the backing store.
+ //
+ // There's no need to check if this element has already been resolved for
+ // Promise.allSettled if {values_array} has not yet grown to the index.
const newLength = index + 1;
const elementsLength = elements.length_intptr;
+
+ // Update the value depending on whether Promise.all or
+ // Promise.allSettled is called.
+ const updatedValue = wrapResultFunctor.Call(nativeContext, value);
+
if (index < elementsLength) {
// The {index} is within bounds of the {elements} backing store, so
// just store the {value} and update the "length" of the {values_array}.
@@ -168,7 +192,7 @@ namespace promise {
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
- value, target, PromiseAllWrapResultAsFulfilledFunctor{});
+ value, target, PromiseAllWrapResultAsFulfilledFunctor{}, false);
}
transitioning javascript builtin
@@ -176,7 +200,7 @@ namespace promise {
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
- value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{});
+ value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}, true);
}
transitioning javascript builtin
@@ -184,6 +208,6 @@ namespace promise {
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
- value, target, PromiseAllSettledWrapResultAsRejectedFunctor{});
+ value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}, true);
}
}

View File

@@ -0,0 +1,156 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Emanuel Ziegler <ecmziegler@chromium.org>
Date: Fri, 5 Jun 2020 19:54:58 +0200
Subject: Do not log code of functions whose module is not fully loaded
This is a reland of change Idb1061cafcba7a2a654a207402dca520f79a3bbe.
The access to wire_bytes has been protected by using atomic operations.
Under some circumstances, Wasm is trying to log code for which the
wire bytes are not fully loaded yet. This can happen during streaming
compilation when a few functions are already fully compiled but the
engine is still streaming the remaining functions.
If the profiler now kicks in, it will attempt to log these freshly
compiled functions. As these functions will not be executed before
the module is fully compiled, we can simply defer the logging in this
case.
R=clemensb@chromium.org
Bug: chromium:1085852
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_isolates_rel_ng
Change-Id: Iccb6607e8adb9fdaf6138d4ccd30de58d6a6cdff
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2230536
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68336}
diff --git a/src/wasm/module-compiler.cc b/src/wasm/module-compiler.cc
index 369dcfd9f79309ce10f0906bdcf11f53ef608deb..815a29640f51ba272a03c30526c0519ec1abd53f 100644
--- a/src/wasm/module-compiler.cc
+++ b/src/wasm/module-compiler.cc
@@ -1080,7 +1080,10 @@ bool ExecuteCompilationUnits(
}
}
- native_module->engine()->LogCode(VectorOf(code_vector));
+ // Defer logging code in case wire bytes were not fully received yet.
+ if (native_module->HasWireBytes()) {
+ native_module->engine()->LogCode(VectorOf(code_vector));
+ }
compile_scope->compilation_state()->OnFinishedUnits(
VectorOf(code_vector), VectorOf(results_to_publish));
@@ -2357,6 +2360,7 @@ void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
} else {
job_->native_module_->SetWireBytes(
{std::move(job_->bytes_copy_), job_->wire_bytes_.length()});
+ job_->native_module_->LogWasmCodes(job_->isolate_);
}
const bool needs_finish = job_->DecrementAndCheckFinisherCount();
DCHECK_IMPLIES(!has_code_section, needs_finish);
diff --git a/src/wasm/wasm-code-manager.cc b/src/wasm/wasm-code-manager.cc
index 99cf484b17821b7b1a09cb52bcc206c4c839e919..99cf3561c8622cd83970d30ea6718362d5f3e58b 100644
--- a/src/wasm/wasm-code-manager.cc
+++ b/src/wasm/wasm-code-manager.cc
@@ -1314,7 +1314,9 @@ class NativeModuleWireBytesStorage final : public WireBytesStorage {
: wire_bytes_(std::move(wire_bytes)) {}
Vector<const uint8_t> GetCode(WireBytesRef ref) const final {
- return wire_bytes_->as_vector().SubVector(ref.offset(), ref.end_offset());
+ return std::atomic_load(&wire_bytes_)
+ ->as_vector()
+ .SubVector(ref.offset(), ref.end_offset());
}
private:
@@ -1325,7 +1327,7 @@ class NativeModuleWireBytesStorage final : public WireBytesStorage {
void NativeModule::SetWireBytes(OwnedVector<const uint8_t> wire_bytes) {
auto shared_wire_bytes =
std::make_shared<OwnedVector<const uint8_t>>(std::move(wire_bytes));
- wire_bytes_ = shared_wire_bytes;
+ std::atomic_store(&wire_bytes_, shared_wire_bytes);
if (!shared_wire_bytes->empty()) {
compilation_state_->SetWireBytesStorage(
std::make_shared<NativeModuleWireBytesStorage>(
diff --git a/src/wasm/wasm-code-manager.h b/src/wasm/wasm-code-manager.h
index 4b176f3ba61bcdfb922c5f5cbb92981d79393ffa..6960c224c32d3c8b237f973ecc399c49793af3e6 100644
--- a/src/wasm/wasm-code-manager.h
+++ b/src/wasm/wasm-code-manager.h
@@ -537,7 +537,9 @@ class V8_EXPORT_PRIVATE NativeModule final {
UseTrapHandler use_trap_handler() const { return use_trap_handler_; }
void set_lazy_compile_frozen(bool frozen) { lazy_compile_frozen_ = frozen; }
bool lazy_compile_frozen() const { return lazy_compile_frozen_; }
- Vector<const uint8_t> wire_bytes() const { return wire_bytes_->as_vector(); }
+ Vector<const uint8_t> wire_bytes() const {
+ return std::atomic_load(&wire_bytes_)->as_vector();
+ }
const WasmModule* module() const { return module_.get(); }
std::shared_ptr<const WasmModule> shared_module() const { return module_; }
size_t committed_code_space() const {
@@ -545,6 +547,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
}
WasmEngine* engine() const { return engine_; }
+ bool HasWireBytes() const {
+ auto wire_bytes = std::atomic_load(&wire_bytes_);
+ return wire_bytes && !wire_bytes->empty();
+ }
void SetWireBytes(OwnedVector<const uint8_t> wire_bytes);
WasmCode* Lookup(Address) const;
diff --git a/test/cctest/wasm/test-streaming-compilation.cc b/test/cctest/wasm/test-streaming-compilation.cc
index 4d3f83daff766edb96d25934bb7ab5564f193046..87da8ea1d62b14f792e9525f5707fb4b4e05cd94 100644
--- a/test/cctest/wasm/test-streaming-compilation.cc
+++ b/test/cctest/wasm/test-streaming-compilation.cc
@@ -1248,6 +1248,48 @@ STREAM_TEST(TestSetModuleCodeSection) {
CHECK(tester.IsPromiseFulfilled());
}
+// Test that profiler does not crash when module is only partly compiled.
+STREAM_TEST(TestProfilingMidStreaming) {
+ StreamTester tester;
+ v8::Isolate* isolate = CcTest::isolate();
+ Isolate* i_isolate = CcTest::i_isolate();
+ Zone* zone = tester.zone();
+
+ // Build module with one exported (named) function.
+ ZoneBuffer buffer(zone);
+ {
+ TestSignatures sigs;
+ WasmModuleBuilder builder(zone);
+ WasmFunctionBuilder* f = builder.AddFunction(sigs.v_v());
+ uint8_t code[] = {kExprEnd};
+ f->EmitCode(code, arraysize(code));
+ builder.AddExport(VectorOf("foo", 3), f);
+ builder.WriteTo(&buffer);
+ }
+
+ // Start profiler to force code logging.
+ v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(isolate);
+ v8::CpuProfilingOptions profile_options;
+ cpu_profiler->StartProfiling(v8::String::Empty(isolate), profile_options);
+
+ // Send incomplete wire bytes and start compilation.
+ tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
+ tester.RunCompilerTasks();
+
+ // Trigger code logging explicitly like the profiler would do.
+ CHECK(WasmCode::ShouldBeLogged(i_isolate));
+ i_isolate->wasm_engine()->LogOutstandingCodesForIsolate(i_isolate);
+ CHECK(tester.IsPromisePending());
+
+ // Finalize stream, stop profiler and clean up.
+ tester.FinishStream();
+ CHECK(tester.IsPromiseFulfilled());
+ v8::CpuProfile* profile =
+ cpu_profiler->StopProfiling(v8::String::Empty(isolate));
+ profile->Delete();
+ cpu_profiler->Dispose();
+}
+
#undef STREAM_TEST
} // namespace wasm

View File

@@ -1,2 +1,3 @@
backport_1076703.patch
backport_978779.patch
check_for_null_before_accessing_sctptransport_map.patch

View File

@@ -0,0 +1,64 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taylor Brandstetter <deadbeef@webrtc.org>
Date: Mon, 13 Jul 2020 11:51:49 -0700
Subject: Check for null before accessing SctpTransport map.
Bug: chromium:1104061
Change-Id: I52d44ff1603341777a873e747c625665bc11bfa5
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/179161
Commit-Queue: Taylor <deadbeef@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31720}
diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc
index 1a6dc334e1fd1f69a094e763d13f71f4c5292a5e..ad68c37ac97263aec8de5fdd1657c1c1a1373895 100644
--- a/media/sctp/sctp_transport.cc
+++ b/media/sctp/sctp_transport.cc
@@ -302,18 +302,21 @@ class SctpTransport::UsrSctpWrapper {
}
static void UninitializeUsrSctp() {
- delete g_transport_map_;
RTC_LOG(LS_INFO) << __FUNCTION__;
// usrsctp_finish() may fail if it's called too soon after the transports
// are
// closed. Wait and try again until it succeeds for up to 3 seconds.
for (size_t i = 0; i < 300; ++i) {
if (usrsctp_finish() == 0) {
+ delete g_transport_map_;
+ g_transport_map_ = nullptr;
return;
}
rtc::Thread::SleepMs(10);
}
+ delete g_transport_map_;
+ g_transport_map_ = nullptr;
RTC_LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
}
@@ -340,6 +343,11 @@ class SctpTransport::UsrSctpWrapper {
size_t length,
uint8_t tos,
uint8_t set_df) {
+ if (!g_transport_map_) {
+ RTC_LOG(LS_ERROR)
+ << "OnSctpOutboundPacket called after usrsctp uninitialized?";
+ return EINVAL;
+ }
SctpTransport* transport =
g_transport_map_->Retrieve(reinterpret_cast<uintptr_t>(addr));
if (!transport) {
@@ -463,6 +471,12 @@ class SctpTransport::UsrSctpWrapper {
// id of the transport that created them, so [0] is as good as any other.
struct sockaddr_conn* sconn =
reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
+ if (!g_transport_map_) {
+ RTC_LOG(LS_ERROR)
+ << "GetTransportFromSocket called after usrsctp uninitialized?";
+ usrsctp_freeladdrs(addrs);
+ return nullptr;
+ }
SctpTransport* transport = g_transport_map_->Retrieve(
reinterpret_cast<uintptr_t>(sconn->sconn_addr));
usrsctp_freeladdrs(addrs);

View File

@@ -8,9 +8,10 @@ const semver = require('semver');
const { ELECTRON_DIR } = require('../../lib/utils');
const notesGenerator = require('./notes.js');
const semverify = version => version.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.');
const semverify = version => version.replace(/^origin\//, '').replace(/[xy]/g, '0').replace(/-/g, '.');
const runGit = async (args) => {
console.info(`Running: git ${args.join(' ')}`);
const response = await GitProcess.exec(args, ELECTRON_DIR);
if (response.exitCode !== 0) {
throw new Error(response.stderr.trim());
@@ -19,15 +20,20 @@ const runGit = async (args) => {
};
const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported');
const tagIsBeta = tag => tag.includes('beta');
const tagIsBeta = tag => tag && tag.includes('beta');
const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag);
const getTagsOf = async (point) => {
return (await runGit(['tag', '--merged', point]))
.split('\n')
.map(tag => tag.trim())
.filter(tag => semver.valid(tag))
.sort(semver.compare);
try {
const tags = await runGit(['tag', '--merged', point]);
return tags.split('\n')
.map(tag => tag.trim())
.filter(tag => semver.valid(tag))
.sort(semver.compare);
} catch (err) {
console.error(`Failed to fetch tags for point ${point}`);
throw err;
}
};
const getTagsOnBranch = async (point) => {
@@ -41,26 +47,36 @@ const getTagsOnBranch = async (point) => {
};
const getBranchOf = async (point) => {
const branches = (await runGit(['branch', '-a', '--contains', point]))
.split('\n')
.map(branch => branch.trim())
.filter(branch => !!branch);
const current = branches.find(branch => branch.startsWith('* '));
return current ? current.slice(2) : branches.shift();
try {
const branches = (await runGit(['branch', '-a', '--contains', point]))
.split('\n')
.map(branch => branch.trim())
.filter(branch => !!branch);
const current = branches.find(branch => branch.startsWith('* '));
return current ? current.slice(2) : branches.shift();
} catch (err) {
console.error(`Failed to fetch branch for ${point}: `, err);
throw err;
}
};
const getAllBranches = async () => {
return (await runGit(['branch', '--remote']))
.split('\n')
.map(branch => branch.trim())
.filter(branch => !!branch)
.filter(branch => branch !== 'origin/HEAD -> origin/master')
.sort();
try {
const branches = await runGit(['branch', '--remote']);
return branches.split('\n')
.map(branch => branch.trim())
.filter(branch => !!branch)
.filter(branch => branch !== 'origin/HEAD -> origin/master')
.sort();
} catch (err) {
console.error('Failed to fetch all branches');
throw err;
}
};
const getStabilizationBranches = async () => {
return (await getAllBranches())
.filter(branch => /^origin\/\d+-\d+-x$/.test(branch));
.filter(branch => /^origin\/\d+-\d+-x$/.test(branch) || /^origin\/\d+-x-y$/.test(branch));
};
const getPreviousStabilizationBranch = async (current) => {
@@ -120,7 +136,7 @@ const getPreviousPoint = async (point) => {
}
};
async function getReleaseNotes (range, newVersion, explicitLinks) {
async function getReleaseNotes (range, newVersion) {
const rangeList = range.split('..') || ['HEAD'];
const to = rangeList.pop();
const from = rangeList.pop() || (await getPreviousPoint(to));
@@ -129,10 +145,9 @@ async function getReleaseNotes (range, newVersion, explicitLinks) {
newVersion = to;
}
console.log(`Generating release notes between ${from} and ${to} for version ${newVersion}`);
const notes = await notesGenerator.get(from, to, newVersion);
const ret = {
text: notesGenerator.render(notes, explicitLinks)
text: notesGenerator.render(notes)
};
if (notes.unknown.length) {
@@ -144,7 +159,7 @@ async function getReleaseNotes (range, newVersion, explicitLinks) {
async function main () {
const opts = minimist(process.argv.slice(2), {
boolean: ['explicit-links', 'help'],
boolean: ['help'],
string: ['version']
});
opts.range = opts._.shift();
@@ -153,14 +168,13 @@ async function main () {
console.log(`
easy usage: ${name} version
full usage: ${name} [begin..]end [--version version] [--explicit-links]
full usage: ${name} [begin..]end [--version version]
* 'begin' and 'end' are two git references -- tags, branches, etc --
from which the release notes are generated.
* if omitted, 'begin' defaults to the previous tag in end's branch.
* if omitted, 'version' defaults to 'end'. Specifying a version is
useful if you're making notes on a new version that isn't tagged yet.
* 'explicit-links' makes every note's issue, commit, or pull an MD link
For example, these invocations are equivalent:
${process.argv[1]} v4.0.1
@@ -169,7 +183,7 @@ For example, these invocations are equivalent:
return 0;
}
const notes = await getReleaseNotes(opts.range, opts.version, opts['explicit-links']);
const notes = await getReleaseNotes(opts.range, opts.version);
console.log(notes.text);
if (notes.warning) {
throw new Error(notes.warning);

View File

@@ -2,25 +2,23 @@
'use strict';
const childProcess = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { GitProcess } = require('dugite');
const octokit = require('@octokit/rest')({
auth: process.env.ELECTRON_GITHUB_TOKEN
});
const semver = require('semver');
const { ELECTRON_VERSION, SRC_DIR } = require('../../lib/utils');
const { ELECTRON_DIR } = require('../../lib/utils');
const MAX_FAIL_COUNT = 3;
const CHECK_INTERVAL = 5000;
const CACHE_DIR = path.resolve(__dirname, '.cache');
const TROP_LOGIN = 'trop[bot]';
const NO_NOTES = 'No notes';
const FOLLOW_REPOS = ['electron/electron', 'electron/node'];
const docTypes = new Set(['doc', 'docs']);
const featTypes = new Set(['feat', 'feature']);
@@ -28,6 +26,8 @@ const fixTypes = new Set(['fix']);
const otherTypes = new Set(['spec', 'build', 'test', 'chore', 'deps', 'refactor', 'tools', 'vendor', 'perf', 'style', 'ci']);
const knownTypes = new Set([...docTypes.keys(), ...featTypes.keys(), ...fixTypes.keys(), ...otherTypes.keys()]);
const getCacheDir = () => process.env.NOTES_CACHE_PATH || path.resolve(__dirname, '.cache');
/**
***
**/
@@ -39,6 +39,13 @@ class GHKey {
this.repo = repo;
this.number = number;
}
static NewFromPull (pull) {
const owner = pull.base.repo.owner.login;
const repo = pull.base.repo.name;
const number = pull.number;
return new GHKey(owner, repo, number);
}
}
class Commit {
@@ -47,10 +54,13 @@ class Commit {
this.owner = owner; // string
this.repo = repo; // string
this.body = null; // string
this.isBreakingChange = false;
this.issueNumber = null; // number
this.note = null; // string
// A set of branches to which this change has been merged.
// '8-x-y' => GHKey { owner: 'electron', repo: 'electron', number: 23714 }
this.trops = new Map(); // Map<string,GHKey>
this.prKeys = new Set(); // GHKey
this.revertHash = null; // string
this.semanticType = null; // string
@@ -99,65 +109,31 @@ const getNoteFromClerk = async (ghKey) => {
return NO_NOTES;
}
if (comment.body.startsWith(PERSIST_LEAD)) {
return comment.body
let lines = comment.body
.slice(PERSIST_LEAD.length).trim() // remove PERSIST_LEAD
.split('\r?\n') // break into lines
.split(/\r?\n/) // split into lines
.map(line => line.trim())
.filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted
.map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines
.join(' ') // join the note lines
.map(line => line.slice(QUOTE_LEAD.length)); // unquote the lines
const firstLine = lines.shift();
// indent anything after the first line to ensure that
// multiline notes with their own sub-lists don't get
// parsed in the markdown as part of the top-level list
// (example: https://github.com/electron/electron/pull/25216)
lines = lines.map(line => ' ' + line);
return [firstLine, ...lines]
.join('\n') // join the lines
.trim();
}
}
};
// copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13
const OMIT_FROM_RELEASE_NOTES_KEYS = [
'no-notes',
'no notes',
'no_notes',
'none',
'no',
'nothing',
'empty',
'blank'
];
const getNoteFromBody = body => {
if (!body) {
return null;
}
const NOTE_PREFIX = 'Notes: ';
const NOTE_HEADER = '#### Release Notes';
let note = body
.split(/\r?\n\r?\n/) // split into paragraphs
.map(paragraph => paragraph.trim())
.map(paragraph => paragraph.startsWith(NOTE_HEADER) ? paragraph.slice(NOTE_HEADER.length).trim() : paragraph)
.find(paragraph => paragraph.startsWith(NOTE_PREFIX));
if (note) {
note = note
.slice(NOTE_PREFIX.length)
.replace(/<!--.*-->/, '') // '<!-- change summary here-->'
.replace(/\r?\n/, ' ') // remove newlines
.trim();
}
if (note && OMIT_FROM_RELEASE_NOTES_KEYS.includes(note.toLowerCase())) {
return NO_NOTES;
}
return note;
};
/**
* Looks for our project's conventions in the commit message:
*
* 'semantic: some description' -- sets semanticType, subject
* 'some description (#99999)' -- sets subject, pr
* 'Fixes #3333' -- sets issueNumber
* 'Merge pull request #99999 from ${branchname}' -- sets pr
* 'This reverts commit ${sha}' -- sets revertHash
* line starting with 'BREAKING CHANGE' in body -- sets isBreakingChange
@@ -175,13 +151,6 @@ const parseCommitMessage = (commitMessage, commit) => {
subject = subject.slice(0, pos).trim();
}
if (body) {
commit.body = body;
const note = getNoteFromBody(body);
if (note) { commit.note = note; }
}
// if the subject ends in ' (#dddd)', treat it as a pull request id
let match;
if ((match = subject.match(/^(.*)\s\(#(\d+)\)$/))) {
@@ -213,7 +182,6 @@ const parseCommitMessage = (commitMessage, commit) => {
// https://help.github.com/articles/closing-issues-using-keywords/
if ((match = body.match(/\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved|for)\s#(\d+)\b/i))) {
commit.issueNumber = parseInt(match[1]);
commit.semanticType = commit.semanticType || 'fix';
}
@@ -237,32 +205,36 @@ const parseCommitMessage = (commitMessage, commit) => {
const parsePullText = (pull, commit) => parseCommitMessage(`${pull.data.title}\n\n${pull.data.body}`, commit);
const getLocalCommitHashes = async (dir, ref) => {
const args = ['log', '-z', '--format=%H', ref];
return (await runGit(dir, args)).split('\0').map(hash => hash.trim());
const args = ['log', '--format=%H', ref];
return (await runGit(dir, args))
.split(/\r?\n/) // split into lines
.map(hash => hash.trim());
};
// return an array of Commits
const getLocalCommits = async (module, point1, point2) => {
const { owner, repo, dir } = module;
const fieldSep = '||';
const format = ['%H', '%B'].join(fieldSep);
const args = ['log', '-z', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`];
const logs = (await runGit(dir, args)).split('\0').map(field => field.trim());
const fieldSep = ',';
const format = ['%H', '%s'].join(fieldSep);
const args = ['log', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`];
const logs = (await runGit(dir, args))
.split(/\r?\n/) // split into lines
.map(field => field.trim());
const commits = [];
for (const log of logs) {
if (!log) {
continue;
}
const [hash, message] = log.split(fieldSep, 2).map(field => field.trim());
commits.push(parseCommitMessage(message, new Commit(hash, owner, repo)));
const [hash, subject] = log.split(fieldSep, 2).map(field => field.trim());
commits.push(parseCommitMessage(subject, new Commit(hash, owner, repo)));
}
return commits;
};
const checkCache = async (name, operation) => {
const filename = path.resolve(CACHE_DIR, name);
const filename = path.resolve(getCacheDir(), name);
if (fs.existsSync(filename)) {
return JSON.parse(fs.readFileSync(filename, 'utf8'));
}
@@ -286,12 +258,42 @@ async function runRetryable (fn, maxRetries) {
}
}
// Silently eat 404s.
if (lastError.status !== 404) throw lastError;
// Silently eat 422s, which come from "No commit found for SHA"
if (lastError.status !== 404 && lastError.status !== 422) throw lastError;
}
const getPullCacheFilename = ghKey => `${ghKey.owner}-${ghKey.repo}-pull-${ghKey.number}`;
const getCommitPulls = async (owner, repo, hash) => {
const name = `${owner}-${repo}-commit-${hash}`;
const retryableFunc = () => octokit.repos.listPullRequestsAssociatedWithCommit({ owner, repo, commit_sha: hash });
let ret = await checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT));
// only merged pulls belong in release notes
if (ret && ret.data) {
ret.data = ret.data.filter(pull => pull.merged_at);
}
// cache the pulls
if (ret && ret.data) {
for (const pull of ret.data) {
const cachefile = getPullCacheFilename(GHKey.NewFromPull(pull));
const payload = { ...ret, data: pull };
await checkCache(cachefile, () => payload);
}
}
// ensure the return value has the expected structure, even on failure
if (!ret || !ret.data) {
ret = { data: [] };
}
return ret;
};
const getPullRequest = async (ghKey) => {
const { number, owner, repo } = ghKey;
const name = `${owner}-${repo}-pull-${number}`;
const name = getPullCacheFilename(ghKey);
const retryableFunc = () => octokit.pulls.get({ pull_number: number, owner, repo });
return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT));
};
@@ -306,10 +308,20 @@ const getComments = async (ghKey) => {
const addRepoToPool = async (pool, repo, from, to) => {
const commonAncestor = await getCommonAncestor(repo.dir, from, to);
// add the commits
const oldHashes = await getLocalCommitHashes(repo.dir, from);
oldHashes.forEach(hash => { pool.processedHashes.add(hash); });
// mark the old branch's commits as old news
for (const oldHash of await getLocalCommitHashes(repo.dir, from)) {
pool.processedHashes.add(oldHash);
}
// get the new branch's commits and the pulls associated with them
const commits = await getLocalCommits(repo, commonAncestor, to);
for (const commit of commits) {
const { owner, repo, hash } = commit;
for (const pull of (await getCommitPulls(owner, repo, hash)).data) {
commit.prKeys.add(GHKey.NewFromPull(pull));
}
}
pool.commits.push(...commits);
// add the pulls
@@ -317,95 +329,59 @@ const addRepoToPool = async (pool, repo, from, to) => {
let prKey;
for (prKey of commit.prKeys.values()) {
const pull = await getPullRequest(prKey);
if (!pull || !pull.data) break; // couldn't get it
if (pool.pulls[prKey.number]) break; // already have it
if (!pull || !pull.data) continue; // couldn't get it
pool.pulls[prKey.number] = pull;
parsePullText(pull, commit);
}
}
};
/***
**** Other Repos
***/
// @return Map<string,GHKey>
// where the key is a branch name (e.g. '7-1-x' or '8-x-y')
// and the value is a GHKey to the PR
async function getMergedTrops (commit, pool) {
const branches = new Map();
// other repos - gn
for (const prKey of commit.prKeys.values()) {
const pull = pool.pulls[prKey.number];
const mergedBranches = new Set(
((pull && pull.data && pull.data.labels) ? pull.data.labels : [])
.map(label => ((label && label.name) ? label.name : '').match(/merged\/([0-9]+-[x0-9]-[xy0-9])/))
.filter(match => match)
.map(match => match[1])
);
const getDepsVariable = async (ref, key) => {
// get a copy of that reference point's DEPS file
const deps = await runGit(ELECTRON_VERSION, ['show', `${ref}:DEPS`]);
const filename = path.resolve(os.tmpdir(), 'DEPS');
fs.writeFileSync(filename, deps);
if (mergedBranches.size > 0) {
const isTropComment = (comment) => comment && comment.user && comment.user.login === TROP_LOGIN;
// query the DEPS file
const response = childProcess.spawnSync(
'gclient',
['getdep', '--deps-file', filename, '--var', key],
{ encoding: 'utf8' }
);
const ghKey = GHKey.NewFromPull(pull.data);
const backportRegex = /backported this PR to "(.*)",\s+please check out #(\d+)/;
const getBranchNameAndPullKey = (comment) => {
const match = ((comment && comment.body) ? comment.body : '').match(backportRegex);
return match ? [match[1], new GHKey(ghKey.owner, ghKey.repo, parseInt(match[2]))] : null;
};
// cleanup
fs.unlinkSync(filename);
return response.stdout.trim();
};
const getDependencyCommitsGN = async (pool, fromRef, toRef) => {
const repos = [{ // just node
owner: 'electron',
repo: 'node',
dir: path.resolve(SRC_DIR, 'third_party', 'electron_node'),
deps_variable_name: 'node_version'
}];
for (const repo of repos) {
// the 'DEPS' file holds the dependency reference point
const key = repo.deps_variable_name;
const from = await getDepsVariable(fromRef, key);
const to = await getDepsVariable(toRef, key);
await addRepoToPool(pool, repo, from, to);
}
};
// Changes are interesting if they make a change relative to a previous
// release in the same series. For example if you fix a Y.0.0 bug, that
// should be included in the Y.0.1 notes even if it's also tropped back
// to X.0.1.
//
// The phrase 'previous release' is important: if this is the first
// prerelease or first stable release in a series, we omit previous
// branches' changes. Otherwise we will have an overwhelmingly long
// list of mostly-irrelevant changes.
const shouldIncludeMultibranchChanges = (version) => {
let show = true;
if (semver.valid(version)) {
const prerelease = semver.prerelease(version);
show = prerelease
? parseInt(prerelease.pop()) > 1
: semver.patch(version) > 0;
const comments = await getComments(ghKey);
((comments && comments.data) ? comments.data : [])
.filter(isTropComment)
.map(getBranchNameAndPullKey)
.filter(pair => pair)
.filter(([branch]) => mergedBranches.has(branch))
.forEach(([branch, key]) => branches.set(branch, key));
}
}
return show;
};
function getOldestMajorBranchOfPull (pull) {
return pull.data.labels
.map(label => label.name.match(/merged\/(\d+)-(\d+)-x/) || label.name.match(/merged\/(\d+)-x-y/))
.filter(label => !!label)
.map(label => parseInt(label[1]))
.filter(major => !!major)
.sort()
.shift();
return branches;
}
function getOldestMajorBranchOfCommit (commit, pool) {
return [ ...commit.prKeys.values() ]
.map(prKey => pool.pulls[prKey.number])
.filter(pull => !!pull)
.map(pull => getOldestMajorBranchOfPull(pull))
.filter(major => !!major)
.sort()
.shift();
// @return the shorthand name of the branch that `ref` is on,
// e.g. a ref of '10.0.0-beta.1' will return '10-x-y'
async function getBranchNameOfRef (ref, dir) {
return (await runGit(dir, ['branch', '--all', '--contains', ref, '--sort', 'version:refname']))
.split(/\r?\n/) // split into lines
.shift() // we sorted by refname and want the first result
.match(/(?:.*\/)?(.*)/)[1] // 'remote/origins/10-x-y' -> '10-x-y'
.trim();
}
/***
@@ -413,26 +389,20 @@ function getOldestMajorBranchOfCommit (commit, pool) {
***/
const getNotes = async (fromRef, toRef, newVersion) => {
if (!fs.existsSync(CACHE_DIR)) {
fs.mkdirSync(CACHE_DIR);
const cacheDir = getCacheDir();
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir);
}
const pool = new Pool();
const toBranch = await getBranchNameOfRef(toRef, ELECTRON_DIR);
console.log(`Generating release notes between ${fromRef} and ${toRef} for version ${newVersion} in branch ${toBranch}`);
// get the electron/electron commits
const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_VERSION };
const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_DIR };
await addRepoToPool(pool, electron, fromRef, toRef);
// Don't include submodules if comparing across major versions;
// there's just too much churn otherwise.
const includeDeps = semver.valid(fromRef) &&
semver.valid(toRef) &&
semver.major(fromRef) === semver.major(toRef);
if (includeDeps) {
await getDependencyCommitsGN(pool, fromRef, toRef);
}
// remove any old commits
pool.commits = pool.commits.filter(commit => !pool.processedHashes.has(commit.hash));
@@ -457,27 +427,24 @@ const getNotes = async (fromRef, toRef, newVersion) => {
// ensure the commit has a note
for (const commit of pool.commits) {
for (const prKey of commit.prKeys.values()) {
commit.note = commit.note || await getNoteFromClerk(prKey);
if (commit.note) {
break;
}
commit.note = await getNoteFromClerk(prKey);
}
// use a fallback note in case someone missed a 'Notes' comment
commit.note = commit.note || commit.subject;
}
// remove non-user-facing commits
pool.commits = pool.commits
.filter(commit => commit && commit.note)
.filter(commit => commit.note !== NO_NOTES)
.filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/)));
.filter(commit => commit.note.match(/^[Bb]ump v\d+\.\d+\.\d+/) === null);
if (!shouldIncludeMultibranchChanges(newVersion)) {
const currentMajor = semver.parse(newVersion).major;
pool.commits = pool.commits
.filter(commit => getOldestMajorBranchOfCommit(commit, pool) >= currentMajor);
for (const commit of pool.commits) {
commit.trops = await getMergedTrops(commit, pool);
}
pool.commits = removeSupercededChromiumUpdates(pool.commits);
pool.commits = removeSupercededStackUpdates(pool.commits);
const notes = {
breaking: [],
@@ -486,7 +453,8 @@ const getNotes = async (fromRef, toRef, newVersion) => {
fix: [],
other: [],
unknown: [],
name: newVersion
name: newVersion,
toBranch
};
pool.commits.forEach(commit => {
@@ -511,50 +479,76 @@ const getNotes = async (fromRef, toRef, newVersion) => {
return notes;
};
const removeSupercededChromiumUpdates = (commits) => {
const chromiumRegex = /^Updated Chromium to \d+\.\d+\.\d+\.\d+/;
const updates = commits.filter(commit => (commit.note || commit.subject).match(chromiumRegex));
const keepers = commits.filter(commit => !updates.includes(commit));
const removeSupercededStackUpdates = (commits) => {
const updateRegex = /^Updated ([a-zA-Z.]+) to v?([\d.]+)/;
const notupdates = [];
// keep the newest update.
if (updates.length) {
const compare = (a, b) => (a.note || a.subject).localeCompare(b.note || b.subject);
keepers.push(updates.sort(compare).pop());
const newest = {};
for (const commit of commits) {
const match = (commit.note || commit.subject).match(updateRegex);
if (!match) {
notupdates.push(commit);
continue;
}
const [, dep, version] = match;
if (!newest[dep] || newest[dep].version < version) {
newest[dep] = { commit, version };
}
}
return keepers;
return [...notupdates, ...Object.values(newest).map(o => o.commit)];
};
/***
**** Render
***/
const renderLink = (commit, explicitLinks) => {
let link;
const { owner, repo } = commit;
const keyIt = commit.prKeys.values().next();
if (keyIt.done) /* no PRs */ {
const { hash } = commit;
const url = `https://github.com/${owner}/${repo}/commit/${hash}`;
const text = owner === 'electron' && repo === 'electron'
? `${hash.slice(0, 8)}`
: `${owner}/${repo}@${hash.slice(0, 8)}`;
link = explicitLinks ? `[${text}](${url})` : text;
} else {
const { number } = keyIt.value;
const url = `https://github.com/${owner}/${repo}/pull/${number}`;
const text = owner === 'electron' && repo === 'electron'
? `#${number}`
: `${owner}/${repo}#${number}`;
link = explicitLinks ? `[${text}](${url})` : text;
}
return link;
};
// @return the pull request's GitHub URL
const buildPullURL = ghKey => `https://github.com/${ghKey.owner}/${ghKey.repo}/pull/${ghKey.number}`;
const renderCommit = (commit, explicitLinks) => {
// clean up the note
let note = commit.note || commit.subject;
const renderPull = ghKey => `[#${ghKey.number}](${buildPullURL(ghKey)})`;
// @return the commit's GitHub URL
const buildCommitURL = commit => `https://github.com/${commit.owner}/${commit.repo}/commit/${commit.hash}`;
const renderCommit = commit => `[${commit.hash.slice(0, 8)}](${buildCommitURL(commit)})`;
// @return a markdown link to the PR if available; otherwise, the git commit
function renderLink (commit) {
const maybePull = commit.prKeys.values().next();
return maybePull.value ? renderPull(maybePull.value) : renderCommit(commit);
}
// @return a terser branch name,
// e.g. '7-2-x' -> '7.2' and '8-x-y' -> '8'
const renderBranchName = name => name.replace(/-[a-zA-Z]/g, '').replace('-', '.');
const renderTrop = (branch, ghKey) => `[${renderBranchName(branch)}](${buildPullURL(ghKey)})`;
// @return markdown-formatted links to other branches' trops,
// e.g. "(Also in 7.2, 8, 9)"
function renderTrops (commit, excludeBranch) {
const body = [...commit.trops.entries()]
.filter(([branch]) => branch !== excludeBranch)
.sort(([branchA], [branchB]) => parseInt(branchA) - parseInt(branchB)) // sort by semver major
.map(([branch, key]) => renderTrop(branch, key))
.join(', ');
return body ? `<span style="font-size:small;">(Also in ${body})</span>` : body;
}
// @return a slightly cleaned-up human-readable change description
function renderDescription (commit) {
let note = commit.note || commit.subject || '';
note = note.trim();
// release notes bullet point every change, so if the note author
// manually started the content with a bullet point, that will confuse
// the markdown renderer -- remove the redundant bullet point
// (example: https://github.com/electron/electron/pull/25216)
if (note.startsWith('*')) {
note = note.slice(1).trim();
}
if (note.length !== 0) {
note = note[0].toUpperCase() + note.substr(1);
@@ -590,30 +584,24 @@ const renderCommit = (commit, explicitLinks) => {
}
}
const link = renderLink(commit, explicitLinks);
return note;
}
return { note, link };
};
// @return markdown-formatted release note line item,
// e.g. '* Fixed a foo. #12345 (Also in 7.2, 8, 9)'
const renderNote = (commit, excludeBranch) =>
`* ${renderDescription(commit)} ${renderLink(commit)} ${renderTrops(commit, excludeBranch)}\n`;
const renderNotes = (notes, explicitLinks) => {
const renderNotes = (notes) => {
const rendered = [`# Release Notes for ${notes.name}\n\n`];
const renderSection = (title, commits) => {
if (commits.length === 0) {
return;
if (commits.length > 0) {
rendered.push(
`## ${title}\n\n`,
...(commits.map(commit => renderNote(commit, notes.toBranch)).sort())
);
}
const notes = new Map();
for (const note of commits.map(commit => renderCommit(commit, explicitLinks))) {
if (!notes.has(note.note)) {
notes.set(note.note, [note.link]);
} else {
notes.get(note.note).push(note.link);
}
}
rendered.push(`## ${title}\n\n`);
const lines = [];
notes.forEach((links, key) => lines.push(` * ${key} ${links.map(link => link.toString()).sort().join(', ')}\n`));
rendered.push(...lines.sort(), '\n');
};
renderSection('Breaking Changes', notes.breaking);
@@ -622,7 +610,7 @@ const renderNotes = (notes, explicitLinks) => {
renderSection('Other Changes', notes.other);
if (notes.docs.length) {
const docs = notes.docs.map(commit => renderLink(commit, explicitLinks)).sort();
const docs = notes.docs.map(commit => renderLink(commit)).sort();
rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n');
}

View File

@@ -211,4 +211,8 @@ async function prepareRelease (isBeta, notesOnly) {
}
}
prepareRelease(!args.stable, args.notesOnly);
prepareRelease(!args.stable, args.notesOnly)
.catch((err) => {
console.error(err);
process.exit(1);
});

View File

@@ -1,8 +1,15 @@
#!/usr/bin/env python
import os
import glob
import os
import shutil
import subprocess
import sys
import tempfile
def is_fs_case_sensitive():
with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
return(not os.path.exists(tmp_file.name.lower()))
sys.path.append(
os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "/../.."))
@@ -22,15 +29,26 @@ PDB_LIST = [
os.path.join(RELEASE_DIR, '{0}.exe.pdb'.format(PROJECT_NAME))
]
NPX_CMD = "npx"
if sys.platform == "win32":
NPX_CMD += ".cmd"
def main():
os.chdir(ELECTRON_DIR)
files = []
if PLATFORM == 'win32':
for pdb in PDB_LIST:
run_symstore(pdb, SYMBOLS_DIR, PRODUCT_NAME)
files = glob.glob(SYMBOLS_DIR + '/*.pdb/*/*.pdb')
else:
files = glob.glob(SYMBOLS_DIR + '/*/*/*.sym') + glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip')
files += glob.glob(SYMBOLS_DIR + '/*/*/*.sym')
for symbol_file in files:
print("Generating Sentry src bundle for: " + symbol_file)
subprocess.check_output([NPX_CMD, '@sentry/cli@1.51.1', 'difutil', 'bundle-sources', symbol_file])
files += glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip')
# The file upload needs to be atom-shell/symbols/:symbol_name/:hash/:symbol
os.chdir(SYMBOLS_DIR)
@@ -38,7 +56,19 @@ def main():
# The symbol server needs lowercase paths, it will fail otherwise
# So lowercase all the file paths here
if is_fs_case_sensitive():
for f in files:
lower_f = f.lower()
if lower_f != f:
if os.path.exists(lower_f):
shutil.rmtree(lower_f)
lower_dir = os.path.dirname(lower_f)
if not os.path.exists(lower_dir):
os.makedirs(lower_dir)
shutil.copy2(f, lower_f)
files = [f.lower() for f in files]
for f in files:
assert os.path.exists(f)
bucket, access_key, secret_key = s3_config()
upload_symbols(bucket, access_key, secret_key, files)

View File

@@ -1,10 +1,10 @@
param([string]$gomaDir=$PWD)
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
if ($timedOut) {
write-error 'Timed out waiting for goma to start'; exit 1;
} else {
Write-Output "Successfully started goma!"
}
param([string]$gomaDir=$PWD)
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
if ($timedOut) {
write-error 'Timed out waiting for goma to start'; exit 1;
} else {
Write-Output "Successfully started goma!"
}

View File

@@ -11,6 +11,7 @@
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_split.h"
@@ -147,7 +148,14 @@ bool ElectronCrashReporterClient::GetCrashDumpLocation(
#else
bool ElectronCrashReporterClient::GetCrashDumpLocation(
base::FilePath* crash_dir) {
return base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
bool result = base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
if (result && !base::PathExists(*crash_dir)) {
return base::CreateDirectory(*crash_dir);
}
}
return result;
}
#endif
@@ -159,7 +167,7 @@ bool ElectronCrashReporterClient::GetCrashMetricsLocation(
#endif // OS_MACOSX || OS_LINUX
bool ElectronCrashReporterClient::IsRunningUnattended() {
return false;
return !collect_stats_consent_;
}
bool ElectronCrashReporterClient::GetCollectStatsConsent() {

View File

@@ -233,7 +233,7 @@ int NodeMain(int argc, char* argv[]) {
node::ResetStdio();
env->set_can_call_into_js(false);
node::Stop(env);
env->stop_sub_worker_contexts();
env->RunCleanup();

View File

@@ -1094,12 +1094,12 @@ Browser::LoginItemSettings App::GetLoginItemSettings(
#if defined(USE_NSS_CERTS)
void App::ImportCertificate(const base::DictionaryValue& options,
net::CompletionRepeatingCallback callback) {
auto browser_context = ElectronBrowserContext::From("", false);
auto* browser_context = ElectronBrowserContext::From("", false);
if (!certificate_manager_model_) {
auto copy = base::DictionaryValue::From(
base::Value::ToUniquePtrValue(options.Clone()));
CertificateManagerModel::Create(
browser_context.get(),
browser_context,
base::BindRepeating(&App::OnCertificateManagerModelCreated,
base::Unretained(this), base::Passed(&copy),
callback));

View File

@@ -159,12 +159,32 @@ void BrowserWindow::DidFirstVisuallyNonEmptyPaint() {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(
[](base::WeakPtr<BrowserWindow> self) {
if (self)
if (self && !self->did_ready_to_show_fired_) {
self->did_ready_to_show_fired_ = true;
self->Emit("ready-to-show");
}
},
GetWeakPtr()));
}
void BrowserWindow::DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) {
// The DidFirstVisuallyNonEmptyPaint event is not very stable that, sometimes
// on some machines it might not be fired, and the actual behavior depends on
// the version of Chromium.
// To work around this bug, we ensure the ready-to-show event is emitted if it
// has not been emitted in did-finish-load event.
// Note that we use did-finish-load event instead of dom-ready event because
// the latter may actually be emitted before the ready-to-show event.
// See also https://github.com/electron/electron/issues/7779.
if (window()->IsVisible() || did_ready_to_show_fired_)
return;
if (render_frame_host->GetParent()) // child frame
return;
did_ready_to_show_fired_ = true;
Emit("ready-to-show");
}
void BrowserWindow::BeforeUnloadDialogCancelled() {
WindowList::WindowCloseCancelled(window());
// Cancel unresponsive event when window close is cancelled.
@@ -288,11 +308,11 @@ void BrowserWindow::OnWindowResize() {
}
void BrowserWindow::OnWindowLeaveFullScreen() {
TopLevelWindow::OnWindowLeaveFullScreen();
#if defined(OS_MACOSX)
if (web_contents()->IsFullscreenForCurrentTab())
web_contents()->ExitFullscreen(true);
#endif
TopLevelWindow::OnWindowLeaveFullScreen();
}
void BrowserWindow::Focus() {

View File

@@ -49,6 +49,8 @@ class BrowserWindow : public TopLevelWindow,
content::RenderViewHost* new_host) override;
void RenderViewCreated(content::RenderViewHost* render_view_host) override;
void DidFirstVisuallyNonEmptyPaint() override;
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override;
void BeforeUnloadDialogCancelled() override;
void OnRendererUnresponsive(content::RenderProcessHost*) override;
@@ -114,6 +116,8 @@ class BrowserWindow : public TopLevelWindow,
// it should be cancelled when we can prove that the window is responsive.
base::CancelableClosure window_unresponsive_closure_;
bool did_ready_to_show_fired_ = false;
#if defined(OS_MACOSX)
std::vector<mojom::DraggableRegionPtr> draggable_regions_;
#endif

View File

@@ -188,8 +188,8 @@ v8::Local<v8::Promise> Cookies::Get(const gin_helper::Dictionary& filter) {
gin_helper::Promise<net::CookieList> promise(isolate());
v8::Local<v8::Promise> handle = promise.GetHandle();
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get());
auto* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
base::DictionaryValue dict;
@@ -224,8 +224,8 @@ v8::Local<v8::Promise> Cookies::Remove(const GURL& url,
cookie_deletion_filter->url = url;
cookie_deletion_filter->cookie_name = name;
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get());
auto* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
manager->DeleteCookies(
@@ -280,8 +280,8 @@ v8::Local<v8::Promise> Cookies::Set(base::DictionaryValue details) {
options.set_include_httponly();
}
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get());
auto* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
manager->SetCanonicalCookie(
*canonical_cookie, url.scheme(), options,
@@ -303,8 +303,8 @@ v8::Local<v8::Promise> Cookies::FlushStore() {
gin_helper::Promise<void> promise(isolate());
v8::Local<v8::Promise> handle = promise.GetHandle();
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get());
auto* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
manager->FlushCookieStore(base::BindOnce(

View File

@@ -58,7 +58,7 @@ class Cookies : public gin_helper::TrackableObject<Cookies> {
std::unique_ptr<base::CallbackList<void(
const net::CookieChangeInfo& change)>::Subscription>
cookie_change_subscription_;
scoped_refptr<ElectronBrowserContext> browser_context_;
ElectronBrowserContext* browser_context_;
DISALLOW_COPY_AND_ASSIGN(Cookies);
};

View File

@@ -40,7 +40,11 @@
#endif
#if defined(OS_LINUX)
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/guid.h"
#include "components/crash/core/app/breakpad_linux.h"
#include "components/crash/core/common/crash_keys.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#include "v8/include/v8.h"
#endif
@@ -81,6 +85,46 @@ bool IsCrashReporterEnabled() {
const std::map<std::string, std::string>& GetGlobalCrashKeys() {
return GetGlobalCrashKeysMutable();
}
bool GetClientIdPath(base::FilePath* path) {
if (base::PathService::Get(electron::DIR_CRASH_DUMPS, path)) {
*path = path->Append("client_id");
return true;
}
return false;
}
std::string ReadClientId() {
base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string client_id;
// "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".length == 36
base::FilePath client_id_path;
if (GetClientIdPath(&client_id_path) &&
(!base::ReadFileToStringWithMaxSize(client_id_path, &client_id, 36) ||
client_id.size() != 36))
return std::string();
return client_id;
}
void WriteClientId(const std::string& client_id) {
DCHECK_EQ(client_id.size(), 36u);
base::ThreadRestrictions::ScopedAllowIO allow_io;
base::FilePath client_id_path;
if (GetClientIdPath(&client_id_path))
base::WriteFile(client_id_path, client_id.data(), client_id.size());
}
std::string GetClientId() {
static base::NoDestructor<std::string> client_id;
if (!client_id->empty())
return *client_id;
*client_id = ReadClientId();
if (client_id->empty()) {
*client_id = base::GenerateGUID();
WriteClientId(*client_id);
}
return *client_id;
}
#endif
void Start(const std::string& submit_url,
@@ -107,6 +151,7 @@ void Start(const std::string& submit_url,
? "node"
: command_line->GetSwitchValueASCII(::switches::kProcessType);
#if defined(OS_LINUX)
::crash_keys::SetMetricsClientIdFromGUID(GetClientId());
auto& global_crash_keys = GetGlobalCrashKeysMutable();
for (const auto& pair : global_extra) {
global_crash_keys[pair.first] = pair.second;

View File

@@ -19,6 +19,7 @@ bool IsCrashReporterEnabled();
#if defined(OS_LINUX)
const std::map<std::string, std::string>& GetGlobalCrashKeys();
std::string GetClientId();
#endif
// JS bindings API; exposed publicly because it's also called from node_main.cc

View File

@@ -60,6 +60,9 @@ struct Converter<in_app_purchase::Product> {
dict.Set("price", val.price);
dict.Set("formattedPrice", val.formattedPrice);
// Currency Information
dict.Set("currencyCode", val.currencyCode);
// Downloadable Content Information
dict.Set("isDownloadable", val.isDownloadable);

View File

@@ -24,29 +24,27 @@
if ((self = [super init])) {
NSDistributedNotificationCenter* distCenter =
[NSDistributedNotificationCenter defaultCenter];
// A notification that the screen was locked.
[distCenter addObserver:self
selector:@selector(onScreenLocked:)
name:@"com.apple.screenIsLocked"
object:nil];
// A notification that the screen was unlocked by the user.
[distCenter addObserver:self
selector:@selector(onScreenUnlocked:)
name:@"com.apple.screenIsUnlocked"
object:nil];
// A notification that the workspace posts before the machine goes to sleep.
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(isSuspending:)
name:NSWorkspaceWillSleepNotification
object:nil];
[distCenter addObserver:self
selector:@selector(isSuspending:)
name:NSWorkspaceWillSleepNotification
object:nil];
// A notification that the workspace posts when the machine wakes from
// sleep.
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(isResuming:)
name:NSWorkspaceDidWakeNotification
object:nil];
[distCenter addObserver:self
selector:@selector(isResuming:)
name:NSWorkspaceDidWakeNotification
object:nil];
}
return self;
}

View File

@@ -39,6 +39,18 @@ void PowerMonitor::InitPlatformSpecificMonitors() {
// Tel windows we want to be notified with session events
WTSRegisterSessionNotification(window_, NOTIFY_FOR_THIS_SESSION);
// For Windows 8 and later, a new "connected standy" mode has been added and
// we must explicitly register for its notifications.
auto RegisterSuspendResumeNotification =
reinterpret_cast<decltype(&::RegisterSuspendResumeNotification)>(
GetProcAddress(GetModuleHandle(L"user32.dll"),
"RegisterSuspendResumeNotification"));
if (RegisterSuspendResumeNotification) {
RegisterSuspendResumeNotification(static_cast<HANDLE>(window_),
DEVICE_NOTIFY_WINDOW_HANDLE);
}
}
LRESULT CALLBACK PowerMonitor::WndProcStatic(HWND hwnd,

View File

@@ -284,7 +284,7 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
SpellcheckService* service =
SpellcheckServiceFactory::GetForContext(browser_context_.get());
SpellcheckServiceFactory::GetForContext(browser_context_);
if (service) {
service->SetHunspellObserver(this);
}
@@ -297,7 +297,7 @@ Session::~Session() {
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
SpellcheckService* service =
SpellcheckServiceFactory::GetForContext(browser_context_.get());
SpellcheckServiceFactory::GetForContext(browser_context_);
if (service) {
service->SetHunspellObserver(nullptr);
}
@@ -366,7 +366,7 @@ v8::Local<v8::Promise> Session::GetCacheSize() {
gin_helper::Promise<int64_t> promise(isolate);
auto handle = promise.GetHandle();
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext()
->ComputeHttpCacheSize(
base::Time(), base::Time::Max(),
@@ -390,7 +390,7 @@ v8::Local<v8::Promise> Session::ClearCache() {
gin_helper::Promise<void> promise(isolate);
auto handle = promise.GetHandle();
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext()
->ClearHttpCache(base::Time(), base::Time::Max(), nullptr,
base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
@@ -486,17 +486,17 @@ void Session::EnableNetworkEmulation(const gin_helper::Dictionary& options) {
conditions->latency = base::TimeDelta::FromMillisecondsD(latency);
}
auto* network_context = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get())
->GetNetworkContext();
auto* network_context =
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext();
network_context->SetNetworkConditions(network_emulation_token_,
std::move(conditions));
}
void Session::DisableNetworkEmulation() {
auto* network_context = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get())
->GetNetworkContext();
auto* network_context =
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext();
network_context->SetNetworkConditions(
network_emulation_token_, network::mojom::NetworkConditions::New());
}
@@ -516,7 +516,7 @@ void Session::SetCertVerifyProc(v8::Local<v8::Value> val,
std::make_unique<CertVerifierClient>(proc),
cert_verifier_client_remote.InitWithNewPipeAndPassReceiver());
}
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext()
->SetCertVerifierClient(std::move(cert_verifier_client_remote));
@@ -569,7 +569,7 @@ v8::Local<v8::Promise> Session::ClearHostResolverCache(
gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext()
->ClearHostCache(nullptr,
base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
@@ -583,7 +583,7 @@ v8::Local<v8::Promise> Session::ClearAuthCache() {
gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext()
->ClearHttpAuthCache(
base::Time(),
@@ -609,9 +609,9 @@ void Session::AllowNTLMCredentialsForDomains(const std::string& domains) {
void Session::SetUserAgent(const std::string& user_agent,
gin_helper::Arguments* args) {
browser_context_->SetUserAgent(user_agent);
auto* network_context = content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get())
->GetNetworkContext();
auto* network_context =
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext();
network_context->SetUserAgent(user_agent);
std::string accept_lang;
@@ -790,10 +790,9 @@ v8::Local<v8::Value> Session::NetLog(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, net_log_);
}
static void StartPreconnectOnUI(
scoped_refptr<ElectronBrowserContext> browser_context,
const GURL& url,
int num_sockets_to_preconnect) {
static void StartPreconnectOnUI(ElectronBrowserContext* browser_context,
const GURL& url,
int num_sockets_to_preconnect) {
std::vector<predictors::PreconnectRequest> requests = {
{url::Origin::Create(url), num_sockets_to_preconnect,
net::NetworkIsolationKey()}};
@@ -823,7 +822,7 @@ void Session::Preconnect(const gin_helper::Dictionary& options,
DCHECK_GT(num_sockets_to_preconnect, 0);
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&StartPreconnectOnUI, base::RetainedRef(browser_context_),
base::BindOnce(&StartPreconnectOnUI, base::Unretained(browser_context_),
url, num_sockets_to_preconnect));
}
@@ -867,7 +866,7 @@ v8::Local<v8::Promise> Session::ListWordsInSpellCheckerDictionary() {
v8::Local<v8::Promise> handle = promise.GetHandle();
SpellcheckService* spellcheck =
SpellcheckServiceFactory::GetForContext(browser_context_.get());
SpellcheckServiceFactory::GetForContext(browser_context_);
if (!spellcheck)
promise.RejectWithErrorMessage(
@@ -893,7 +892,7 @@ bool Session::AddWordToSpellCheckerDictionary(const std::string& word) {
return false;
SpellcheckService* spellcheck =
SpellcheckServiceFactory::GetForContext(browser_context_.get());
SpellcheckServiceFactory::GetForContext(browser_context_);
if (!spellcheck)
return false;
@@ -915,7 +914,7 @@ bool Session::RemoveWordFromSpellCheckerDictionary(const std::string& word) {
return false;
SpellcheckService* service =
SpellcheckServiceFactory::GetForContext(browser_context_.get());
SpellcheckServiceFactory::GetForContext(browser_context_);
if (!service)
return false;
@@ -952,7 +951,7 @@ gin::Handle<Session> Session::CreateFrom(
gin::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
const std::string& partition,
base::DictionaryValue options) {
scoped_refptr<ElectronBrowserContext> browser_context;
ElectronBrowserContext* browser_context;
if (partition.empty()) {
browser_context =
ElectronBrowserContext::From("", false, std::move(options));
@@ -965,7 +964,7 @@ gin::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
browser_context =
ElectronBrowserContext::From(partition, true, std::move(options));
}
return CreateFrom(isolate, browser_context.get());
return CreateFrom(isolate, browser_context);
}
// static

View File

@@ -57,9 +57,7 @@ class Session : public gin_helper::TrackableObject<Session>,
const std::string& partition,
base::DictionaryValue options = base::DictionaryValue());
ElectronBrowserContext* browser_context() const {
return browser_context_.get();
}
ElectronBrowserContext* browser_context() const { return browser_context_; }
// gin_helper::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
@@ -146,7 +144,7 @@ class Session : public gin_helper::TrackableObject<Session>,
// The client id to enable the network throttler.
base::UnguessableToken network_emulation_token_;
scoped_refptr<ElectronBrowserContext> browser_context_;
ElectronBrowserContext* browser_context_;
DISALLOW_COPY_AND_ASSIGN(Session);
};

Some files were not shown because too many files have changed in this diff Show More