mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
088bc334f0 | ||
|
|
8836c814b9 | ||
|
|
5b6e117041 | ||
|
|
845716019a | ||
|
|
308470c98d | ||
|
|
9a8ebbb471 | ||
|
|
c90f41d0c4 | ||
|
|
f275211555 | ||
|
|
5b04b64397 | ||
|
|
066f5136d5 | ||
|
|
c0cc008368 | ||
|
|
b8e7bcb621 | ||
|
|
170fb689c0 | ||
|
|
15b11599a2 | ||
|
|
621cc90674 | ||
|
|
7d791b7f46 | ||
|
|
4e61bc3cfd | ||
|
|
d82832d8bc | ||
|
|
7cb6f61af0 | ||
|
|
4ab2254879 |
@@ -1 +1 @@
|
||||
12.0.11
|
||||
12.0.12
|
||||
@@ -11,9 +11,9 @@
|
||||
* `colorDepth` Number - The number of bits per pixel.
|
||||
* `depthPerComponent` Number - The number of bits per color component.
|
||||
* `displayFrequency` Number - The display refresh rate.
|
||||
* `bounds` [Rectangle](rectangle.md)
|
||||
* `bounds` [Rectangle](rectangle.md) - the bounds of the display in DIP points.
|
||||
* `size` [Size](size.md)
|
||||
* `workArea` [Rectangle](rectangle.md)
|
||||
* `workArea` [Rectangle](rectangle.md) - the work area of the display in DIP points.
|
||||
* `workAreaSize` [Size](size.md)
|
||||
* `internal` Boolean - `true` for an internal display and `false` for an external display
|
||||
|
||||
|
||||
@@ -117,15 +117,18 @@ const mainWindow = new BrowserWindow({
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url === 'about:blank') {
|
||||
return {
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
backgroundColor: 'black',
|
||||
webPreferences: {
|
||||
preload: 'my-child-window-preload-script.js'
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
backgroundColor: 'black',
|
||||
webPreferences: {
|
||||
preload: 'my-child-window-preload-script.js'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return { action: 'deny' }
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
<p>
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
Click on the title with the <pre>Command</pre> or <pre>Control</pre> key pressed.
|
||||
You should see a popup with the represented file at the top.
|
||||
</p>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,10 +4,7 @@ const os = require('os');
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 32 KiB |
0
docs/images/versioning-sketch-2.png
Executable file → Normal file
0
docs/images/versioning-sketch-2.png
Executable file → Normal file
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -120,9 +120,9 @@ file in the directory you executed it in. Both files can be analyzed using
|
||||
the Chrome Developer Tools, using the `Performance` and `Memory` tabs
|
||||
respectively.
|
||||
|
||||
![performance-cpu-prof]
|
||||
![Performance CPU Profile][performance-cpu-prof]
|
||||
|
||||
![performance-heap-prof]
|
||||
![Performance Heap Memory Profile][performance-heap-prof]
|
||||
|
||||
In this example, on the author's machine, we saw that loading `request` took
|
||||
almost half a second, whereas `node-fetch` took dramatically less memory
|
||||
|
||||
@@ -20,23 +20,40 @@ To set the represented file of window, you can use the
|
||||
|
||||
## Example
|
||||
|
||||
Starting with a working application from the
|
||||
[Quick Start Guide](quick-start.md), add the following lines to the
|
||||
`main.js` file:
|
||||
|
||||
```javascript fiddle='docs/fiddles/features/represented-file'
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const os = require('os');
|
||||
|
||||
function createWindow () {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const win = new BrowserWindow()
|
||||
|
||||
win.setRepresentedFilename('/etc/passwd')
|
||||
win.setRepresentedFilename(os.homedir())
|
||||
win.setDocumentEdited(true)
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
After launching the Electron application, click on the title with `Command` or
|
||||
`Control` key pressed. You should see a popup with the file you just defined:
|
||||
`Control` key pressed. You should see a popup with the represented file at the top.
|
||||
In this guide, this is the current user's home directory:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -490,63 +490,83 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
}
|
||||
};
|
||||
|
||||
function fsReadFileAsar (pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (pathInfo.isAsar) {
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (options === null || options === undefined) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.size === 0) {
|
||||
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { readFile } = fs;
|
||||
fs.readFile = function (pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return readFile.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (options === null || options === undefined) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
if (!pathInfo.isAsar) {
|
||||
return readFile.apply(this, arguments);
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.size === 0) {
|
||||
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
return fsReadFileAsar(pathArgument, options, callback);
|
||||
};
|
||||
|
||||
fs.promises.readFile = util.promisify(fs.readFile);
|
||||
const { readFile: readFilePromise } = fs.promises;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
fs.promises.readFile = function (pathArgument: string, options: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) {
|
||||
return readFilePromise.apply(this, arguments);
|
||||
}
|
||||
|
||||
const p = util.promisify(fsReadFileAsar);
|
||||
return p(pathArgument, options);
|
||||
};
|
||||
|
||||
const { readFileSync } = fs;
|
||||
fs.readFileSync = function (pathArgument: string, options: any) {
|
||||
|
||||
@@ -271,7 +271,7 @@ const warnAboutAllowedPopups = function () {
|
||||
|
||||
const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || isLocalhost()) return;
|
||||
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true;
|
||||
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false;
|
||||
if (!remoteModuleEnabled) return;
|
||||
|
||||
if (getIsRemoteProtocol()) {
|
||||
@@ -298,7 +298,9 @@ const logSecurityWarnings = function (
|
||||
warnAboutEnableBlinkFeatures(webPreferences);
|
||||
warnAboutInsecureCSP();
|
||||
warnAboutAllowedPopups();
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
||||
if (BUILDFLAG(ENABLE_REMOTE_MODULE)) {
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
||||
}
|
||||
};
|
||||
|
||||
const getWebPreferences = async function () {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "12.0.11",
|
||||
"version": "12.0.12",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -130,3 +130,6 @@ media_feeds_disable_media_feeds_and_related_features.patch
|
||||
remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch
|
||||
autofill_fixed_refill_of_changed_form.patch
|
||||
x11_fix_window_enumeration_order_when_wm_doesn_t_set.patch
|
||||
cherry-pick-34d5af37f9ac.patch
|
||||
m90-lts_longtaskdetector_remove_container_mutation_during.patch
|
||||
m90-lts_reduce_memory_consumption_on.patch
|
||||
|
||||
101
patches/chromium/cherry-pick-34d5af37f9ac.patch
Normal file
101
patches/chromium/cherry-pick-34d5af37f9ac.patch
Normal file
@@ -0,0 +1,101 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Asami Doi <asamidoi@chromium.org>
|
||||
Date: Thu, 10 Jun 2021 07:03:17 +0000
|
||||
Subject: BFCache: remove a controllee stored in `bfcached_controllee_map_`
|
||||
|
||||
This CL fixes the UAF that happens with the following case:
|
||||
Let's assume we have 2 service workers (sw1.js and sw2.js) are
|
||||
registered in the same page. When the second service worker (sw2.js) is
|
||||
registered, ServiceWorkerContainerHost::UpdateController() is called
|
||||
and the previous SWVersion (sw1.js) removes a controllee from
|
||||
`controllee_map_`. If BackForwardCache is enabled, a controllee is
|
||||
stored in `bfcached_controllee_map_` instead and the controllee will
|
||||
not be removed in ServiceWorkerContainerHost::UpdateController().
|
||||
When ServiceWorkerContainerHost::UpdateController() is called and
|
||||
keep a controllee in `bfcached_controllee_map_`, and a page navigates to
|
||||
a different page (evicts BFCache), use-after-free (UAF) happens.
|
||||
|
||||
This CL updates ServiceWorkerContainerHost::UpdateController()
|
||||
to remove a controllee from `bfcached_controllee_map_` if it exists.
|
||||
|
||||
(cherry picked from commit a2414a05a486ca0ad18ba4caf78e883a668a0555)
|
||||
|
||||
(cherry picked from commit 7cd7f6741fc4491c2f7ef21052a370ee23887e37)
|
||||
|
||||
Bug: 1212618
|
||||
Change-Id: I13e023e6d273268a08ea9276a056f7f5acba39cd
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2919020
|
||||
Commit-Queue: Asami Doi <asamidoi@chromium.org>
|
||||
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
|
||||
Cr-Original-Original-Commit-Position: refs/heads/master@{#887109}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2929401
|
||||
Reviewed-by: Krishna Govind <govind@chromium.org>
|
||||
Reviewed-by: Ben Mason <benmason@chromium.org>
|
||||
Reviewed-by: Prudhvi Kumar Bommana <pbommana@google.com>
|
||||
Commit-Queue: Krishna Govind <govind@chromium.org>
|
||||
Owners-Override: Krishna Govind <govind@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/branch-heads/4472@{#1375}
|
||||
Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2944946
|
||||
Owners-Override: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Reviewed-by: Achuith Bhandarkar <achuith@chromium.org>
|
||||
Commit-Queue: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/4430@{#1512}
|
||||
Cr-Branched-From: e5ce7dc4f7518237b3d9bb93cccca35d25216cbe-refs/heads/master@{#857950}
|
||||
|
||||
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
|
||||
index 0b1981efd9eb008caea1c94da138a2ed7c386bc5..9a2193ac5a4e8a738bef8e391145deecef9b9ac2 100644
|
||||
--- a/content/browser/service_worker/service_worker_container_host.cc
|
||||
+++ b/content/browser/service_worker/service_worker_container_host.cc
|
||||
@@ -138,7 +138,7 @@ ServiceWorkerContainerHost::~ServiceWorkerContainerHost() {
|
||||
}
|
||||
|
||||
if (IsContainerForClient() && controller_)
|
||||
- controller_->OnControlleeDestroyed(client_uuid());
|
||||
+ controller_->Uncontrol(client_uuid());
|
||||
|
||||
// Remove |this| as an observer of ServiceWorkerRegistrations.
|
||||
// TODO(falken): Use ScopedObserver instead of this explicit call.
|
||||
@@ -1244,7 +1244,7 @@ void ServiceWorkerContainerHost::UpdateController(
|
||||
}
|
||||
}
|
||||
if (previous_version)
|
||||
- previous_version->RemoveControllee(client_uuid());
|
||||
+ previous_version->Uncontrol(client_uuid());
|
||||
|
||||
// SetController message should be sent only for clients.
|
||||
DCHECK(IsContainerForClient());
|
||||
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
|
||||
index 798d0bd350976cf2724e4d4b726c24d63e0a5ec3..5e268a5331e80ff7c6c93c425171bbd31ddb342a 100644
|
||||
--- a/content/browser/service_worker/service_worker_version.cc
|
||||
+++ b/content/browser/service_worker/service_worker_version.cc
|
||||
@@ -886,8 +886,7 @@ void ServiceWorkerVersion::RemoveControlleeFromBackForwardCacheMap(
|
||||
bfcached_controllee_map_.erase(client_uuid);
|
||||
}
|
||||
|
||||
-void ServiceWorkerVersion::OnControlleeDestroyed(
|
||||
- const std::string& client_uuid) {
|
||||
+void ServiceWorkerVersion::Uncontrol(const std::string& client_uuid) {
|
||||
if (!IsBackForwardCacheEnabled()) {
|
||||
RemoveControllee(client_uuid);
|
||||
} else {
|
||||
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
|
||||
index cb5afe636f45b70fce2ff313641834bf7bddff30..a96d2c4575fa3879cf6133fbe656cb20745bd66a 100644
|
||||
--- a/content/browser/service_worker/service_worker_version.h
|
||||
+++ b/content/browser/service_worker/service_worker_version.h
|
||||
@@ -394,9 +394,12 @@ class CONTENT_EXPORT ServiceWorkerVersion
|
||||
void RestoreControlleeFromBackForwardCacheMap(const std::string& client_uuid);
|
||||
// Called when a back-forward cached controllee is evicted or destroyed.
|
||||
void RemoveControlleeFromBackForwardCacheMap(const std::string& client_uuid);
|
||||
- // Called when a controllee is destroyed. Remove controllee from whichever
|
||||
- // map it belongs to, or do nothing when it is already removed.
|
||||
- void OnControlleeDestroyed(const std::string& client_uuid);
|
||||
+ // Called when this version should no longer be the controller of this client.
|
||||
+ // Called when the controllee is destroyed or it changes controller. Removes
|
||||
+ // controllee from whichever map it belongs to, or do nothing when it is
|
||||
+ // already removed. This function is different from RemoveController(), which
|
||||
+ // can only be called if the controllee is not in the back-forward cache map.
|
||||
+ void Uncontrol(const std::string& client_uuid);
|
||||
|
||||
// Returns true if this version has a controllee.
|
||||
// Note regarding BackForwardCache:
|
||||
@@ -0,0 +1,99 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yutaka Hirano <yhirano@chromium.org>
|
||||
Date: Fri, 11 Jun 2021 08:05:05 +0000
|
||||
Subject: Remove container mutation during iteration
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
On LongTaskDetector, we call OnLongTaskDetected for all registered
|
||||
observers. Some observers call LongTaskDetector::UnregisterObserver
|
||||
in the callback, which is problematic because container mutation is
|
||||
not allowed during iteration.
|
||||
|
||||
Copy the observer set to avoid the violation.
|
||||
|
||||
(cherry picked from commit 702f4d4ddb963cafb0d133972282dfc803510b75)
|
||||
|
||||
(cherry picked from commit e88c656a9fb4a7bb1c66ddcedae8049a448ebef4)
|
||||
|
||||
Bug: 1210487
|
||||
Change-Id: Iccea748ac144def6884be8cf542cdc3572bed81a
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2909934
|
||||
Reviewed-by: Deep Roy <dproy@chromium.org>
|
||||
Reviewed-by: Nicolás Peña Moreno <npm@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Original-Original-Commit-Position: refs/heads/master@{#885033}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2939704
|
||||
Auto-Submit: Yutaka Hirano <yhirano@chromium.org>
|
||||
Owners-Override: Prudhvi Kumar Bommana <pbommana@google.com>
|
||||
Reviewed-by: Prudhvi Kumar Bommana <pbommana@google.com>
|
||||
Cr-Original-Commit-Position: refs/branch-heads/4472@{#1443}
|
||||
Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2945126
|
||||
Owners-Override: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Reviewed-by: Artem Sumaneev <asumaneev@google.com>
|
||||
Commit-Queue: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/4430@{#1518}
|
||||
Cr-Branched-From: e5ce7dc4f7518237b3d9bb93cccca35d25216cbe-refs/heads/master@{#857950}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/loader/long_task_detector.cc b/third_party/blink/renderer/core/loader/long_task_detector.cc
|
||||
index 7e1499b1ddde30db2344db6fd9a9d3e7be574033..f040ae5053265fb136c629f106aeefa0b01130f1 100644
|
||||
--- a/third_party/blink/renderer/core/loader/long_task_detector.cc
|
||||
+++ b/third_party/blink/renderer/core/loader/long_task_detector.cc
|
||||
@@ -43,7 +43,10 @@ void LongTaskDetector::DidProcessTask(base::TimeTicks start_time,
|
||||
if ((end_time - start_time) < LongTaskDetector::kLongTaskThreshold)
|
||||
return;
|
||||
|
||||
- for (auto& observer : observers_) {
|
||||
+ // We copy `observers_` because it might be mutated in OnLongTaskDetected,
|
||||
+ // and container mutation is not allowed during iteration.
|
||||
+ const HeapHashSet<Member<LongTaskObserver>> observers = observers_;
|
||||
+ for (auto& observer : observers) {
|
||||
observer->OnLongTaskDetected(start_time, end_time);
|
||||
}
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/core/loader/long_task_detector_test.cc b/third_party/blink/renderer/core/loader/long_task_detector_test.cc
|
||||
index 3384fa8ebfb0bd3ad1c408390db3fcb26edc4118..04959d3b682ddbf40577adc5799fe57a9ae9d500 100644
|
||||
--- a/third_party/blink/renderer/core/loader/long_task_detector_test.cc
|
||||
+++ b/third_party/blink/renderer/core/loader/long_task_detector_test.cc
|
||||
@@ -27,9 +27,24 @@ class TestLongTaskObserver :
|
||||
last_long_task_start = start_time;
|
||||
last_long_task_end = end_time;
|
||||
}
|
||||
-}; // Anonymous namespace
|
||||
+};
|
||||
+
|
||||
+class SelfUnregisteringObserver
|
||||
+ : public GarbageCollected<SelfUnregisteringObserver>,
|
||||
+ public LongTaskObserver {
|
||||
+ public:
|
||||
+ void OnLongTaskDetected(base::TimeTicks, base::TimeTicks) override {
|
||||
+ called_ = true;
|
||||
+ LongTaskDetector::Instance().UnregisterObserver(this);
|
||||
+ }
|
||||
+ bool IsCalled() const { return called_; }
|
||||
+
|
||||
+ private:
|
||||
+ bool called_ = false;
|
||||
+};
|
||||
|
||||
} // namespace
|
||||
+
|
||||
class LongTaskDetectorTest : public testing::Test {
|
||||
public:
|
||||
// Public because it's executed on a task queue.
|
||||
@@ -126,4 +141,13 @@ TEST_F(LongTaskDetectorTest, RegisterSameObserverTwice) {
|
||||
long_task_end_when_registered);
|
||||
}
|
||||
|
||||
+TEST_F(LongTaskDetectorTest, SelfUnregisteringObserver) {
|
||||
+ auto* observer = MakeGarbageCollected<SelfUnregisteringObserver>();
|
||||
+
|
||||
+ LongTaskDetector::Instance().RegisterObserver(observer);
|
||||
+ SimulateTask(LongTaskDetector::kLongTaskThreshold +
|
||||
+ base::TimeDelta::FromMilliseconds(10));
|
||||
+ EXPECT_TRUE(observer->IsCalled());
|
||||
+}
|
||||
+
|
||||
} // namespace blink
|
||||
118
patches/chromium/m90-lts_reduce_memory_consumption_on.patch
Normal file
118
patches/chromium/m90-lts_reduce_memory_consumption_on.patch
Normal file
@@ -0,0 +1,118 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yutaka Hirano <yhirano@chromium.org>
|
||||
Date: Fri, 11 Jun 2021 08:42:15 +0000
|
||||
Subject: Reduce memory consumption on LongTaskObserver::DidProcessTask
|
||||
|
||||
https://crrev.com/c/2909934 fixed a security issue, but it introduced a
|
||||
copy operation for each DidProcessTask for a long task. We see a memory
|
||||
regression on the change, and this is an attempt to mitigate the
|
||||
regression.
|
||||
|
||||
(cherry picked from commit 8097e73295a88e64d8318d982847a5e4f2bcc4d2)
|
||||
|
||||
(cherry picked from commit 7be6a34fe2f01af881bb074bc616bf5b6b5f7c31)
|
||||
|
||||
Bug: 1210487, 1211539
|
||||
Change-Id: Ib9101e29d70fadb11b7967754e847bb5cc754feb
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2915153
|
||||
Reviewed-by: Benoit L <lizeb@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Original-Original-Commit-Position: refs/heads/master@{#886221}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2944320
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Original-Commit-Position: refs/branch-heads/4472@{#1460}
|
||||
Cr-Original-Branched-From: 3d60439cfb36485e76a1c5bb7f513d3721b20da1-refs/heads/master@{#870763}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2948750
|
||||
Owners-Override: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Reviewed-by: Artem Sumaneev <asumaneev@google.com>
|
||||
Commit-Queue: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/4430@{#1520}
|
||||
Cr-Branched-From: e5ce7dc4f7518237b3d9bb93cccca35d25216cbe-refs/heads/master@{#857950}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/loader/long_task_detector.cc b/third_party/blink/renderer/core/loader/long_task_detector.cc
|
||||
index f040ae5053265fb136c629f106aeefa0b01130f1..3816779cfafaef06295734b4a8a2f033bf752691 100644
|
||||
--- a/third_party/blink/renderer/core/loader/long_task_detector.cc
|
||||
+++ b/third_party/blink/renderer/core/loader/long_task_detector.cc
|
||||
@@ -24,6 +24,7 @@ LongTaskDetector::LongTaskDetector() = default;
|
||||
void LongTaskDetector::RegisterObserver(LongTaskObserver* observer) {
|
||||
DCHECK(IsMainThread());
|
||||
DCHECK(observer);
|
||||
+ DCHECK(!iterating_);
|
||||
if (observers_.insert(observer).is_new_entry && observers_.size() == 1) {
|
||||
// Number of observers just became non-zero.
|
||||
Thread::Current()->AddTaskTimeObserver(this);
|
||||
@@ -32,6 +33,10 @@ void LongTaskDetector::RegisterObserver(LongTaskObserver* observer) {
|
||||
|
||||
void LongTaskDetector::UnregisterObserver(LongTaskObserver* observer) {
|
||||
DCHECK(IsMainThread());
|
||||
+ if (iterating_) {
|
||||
+ observers_to_be_removed_.push_back(observer);
|
||||
+ return;
|
||||
+ }
|
||||
observers_.erase(observer);
|
||||
if (observers_.size() == 0) {
|
||||
Thread::Current()->RemoveTaskTimeObserver(this);
|
||||
@@ -43,16 +48,21 @@ void LongTaskDetector::DidProcessTask(base::TimeTicks start_time,
|
||||
if ((end_time - start_time) < LongTaskDetector::kLongTaskThreshold)
|
||||
return;
|
||||
|
||||
- // We copy `observers_` because it might be mutated in OnLongTaskDetected,
|
||||
- // and container mutation is not allowed during iteration.
|
||||
- const HeapHashSet<Member<LongTaskObserver>> observers = observers_;
|
||||
- for (auto& observer : observers) {
|
||||
+ iterating_ = true;
|
||||
+ for (auto& observer : observers_) {
|
||||
observer->OnLongTaskDetected(start_time, end_time);
|
||||
}
|
||||
+ iterating_ = false;
|
||||
+
|
||||
+ for (const auto& observer : observers_to_be_removed_) {
|
||||
+ UnregisterObserver(observer);
|
||||
+ }
|
||||
+ observers_to_be_removed_.clear();
|
||||
}
|
||||
|
||||
void LongTaskDetector::Trace(Visitor* visitor) const {
|
||||
visitor->Trace(observers_);
|
||||
+ visitor->Trace(observers_to_be_removed_);
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/core/loader/long_task_detector.h b/third_party/blink/renderer/core/loader/long_task_detector.h
|
||||
index dc6f0dbab5c059b83bfe4212f0126e9690ab1a7f..5fd4bb8d2abcc67dd4e47927daa260fa37bc4aca 100644
|
||||
--- a/third_party/blink/renderer/core/loader/long_task_detector.h
|
||||
+++ b/third_party/blink/renderer/core/loader/long_task_detector.h
|
||||
@@ -49,6 +49,8 @@ class CORE_EXPORT LongTaskDetector final
|
||||
base::TimeTicks end_time) override;
|
||||
|
||||
HeapHashSet<Member<LongTaskObserver>> observers_;
|
||||
+ HeapVector<Member<LongTaskObserver>> observers_to_be_removed_;
|
||||
+ bool iterating_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LongTaskDetector);
|
||||
};
|
||||
diff --git a/third_party/blink/renderer/core/loader/long_task_detector_test.cc b/third_party/blink/renderer/core/loader/long_task_detector_test.cc
|
||||
index 04959d3b682ddbf40577adc5799fe57a9ae9d500..403ba452362a3fa2a6b24f238bad35d9eb1bac0c 100644
|
||||
--- a/third_party/blink/renderer/core/loader/long_task_detector_test.cc
|
||||
+++ b/third_party/blink/renderer/core/loader/long_task_detector_test.cc
|
||||
@@ -39,6 +39,8 @@ class SelfUnregisteringObserver
|
||||
}
|
||||
bool IsCalled() const { return called_; }
|
||||
|
||||
+ void Reset() { called_ = false; }
|
||||
+
|
||||
private:
|
||||
bool called_ = false;
|
||||
};
|
||||
@@ -148,6 +150,11 @@ TEST_F(LongTaskDetectorTest, SelfUnregisteringObserver) {
|
||||
SimulateTask(LongTaskDetector::kLongTaskThreshold +
|
||||
base::TimeDelta::FromMilliseconds(10));
|
||||
EXPECT_TRUE(observer->IsCalled());
|
||||
+ observer->Reset();
|
||||
+
|
||||
+ SimulateTask(LongTaskDetector::kLongTaskThreshold +
|
||||
+ base::TimeDelta::FromMilliseconds(10));
|
||||
+ EXPECT_FALSE(observer->IsCalled());
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
@@ -20,3 +20,4 @@ merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch
|
||||
merged_const-tracking_generalize_constness_when_delete_properties.patch
|
||||
merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch
|
||||
reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch
|
||||
m90-lts_squashed_multiple_commits.patch
|
||||
|
||||
299
patches/v8/m90-lts_squashed_multiple_commits.patch
Normal file
299
patches/v8/m90-lts_squashed_multiple_commits.patch
Normal file
@@ -0,0 +1,299 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: "ishell@chromium.org" <ishell@chromium.org>
|
||||
Date: Tue, 8 Jun 2021 17:33:32 +0200
|
||||
Subject: Squashed multiple commits.
|
||||
|
||||
Merged: [runtime] Fix handling of interceptors
|
||||
Revision: f9857fdf74
|
||||
|
||||
Merged: [runtime] Fix handling of interceptors, pt.2
|
||||
Revision: 1f5113816c
|
||||
|
||||
BUG=chromium:1216437
|
||||
NOTRY=true
|
||||
NOPRESUBMIT=true
|
||||
NOTREECHECKS=true
|
||||
|
||||
(cherry picked from commit 1936d568193b37d50d99218724ebbb76785a30d2)
|
||||
|
||||
Change-Id: Ief3da51866c8d0b5e85c76fad00b25ac2379f615
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2947407
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/branch-heads/9.1@{#71}
|
||||
Cr-Original-Branched-From: 0e4ac64a8cf298b14034a22f9fe7b085d2cb238d-refs/heads/9.1.269@{#1}
|
||||
Cr-Original-Branched-From: f565e72d5ba88daae35a59d0f978643e2343e912-refs/heads/master@{#73847}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2948661
|
||||
Reviewed-by: Igor Sheludko <ishell@chromium.org>
|
||||
Reviewed-by: Artem Sumaneev <asumaneev@google.com>
|
||||
Commit-Queue: Victor-Gabriel Savu <vsavu@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/9.0@{#64}
|
||||
Cr-Branched-From: bd0108b4c88e0d6f2350cb79b5f363fbd02f3eb7-refs/heads/9.0.257@{#1}
|
||||
Cr-Branched-From: 349bcc6a075411f1a7ce2d866c3dfeefc2efa39d-refs/heads/master@{#73001}
|
||||
|
||||
diff --git a/src/objects/objects.cc b/src/objects/objects.cc
|
||||
index 5e17fa85fca29e709204950382c610f40d616a64..c5450d50527bb8db1ae67b93aafc4b455b10053b 100644
|
||||
--- a/src/objects/objects.cc
|
||||
+++ b/src/objects/objects.cc
|
||||
@@ -2519,9 +2519,21 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
|
||||
if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
|
||||
return WriteToReadOnlyProperty(it, value, should_throw);
|
||||
}
|
||||
- if (maybe_attributes.FromJust() == ABSENT) break;
|
||||
- *found = false;
|
||||
- return Nothing<bool>();
|
||||
+ // At this point we might have called interceptor's query or getter
|
||||
+ // callback. Assuming that the callbacks have side effects, we use
|
||||
+ // Object::SetSuperProperty() which works properly regardless on
|
||||
+ // whether the property was present on the receiver or not when
|
||||
+ // storing to the receiver.
|
||||
+ if (maybe_attributes.FromJust() == ABSENT) {
|
||||
+ // Proceed lookup from the next state.
|
||||
+ it->Next();
|
||||
+ } else {
|
||||
+ // Finish lookup in order to make Object::SetSuperProperty() store
|
||||
+ // property to the receiver.
|
||||
+ it->NotFound();
|
||||
+ }
|
||||
+ return Object::SetSuperProperty(it, value, store_origin,
|
||||
+ should_throw);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2596,6 +2608,8 @@ Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value,
|
||||
if (found) return result;
|
||||
}
|
||||
|
||||
+ // TODO(ishell): refactor this: both SetProperty and and SetSuperProperty have
|
||||
+ // this piece of code.
|
||||
// If the receiver is the JSGlobalObject, the store was contextual. In case
|
||||
// the property did not exist yet on the global object itself, we have to
|
||||
// throw a reference error in strict mode. In sloppy mode, we continue.
|
||||
@@ -2632,6 +2646,8 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value,
|
||||
}
|
||||
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver());
|
||||
|
||||
+ // Note, the callers rely on the fact that this code is redoing the full own
|
||||
+ // lookup from scratch.
|
||||
LookupIterator::Configuration c = LookupIterator::OWN;
|
||||
LookupIterator own_lookup =
|
||||
it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c)
|
||||
@@ -2694,6 +2710,25 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value,
|
||||
}
|
||||
}
|
||||
|
||||
+ // TODO(ishell): refactor this: both SetProperty and and SetSuperProperty have
|
||||
+ // this piece of code.
|
||||
+ // If the receiver is the JSGlobalObject, the store was contextual. In case
|
||||
+ // the property did not exist yet on the global object itself, we have to
|
||||
+ // throw a reference error in strict mode. In sloppy mode, we continue.
|
||||
+ if (receiver->IsJSGlobalObject() &&
|
||||
+ (GetShouldThrow(isolate, should_throw) == ShouldThrow::kThrowOnError)) {
|
||||
+ if (own_lookup.state() == LookupIterator::TRANSITION) {
|
||||
+ // The property cell that we have created is garbage because we are going
|
||||
+ // to throw now instead of putting it into the global dictionary. However,
|
||||
+ // the cell might already have been stored into the feedback vector, so
|
||||
+ // we must invalidate it nevertheless.
|
||||
+ own_lookup.transition_cell()->ClearAndInvalidate(ReadOnlyRoots(isolate));
|
||||
+ }
|
||||
+ isolate->Throw(*isolate->factory()->NewReferenceError(
|
||||
+ MessageTemplate::kNotDefined, own_lookup.GetName()));
|
||||
+ return Nothing<bool>();
|
||||
+ }
|
||||
+
|
||||
return AddDataProperty(&own_lookup, value, NONE, should_throw, store_origin);
|
||||
}
|
||||
|
||||
diff --git a/test/cctest/test-api-interceptors.cc b/test/cctest/test-api-interceptors.cc
|
||||
index 236053eb45c02201d3425ea2fd98cf3913084bfe..3d3e970fa52b633ffc9e734077e7971ae37306bc 100644
|
||||
--- a/test/cctest/test-api-interceptors.cc
|
||||
+++ b/test/cctest/test-api-interceptors.cc
|
||||
@@ -875,9 +875,11 @@ THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
|
||||
CHECK(!value->BooleanValue(isolate));
|
||||
}
|
||||
|
||||
-static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
|
||||
- v8::GenericNamedPropertyQueryCallback query,
|
||||
- const char* source, int expected) {
|
||||
+namespace {
|
||||
+
|
||||
+void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
|
||||
+ v8::GenericNamedPropertyQueryCallback query,
|
||||
+ const char* source, int expected) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
||||
@@ -892,14 +894,13 @@ static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
|
||||
CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
|
||||
}
|
||||
|
||||
-static void CheckInterceptorLoadIC(
|
||||
- v8::GenericNamedPropertyGetterCallback getter, const char* source,
|
||||
- int expected) {
|
||||
+void CheckInterceptorLoadIC(v8::GenericNamedPropertyGetterCallback getter,
|
||||
+ const char* source, int expected) {
|
||||
CheckInterceptorIC(getter, nullptr, source, expected);
|
||||
}
|
||||
|
||||
-static void InterceptorLoadICGetter(
|
||||
- Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
+void InterceptorLoadICGetter(Local<Name> name,
|
||||
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
ApiTestFuzzer::Fuzz();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
CHECK_EQ(isolate, info.GetIsolate());
|
||||
@@ -909,6 +910,7 @@ static void InterceptorLoadICGetter(
|
||||
info.GetReturnValue().Set(v8::Integer::New(isolate, 42));
|
||||
}
|
||||
|
||||
+} // namespace
|
||||
|
||||
// This test should hit the load IC for the interceptor case.
|
||||
THREADED_TEST(InterceptorLoadIC) {
|
||||
@@ -925,9 +927,23 @@ THREADED_TEST(InterceptorLoadIC) {
|
||||
// configurations of interceptor and explicit fields works fine
|
||||
// (those cases are special cased to get better performance).
|
||||
|
||||
-static void InterceptorLoadXICGetter(
|
||||
+namespace {
|
||||
+
|
||||
+void InterceptorLoadXICGetter(Local<Name> name,
|
||||
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
+ ApiTestFuzzer::Fuzz();
|
||||
+ info.GetReturnValue().Set(
|
||||
+ v8_str("x")
|
||||
+ ->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
||||
+ .FromJust()
|
||||
+ ? v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42))
|
||||
+ : v8::Local<v8::Value>());
|
||||
+}
|
||||
+
|
||||
+void InterceptorLoadXICGetterWithSideEffects(
|
||||
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
ApiTestFuzzer::Fuzz();
|
||||
+ CompileRun("interceptor_getter_side_effect()");
|
||||
info.GetReturnValue().Set(
|
||||
v8_str("x")
|
||||
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
||||
@@ -936,6 +952,7 @@ static void InterceptorLoadXICGetter(
|
||||
: v8::Local<v8::Value>());
|
||||
}
|
||||
|
||||
+} // namespace
|
||||
|
||||
THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
|
||||
CheckInterceptorLoadIC(InterceptorLoadXICGetter,
|
||||
@@ -1460,6 +1477,18 @@ void HasICQueryToggle(TKey name,
|
||||
isolate, toggle ? v8::internal::ABSENT : v8::internal::NONE));
|
||||
}
|
||||
|
||||
+template <typename TKey, v8::internal::PropertyAttributes attribute>
|
||||
+void HasICQuerySideEffect(TKey name,
|
||||
+ const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
||||
+ ApiTestFuzzer::Fuzz();
|
||||
+ v8::Isolate* isolate = CcTest::isolate();
|
||||
+ CHECK_EQ(isolate, info.GetIsolate());
|
||||
+ CompileRun("interceptor_query_side_effect()");
|
||||
+ if (attribute != v8::internal::ABSENT) {
|
||||
+ info.GetReturnValue().Set(v8::Integer::New(isolate, attribute));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
int named_query_counter = 0;
|
||||
void NamedQueryCallback(Local<Name> name,
|
||||
const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
||||
@@ -1525,6 +1554,42 @@ THREADED_TEST(InterceptorHasICQueryToggle) {
|
||||
500);
|
||||
}
|
||||
|
||||
+THREADED_TEST(InterceptorStoreICWithSideEffectfulCallbacks) {
|
||||
+ CheckInterceptorIC(EmptyInterceptorGetter,
|
||||
+ HasICQuerySideEffect<Local<Name>, v8::internal::ABSENT>,
|
||||
+ "let r;"
|
||||
+ "let inside_side_effect = false;"
|
||||
+ "let interceptor_query_side_effect = function() {"
|
||||
+ " if (!inside_side_effect) {"
|
||||
+ " inside_side_effect = true;"
|
||||
+ " r.x = 153;"
|
||||
+ " inside_side_effect = false;"
|
||||
+ " }"
|
||||
+ "};"
|
||||
+ "for (var i = 0; i < 20; i++) {"
|
||||
+ " r = { __proto__: o };"
|
||||
+ " r.x = i;"
|
||||
+ "}",
|
||||
+ 19);
|
||||
+
|
||||
+ CheckInterceptorIC(InterceptorLoadXICGetterWithSideEffects,
|
||||
+ nullptr, // query callback is not provided
|
||||
+ "let r;"
|
||||
+ "let inside_side_effect = false;"
|
||||
+ "let interceptor_getter_side_effect = function() {"
|
||||
+ " if (!inside_side_effect) {"
|
||||
+ " inside_side_effect = true;"
|
||||
+ " r.y = 153;"
|
||||
+ " inside_side_effect = false;"
|
||||
+ " }"
|
||||
+ "};"
|
||||
+ "for (var i = 0; i < 20; i++) {"
|
||||
+ " r = { __proto__: o };"
|
||||
+ " r.y = i;"
|
||||
+ "}",
|
||||
+ 19);
|
||||
+}
|
||||
+
|
||||
static void InterceptorStoreICSetter(
|
||||
Local<Name> key, Local<Value> value,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
@@ -1574,6 +1639,52 @@ THREADED_TEST(InterceptorStoreICWithNoSetter) {
|
||||
CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
|
||||
}
|
||||
|
||||
+THREADED_TEST(EmptyInterceptorDoesNotShadowReadOnlyProperty) {
|
||||
+ // Interceptor should not shadow readonly property 'x' on the prototype, and
|
||||
+ // attempt to store to 'x' must throw.
|
||||
+ CheckInterceptorIC(EmptyInterceptorGetter,
|
||||
+ HasICQuery<Local<Name>, v8::internal::ABSENT>,
|
||||
+ "'use strict';"
|
||||
+ "let p = {};"
|
||||
+ "Object.defineProperty(p, 'x', "
|
||||
+ " {value: 153, writable: false});"
|
||||
+ "o.__proto__ = p;"
|
||||
+ "let result = 0;"
|
||||
+ "let r;"
|
||||
+ "for (var i = 0; i < 20; i++) {"
|
||||
+ " r = { __proto__: o };"
|
||||
+ " try {"
|
||||
+ " r.x = i;"
|
||||
+ " } catch (e) {"
|
||||
+ " result++;"
|
||||
+ " }"
|
||||
+ "}"
|
||||
+ "result",
|
||||
+ 20);
|
||||
+}
|
||||
+
|
||||
+THREADED_TEST(InterceptorShadowsReadOnlyProperty) {
|
||||
+ // Interceptor claims that it has a writable property 'x', so the existence
|
||||
+ // of the readonly property 'x' on the prototype should not cause exceptions.
|
||||
+ CheckInterceptorIC(InterceptorLoadXICGetter,
|
||||
+ nullptr, // query callback
|
||||
+ "'use strict';"
|
||||
+ "let p = {};"
|
||||
+ "Object.defineProperty(p, 'x', "
|
||||
+ " {value: 153, writable: false});"
|
||||
+ "o.__proto__ = p;"
|
||||
+ "let result = 0;"
|
||||
+ "let r;"
|
||||
+ "for (var i = 0; i < 20; i++) {"
|
||||
+ " r = { __proto__: o };"
|
||||
+ " try {"
|
||||
+ " r.x = i;"
|
||||
+ " result++;"
|
||||
+ " } catch (e) {}"
|
||||
+ "}"
|
||||
+ "result",
|
||||
+ 20);
|
||||
+}
|
||||
|
||||
THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
@@ -394,6 +394,10 @@ void BrowserWindow::ResetBrowserViews() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void BrowserWindow::OnDevToolsResized() {
|
||||
UpdateDraggableRegions(draggable_regions_);
|
||||
}
|
||||
|
||||
void BrowserWindow::SetVibrancy(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> value) {
|
||||
std::string type = gin::V8ToString(isolate, value);
|
||||
|
||||
@@ -63,9 +63,7 @@ class BrowserWindow : public BaseWindow,
|
||||
void OnActivateContents() override;
|
||||
void OnPageTitleUpdated(const base::string16& title,
|
||||
bool explicit_set) override;
|
||||
#if defined(OS_MAC)
|
||||
void OnDevToolsResized() override;
|
||||
#endif
|
||||
|
||||
// NativeWindowObserver:
|
||||
void RequestPreferredWidth(int* width) override;
|
||||
@@ -121,9 +119,7 @@ class BrowserWindow : public BaseWindow,
|
||||
// it should be cancelled when we can prove that the window is responsive.
|
||||
base::CancelableClosure window_unresponsive_closure_;
|
||||
|
||||
#if defined(OS_MAC)
|
||||
std::vector<mojom::DraggableRegionPtr> draggable_regions_;
|
||||
#endif
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
|
||||
@@ -37,10 +37,6 @@ void BrowserWindow::OverrideNSWindowContentView(
|
||||
[contentView viewDidMoveToWindow];
|
||||
}
|
||||
|
||||
void BrowserWindow::OnDevToolsResized() {
|
||||
UpdateDraggableRegions(draggable_regions_);
|
||||
}
|
||||
|
||||
void BrowserWindow::UpdateDraggableRegions(
|
||||
const std::vector<mojom::DraggableRegionPtr>& regions) {
|
||||
if (window_->has_frame())
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "shell/browser/api/electron_api_browser_window.h"
|
||||
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "ui/aura/window.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -14,8 +15,20 @@ void BrowserWindow::UpdateDraggableRegions(
|
||||
const std::vector<mojom::DraggableRegionPtr>& regions) {
|
||||
if (window_->has_frame())
|
||||
return;
|
||||
|
||||
if (&draggable_regions_ != ®ions) {
|
||||
auto const offset =
|
||||
web_contents()->GetNativeView()->GetBoundsInRootWindow();
|
||||
auto snapped_regions = mojo::Clone(regions);
|
||||
for (auto& snapped_region : snapped_regions) {
|
||||
snapped_region->bounds.Offset(offset.x(), offset.y());
|
||||
}
|
||||
|
||||
draggable_regions_ = mojo::Clone(snapped_regions);
|
||||
}
|
||||
|
||||
static_cast<NativeWindowViews*>(window_.get())
|
||||
->UpdateDraggableRegions(regions);
|
||||
->UpdateDraggableRegions(draggable_regions_);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -129,7 +129,7 @@ v8::Local<v8::Value> HttpResponseHeadersToV8(
|
||||
!value.empty()) {
|
||||
net::HttpContentDisposition header(value, std::string());
|
||||
std::string decodedFilename =
|
||||
header.is_attachment() ? " attachement" : " inline";
|
||||
header.is_attachment() ? " attachment" : " inline";
|
||||
decodedFilename += "; filename=" + header.filename();
|
||||
value = decodedFilename;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
|
||||
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
||||
break;
|
||||
case AdapterPresence::POWERED_ON:
|
||||
rescan_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -87,7 +88,7 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
||||
case DiscoveryState::DISCOVERING:
|
||||
// The first time this state fires is due to a rescan triggering so set a
|
||||
// flag to ignore devices
|
||||
if (!refreshing_) {
|
||||
if (rescan_ && !refreshing_) {
|
||||
refreshing_ = true;
|
||||
} else {
|
||||
// The second time this state fires we are now safe to pick a device
|
||||
|
||||
@@ -42,6 +42,7 @@ class BluetoothChooser : public content::BluetoothChooser {
|
||||
EventHandler event_handler_;
|
||||
int num_retries_ = 0;
|
||||
bool refreshing_ = false;
|
||||
bool rescan_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BluetoothChooser);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "mojo/public/cpp/bindings/binding.h"
|
||||
#include "net/base/completion_repeating_callback.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/http/http_util.h"
|
||||
#include "services/metrics/public/cpp/ukm_source_id.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
@@ -305,6 +306,14 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnComplete(
|
||||
|
||||
void ProxyingURLLoaderFactory::InProgressRequest::OnLoaderCreated(
|
||||
mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
|
||||
// When CORS is involved there may be multiple network::URLLoader associated
|
||||
// with this InProgressRequest, because CorsURLLoader may create a new
|
||||
// network::URLLoader for the same request id in redirect handling - see
|
||||
// CorsURLLoader::FollowRedirect. In such a case the old network::URLLoader
|
||||
// is going to be detached fairly soon, so we don't need to take care of it.
|
||||
// We need this explicit reset to avoid a DCHECK failure in mojo::Receiver.
|
||||
header_client_receiver_.reset();
|
||||
|
||||
header_client_receiver_.Bind(std::move(receiver));
|
||||
if (for_cors_preflight_) {
|
||||
// In this case we don't have |target_loader_| and
|
||||
@@ -548,13 +557,17 @@ void ProxyingURLLoaderFactory::InProgressRequest::
|
||||
override_headers_ = nullptr;
|
||||
|
||||
if (for_cors_preflight_) {
|
||||
// If this is for CORS preflight, there is no associated client. We notify
|
||||
// the completion here, and deletes |this|.
|
||||
// If this is for CORS preflight, there is no associated client.
|
||||
info_->AddResponseInfoFromResourceResponse(*current_response_);
|
||||
// Do not finish proxied preflight requests that require proxy auth.
|
||||
// The request is not finished yet, give control back to network service
|
||||
// which will start authentication process.
|
||||
if (info_->response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED)
|
||||
return;
|
||||
// We notify the completion here, and delete |this|.
|
||||
factory_->web_request_api()->OnResponseStarted(&info_.value(), request_);
|
||||
factory_->web_request_api()->OnCompleted(&info_.value(), request_, net::OK);
|
||||
|
||||
// Deletes |this|.
|
||||
factory_->RemoveRequest(network_service_request_id_, request_id_);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ void URLPipeLoader::OnDataReceived(base::StringPiece string_piece,
|
||||
producer_->Write(
|
||||
std::make_unique<mojo::StringDataSource>(
|
||||
string_piece, mojo::StringDataSource::AsyncWritingMode::
|
||||
STRING_STAYS_VALID_UNTIL_COMPLETION),
|
||||
STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION),
|
||||
base::BindOnce(&URLPipeLoader::OnWrite, weak_factory_.GetWeakPtr(),
|
||||
std::move(resume)));
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 12,0,11,0
|
||||
PRODUCTVERSION 12,0,11,0
|
||||
FILEVERSION 12,0,12,0
|
||||
PRODUCTVERSION 12,0,12,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "12.0.11"
|
||||
VALUE "FileVersion", "12.0.12"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "12.0.11"
|
||||
VALUE "ProductVersion", "12.0.12"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -160,13 +160,12 @@
|
||||
|
||||
// Switch to new state.
|
||||
devtools_docked_ = docked;
|
||||
auto* inspectable_web_contents =
|
||||
inspectableWebContentsView_->inspectable_web_contents();
|
||||
auto* devToolsWebContents =
|
||||
inspectable_web_contents->GetDevToolsWebContents();
|
||||
auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
|
||||
if (!docked) {
|
||||
auto* inspectable_web_contents =
|
||||
inspectableWebContentsView_->inspectable_web_contents();
|
||||
auto* devToolsWebContents =
|
||||
inspectable_web_contents->GetDevToolsWebContents();
|
||||
auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
|
||||
|
||||
auto styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
|
||||
NSMiniaturizableWindowMask | NSWindowStyleMaskResizable |
|
||||
NSTexturedBackgroundWindowMask |
|
||||
@@ -189,6 +188,9 @@
|
||||
devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||
|
||||
[contentView addSubview:devToolsView];
|
||||
[devToolsView setMouseDownCanMoveWindow:NO];
|
||||
} else {
|
||||
[devToolsView setMouseDownCanMoveWindow:YES];
|
||||
}
|
||||
[self setDevToolsVisible:YES activate:activate];
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ int FramelessView::ResizingBorderHitTest(const gfx::Point& point) {
|
||||
bool can_ever_resize = frame_->widget_delegate()
|
||||
? frame_->widget_delegate()->CanResize()
|
||||
: false;
|
||||
|
||||
// https://github.com/electron/electron/issues/611
|
||||
// If window isn't resizable, we should always return HTCLIENT, otherwise the
|
||||
// hover state of DOM will not be cleared probably.
|
||||
if (!can_ever_resize)
|
||||
return HTCLIENT;
|
||||
|
||||
// Don't allow overlapping resize handles when the window is maximized or
|
||||
// fullscreen, as it can't be resized in those states.
|
||||
int resize_border = frame_->IsMaximized() || frame_->IsFullscreen()
|
||||
|
||||
@@ -132,6 +132,10 @@ void InspectableWebContentsViewViews::ShowDevTools(bool activate) {
|
||||
} else {
|
||||
devtools_window_->ShowInactive();
|
||||
}
|
||||
|
||||
// Update draggable regions to account for the new dock position.
|
||||
if (GetDelegate())
|
||||
GetDelegate()->DevToolsResized();
|
||||
} else {
|
||||
devtools_web_view_->SetVisible(true);
|
||||
devtools_web_view_->SetWebContents(
|
||||
@@ -232,6 +236,9 @@ void InspectableWebContentsViewViews::Layout() {
|
||||
|
||||
devtools_web_view_->SetBoundsRect(new_devtools_bounds);
|
||||
contents_web_view_->SetBoundsRect(new_contents_bounds);
|
||||
|
||||
if (GetDelegate())
|
||||
GetDelegate()->DevToolsResized();
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -489,9 +489,13 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
// Node.js requires that microtask checkpoints be explicitly invoked.
|
||||
is.policy = v8::MicrotasksPolicy::kExplicit;
|
||||
} else {
|
||||
// Match Blink's behavior by allowing microtasks invocation to be controlled
|
||||
// by MicrotasksScope objects.
|
||||
is.policy = v8::MicrotasksPolicy::kScoped;
|
||||
// Blink expects the microtasks policy to be kScoped, but Node.js expects it
|
||||
// to be kExplicit. In the renderer, there can be many contexts within the
|
||||
// same isolate, so we don't want to change the existing policy here, which
|
||||
// could be either kExplicit or kScoped depending on whether we're executing
|
||||
// from within a Node.js or a Blink entrypoint. Instead, the policy is
|
||||
// toggled to kExplicit when entering Node.js through UvRunOnce.
|
||||
is.policy = context->GetIsolate()->GetMicrotasksPolicy();
|
||||
|
||||
// We do not want to use Node.js' message listener as it interferes with
|
||||
// Blink's.
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('webRequest module', () => {
|
||||
res.setHeader('Location', 'http://' + req.rawHeaders[1]);
|
||||
res.end();
|
||||
} else if (req.url === '/contentDisposition') {
|
||||
res.setHeader('content-disposition', [' attachement; filename=aa%E4%B8%ADaa.txt']);
|
||||
res.setHeader('content-disposition', [' attachment; filename=aa%E4%B8%ADaa.txt']);
|
||||
const content = req.url;
|
||||
res.end(content);
|
||||
} else {
|
||||
@@ -306,11 +306,11 @@ describe('webRequest module', () => {
|
||||
|
||||
it('does not change content-disposition header by default', async () => {
|
||||
ses.webRequest.onHeadersReceived((details, callback) => {
|
||||
expect(details.responseHeaders!['content-disposition']).to.deep.equal([' attachement; filename=aa中aa.txt']);
|
||||
expect(details.responseHeaders!['content-disposition']).to.deep.equal([' attachment; filename=aa中aa.txt']);
|
||||
callback({});
|
||||
});
|
||||
const { data, headers } = await ajax(defaultURL + 'contentDisposition');
|
||||
expect(headers).to.match(/^content-disposition: attachement; filename=aa%E4%B8%ADaa.txt$/m);
|
||||
expect(headers).to.match(/^content-disposition: attachment; filename=aa%E4%B8%ADaa.txt$/m);
|
||||
expect(data).to.equal('/contentDisposition');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
// `setImmediate` schedules a function to be run on the next UV tick, which
|
||||
// ensures that `window.open` is run from `UvRunOnce()`.
|
||||
setImmediate(async () => {
|
||||
if (!location.hash) {
|
||||
const p = new Promise(resolve => {
|
||||
window.addEventListener('message', resolve, { once: true });
|
||||
});
|
||||
window.open('#foo', '', 'nodeIntegration=no,show=no');
|
||||
const e = await p;
|
||||
|
||||
window.close();
|
||||
} else {
|
||||
window.opener.postMessage('foo', '*');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
nativeWindowOpen: true
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.on('close', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
mainWindow.loadFile('index.html');
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
});
|
||||
1
spec-main/fixtures/dogs-running.txt
Normal file
1
spec-main/fixtures/dogs-running.txt
Normal file
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
import * as childProcess from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import { emittedOnce } from './events-helpers';
|
||||
@@ -199,6 +200,17 @@ describe('node feature', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('fs.readFile', () => {
|
||||
it('can accept a FileHandle as the Path argument', async () => {
|
||||
const filePathForHandle = path.resolve(mainFixturesPath, 'dogs-running.txt');
|
||||
const fileHandle = await fs.promises.open(filePathForHandle, 'r');
|
||||
|
||||
const file = await fs.promises.readFile(fileHandle, { encoding: 'utf8' });
|
||||
expect(file).to.not.be.empty();
|
||||
await fileHandle.close();
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(features.isRunAsNodeEnabled())('inspector', () => {
|
||||
let child: childProcess.ChildProcessWithoutNullStreams;
|
||||
let exitPromise: Promise<any[]>;
|
||||
|
||||
@@ -9,6 +9,9 @@ import { BrowserWindow, WebPreferences } from 'electron/main';
|
||||
import { closeWindow } from './window-helpers';
|
||||
import { AddressInfo } from 'net';
|
||||
import { emittedUntil } from './events-helpers';
|
||||
import { ifit } from './spec-helpers';
|
||||
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
const messageContainsSecurityWarning = (event: Event, level: number, message: string) => {
|
||||
return message.indexOf('Electron Security Warning') > -1;
|
||||
@@ -226,10 +229,10 @@ describe('security warnings', () => {
|
||||
expect(message).to.not.include('insecure-resources.html');
|
||||
});
|
||||
|
||||
it('should warn about enabled remote module with remote content', async () => {
|
||||
ifit(features.isRemoteModuleEnabled())('should warn about enabled remote module with remote content', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences
|
||||
webPreferences: { ...webPreferences, enableRemoteModule: true }
|
||||
});
|
||||
|
||||
w.loadURL(`${serverUrl}/base-page-security.html`);
|
||||
@@ -237,10 +240,10 @@ describe('security warnings', () => {
|
||||
expect(message).to.include('enableRemoteModule');
|
||||
});
|
||||
|
||||
it('should not warn about enabled remote module with remote content from localhost', async () => {
|
||||
ifit(features.isRemoteModuleEnabled())('should not warn about enabled remote module with remote content from localhost', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences
|
||||
webPreferences: { ...webPreferences, enableRemoteModule: true }
|
||||
});
|
||||
w.loadURL(`${serverUrl}/base-page-security-onload-message.html`);
|
||||
const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded);
|
||||
|
||||
Reference in New Issue
Block a user