mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2ffa6fe31 | ||
|
|
f7bb17ebd1 | ||
|
|
f9ed0eaee4 | ||
|
|
8933e7e2a9 | ||
|
|
262f4d34cf | ||
|
|
b721d420d5 | ||
|
|
4517547f07 | ||
|
|
4f30f731ee | ||
|
|
3b81b7efbc | ||
|
|
acd34fdca9 | ||
|
|
84ecd700db | ||
|
|
cbc0f6720e | ||
|
|
8fbabeaca7 | ||
|
|
4c737147c3 | ||
|
|
5ebc741819 | ||
|
|
cd6ad4d1bb | ||
|
|
19f0abd62e | ||
|
|
9d4f8a06e8 | ||
|
|
a43014a410 | ||
|
|
803698e2c4 | ||
|
|
557cadddcb | ||
|
|
fe01ed750a | ||
|
|
7f26c72af6 | ||
|
|
7a3e587a1d | ||
|
|
bf14d05830 | ||
|
|
384642792e | ||
|
|
5bbac9ae30 | ||
|
|
0da6411d11 | ||
|
|
297be64122 | ||
|
|
1c47ba0a91 | ||
|
|
1cd7a71bb1 | ||
|
|
feb81b6841 | ||
|
|
a3d9e4be58 | ||
|
|
ec4683cd91 | ||
|
|
a02e0f0f02 | ||
|
|
97eee463fa | ||
|
|
ed9a12cba7 | ||
|
|
fe4d3a2484 | ||
|
|
ad9a90ec53 | ||
|
|
8647232c48 | ||
|
|
f9e28e3e50 | ||
|
|
3698f89205 | ||
|
|
238cc80cef |
@@ -1,3 +1,17 @@
|
||||
{
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json"
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json",
|
||||
"no-angle-brackets": true,
|
||||
"no-inline-html": {
|
||||
"allowed_elements": [
|
||||
"br",
|
||||
"details",
|
||||
"img",
|
||||
"li",
|
||||
"summary",
|
||||
"ul",
|
||||
"unknown",
|
||||
"Tabs",
|
||||
"TabItem",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
24
BUILD.gn
24
BUILD.gn
@@ -81,18 +81,11 @@ if (is_linux) {
|
||||
]
|
||||
}
|
||||
|
||||
# Generates electron_gtk_stubs.h header which contains
|
||||
# stubs for extracting function ptrs from the gtk library.
|
||||
# Function signatures for which stubs are required should be
|
||||
# declared in electron_gtk.sigs, currently this file contains
|
||||
# signatures for the functions used with native file chooser
|
||||
# implementation. In future, this file can be extended to contain
|
||||
# gtk4 stubs to switch gtk version in runtime.
|
||||
# Generates headers which contain stubs for extracting function ptrs
|
||||
# from the gtk library. Function signatures for which stubs are
|
||||
# required should be declared in the sig files.
|
||||
generate_stubs("electron_gtk_stubs") {
|
||||
sigs = [
|
||||
"shell/browser/ui/electron_gdk_pixbuf.sigs",
|
||||
"shell/browser/ui/electron_gtk.sigs",
|
||||
]
|
||||
sigs = [ "shell/browser/ui/electron_gdk_pixbuf.sigs" ]
|
||||
extra_header = "shell/browser/ui/electron_gtk.fragment"
|
||||
output_name = "electron_gtk_stubs"
|
||||
public_deps = [ "//ui/gtk:gtk_config" ]
|
||||
@@ -473,6 +466,7 @@ source_set("electron_lib") {
|
||||
"//net:extras",
|
||||
"//net:net_resources",
|
||||
"//printing/buildflags",
|
||||
"//services/device/public/cpp/bluetooth:bluetooth",
|
||||
"//services/device/public/cpp/geolocation",
|
||||
"//services/device/public/cpp/hid",
|
||||
"//services/device/public/mojom",
|
||||
@@ -704,6 +698,8 @@ source_set("electron_lib") {
|
||||
sources += [
|
||||
"shell/browser/printing/print_view_manager_electron.cc",
|
||||
"shell/browser/printing/print_view_manager_electron.h",
|
||||
"shell/browser/printing/printing_utils.cc",
|
||||
"shell/browser/printing/printing_utils.h",
|
||||
"shell/renderer/printing/print_render_frame_helper_delegate.cc",
|
||||
"shell/renderer/printing/print_render_frame_helper_delegate.h",
|
||||
]
|
||||
@@ -1468,8 +1464,10 @@ dist_zip("hunspell_dictionaries_zip") {
|
||||
}
|
||||
|
||||
copy("libcxx_headers") {
|
||||
sources = libcxx_headers + libcxx_licenses +
|
||||
[ "//buildtools/third_party/libc++/__config_site" ]
|
||||
sources = libcxx_headers + libcxx_licenses + [
|
||||
"//buildtools/third_party/libc++/__assertion_handler",
|
||||
"//buildtools/third_party/libc++/__config_site",
|
||||
]
|
||||
outputs = [ "$target_gen_dir/electron_libcxx_include/{{source_root_relative_dir}}/{{source_file_part}}" ]
|
||||
}
|
||||
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'122.0.6261.139',
|
||||
'122.0.6261.156',
|
||||
'node_version':
|
||||
'v20.9.0',
|
||||
'nan_version':
|
||||
|
||||
@@ -17,6 +17,8 @@ following properties:
|
||||
method.
|
||||
* `url` string (optional) - The request URL. Must be provided in the absolute
|
||||
form with the protocol scheme specified as http or https.
|
||||
* `headers` Record\<string, string | string[]\> (optional) - Headers to be sent
|
||||
with the request.
|
||||
* `session` Session (optional) - The [`Session`](session.md) instance with
|
||||
which the request is associated.
|
||||
* `partition` string (optional) - The name of the [`partition`](session.md)
|
||||
|
||||
@@ -174,7 +174,7 @@ dialog.showOpenDialog(mainWindow, {
|
||||
* `dontAddToRecent` _Windows_ - Do not add the item being saved to the recent documents list.
|
||||
* `securityScopedBookmarks` boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](https://developer.apple.com/library/content/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16) when packaged for the Mac App Store. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
||||
|
||||
Returns `string | undefined`, the path of the file chosen by the user; if the dialog is cancelled it returns `undefined`.
|
||||
Returns `string`, the path of the file chosen by the user; if the dialog is cancelled it returns an empty string.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -207,7 +207,7 @@ The `filters` specifies an array of file types that can be displayed, see
|
||||
Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
|
||||
* `canceled` boolean - whether or not the dialog was canceled.
|
||||
* `filePath` string (optional) - If the dialog is canceled, this will be `undefined`.
|
||||
* `filePath` string - If the dialog is canceled, this will be an empty string.
|
||||
* `bookmark` string (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. (For return values, see [table here](#bookmarks-array).)
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
29
docs/api/navigation-history.md
Normal file
29
docs/api/navigation-history.md
Normal file
@@ -0,0 +1,29 @@
|
||||
## Class: NavigationHistory
|
||||
|
||||
> Manage a list of navigation entries, representing the user's browsing history within the application.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)<br />
|
||||
_This class is not exported from the `'electron'` module. It is only available as a return value of other methods in the Electron API._
|
||||
|
||||
Each navigation entry corresponds to a specific page. The indexing system follows a sequential order, where the first available navigation entry is at index 0, representing the earliest visited page, and the latest navigation entry is at index N, representing the most recent page. Maintaining this ordered list of navigation entries enables seamless navigation both backward and forward through the user's browsing history.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
#### `navigationHistory.getActiveIndex()`
|
||||
|
||||
Returns `Integer` - The index of the current page, from which we would go back/forward or reload.
|
||||
|
||||
#### `navigationHistory.getEntryAtIndex(index)`
|
||||
|
||||
* `index` Integer
|
||||
|
||||
Returns `Object`:
|
||||
|
||||
* `url` string - The URL of the navigation entry at the given index.
|
||||
* `title` string - The page title of the navigation entry at the given index.
|
||||
|
||||
If index is out of bounds (greater than history length or less than 0), null will be returned.
|
||||
|
||||
#### `navigationHistory.length()`
|
||||
|
||||
Returns `Integer` - History length.
|
||||
@@ -813,6 +813,8 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
* `pointerLock` - Request to directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
|
||||
* `keyboardLock` - Request capture of keypresses for any or all of the keys on the physical keyboard via the [Keyboard Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/lock). These requests always appear to originate from the main frame.
|
||||
* `openExternal` - Request to open links in external applications.
|
||||
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `window-management` - Request access to enumerate screens using the [`getScreenDetails`](https://developer.chrome.com/en/articles/multi-screen-window-placement/) API.
|
||||
* `unknown` - An unrecognized permission request.
|
||||
* `callback` Function
|
||||
@@ -861,6 +863,8 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
|
||||
* `openExternal` - Open links in external applications.
|
||||
* `pointerLock` - Directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
|
||||
* `serial` - Read from and write to serial devices with the [Web Serial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API).
|
||||
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
|
||||
* `requestingOrigin` string - The origin URL of the permission check
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
|
||||
@@ -2200,6 +2200,10 @@ A `Integer` representing the unique ID of this WebContents. Each ID is unique am
|
||||
|
||||
A [`Session`](session.md) used by this webContents.
|
||||
|
||||
#### `contents.navigationHistory` _Readonly_
|
||||
|
||||
A [`NavigationHistory`](navigation-history.md) used by this webContents.
|
||||
|
||||
#### `contents.hostWebContents` _Readonly_
|
||||
|
||||
A [`WebContents`](web-contents.md) instance that might own this `WebContents`.
|
||||
|
||||
@@ -9,7 +9,7 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 29.0.0 | 2023-Dec-07 | 2024-Jan-24 | 2024-Feb-20 | 2024-Aug-20 | M122 | v18.19 | ✅ |
|
||||
| 29.0.0 | 2023-Dec-07 | 2024-Jan-24 | 2024-Feb-20 | 2024-Aug-20 | M122 | v20.9 | ✅ |
|
||||
| 28.0.0 | 2023-Oct-11 | 2023-Nov-06 | 2023-Dec-05 | 2024-Jun-11 | M120 | v18.18 | ✅ |
|
||||
| 27.0.0 | 2023-Aug-17 | 2023-Sep-13 | 2023-Oct-10 | 2024-Apr-16 | M118 | v18.17 | ✅ |
|
||||
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | 2024-Feb-20 | M116 | v18.16 | 🚫 |
|
||||
|
||||
@@ -234,7 +234,7 @@ Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRende
|
||||
<details><summary>Typed import aliases</summary>
|
||||
|
||||
For better type checking when writing TypeScript code, you can choose to import
|
||||
main process modules from <code>electron/main</code>.
|
||||
main process modules from `electron/main`.
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow } = require('electron/main')
|
||||
|
||||
@@ -152,7 +152,7 @@ command that can handle the version bumping and tagging for you.
|
||||
#### Bonus: Publishing in GitHub Actions
|
||||
|
||||
Publishing locally can be painful, especially because you can only create distributables
|
||||
for your host operating system (i.e. you can't publish a Window `.exe` file from macOS).
|
||||
for your host operating system (i.e. you can't publish a Windows `.exe` file from macOS).
|
||||
|
||||
A solution for this would be to publish your app via automation workflows
|
||||
such as [GitHub Actions][], which can run tasks in the
|
||||
|
||||
@@ -33,6 +33,7 @@ auto_filenames = {
|
||||
"docs/api/message-port-main.md",
|
||||
"docs/api/native-image.md",
|
||||
"docs/api/native-theme.md",
|
||||
"docs/api/navigation-history.md",
|
||||
"docs/api/net-log.md",
|
||||
"docs/api/net.md",
|
||||
"docs/api/notification.md",
|
||||
|
||||
@@ -34,7 +34,7 @@ filenames = {
|
||||
"shell/browser/notifications/linux/notification_presenter_linux.h",
|
||||
"shell/browser/relauncher_linux.cc",
|
||||
"shell/browser/ui/electron_desktop_window_tree_host_linux.cc",
|
||||
"shell/browser/ui/file_dialog_gtk.cc",
|
||||
"shell/browser/ui/file_dialog_linux.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.h",
|
||||
"shell/browser/ui/gtk/menu_util.cc",
|
||||
|
||||
@@ -518,6 +518,17 @@ WebContents.prototype._init = function () {
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
// Add navigationHistory property which handles session history,
|
||||
// maintaining a list of navigation entries for backward and forward navigation.
|
||||
Object.defineProperty(this, 'navigationHistory', {
|
||||
value: {
|
||||
getActiveIndex: this._getActiveIndex.bind(this),
|
||||
length: this._historyLength.bind(this),
|
||||
getEntryAtIndex: this._getNavigationEntryAtIndex.bind(this)
|
||||
},
|
||||
writable: false
|
||||
});
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderToEvent(event, this);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@electron/docs-parser": "^1.2.0",
|
||||
"@electron/fiddle-core": "^1.0.4",
|
||||
"@electron/github-app-auth": "^2.0.0",
|
||||
"@electron/lint-roller": "^1.9.0",
|
||||
"@electron/lint-roller": "^1.12.1",
|
||||
"@electron/typescript-definitions": "^8.15.2",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@primer/octicons": "^10.0.0",
|
||||
|
||||
4
patches/DirectXShaderCompiler/.patches
Normal file
4
patches/DirectXShaderCompiler/.patches
Normal file
@@ -0,0 +1,4 @@
|
||||
cherry-pick-a65e511a14b4.patch
|
||||
cherry-pick-bc18aec94c82.patch
|
||||
cherry-pick-bd7aa9779873.patch
|
||||
cherry-pick-2a434fd0af6b.patch
|
||||
419
patches/DirectXShaderCompiler/cherry-pick-2a434fd0af6b.patch
Normal file
419
patches/DirectXShaderCompiler/cherry-pick-2a434fd0af6b.patch
Normal file
@@ -0,0 +1,419 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Antonio Maiorano <amaiorano@google.com>
|
||||
Date: Wed, 8 May 2024 13:38:38 -0400
|
||||
Subject: Fix invalid IR from scalarrepl-param-hlsl in ReplaceConstantWithInst
|
||||
(#6556)
|
||||
|
||||
ReplaceConstantWithInst(C, V) replaces uses of C in the current function
|
||||
with V. If such a use C is an instruction I, the it replaces uses of C
|
||||
in I with V. However, this function did not make sure to only perform
|
||||
this replacement if V dominates I. As a result, it may end up replacing
|
||||
uses of C in instructions before the definition of V.
|
||||
|
||||
The fix is to lazily compute the dominator tree in
|
||||
ReplaceConstantWithInst so that we can guard the replacement with that
|
||||
dominance check.
|
||||
|
||||
Bug: chromium:333414294
|
||||
Change-Id: I2a8bf64094298b49a1887cc7c1334e91a745c396
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/microsoft/DirectXShaderCompiler/+/5525429
|
||||
Reviewed-by: James Price <jrprice@google.com>
|
||||
Reviewed-by: dan sinclair <dsinclair@google.com>
|
||||
|
||||
diff --git a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp
|
||||
index 3f8ffdbcfa09a96899295fd85291cedb879a248b..9b843ef0e49e554001b827e30eb6256853d90f5b 100644
|
||||
--- a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp
|
||||
+++ b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp
|
||||
@@ -3271,15 +3271,34 @@ bool SROA_Helper::DoScalarReplacement(GlobalVariable *GV,
|
||||
return true;
|
||||
}
|
||||
|
||||
-static void ReplaceConstantWithInst(Constant *C, Value *V,
|
||||
+// Replaces uses of constant C in the current function
|
||||
+// with V, when those uses are dominated by V.
|
||||
+// Returns true if it was completely replaced.
|
||||
+static bool ReplaceConstantWithInst(Constant *C, Value *V,
|
||||
IRBuilder<> &Builder) {
|
||||
+ bool bReplacedAll = true;
|
||||
Function *F = Builder.GetInsertBlock()->getParent();
|
||||
+ Instruction *VInst = dyn_cast<Instruction>(V);
|
||||
+ // Lazily calculate dominance
|
||||
+ DominatorTree DT;
|
||||
+ bool Calculated = false;
|
||||
+ auto Dominates = [&](llvm::Instruction *Def, llvm::Instruction *User) {
|
||||
+ if (!Calculated) {
|
||||
+ DT.recalculate(*F);
|
||||
+ Calculated = true;
|
||||
+ }
|
||||
+ return DT.dominates(Def, User);
|
||||
+ };
|
||||
+
|
||||
for (auto it = C->user_begin(); it != C->user_end();) {
|
||||
User *U = *(it++);
|
||||
if (Instruction *I = dyn_cast<Instruction>(U)) {
|
||||
if (I->getParent()->getParent() != F)
|
||||
continue;
|
||||
- I->replaceUsesOfWith(C, V);
|
||||
+ if (VInst && Dominates(VInst, I))
|
||||
+ I->replaceUsesOfWith(C, V);
|
||||
+ else
|
||||
+ bReplacedAll = false;
|
||||
} else {
|
||||
// Skip unused ConstantExpr.
|
||||
if (U->user_empty())
|
||||
@@ -3288,10 +3307,12 @@ static void ReplaceConstantWithInst(Constant *C, Value *V,
|
||||
Instruction *Inst = CE->getAsInstruction();
|
||||
Builder.Insert(Inst);
|
||||
Inst->replaceUsesOfWith(C, V);
|
||||
- ReplaceConstantWithInst(CE, Inst, Builder);
|
||||
+ if (!ReplaceConstantWithInst(CE, Inst, Builder))
|
||||
+ bReplacedAll = false;
|
||||
}
|
||||
}
|
||||
C->removeDeadConstantUsers();
|
||||
+ return bReplacedAll;
|
||||
}
|
||||
|
||||
static void ReplaceUnboundedArrayUses(Value *V, Value *Src) {
|
||||
@@ -3531,7 +3552,8 @@ static bool ReplaceMemcpy(Value *V, Value *Src, MemCpyInst *MC,
|
||||
} else {
|
||||
// Replace Constant with a non-Constant.
|
||||
IRBuilder<> Builder(MC);
|
||||
- ReplaceConstantWithInst(C, Src, Builder);
|
||||
+ if (!ReplaceConstantWithInst(C, Src, Builder))
|
||||
+ return false;
|
||||
}
|
||||
} else {
|
||||
// Try convert special pattern for cbuffer which copy array of float4 to
|
||||
@@ -3539,7 +3561,8 @@ static bool ReplaceMemcpy(Value *V, Value *Src, MemCpyInst *MC,
|
||||
if (!tryToReplaceCBVec4ArrayToScalarArray(V, TyV, Src, TySrc, MC, DL)) {
|
||||
IRBuilder<> Builder(MC);
|
||||
Src = Builder.CreateBitCast(Src, V->getType());
|
||||
- ReplaceConstantWithInst(C, Src, Builder);
|
||||
+ if (!ReplaceConstantWithInst(C, Src, Builder))
|
||||
+ return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -5447,9 +5470,9 @@ void SROA_Parameter_HLSL::flattenArgument(
|
||||
if (Ty->isPointerTy())
|
||||
Ty = Ty->getPointerElementType();
|
||||
unsigned size = DL.getTypeAllocSize(Ty);
|
||||
-#if 0 // HLSL Change
|
||||
+#if 0 // HLSL Change
|
||||
DIExpression *DDIExp = DIB.createBitPieceExpression(debugOffset, size);
|
||||
-#else // HLSL Change
|
||||
+#else // HLSL Change
|
||||
Type *argTy = Arg->getType();
|
||||
if (argTy->isPointerTy())
|
||||
argTy = argTy->getPointerElementType();
|
||||
diff --git a/tools/clang/test/DXC/Passes/ScalarReplHLSL/scalarrepl-param-hlsl-const-to-local-and-back.hlsl b/tools/clang/test/DXC/Passes/ScalarReplHLSL/scalarrepl-param-hlsl-const-to-local-and-back.hlsl
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0f30050e69decaf7da3f2ae645611c1a49a4a719
|
||||
--- /dev/null
|
||||
+++ b/tools/clang/test/DXC/Passes/ScalarReplHLSL/scalarrepl-param-hlsl-const-to-local-and-back.hlsl
|
||||
@@ -0,0 +1,45 @@
|
||||
+// RUN: not %dxc -T ps_6_2 %s 2>&1 | FileCheck %s
|
||||
+
|
||||
+// Validate that copying from static array to local, then back to static
|
||||
+// array does not crash the compiler. This was resulting in an invalid
|
||||
+// ReplaceConstantWithInst from ScalarReplAggregatesHLSL, which would
|
||||
+// result in referenced deleted instruction in a later pass.
|
||||
+
|
||||
+// CHECK: error: Loop must have break.
|
||||
+
|
||||
+static int arr1[10] = (int[10])0;
|
||||
+static int arr2[10] = (int[10])0;
|
||||
+static float result = 0;
|
||||
+ByteAddressBuffer buff : register(t0);
|
||||
+
|
||||
+void foo() {
|
||||
+ int i = 0;
|
||||
+ if (buff.Load(0u)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ arr2[i] = arr1[i];
|
||||
+ result = float(arr1[0]);
|
||||
+}
|
||||
+
|
||||
+struct tint_symbol {
|
||||
+ float4 value : SV_Target0;
|
||||
+};
|
||||
+
|
||||
+float main_inner() {
|
||||
+ foo();
|
||||
+ bool cond = false;
|
||||
+ while (true) {
|
||||
+ if (cond) { break; }
|
||||
+ }
|
||||
+ int arr1_copy[10] = arr1; // constant to local
|
||||
+ arr1 = arr1_copy; // local to constant
|
||||
+ foo();
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+tint_symbol main() {
|
||||
+ float inner_result = main_inner();
|
||||
+ tint_symbol wrapper_result = (tint_symbol)0;
|
||||
+ wrapper_result.value.x = inner_result;
|
||||
+ return wrapper_result;
|
||||
+}
|
||||
diff --git a/tools/clang/test/DXC/Passes/ScalarReplHLSL/scalarrepl-param-hlsl-const-to-local-and-back.ll b/tools/clang/test/DXC/Passes/ScalarReplHLSL/scalarrepl-param-hlsl-const-to-local-and-back.ll
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6ca08ab3a9c500cacb715f63ee407c7add4fc51c
|
||||
--- /dev/null
|
||||
+++ b/tools/clang/test/DXC/Passes/ScalarReplHLSL/scalarrepl-param-hlsl-const-to-local-and-back.ll
|
||||
@@ -0,0 +1,253 @@
|
||||
+; RUN: %dxopt %s -hlsl-passes-resume -scalarrepl-param-hlsl -S | FileCheck %s
|
||||
+
|
||||
+; The first memcpy, from arr1 to arr1_copy.i, should be replaced by a series of 10 loads and stores,
|
||||
+; while the second memcpy, from arr1_copy.i back to arr1, should be removed:
|
||||
+; %19 = bitcast [10 x i32]* %arr1_copy.i to i8*, !dbg !33 ; line:25 col:23
|
||||
+; call void @llvm.memcpy.p0i8.p0i8.i64(i8* %19, i8* bitcast ([10 x i32]* @arr1 to i8*), i64 40, i32 1, i1 false) #0, !dbg !33 ; line:25 col:23
|
||||
+; %20 = bitcast [10 x i32]* %arr1_copy.i to i8*, !dbg !34 ; line:26 col:10
|
||||
+; call void @llvm.memcpy.p0i8.p0i8.i64(i8* bitcast ([10 x i32]* @arr1 to i8*), i8* %20, i64 40, i32 1, i1 false) #0, !dbg !34 ; line:26 col:10
|
||||
+; store i32 0, i32* %i.i.1.i, align 4, !dbg !35, !tbaa !12 ; line:7 col:7
|
||||
+
|
||||
+; CHECK: [[DEST0:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 0
|
||||
+; CHECK-NEXT: [[SRC0:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 0)
|
||||
+; CHECK-NEXT: store i32 [[SRC0:%[a-z0-9\.]+]], i32* [[DEST0:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST1:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 1
|
||||
+; CHECK-NEXT: [[SRC1:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 1)
|
||||
+; CHECK-NEXT: store i32 [[SRC1:%[a-z0-9\.]+]], i32* [[DEST1:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST2:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 2
|
||||
+; CHECK-NEXT: [[SRC2:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 2)
|
||||
+; CHECK-NEXT: store i32 [[SRC2:%[a-z0-9\.]+]], i32* [[DEST2:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST3:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 3
|
||||
+; CHECK-NEXT: [[SRC3:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 3)
|
||||
+; CHECK-NEXT: store i32 [[SRC3:%[a-z0-9\.]+]], i32* [[DEST3:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST4:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 4
|
||||
+; CHECK-NEXT: [[SRC4:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 4)
|
||||
+; CHECK-NEXT: store i32 [[SRC4:%[a-z0-9\.]+]], i32* [[DEST4:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST5:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 5
|
||||
+; CHECK-NEXT: [[SRC5:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 5)
|
||||
+; CHECK-NEXT: store i32 [[SRC5:%[a-z0-9\.]+]], i32* [[DEST5:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST6:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 6
|
||||
+; CHECK-NEXT: [[SRC6:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 6)
|
||||
+; CHECK-NEXT: store i32 [[SRC6:%[a-z0-9\.]+]], i32* [[DEST6:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST7:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 7
|
||||
+; CHECK-NEXT: [[SRC7:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 7)
|
||||
+; CHECK-NEXT: store i32 [[SRC7:%[a-z0-9\.]+]], i32* [[DEST7:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST8:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 8
|
||||
+; CHECK-NEXT: [[SRC8:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 8)
|
||||
+; CHECK-NEXT: store i32 [[SRC8:%[a-z0-9\.]+]], i32* [[DEST8:%[a-z0-9\.]+]]
|
||||
+; CHECK-NEXT: [[DEST9:%[a-z0-9\.]+]] = getelementptr inbounds [10 x i32], [10 x i32]* %arr1_copy.i, i32 0, i32 9
|
||||
+; CHECK-NEXT: [[SRC9:%[a-z0-9\.]+]] = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 9)
|
||||
+; CHECK-NEXT: store i32 [[SRC9:%[a-z0-9\.]+]], i32* [[DEST9:%[a-z0-9\.]+]]
|
||||
+
|
||||
+;
|
||||
+; Buffer Definitions:
|
||||
+;
|
||||
+; cbuffer $Globals
|
||||
+; {
|
||||
+;
|
||||
+; [0 x i8] (type annotation not present)
|
||||
+;
|
||||
+; }
|
||||
+;
|
||||
+;
|
||||
+; Resource Bindings:
|
||||
+;
|
||||
+; Name Type Format Dim ID HLSL Bind Count
|
||||
+; ------------------------------ ---------- ------- ----------- ------- -------------- ------
|
||||
+; $Globals cbuffer NA NA CB0 cb4294967295 1
|
||||
+; buff texture byte r/o T0 t0 1
|
||||
+;
|
||||
+target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
|
||||
+target triple = "dxil-ms-dx"
|
||||
+
|
||||
+%struct.ByteAddressBuffer = type { i32 }
|
||||
+%ConstantBuffer = type opaque
|
||||
+%struct.tint_symbol = type { <4 x float> }
|
||||
+%dx.types.Handle = type { i8* }
|
||||
+%dx.types.ResourceProperties = type { i32, i32 }
|
||||
+
|
||||
+@"\01?buff@@3UByteAddressBuffer@@A" = external global %struct.ByteAddressBuffer, align 4
|
||||
+@arr1 = internal global [10 x i32] zeroinitializer, align 4
|
||||
+@arr2 = internal global [10 x i32] zeroinitializer, align 4
|
||||
+@"$Globals" = external constant %ConstantBuffer
|
||||
+
|
||||
+; Function Attrs: nounwind
|
||||
+define void @main(%struct.tint_symbol* noalias sret %agg.result) #0 {
|
||||
+ %1 = alloca float
|
||||
+ store float 0.000000e+00, float* %1
|
||||
+ %i.i.1.i = alloca i32, align 4
|
||||
+ %i.i.i = alloca i32, align 4
|
||||
+ %cond.i = alloca i32, align 4
|
||||
+ %arr1_copy.i = alloca [10 x i32], align 4
|
||||
+ %inner_result = alloca float, align 4
|
||||
+ %wrapper_result = alloca %struct.tint_symbol, align 4
|
||||
+ store i32 0, i32* %i.i.i, align 4, !dbg !23, !tbaa !31 ; line:7 col:7
|
||||
+ %2 = load %struct.ByteAddressBuffer, %struct.ByteAddressBuffer* @"\01?buff@@3UByteAddressBuffer@@A", !dbg !35 ; line:8 col:7
|
||||
+ %3 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %struct.ByteAddressBuffer)"(i32 0, %struct.ByteAddressBuffer %2) #0, !dbg !35 ; line:8 col:7
|
||||
+ %4 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer)"(i32 14, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 11, i32 0 }, %struct.ByteAddressBuffer undef) #0, !dbg !35 ; line:8 col:7
|
||||
+ %5 = call i32 @"dx.hl.op.ro.i32 (i32, %dx.types.Handle, i32)"(i32 231, %dx.types.Handle %4, i32 0) #0, !dbg !35 ; line:8 col:7
|
||||
+ %6 = icmp ne i32 %5, 0, !dbg !35 ; line:8 col:7
|
||||
+ br i1 %6, label %"\01?foo@@YAXXZ.exit.i", label %7, !dbg !35 ; line:8 col:7
|
||||
+
|
||||
+; <label>:7 ; preds = %0
|
||||
+ %8 = load i32, i32* %i.i.i, align 4, !dbg !36, !tbaa !31 ; line:11 col:18
|
||||
+ %9 = getelementptr inbounds [10 x i32], [10 x i32]* @arr1, i32 0, i32 %8, !dbg !37 ; line:11 col:13
|
||||
+ %10 = load i32, i32* %9, align 4, !dbg !37, !tbaa !31 ; line:11 col:13
|
||||
+ %11 = load i32, i32* %i.i.i, align 4, !dbg !38, !tbaa !31 ; line:11 col:8
|
||||
+ %12 = getelementptr inbounds [10 x i32], [10 x i32]* @arr2, i32 0, i32 %11, !dbg !39 ; line:11 col:3
|
||||
+ store i32 %10, i32* %12, align 4, !dbg !40, !tbaa !31 ; line:11 col:11
|
||||
+ %13 = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 0), align 4, !dbg !41, !tbaa !31 ; line:12 col:18
|
||||
+ %14 = sitofp i32 %13 to float, !dbg !41 ; line:12 col:18
|
||||
+ store float %14, float* %1, align 4, !dbg !42, !tbaa !43 ; line:12 col:10
|
||||
+ br label %"\01?foo@@YAXXZ.exit.i", !dbg !45 ; line:13 col:1
|
||||
+
|
||||
+"\01?foo@@YAXXZ.exit.i": ; preds = %7, %0
|
||||
+ store i32 0, i32* %cond.i, align 4, !dbg !46, !tbaa !47 ; line:21 col:8
|
||||
+ br label %15, !dbg !49 ; line:22 col:3
|
||||
+
|
||||
+; <label>:15 ; preds = %15, %"\01?foo@@YAXXZ.exit.i"
|
||||
+ %16 = load i32, i32* %cond.i, align 4, !dbg !50, !tbaa !47, !range !51 ; line:23 col:9
|
||||
+ %17 = icmp ne i32 %16, 0, !dbg !50 ; line:23 col:9
|
||||
+ br i1 %17, label %18, label %15, !dbg !50 ; line:23 col:9
|
||||
+
|
||||
+; <label>:18 ; preds = %15
|
||||
+ %19 = bitcast [10 x i32]* %arr1_copy.i to i8*, !dbg !52 ; line:25 col:23
|
||||
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %19, i8* bitcast ([10 x i32]* @arr1 to i8*), i64 40, i32 1, i1 false) #0, !dbg !52 ; line:25 col:23
|
||||
+ %20 = bitcast [10 x i32]* %arr1_copy.i to i8*, !dbg !53 ; line:26 col:10
|
||||
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* bitcast ([10 x i32]* @arr1 to i8*), i8* %20, i64 40, i32 1, i1 false) #0, !dbg !53 ; line:26 col:10
|
||||
+ store i32 0, i32* %i.i.1.i, align 4, !dbg !54, !tbaa !31 ; line:7 col:7
|
||||
+ %21 = load %struct.ByteAddressBuffer, %struct.ByteAddressBuffer* @"\01?buff@@3UByteAddressBuffer@@A", !dbg !56 ; line:8 col:7
|
||||
+ %22 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %struct.ByteAddressBuffer)"(i32 0, %struct.ByteAddressBuffer %21) #0, !dbg !56 ; line:8 col:7
|
||||
+ %23 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer)"(i32 14, %dx.types.Handle %22, %dx.types.ResourceProperties { i32 11, i32 0 }, %struct.ByteAddressBuffer undef) #0, !dbg !56 ; line:8 col:7
|
||||
+ %24 = call i32 @"dx.hl.op.ro.i32 (i32, %dx.types.Handle, i32)"(i32 231, %dx.types.Handle %23, i32 0) #0, !dbg !56 ; line:8 col:7
|
||||
+ %25 = icmp ne i32 %24, 0, !dbg !56 ; line:8 col:7
|
||||
+ br i1 %25, label %"\01?main_inner@@YAMXZ.exit", label %26, !dbg !56 ; line:8 col:7
|
||||
+
|
||||
+; <label>:26 ; preds = %18
|
||||
+ %27 = load i32, i32* %i.i.1.i, align 4, !dbg !57, !tbaa !31 ; line:11 col:18
|
||||
+ %28 = getelementptr inbounds [10 x i32], [10 x i32]* @arr1, i32 0, i32 %27, !dbg !58 ; line:11 col:13
|
||||
+ %29 = load i32, i32* %28, align 4, !dbg !58, !tbaa !31 ; line:11 col:13
|
||||
+ %30 = load i32, i32* %i.i.1.i, align 4, !dbg !59, !tbaa !31 ; line:11 col:8
|
||||
+ %31 = getelementptr inbounds [10 x i32], [10 x i32]* @arr2, i32 0, i32 %30, !dbg !60 ; line:11 col:3
|
||||
+ store i32 %29, i32* %31, align 4, !dbg !61, !tbaa !31 ; line:11 col:11
|
||||
+ %32 = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @arr1, i32 0, i32 0), align 4, !dbg !62, !tbaa !31 ; line:12 col:18
|
||||
+ %33 = sitofp i32 %32 to float, !dbg !62 ; line:12 col:18
|
||||
+ store float %33, float* %1, align 4, !dbg !63, !tbaa !43 ; line:12 col:10
|
||||
+ br label %"\01?main_inner@@YAMXZ.exit", !dbg !64 ; line:13 col:1
|
||||
+
|
||||
+"\01?main_inner@@YAMXZ.exit": ; preds = %18, %26
|
||||
+ %34 = load float, float* %1, align 4, !dbg !65, !tbaa !43 ; line:28 col:10
|
||||
+ store float %34, float* %inner_result, align 4, !dbg !66, !tbaa !43 ; line:32 col:9
|
||||
+ %35 = getelementptr inbounds %struct.tint_symbol, %struct.tint_symbol* %wrapper_result, i32 0, i32 0, !dbg !67 ; line:33 col:45
|
||||
+ store <4 x float> zeroinitializer, <4 x float>* %35, !dbg !67 ; line:33 col:45
|
||||
+ %36 = load float, float* %inner_result, align 4, !dbg !68, !tbaa !43 ; line:34 col:28
|
||||
+ %37 = getelementptr inbounds %struct.tint_symbol, %struct.tint_symbol* %wrapper_result, i32 0, i32 0, !dbg !69 ; line:34 col:18
|
||||
+ %38 = load <4 x float>, <4 x float>* %37, align 4, !dbg !70 ; line:34 col:26
|
||||
+ %39 = getelementptr <4 x float>, <4 x float>* %37, i32 0, i32 0, !dbg !70 ; line:34 col:26
|
||||
+ store float %36, float* %39, !dbg !70 ; line:34 col:26
|
||||
+ %40 = bitcast %struct.tint_symbol* %agg.result to i8*, !dbg !71 ; line:35 col:10
|
||||
+ %41 = bitcast %struct.tint_symbol* %wrapper_result to i8*, !dbg !71 ; line:35 col:10
|
||||
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %40, i8* %41, i64 16, i32 1, i1 false), !dbg !71 ; line:35 col:10
|
||||
+ ret void, !dbg !72 ; line:35 col:3
|
||||
+}
|
||||
+
|
||||
+; Function Attrs: nounwind
|
||||
+declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) #0
|
||||
+
|
||||
+; Function Attrs: nounwind readonly
|
||||
+declare i32 @"dx.hl.op.ro.i32 (i32, %dx.types.Handle, i32)"(i32, %dx.types.Handle, i32) #1
|
||||
+
|
||||
+; Function Attrs: nounwind readnone
|
||||
+declare %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %struct.ByteAddressBuffer)"(i32, %struct.ByteAddressBuffer) #2
|
||||
+
|
||||
+; Function Attrs: nounwind readnone
|
||||
+declare %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer)"(i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer) #2
|
||||
+
|
||||
+attributes #0 = { nounwind }
|
||||
+attributes #1 = { nounwind readonly }
|
||||
+attributes #2 = { nounwind readnone }
|
||||
+
|
||||
+!llvm.module.flags = !{!0}
|
||||
+!pauseresume = !{!1}
|
||||
+!llvm.ident = !{!2}
|
||||
+!dx.version = !{!3}
|
||||
+!dx.valver = !{!4}
|
||||
+!dx.shaderModel = !{!5}
|
||||
+!dx.typeAnnotations = !{!6, !9}
|
||||
+!dx.entryPoints = !{!14}
|
||||
+!dx.fnprops = !{!20}
|
||||
+!dx.options = !{!21, !22}
|
||||
+
|
||||
+!0 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
+!1 = !{!"hlsl-hlemit", !"hlsl-hlensure"}
|
||||
+!2 = !{!"dxc(private) 1.8.0.4547 (14ec4b49d)"}
|
||||
+!3 = !{i32 1, i32 2}
|
||||
+!4 = !{i32 1, i32 8}
|
||||
+!5 = !{!"ps", i32 6, i32 2}
|
||||
+!6 = !{i32 0, %struct.tint_symbol undef, !7}
|
||||
+!7 = !{i32 16, !8}
|
||||
+!8 = !{i32 6, !"value", i32 3, i32 0, i32 4, !"SV_Target0", i32 7, i32 9}
|
||||
+!9 = !{i32 1, void (%struct.tint_symbol*)* @main, !10}
|
||||
+!10 = !{!11, !13}
|
||||
+!11 = !{i32 0, !12, !12}
|
||||
+!12 = !{}
|
||||
+!13 = !{i32 1, !12, !12}
|
||||
+!14 = !{void (%struct.tint_symbol*)* @main, !"main", null, !15, null}
|
||||
+!15 = !{!16, null, !18, null}
|
||||
+!16 = !{!17}
|
||||
+!17 = !{i32 0, %struct.ByteAddressBuffer* @"\01?buff@@3UByteAddressBuffer@@A", !"buff", i32 0, i32 0, i32 1, i32 11, i32 0, null}
|
||||
+!18 = !{!19}
|
||||
+!19 = !{i32 0, %ConstantBuffer* @"$Globals", !"$Globals", i32 0, i32 -1, i32 1, i32 0, null}
|
||||
+!20 = !{void (%struct.tint_symbol*)* @main, i32 0, i1 false}
|
||||
+!21 = !{i32 144}
|
||||
+!22 = !{i32 -1}
|
||||
+!23 = !DILocation(line: 7, column: 7, scope: !24, inlinedAt: !27)
|
||||
+!24 = !DISubprogram(name: "foo", scope: !25, file: !25, line: 6, type: !26, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: false)
|
||||
+!25 = !DIFile(filename: "333414294_simplifed.hlsl", directory: "")
|
||||
+!26 = !DISubroutineType(types: !12)
|
||||
+!27 = distinct !DILocation(line: 20, column: 3, scope: !28, inlinedAt: !29)
|
||||
+!28 = !DISubprogram(name: "main_inner", scope: !25, file: !25, line: 19, type: !26, isLocal: false, isDefinition: true, scopeLine: 19, flags: DIFlagPrototyped, isOptimized: false)
|
||||
+!29 = distinct !DILocation(line: 32, column: 24, scope: !30)
|
||||
+!30 = !DISubprogram(name: "main", scope: !25, file: !25, line: 31, type: !26, isLocal: false, isDefinition: true, scopeLine: 31, flags: DIFlagPrototyped, isOptimized: false, function: void (%struct.tint_symbol*)* @main)
|
||||
+!31 = !{!32, !32, i64 0}
|
||||
+!32 = !{!"int", !33, i64 0}
|
||||
+!33 = !{!"omnipotent char", !34, i64 0}
|
||||
+!34 = !{!"Simple C/C++ TBAA"}
|
||||
+!35 = !DILocation(line: 8, column: 7, scope: !24, inlinedAt: !27)
|
||||
+!36 = !DILocation(line: 11, column: 18, scope: !24, inlinedAt: !27)
|
||||
+!37 = !DILocation(line: 11, column: 13, scope: !24, inlinedAt: !27)
|
||||
+!38 = !DILocation(line: 11, column: 8, scope: !24, inlinedAt: !27)
|
||||
+!39 = !DILocation(line: 11, column: 3, scope: !24, inlinedAt: !27)
|
||||
+!40 = !DILocation(line: 11, column: 11, scope: !24, inlinedAt: !27)
|
||||
+!41 = !DILocation(line: 12, column: 18, scope: !24, inlinedAt: !27)
|
||||
+!42 = !DILocation(line: 12, column: 10, scope: !24, inlinedAt: !27)
|
||||
+!43 = !{!44, !44, i64 0}
|
||||
+!44 = !{!"float", !33, i64 0}
|
||||
+!45 = !DILocation(line: 13, column: 1, scope: !24, inlinedAt: !27)
|
||||
+!46 = !DILocation(line: 21, column: 8, scope: !28, inlinedAt: !29)
|
||||
+!47 = !{!48, !48, i64 0}
|
||||
+!48 = !{!"bool", !33, i64 0}
|
||||
+!49 = !DILocation(line: 22, column: 3, scope: !28, inlinedAt: !29)
|
||||
+!50 = !DILocation(line: 23, column: 9, scope: !28, inlinedAt: !29)
|
||||
+!51 = !{i32 0, i32 2}
|
||||
+!52 = !DILocation(line: 25, column: 23, scope: !28, inlinedAt: !29)
|
||||
+!53 = !DILocation(line: 26, column: 10, scope: !28, inlinedAt: !29)
|
||||
+!54 = !DILocation(line: 7, column: 7, scope: !24, inlinedAt: !55)
|
||||
+!55 = distinct !DILocation(line: 27, column: 3, scope: !28, inlinedAt: !29)
|
||||
+!56 = !DILocation(line: 8, column: 7, scope: !24, inlinedAt: !55)
|
||||
+!57 = !DILocation(line: 11, column: 18, scope: !24, inlinedAt: !55)
|
||||
+!58 = !DILocation(line: 11, column: 13, scope: !24, inlinedAt: !55)
|
||||
+!59 = !DILocation(line: 11, column: 8, scope: !24, inlinedAt: !55)
|
||||
+!60 = !DILocation(line: 11, column: 3, scope: !24, inlinedAt: !55)
|
||||
+!61 = !DILocation(line: 11, column: 11, scope: !24, inlinedAt: !55)
|
||||
+!62 = !DILocation(line: 12, column: 18, scope: !24, inlinedAt: !55)
|
||||
+!63 = !DILocation(line: 12, column: 10, scope: !24, inlinedAt: !55)
|
||||
+!64 = !DILocation(line: 13, column: 1, scope: !24, inlinedAt: !55)
|
||||
+!65 = !DILocation(line: 28, column: 10, scope: !28, inlinedAt: !29)
|
||||
+!66 = !DILocation(line: 32, column: 9, scope: !30)
|
||||
+!67 = !DILocation(line: 33, column: 45, scope: !30)
|
||||
+!68 = !DILocation(line: 34, column: 28, scope: !30)
|
||||
+!69 = !DILocation(line: 34, column: 18, scope: !30)
|
||||
+!70 = !DILocation(line: 34, column: 26, scope: !30)
|
||||
+!71 = !DILocation(line: 35, column: 10, scope: !30)
|
||||
+!72 = !DILocation(line: 35, column: 3, scope: !30)
|
||||
66
patches/DirectXShaderCompiler/cherry-pick-a65e511a14b4.patch
Normal file
66
patches/DirectXShaderCompiler/cherry-pick-a65e511a14b4.patch
Normal file
@@ -0,0 +1,66 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Antonio Maiorano <amaiorano@google.com>
|
||||
Date: Wed, 3 Apr 2024 15:58:51 -0400
|
||||
Subject: Fix ASAN use-after-free on unreferenced self-assignment of struct
|
||||
instance (#6466)
|
||||
|
||||
When deleting an unused memcpy, ScalarReplAggregatesHLSL was attempting
|
||||
to delete both the target and the source of the memcpy without first
|
||||
checking if they were both same, resulting in a double-delete.
|
||||
|
||||
Bug: chromium:331123811
|
||||
Change-Id: Idaef95a06b10a7fb6f0ca2e662972a44ec662fbc
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/microsoft/DirectXShaderCompiler/+/5419225
|
||||
Reviewed-by: David Neto <dneto@google.com>
|
||||
Reviewed-by: dan sinclair <dsinclair@chromium.org>
|
||||
Reviewed-by: Ben Clayton <bclayton@chromium.org>
|
||||
|
||||
diff --git a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp
|
||||
index 59f32a953ac5991e38c44d685f0f8fc589377b4d..3f8ffdbcfa09a96899295fd85291cedb879a248b 100644
|
||||
--- a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp
|
||||
+++ b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp
|
||||
@@ -1003,9 +1003,11 @@ void DeleteMemcpy(MemCpyInst *MI) {
|
||||
if (op0->user_empty())
|
||||
op0->eraseFromParent();
|
||||
}
|
||||
- if (Instruction *op1 = dyn_cast<Instruction>(Op1)) {
|
||||
- if (op1->user_empty())
|
||||
- op1->eraseFromParent();
|
||||
+ if (Op0 != Op1) {
|
||||
+ if (Instruction *op1 = dyn_cast<Instruction>(Op1)) {
|
||||
+ if (op1->user_empty())
|
||||
+ op1->eraseFromParent();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/tools/clang/test/DXC/unreferenced_struct_selft_assignment_crash.hlsl b/tools/clang/test/DXC/unreferenced_struct_selft_assignment_crash.hlsl
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..81adf71867c9868992372e12dc1ba81aebb48344
|
||||
--- /dev/null
|
||||
+++ b/tools/clang/test/DXC/unreferenced_struct_selft_assignment_crash.hlsl
|
||||
@@ -0,0 +1,24 @@
|
||||
+// RUN: %dxc -T cs_6_0 %s | FileCheck %s
|
||||
+
|
||||
+// Validate that self-assignment of a static struct instance that is not
|
||||
+// referenced does not crash the compiler. This was resulting in an ASAN
|
||||
+// use-after-free in ScalarReplAggregatesHLSL because DeleteMemcpy would
|
||||
+// attempt to delete both source and target, even if both were the same.
|
||||
+// CHECK: define void @main() {
|
||||
+// CHECK-NEXT: ret void
|
||||
+// CHECK-NEXT: }
|
||||
+
|
||||
+struct MyStruct {
|
||||
+ int m0;
|
||||
+};
|
||||
+
|
||||
+static MyStruct s;
|
||||
+
|
||||
+void foo() {
|
||||
+ s = s;
|
||||
+}
|
||||
+
|
||||
+[numthreads(1, 1, 1)]
|
||||
+void main() {
|
||||
+ foo();
|
||||
+}
|
||||
57
patches/DirectXShaderCompiler/cherry-pick-bc18aec94c82.patch
Normal file
57
patches/DirectXShaderCompiler/cherry-pick-bc18aec94c82.patch
Normal file
@@ -0,0 +1,57 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Antonio Maiorano <amaiorano@google.com>
|
||||
Date: Thu, 18 Apr 2024 13:07:04 -0400
|
||||
Subject: Replace dynamic_cast with virtual call (#6515)
|
||||
|
||||
Make TextDiagnosticPrinter::setPrefix a virtual function in base class
|
||||
DiagnosticConsumer. This allows us to avoid using dynamic_cast in
|
||||
BackendConsumer::DxilDiagHandler, required for codebases that do not
|
||||
enable RTTI. This is also the only place in the codebase that uses RTTI
|
||||
(AFAICT).
|
||||
|
||||
Bug: chromium:333420620
|
||||
Change-Id: Ida73077f24fdb4b705b5d868b04ac6cfecb30327
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/microsoft/DirectXShaderCompiler/+/5464347
|
||||
Reviewed-by: dan sinclair <dsinclair@chromium.org>
|
||||
Reviewed-by: David Neto <dneto@google.com>
|
||||
|
||||
diff --git a/tools/clang/include/clang/Basic/Diagnostic.h b/tools/clang/include/clang/Basic/Diagnostic.h
|
||||
index dc9f781c093c0bc8f6da773d514ac6d1503f842d..0b98dffb94185e242320409d43b74dae2c2a908d 100644
|
||||
--- a/tools/clang/include/clang/Basic/Diagnostic.h
|
||||
+++ b/tools/clang/include/clang/Basic/Diagnostic.h
|
||||
@@ -1395,6 +1395,8 @@ public:
|
||||
/// warnings and errors.
|
||||
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
||||
const Diagnostic &Info);
|
||||
+
|
||||
+ virtual void setPrefix(std::string Value) {} // HLSL Change
|
||||
};
|
||||
|
||||
/// \brief A diagnostic client that ignores all diagnostics.
|
||||
diff --git a/tools/clang/include/clang/Frontend/TextDiagnosticPrinter.h b/tools/clang/include/clang/Frontend/TextDiagnosticPrinter.h
|
||||
index 04a570559fe06a0670ef8a7e6e94c40aa37e55a9..936031e09673a09c6e1164c515efce02ac51b910 100644
|
||||
--- a/tools/clang/include/clang/Frontend/TextDiagnosticPrinter.h
|
||||
+++ b/tools/clang/include/clang/Frontend/TextDiagnosticPrinter.h
|
||||
@@ -45,7 +45,8 @@ public:
|
||||
/// setPrefix - Set the diagnostic printer prefix string, which will be
|
||||
/// printed at the start of any diagnostics. If empty, no prefix string is
|
||||
/// used.
|
||||
- void setPrefix(std::string Value) { Prefix = Value; }
|
||||
+ // HLSL Change: add override
|
||||
+ void setPrefix(std::string Value) override { Prefix = Value; }
|
||||
|
||||
void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override;
|
||||
void EndSourceFile() override;
|
||||
diff --git a/tools/clang/lib/CodeGen/CodeGenAction.cpp b/tools/clang/lib/CodeGen/CodeGenAction.cpp
|
||||
index 4fa721e812296356e31fc1bf6ea35ce295c2592c..68ebaadf5a8960c8def189248412136fe9543422 100644
|
||||
--- a/tools/clang/lib/CodeGen/CodeGenAction.cpp
|
||||
+++ b/tools/clang/lib/CodeGen/CodeGenAction.cpp
|
||||
@@ -557,7 +557,7 @@ BackendConsumer::DxilDiagHandler(const llvm::DiagnosticInfoDxil &D) {
|
||||
|
||||
// If no location information is available, add function name
|
||||
if (Loc.isInvalid()) {
|
||||
- auto *DiagClient = dynamic_cast<TextDiagnosticPrinter*>(Diags.getClient());
|
||||
+ auto *DiagClient = Diags.getClient();
|
||||
auto *func = D.getFunction();
|
||||
if (DiagClient && func)
|
||||
DiagClient->setPrefix("Function: " + func->getName().str());
|
||||
161
patches/DirectXShaderCompiler/cherry-pick-bd7aa9779873.patch
Normal file
161
patches/DirectXShaderCompiler/cherry-pick-bd7aa9779873.patch
Normal file
@@ -0,0 +1,161 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Antonio Maiorano <amaiorano@google.com>
|
||||
Date: Thu, 25 Apr 2024 16:49:11 -0400
|
||||
Subject: Fixed crash in loop unroll caused by bug in structurize loop exits
|
||||
(#6548)
|
||||
|
||||
Fixed a bug in `hlsl::RemoveUnstructuredLoopExits` where when a new
|
||||
exiting block is created from splitting, it was added to the current
|
||||
loop being processed, when it could also part of an inner loop. Not
|
||||
adding the new block to inner loops that it's part of makes the inner
|
||||
loops malformed, and causes crash.
|
||||
|
||||
This fix adds the new block to the inner most loop that it should be
|
||||
part of. Also adds the `StructurizeLoopExits` option to `loop-unroll`
|
||||
pass, which was missing before.
|
||||
|
||||
Bug: chromium:333508731
|
||||
Change-Id: I7efc21bc61aeb81b4906a600c35272af232710ea
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/microsoft/DirectXShaderCompiler/+/5490380
|
||||
Reviewed-by: James Price <jrprice@google.com>
|
||||
Reviewed-by: Ben Clayton <bclayton@chromium.org>
|
||||
|
||||
diff --git a/lib/Transforms/Scalar/DxilRemoveUnstructuredLoopExits.cpp b/lib/Transforms/Scalar/DxilRemoveUnstructuredLoopExits.cpp
|
||||
index b6a07d6b27a23ee3831e84cee82299d6d405a288..ef6718f0f22ee33e3f16f9801a64c1a6fb6c653a 100644
|
||||
--- a/lib/Transforms/Scalar/DxilRemoveUnstructuredLoopExits.cpp
|
||||
+++ b/lib/Transforms/Scalar/DxilRemoveUnstructuredLoopExits.cpp
|
||||
@@ -447,7 +447,12 @@ static bool RemoveUnstructuredLoopExitsIteration(BasicBlock *exiting_block,
|
||||
new_exiting_block->splitBasicBlock(new_exiting_block->getFirstNonPHI());
|
||||
new_exiting_block->setName("dx.struct_exit.new_exiting");
|
||||
new_not_exiting_block->setName(old_name);
|
||||
- L->addBasicBlockToLoop(new_not_exiting_block, *LI);
|
||||
+ // Query for new_exiting_block's own loop to add new_not_exiting_block to.
|
||||
+ // It's possible that new_exiting_block is part of another inner loop
|
||||
+ // separate from L. If added directly to L, the inner loop(s) will not
|
||||
+ // contain new_not_exiting_block, making them malformed.
|
||||
+ Loop *inner_loop_of_exiting_block = LI->getLoopFor(new_exiting_block);
|
||||
+ inner_loop_of_exiting_block->addBasicBlockToLoop(new_not_exiting_block, *LI);
|
||||
|
||||
// Branch to latch_exit
|
||||
new_exiting_block->getTerminator()->eraseFromParent();
|
||||
diff --git a/lib/Transforms/Scalar/LoopUnrollPass.cpp b/lib/Transforms/Scalar/LoopUnrollPass.cpp
|
||||
index dd520f7e57d25311be7f3773849a00efaabe6717..b17a5a4a0bc368f16020c4153370ea2c92e5c26c 100644
|
||||
--- a/lib/Transforms/Scalar/LoopUnrollPass.cpp
|
||||
+++ b/lib/Transforms/Scalar/LoopUnrollPass.cpp
|
||||
@@ -155,6 +155,18 @@ namespace {
|
||||
bool UserAllowPartial;
|
||||
bool UserRuntime;
|
||||
|
||||
+ // HLSL Change - begin
|
||||
+ // Function overrides that resolve options when used for DxOpt
|
||||
+ void applyOptions(PassOptions O) override {
|
||||
+ GetPassOptionBool(O, "StructurizeLoopExits", &StructurizeLoopExits,
|
||||
+ false);
|
||||
+ }
|
||||
+ void dumpConfig(raw_ostream &OS) override {
|
||||
+ LoopPass::dumpConfig(OS);
|
||||
+ OS << ",StructurizeLoopExits=" << StructurizeLoopExits;
|
||||
+ }
|
||||
+ // HLSL Change - end
|
||||
+
|
||||
bool runOnLoop(Loop *L, LPPassManager &LPM) override;
|
||||
|
||||
/// This transformation requires natural loop information & requires that
|
||||
diff --git a/tools/clang/test/DXC/loop_structurize_exit_inner_latch_regression.ll b/tools/clang/test/DXC/loop_structurize_exit_inner_latch_regression.ll
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..743135541cd8faec287164ba3b321a59432832b6
|
||||
--- /dev/null
|
||||
+++ b/tools/clang/test/DXC/loop_structurize_exit_inner_latch_regression.ll
|
||||
@@ -0,0 +1,75 @@
|
||||
+; RUN: %dxopt %s -S -loop-unroll,StructurizeLoopExits=1 | FileCheck %s
|
||||
+; RUN: %dxopt %s -S -dxil-loop-unroll,StructurizeLoopExits=1 | FileCheck %s
|
||||
+
|
||||
+; CHECK: mul nsw i32
|
||||
+; CHECK: mul nsw i32
|
||||
+; CHECK-NOT: mul nsw i32
|
||||
+
|
||||
+; This is a regression test for a crash in loop unroll. When there are multiple
|
||||
+; exits, the compiler will run hlsl::RemoveUnstructuredLoopExits to try to
|
||||
+; avoid unstructured code.
|
||||
+;
|
||||
+; In this test, the compiler will try to unroll the middle loop. The exit edge
|
||||
+; from %land.lhs.true to %if.then will be removed, and %if.end will be split at
|
||||
+; the beginning, and branch to %if.end instead.
|
||||
+;
|
||||
+; Since the new split block at %if.end becomes the new latch of the inner-most
|
||||
+; loop, it needs to be added to the Loop analysis structure of the inner loop.
|
||||
+; However, it was only added to the current middle loop that is being unrolled.
|
||||
+
|
||||
+target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
|
||||
+target triple = "dxil-ms-dx"
|
||||
+
|
||||
+; Function Attrs: nounwind
|
||||
+define void @main(i32 *%arg0, i32 *%arg1, i32 *%arg2) #0 {
|
||||
+entry:
|
||||
+ br label %while.body.3.preheader.lr.ph
|
||||
+
|
||||
+while.body.3.preheader.lr.ph.loopexit: ; preds = %for.inc
|
||||
+ br label %while.body.3.preheader.lr.ph
|
||||
+
|
||||
+while.body.3.preheader.lr.ph: ; preds = %while.body.3.preheader.lr.ph.loopexit, %entry
|
||||
+ br label %while.body.3.preheader
|
||||
+
|
||||
+while.body.3.preheader: ; preds = %while.body.3.preheader.lr.ph, %for.inc
|
||||
+ %i.0 = phi i32 [ 0, %while.body.3.preheader.lr.ph ], [ %inc, %for.inc ]
|
||||
+ br label %while.body.3
|
||||
+
|
||||
+while.body.3: ; preds = %while.body.3.preheader, %if.end
|
||||
+ %load_arg0 = load i32, i32* %arg0
|
||||
+ %cmp4 = icmp sgt i32 %load_arg0, 0
|
||||
+ br i1 %cmp4, label %land.lhs.true, label %if.end
|
||||
+
|
||||
+land.lhs.true: ; preds = %while.body.3
|
||||
+ %load_arg1 = load i32, i32* %arg1
|
||||
+ %load_arg2 = load i32, i32* %arg2
|
||||
+ %mul = mul nsw i32 %load_arg2, %load_arg1
|
||||
+ %cmp7 = icmp eq i32 %mul, 10
|
||||
+ br i1 %cmp7, label %if.then, label %if.end
|
||||
+
|
||||
+if.then: ; preds = %land.lhs.true
|
||||
+ ret void
|
||||
+
|
||||
+if.end: ; preds = %land.lhs.true, %while.body.3
|
||||
+ %cmp10 = icmp sle i32 %i.0, 4
|
||||
+ br i1 %cmp10, label %for.inc, label %while.body.3
|
||||
+
|
||||
+for.inc: ; preds = %if.end
|
||||
+ %inc = add nsw i32 %i.0, 1
|
||||
+ %cmp = icmp slt i32 %inc, 2
|
||||
+ br i1 %cmp, label %while.body.3.preheader, label %while.body.3.preheader.lr.ph.loopexit, !llvm.loop !3
|
||||
+}
|
||||
+
|
||||
+attributes #0 = { nounwind }
|
||||
+attributes #1 = { nounwind readnone }
|
||||
+attributes #2 = { nounwind readonly }
|
||||
+
|
||||
+!llvm.module.flags = !{!0}
|
||||
+!pauseresume = !{!1}
|
||||
+!llvm.ident = !{!2}
|
||||
+
|
||||
+!0 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
+!1 = !{!"hlsl-dxilemit", !"hlsl-dxilload"}
|
||||
+!2 = !{!"dxc(private) 1.8.0.14563 (main, 07ce88034-dirty)"}
|
||||
+!3 = distinct !{!3, !4}
|
||||
+!4 = !{!"llvm.loop.unroll.full"}
|
||||
diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py
|
||||
index 77f5671016eb66a4ddf8a943ec8cb05e8d87c9cd..ca8d16bd2562e26e8572413499d32dc2232de5c0 100644
|
||||
--- a/utils/hct/hctdb.py
|
||||
+++ b/utils/hct/hctdb.py
|
||||
@@ -6680,6 +6680,12 @@ class db_dxil(object):
|
||||
"t": "unsigned",
|
||||
"d": "Unrolled size limit for loops with an unroll(full) or unroll_count pragma.",
|
||||
},
|
||||
+ {
|
||||
+ "n": "StructurizeLoopExits",
|
||||
+ "t": "bool",
|
||||
+ "c": 1,
|
||||
+ "d": "Whether the unroller should try to structurize loop exits first.",
|
||||
+ },
|
||||
],
|
||||
)
|
||||
add_pass("mldst-motion", "MergedLoadStoreMotion", "MergedLoadStoreMotion", [])
|
||||
4
patches/angle/.patches
Normal file
4
patches/angle/.patches
Normal file
@@ -0,0 +1,4 @@
|
||||
m123_vulkan_fix_access_to_inactive_attributes.patch
|
||||
cherry-pick-f6672dbbe223.patch
|
||||
cherry-pick-ba3b4e239620.patch
|
||||
cherry-pick-c67f290ef0f0.patch
|
||||
249
patches/angle/cherry-pick-ba3b4e239620.patch
Normal file
249
patches/angle/cherry-pick-ba3b4e239620.patch
Normal file
@@ -0,0 +1,249 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Date: Mon, 8 Apr 2024 10:14:45 -0400
|
||||
Subject: M123: SPIR-V: Fix const constructors with single scalar
|
||||
|
||||
These constructors may be generated because of
|
||||
RemoveArrayLengthTraverser.
|
||||
|
||||
Bug: chromium:332546345
|
||||
Change-Id: I2b2bf3728ef5bae148abc2a8518f8f3f42850025
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5462388
|
||||
(cherry picked from commit 0b776d32f69a932acb61963d9daad9e13f610944)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5473406
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Reviewed-by: Geoff Lang <geofflang@chromium.org>
|
||||
Reviewed-by: Daniel Gagnon <dgagnon@google.com>
|
||||
|
||||
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
|
||||
index eddc5e724e8e949aff0624c6caf9aadec7c14647..57a71c5d13fbf5e458c26b084e1f9834edb37f58 100644
|
||||
--- a/src/compiler/translator/Compiler.cpp
|
||||
+++ b/src/compiler/translator/Compiler.cpp
|
||||
@@ -1056,6 +1056,11 @@ bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
|
||||
{
|
||||
return false;
|
||||
}
|
||||
+ // Fold the expressions again, because |RemoveArrayLengthMethod| can introduce new constants.
|
||||
+ if (!FoldExpressions(this, root, &mDiagnostics))
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
|
||||
if (!RemoveUnreferencedVariables(this, root, &mSymbolTable))
|
||||
{
|
||||
diff --git a/src/compiler/translator/spirv/OutputSPIRV.cpp b/src/compiler/translator/spirv/OutputSPIRV.cpp
|
||||
index caa8f956716abf53aaeb58a5f654f5a4f04c4d6a..67b1fdd4784660483a408f1ee27ce48b05ffcb0a 100644
|
||||
--- a/src/compiler/translator/spirv/OutputSPIRV.cpp
|
||||
+++ b/src/compiler/translator/spirv/OutputSPIRV.cpp
|
||||
@@ -1335,6 +1335,8 @@ spirv::IdRef OutputSPIRVTraverser::createComplexConstant(const TType &type,
|
||||
|
||||
if (type.isMatrix() && !type.isArray())
|
||||
{
|
||||
+ ASSERT(parameters.size() == type.getRows() * type.getCols());
|
||||
+
|
||||
// Matrices are constructed from their columns.
|
||||
spirv::IdRefList columnIds;
|
||||
|
||||
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
|
||||
index cfe6414d52647db92669be954cadd15344603559..a4035db329548491c91e2f7383f837001540c065 100644
|
||||
--- a/src/tests/gl_tests/GLSLTest.cpp
|
||||
+++ b/src/tests/gl_tests/GLSLTest.cpp
|
||||
@@ -8486,6 +8486,198 @@ void main()
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
}
|
||||
|
||||
+// Test that array length inside vector constructor works.
|
||||
+TEST_P(GLSLTest_ES3, ArrayLengthInVectorConstructor)
|
||||
+{
|
||||
+ const char kVS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+flat out uvec4 v;
|
||||
+
|
||||
+int[1] f0()
|
||||
+{
|
||||
+ return int[1](1);
|
||||
+}
|
||||
+void main()
|
||||
+{
|
||||
+ v = uvec4(vec4(f0().length()));
|
||||
+
|
||||
+ gl_Position.x = ((gl_VertexID & 1) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.y = ((gl_VertexID & 2) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.zw = vec2(0, 1);
|
||||
+})";
|
||||
+
|
||||
+ const char kFS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+flat in uvec4 v;
|
||||
+out vec4 color;
|
||||
+
|
||||
+bool isEq(uint a, float b) { return abs(float(a) - b) < 0.01; }
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (isEq(v[0], 1.) &&
|
||||
+ isEq(v[1], 1.) &&
|
||||
+ isEq(v[2], 1.) &&
|
||||
+ isEq(v[3], 1.))
|
||||
+ {
|
||||
+ color = vec4(0, 1, 0, 1);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ color = vec4(1, 0, 0, 1);
|
||||
+ }
|
||||
+})";
|
||||
+
|
||||
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
|
||||
+ glUseProgram(program);
|
||||
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
+}
|
||||
+
|
||||
+// Test that array length inside vector constructor works in complex expression.
|
||||
+TEST_P(GLSLTest_ES3, ArrayLengthInVectorConstructorComplex)
|
||||
+{
|
||||
+ const char kVS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+out vec4 v;
|
||||
+
|
||||
+int[1] f0()
|
||||
+{
|
||||
+ return int[1](1);
|
||||
+}
|
||||
+void main()
|
||||
+{
|
||||
+ v = vec4(float(uint(f0().length()) + 1u) / 4.);
|
||||
+
|
||||
+ gl_Position.x = ((gl_VertexID & 1) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.y = ((gl_VertexID & 2) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.zw = vec2(0, 1);
|
||||
+})";
|
||||
+
|
||||
+ const char kFS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+in vec4 v;
|
||||
+out vec4 color;
|
||||
+
|
||||
+bool isEq(float a, float b) { return abs(float(a) - b) < 0.01; }
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (isEq(v[0], 0.5) &&
|
||||
+ isEq(v[1], 0.5) &&
|
||||
+ isEq(v[2], 0.5) &&
|
||||
+ isEq(v[3], 0.5))
|
||||
+ {
|
||||
+ color = vec4(0, 1, 0, 1);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ color = vec4(1, 0, 0, 1);
|
||||
+ }
|
||||
+})";
|
||||
+
|
||||
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
|
||||
+ glUseProgram(program);
|
||||
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
+}
|
||||
+
|
||||
+// Test that array length inside matrix constructor works.
|
||||
+TEST_P(GLSLTest_ES3, ArrayLengthInMatrixConstructor)
|
||||
+{
|
||||
+ const char kVS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+out mat2x2 v;
|
||||
+
|
||||
+int[1] f0()
|
||||
+{
|
||||
+ return int[1](1);
|
||||
+}
|
||||
+void main()
|
||||
+{
|
||||
+ v = mat2x2(f0().length());
|
||||
+
|
||||
+ gl_Position.x = ((gl_VertexID & 1) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.y = ((gl_VertexID & 2) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.zw = vec2(0, 1);
|
||||
+})";
|
||||
+
|
||||
+ const char kFS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+in mat2x2 v;
|
||||
+out vec4 color;
|
||||
+
|
||||
+bool isEq(float a, float b) { return abs(a - b) < 0.01; }
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (isEq(v[0][0], 1.) &&
|
||||
+ isEq(v[0][1], 0.) &&
|
||||
+ isEq(v[1][0], 0.) &&
|
||||
+ isEq(v[1][1], 1.))
|
||||
+ {
|
||||
+ color = vec4(0, 1, 0, 1);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ color = vec4(1, 0, 0, 1);
|
||||
+ }
|
||||
+})";
|
||||
+
|
||||
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
|
||||
+ glUseProgram(program);
|
||||
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
+}
|
||||
+
|
||||
+// Test that array length inside vector constructor inside matrix constructor works.
|
||||
+TEST_P(GLSLTest_ES3, ArrayLengthInVectorInMatrixConstructor)
|
||||
+{
|
||||
+ const char kVS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+out mat2x2 v;
|
||||
+
|
||||
+int[1] f0()
|
||||
+{
|
||||
+ return int[1](1);
|
||||
+}
|
||||
+void main()
|
||||
+{
|
||||
+ v = mat2x2(vec2(f0().length()), f0().length(), 0);
|
||||
+
|
||||
+ gl_Position.x = ((gl_VertexID & 1) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.y = ((gl_VertexID & 2) == 0 ? -1.0 : 1.0);
|
||||
+ gl_Position.zw = vec2(0, 1);
|
||||
+})";
|
||||
+
|
||||
+ const char kFS[] = R"(#version 300 es
|
||||
+precision highp float;
|
||||
+in mat2x2 v;
|
||||
+out vec4 color;
|
||||
+
|
||||
+bool isEq(float a, float b) { return abs(a - b) < 0.01; }
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ if (isEq(v[0][0], 1.) &&
|
||||
+ isEq(v[0][1], 1.) &&
|
||||
+ isEq(v[1][0], 1.) &&
|
||||
+ isEq(v[1][1], 0.))
|
||||
+ {
|
||||
+ color = vec4(0, 1, 0, 1);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ color = vec4(1, 0, 0, 1);
|
||||
+ }
|
||||
+})";
|
||||
+
|
||||
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
|
||||
+ glUseProgram(program);
|
||||
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
|
||||
+}
|
||||
+
|
||||
// Test that statements inside switch() get translated to correct HLSL.
|
||||
TEST_P(GLSLTest_ES3, DifferentStatementsInsideSwitch)
|
||||
{
|
||||
32
patches/angle/cherry-pick-c67f290ef0f0.patch
Normal file
32
patches/angle/cherry-pick-c67f290ef0f0.patch
Normal file
@@ -0,0 +1,32 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Mon, 29 Apr 2024 15:27:36 -0400
|
||||
Subject: M124: GL: Sync unpack state for glCompressedTexSubImage3D
|
||||
|
||||
Unpack state is supposed to be ignored for compressed tex image calls
|
||||
but some drivers use it anyways and read incorrect data.
|
||||
|
||||
Texture3DTestES3.PixelUnpackStateTexSubImage covers this case.
|
||||
|
||||
Bug: chromium:337766133
|
||||
Change-Id: Ic11a056113b1850bd5b4d6840527164a12849a22
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5498735
|
||||
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
(cherry picked from commit 1bb1ee061fe0bce322fb93b447a72e72c993a1f2)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5518811
|
||||
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
|
||||
Reviewed-by: Srinivas Sista <srinivassista@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
|
||||
index c659aacb9e48d7eab033f0ea59d3514f557a430b..f96eefe53f11a8a57fc88998c2ba22a2dacf1d65 100644
|
||||
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
|
||||
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
|
||||
@@ -664,6 +664,7 @@ angle::Result TextureGL::setCompressedSubImage(const gl::Context *context,
|
||||
nativegl::GetCompressedSubTexImageFormat(functions, features, format);
|
||||
|
||||
stateManager->bindTexture(getType(), mTextureID);
|
||||
+ ANGLE_TRY(stateManager->setPixelUnpackState(context, unpack));
|
||||
if (nativegl::UseTexImage2D(getType()))
|
||||
{
|
||||
ASSERT(area.z == 0 && area.depth == 1);
|
||||
267
patches/angle/cherry-pick-f6672dbbe223.patch
Normal file
267
patches/angle/cherry-pick-f6672dbbe223.patch
Normal file
@@ -0,0 +1,267 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Date: Mon, 25 Mar 2024 14:46:56 -0400
|
||||
Subject: M123: Translator: Disallow samplers in structs in interface blocks
|
||||
|
||||
As disallowed by the spec:
|
||||
|
||||
> Types and declarators are the same as for other uniform variable
|
||||
> declarations outside blocks, with these exceptions:
|
||||
>
|
||||
> * opaque types are not allowed
|
||||
|
||||
Bug: chromium:328859176
|
||||
Change-Id: Ib94977860102329e520e635c3757827c93ca2163
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5391986
|
||||
Auto-Submit: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Reviewed-by: Geoff Lang <geofflang@chromium.org>
|
||||
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
(cherry picked from commit a0fa06f6d79ced897c0fe2795551268199d29806)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5435737
|
||||
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
|
||||
|
||||
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
|
||||
index 38bd4ca13a4a8828ebbe64e19bd740639bce5083..98b4dc46fec65269a87ee920d8e63a1d0350fbae 100644
|
||||
--- a/src/compiler/translator/ParseContext.cpp
|
||||
+++ b/src/compiler/translator/ParseContext.cpp
|
||||
@@ -34,27 +34,39 @@ namespace
|
||||
|
||||
const int kWebGLMaxStructNesting = 4;
|
||||
|
||||
-bool ContainsSampler(const TStructure *structType);
|
||||
+struct IsSamplerFunc
|
||||
+{
|
||||
+ bool operator()(TBasicType type) { return IsSampler(type); }
|
||||
+};
|
||||
+struct IsOpaqueFunc
|
||||
+{
|
||||
+ bool operator()(TBasicType type) { return IsOpaqueType(type); }
|
||||
+};
|
||||
+
|
||||
+template <typename OpaqueFunc>
|
||||
+bool ContainsOpaque(const TStructure *structType);
|
||||
|
||||
-bool ContainsSampler(const TType &type)
|
||||
+template <typename OpaqueFunc>
|
||||
+bool ContainsOpaque(const TType &type)
|
||||
{
|
||||
- if (IsSampler(type.getBasicType()))
|
||||
+ if (OpaqueFunc{}(type.getBasicType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (type.getBasicType() == EbtStruct)
|
||||
{
|
||||
- return ContainsSampler(type.getStruct());
|
||||
+ return ContainsOpaque<OpaqueFunc>(type.getStruct());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
-bool ContainsSampler(const TStructure *structType)
|
||||
+template <typename OpaqueFunc>
|
||||
+bool ContainsOpaque(const TStructure *structType)
|
||||
{
|
||||
for (const auto &field : structType->fields())
|
||||
{
|
||||
- if (ContainsSampler(*field->type()))
|
||||
+ if (ContainsOpaque<OpaqueFunc>(*field->type()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1113,7 +1125,7 @@ bool TParseContext::checkIsNotOpaqueType(const TSourceLoc &line,
|
||||
{
|
||||
if (pType.type == EbtStruct)
|
||||
{
|
||||
- if (ContainsSampler(pType.userDef))
|
||||
+ if (ContainsOpaque<IsSamplerFunc>(pType.userDef))
|
||||
{
|
||||
std::stringstream reasonStream = sh::InitializeStream<std::stringstream>();
|
||||
reasonStream << reason << " (structure contains a sampler)";
|
||||
@@ -4979,12 +4991,9 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
|
||||
{
|
||||
TField *field = (*fieldList)[memberIndex];
|
||||
TType *fieldType = field->type();
|
||||
- if (IsOpaqueType(fieldType->getBasicType()))
|
||||
+ if (ContainsOpaque<IsOpaqueFunc>(*fieldType))
|
||||
{
|
||||
- std::string reason("unsupported type - ");
|
||||
- reason += fieldType->getBasicString();
|
||||
- reason += " types are not allowed in interface blocks";
|
||||
- error(field->line(), reason.c_str(), fieldType->getBasicString());
|
||||
+ error(field->line(), "Opaque types are not allowed in interface blocks", blockName);
|
||||
}
|
||||
|
||||
const TQualifier qualifier = fieldType->getQualifier();
|
||||
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
|
||||
index ed63153e49b2425a974a497a1fae2f9fdc79afa6..cfe6414d52647db92669be954cadd15344603559 100644
|
||||
--- a/src/tests/gl_tests/GLSLTest.cpp
|
||||
+++ b/src/tests/gl_tests/GLSLTest.cpp
|
||||
@@ -6716,7 +6716,34 @@ void main()
|
||||
gl_FragColor = vec4(f(us), 0, 0, 1);
|
||||
})";
|
||||
|
||||
- CompileShader(GL_FRAGMENT_SHADER, kFS);
|
||||
+ GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS);
|
||||
+ EXPECT_NE(fs, 0u);
|
||||
+ ASSERT_GL_NO_ERROR();
|
||||
+}
|
||||
+
|
||||
+// Test that structs with samplers are not allowed in interface blocks. This is forbidden per
|
||||
+// GLES3:
|
||||
+//
|
||||
+// > Types and declarators are the same as for other uniform variable declarations outside blocks,
|
||||
+// > with these exceptions:
|
||||
+// > * opaque types are not allowed
|
||||
+TEST_P(GLSLTest_ES3, StructWithSamplersDisallowedInInterfaceBlock)
|
||||
+{
|
||||
+ const char kFS[] = R"(#version 300 es
|
||||
+precision mediump float;
|
||||
+struct S { sampler2D samp; bool b; };
|
||||
+
|
||||
+layout(std140) uniform Buffer { S s; } buffer;
|
||||
+
|
||||
+out vec4 color;
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ color = texture(buffer.s.samp, vec2(0));
|
||||
+})";
|
||||
+
|
||||
+ GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS);
|
||||
+ EXPECT_EQ(fs, 0u);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
|
||||
@@ -18357,6 +18384,116 @@ void main() {
|
||||
EXPECT_EQ(0u, shader);
|
||||
}
|
||||
|
||||
+// Same as TooManyFieldsInStruct, but with samplers in the struct.
|
||||
+TEST_P(GLSLTest_ES3, TooManySamplerFieldsInStruct)
|
||||
+{
|
||||
+ std::ostringstream fs;
|
||||
+ fs << R"(#version 300 es
|
||||
+precision highp float;
|
||||
+struct TooManyFields
|
||||
+{
|
||||
+)";
|
||||
+ for (uint32_t i = 0; i < (1 << 16); ++i)
|
||||
+ {
|
||||
+ fs << " sampler2D field" << i << ";\n";
|
||||
+ }
|
||||
+ fs << R"(};
|
||||
+uniform TooManyFields s;
|
||||
+out vec4 color;
|
||||
+void main() {
|
||||
+ color = texture(s.field0, vec2(0));
|
||||
+})";
|
||||
+
|
||||
+ GLuint shader = CompileShader(GL_FRAGMENT_SHADER, fs.str().c_str());
|
||||
+ EXPECT_EQ(0u, shader);
|
||||
+}
|
||||
+
|
||||
+// More complex variation of ManySamplerFieldsInStruct. This one compiles fine.
|
||||
+TEST_P(GLSLTest_ES3, ManySamplerFieldsInStructComplex)
|
||||
+{
|
||||
+ // D3D and OpenGL may be more restrictive about this many samplers.
|
||||
+ ANGLE_SKIP_TEST_IF(IsD3D() || IsOpenGL());
|
||||
+
|
||||
+ std::ostringstream fs;
|
||||
+ fs << R"(#version 300 es
|
||||
+precision highp float;
|
||||
+
|
||||
+struct X {
|
||||
+ mediump sampler2D a[0xf00];
|
||||
+ mediump sampler2D b[0xf00];
|
||||
+ mediump sampler2D c[0xf000];
|
||||
+ mediump sampler2D d[0xf00];
|
||||
+};
|
||||
+
|
||||
+struct Y {
|
||||
+ X s1;
|
||||
+ mediump sampler2D a[0xf00];
|
||||
+ mediump sampler2D b[0xf000];
|
||||
+ mediump sampler2D c[0x14000];
|
||||
+};
|
||||
+
|
||||
+struct S {
|
||||
+ Y s1;
|
||||
+};
|
||||
+
|
||||
+struct structBuffer { S s; };
|
||||
+
|
||||
+uniform structBuffer b;
|
||||
+
|
||||
+out vec4 color;
|
||||
+void main()
|
||||
+{
|
||||
+ color = texture(b.s.s1.s1.c[0], vec2(0));
|
||||
+})";
|
||||
+
|
||||
+ GLuint shader = CompileShader(GL_FRAGMENT_SHADER, fs.str().c_str());
|
||||
+ EXPECT_NE(0u, shader);
|
||||
+}
|
||||
+
|
||||
+// Make sure a large array of samplers works.
|
||||
+TEST_P(GLSLTest, ManySamplers)
|
||||
+{
|
||||
+ // D3D and OpenGL may be more restrictive about this many samplers.
|
||||
+ ANGLE_SKIP_TEST_IF(IsD3D() || IsOpenGL());
|
||||
+
|
||||
+ std::ostringstream fs;
|
||||
+ fs << R"(precision highp float;
|
||||
+
|
||||
+uniform mediump sampler2D c[0x12000];
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ gl_FragColor = texture2D(c[0], vec2(0));
|
||||
+})";
|
||||
+
|
||||
+ GLuint shader = CompileShader(GL_FRAGMENT_SHADER, fs.str().c_str());
|
||||
+ EXPECT_NE(0u, shader);
|
||||
+}
|
||||
+
|
||||
+// Make sure a large array of samplers works when declared in a struct.
|
||||
+TEST_P(GLSLTest, ManySamplersInStruct)
|
||||
+{
|
||||
+ // D3D and OpenGL may be more restrictive about this many samplers.
|
||||
+ ANGLE_SKIP_TEST_IF(IsD3D() || IsOpenGL());
|
||||
+
|
||||
+ std::ostringstream fs;
|
||||
+ fs << R"(precision highp float;
|
||||
+
|
||||
+struct X {
|
||||
+ mediump sampler2D c[0x12000];
|
||||
+};
|
||||
+
|
||||
+uniform X x;
|
||||
+
|
||||
+void main()
|
||||
+{
|
||||
+ gl_FragColor = texture2D(x.c[0], vec2(0));
|
||||
+})";
|
||||
+
|
||||
+ GLuint shader = CompileShader(GL_FRAGMENT_SHADER, fs.str().c_str());
|
||||
+ EXPECT_NE(0u, shader);
|
||||
+}
|
||||
+
|
||||
// Test that passing large arrays to functions are compiled correctly. Regression test for the
|
||||
// SPIR-V generator that made a copy of the array to pass to the function, by decomposing and
|
||||
// reconstructing it (in the absence of OpCopyLogical), but the reconstruction instruction has a
|
||||
diff --git a/src/tests/gl_tests/PixelLocalStorageTest.cpp b/src/tests/gl_tests/PixelLocalStorageTest.cpp
|
||||
index c49ba5741ad565ad9637fb2188a472ccbebc6284..126936271eb25eec601349a560fabc6f0f7d4b75 100644
|
||||
--- a/src/tests/gl_tests/PixelLocalStorageTest.cpp
|
||||
+++ b/src/tests/gl_tests/PixelLocalStorageTest.cpp
|
||||
@@ -5573,8 +5573,7 @@ TEST_P(PixelLocalStorageCompilerTest, Declarations)
|
||||
EXPECT_FALSE(log.compileFragmentShader(kPLSInStruct));
|
||||
EXPECT_TRUE(log.has("ERROR: 0:5: 'pixelLocalANGLE' : disallowed type in struct"));
|
||||
EXPECT_TRUE(
|
||||
- log.has("ERROR: 0:10: 'pixelLocalANGLE' : unsupported type - pixelLocalANGLE types are not "
|
||||
- "allowed in interface blocks"));
|
||||
+ log.has("ERROR: 0:10: 'PLSBlock' : Opaque types are not allowed in interface blocks"));
|
||||
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Tue, 12 Mar 2024 16:06:37 -0400
|
||||
Subject: M123: Vulkan: Fix access to inactive attributes
|
||||
|
||||
... within range of active ones. Since a buffer is bound for inactive
|
||||
attributes, it must be considered accessed.
|
||||
|
||||
Ultimately, the nullDescriptor feature could be used to avoid binding a
|
||||
buffer for inactive attributes.
|
||||
|
||||
Bug: chromium:327807820
|
||||
Change-Id: I953b419d8ec51760e8848409024cad5083888fa2
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5386431
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@google.com>
|
||||
|
||||
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
|
||||
index 63bfa0729b266ceca54e10153f561f74a1be0c27..a0cbaf8cefbae1453922e09aadcd13df6f478782 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
|
||||
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
|
||||
@@ -2610,8 +2610,7 @@ angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *d
|
||||
vertexArrayVk->getCurrentArrayBuffers();
|
||||
|
||||
// Mark all active vertex buffers as accessed.
|
||||
- const gl::AttributesMask attribsMask = executable->getActiveAttribLocationsMask();
|
||||
- for (size_t attribIndex : attribsMask)
|
||||
+ for (uint32_t attribIndex = 0; attribIndex < maxAttrib; ++attribIndex)
|
||||
{
|
||||
vk::BufferHelper *arrayBuffer = arrayBufferResources[attribIndex];
|
||||
if (arrayBuffer)
|
||||
diff --git a/src/tests/gl_tests/VertexAttributeTest.cpp b/src/tests/gl_tests/VertexAttributeTest.cpp
|
||||
index b8a1c87728b3ba54a32cf0e4da6ca626c05d1d92..773bbf026821795c0db34239d27fd2bb1e5a751a 100644
|
||||
--- a/src/tests/gl_tests/VertexAttributeTest.cpp
|
||||
+++ b/src/tests/gl_tests/VertexAttributeTest.cpp
|
||||
@@ -1256,6 +1256,19 @@ class VertexAttributeOORTest : public VertexAttributeTest
|
||||
}
|
||||
};
|
||||
|
||||
+class RobustVertexAttributeTest : public VertexAttributeTest
|
||||
+{
|
||||
+ public:
|
||||
+ RobustVertexAttributeTest()
|
||||
+ {
|
||||
+ // mac GL and metal do not support robustness.
|
||||
+ if (!IsMac() && !IsIOS())
|
||||
+ {
|
||||
+ setRobustAccess(true);
|
||||
+ }
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
// Verify that drawing with a large out-of-range offset generates INVALID_OPERATION.
|
||||
// Requires WebGL compatibility with robust access behaviour disabled.
|
||||
TEST_P(VertexAttributeOORTest, ANGLEDrawArraysBufferTooSmall)
|
||||
@@ -1316,6 +1329,48 @@ TEST_P(VertexAttributeOORTest, ANGLEDrawArraysOutOfBoundsCases)
|
||||
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
+// Test that enabling a buffer in an unused attribute doesn't crash. There should be an active
|
||||
+// attribute after that.
|
||||
+TEST_P(RobustVertexAttributeTest, BoundButUnusedBuffer)
|
||||
+{
|
||||
+ constexpr char kVS[] = R"(attribute vec2 offset;
|
||||
+void main()
|
||||
+{
|
||||
+ gl_Position = vec4(offset.xy, 0, 1);
|
||||
+ gl_PointSize = 1.0;
|
||||
+})";
|
||||
+
|
||||
+ constexpr char kFS[] = R"(precision mediump float;
|
||||
+void main()
|
||||
+{
|
||||
+ gl_FragColor = vec4(1.0, 0, 0, 1.0);
|
||||
+})";
|
||||
+
|
||||
+ const GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS);
|
||||
+ const GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS);
|
||||
+
|
||||
+ GLuint program = glCreateProgram();
|
||||
+ glBindAttribLocation(program, 1, "offset");
|
||||
+ glAttachShader(program, vs);
|
||||
+ glAttachShader(program, fs);
|
||||
+ glLinkProgram(program);
|
||||
+
|
||||
+ GLBuffer buffer;
|
||||
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
+ glBufferData(GL_ARRAY_BUFFER, 100, nullptr, GL_STATIC_DRAW);
|
||||
+
|
||||
+ // Enable an unused attribute that is within the range of active attributes (not beyond it)
|
||||
+ glEnableVertexAttribArray(0);
|
||||
+ glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0);
|
||||
+
|
||||
+ glUseProgram(program);
|
||||
+ glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
+
|
||||
+ // Destroy the buffer. Regression test for a tracking bug where the buffer was used by
|
||||
+ // SwiftShader (even though location 1 is inactive), but not marked as used by ANGLE.
|
||||
+ buffer.reset();
|
||||
+}
|
||||
+
|
||||
// Verify that using a different start vertex doesn't mess up the draw.
|
||||
TEST_P(VertexAttributeTest, DrawArraysWithBufferOffset)
|
||||
{
|
||||
@@ -4913,6 +4968,8 @@ ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
|
||||
ES3_METAL().disable(Feature::HasExplicitMemBarrier).disable(Feature::HasCheapRenderPass),
|
||||
ES3_METAL().disable(Feature::HasExplicitMemBarrier).enable(Feature::HasCheapRenderPass));
|
||||
|
||||
+ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(RobustVertexAttributeTest);
|
||||
+
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VertexAttributeTestES3);
|
||||
ANGLE_INSTANTIATE_TEST_ES3_AND(
|
||||
VertexAttributeTestES3,
|
||||
@@ -133,3 +133,13 @@ fix_getcursorscreenpoint_wrongly_returns_0_0.patch
|
||||
fix_add_support_for_skipping_first_2_no-op_refreshes_in_thumb_cap.patch
|
||||
remove_dxdiag_telemetry_code.patch
|
||||
cherry-pick-2607ddacd643.patch
|
||||
cherry-pick-1b1f34234346.patch
|
||||
bindings_refactor_domdatastore.patch
|
||||
merge_fix_domarraybuffer_isdetached_and_comment_out_a_check.patch
|
||||
cherry-pick-98bcf9ef5cdd.patch
|
||||
cherry-pick-c1f25647c2fc.patch
|
||||
cherry-pick-013961609785.patch
|
||||
a11y_avoid_clearing_resetting_focus_on_an_already_focus_event.patch
|
||||
cherry-pick-b2cc7b7ac538.patch
|
||||
feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch
|
||||
cherry-pick-03609e39be8c.patch
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Beaudry <benjamin.beaudry@microsoft.com>
|
||||
Date: Fri, 8 Mar 2024 21:16:50 +0000
|
||||
Subject: Avoid clearing/resetting focus on an already focus event
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
When we set the focus via an accessibility API on an element that
|
||||
is already focused, we first remove the focus and reset it. This
|
||||
leads to blur/focusout/focusin events being fired again.
|
||||
|
||||
This behavior is different than the one we get when we set focus
|
||||
programmatically, with a mouse, or a keyboard, on an already
|
||||
focused element. In order to keep the same experience across all
|
||||
input modalities, this CL removes the divergent focus behavior
|
||||
for accessibility APIs.
|
||||
|
||||
We tried to remove this code two years ago (CL:3547796) but it
|
||||
got reverted due to bug:40850837. This time, I made sure to discuss
|
||||
this issue with the Chrome Android accessibility owners to make
|
||||
sure it's safe to remove. They landed CL:5345750, and then gave
|
||||
us the green light to land this CL.
|
||||
|
||||
Fixed: 40830307
|
||||
Change-Id: I8ad70ed6813e0ae52238292f1b7e6d038a5238f1
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5356613
|
||||
Reviewed-by: Mark Schillaci <mschillaci@google.com>
|
||||
Commit-Queue: Benjamin Beaudry <benjamin.beaudry@microsoft.com>
|
||||
Auto-Submit: Benjamin Beaudry <benjamin.beaudry@microsoft.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1270380}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
|
||||
index a94e88e79c4fb5ed1336a776eafe3e302f5d50f8..a5c4b06e278f805e81799b2d19f27bffe0701c68 100644
|
||||
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
|
||||
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
|
||||
@@ -5095,19 +5095,6 @@ bool AXNodeObject::OnNativeFocusAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
- // If this node is already the currently focused node, then calling
|
||||
- // focus() won't do anything. That is a problem when focus is removed
|
||||
- // from the webpage to chrome, and then returns. In these cases, we need
|
||||
- // to do what keyboard and mouse focus do, which is reset focus first.
|
||||
- if (document->FocusedElement() == element) {
|
||||
- document->ClearFocusedElement();
|
||||
-
|
||||
- // Calling ClearFocusedElement could result in changes to the document,
|
||||
- // like this AXObject becoming detached.
|
||||
- if (IsDetached())
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
if (base::FeatureList::IsEnabled(blink::features::kSimulateClickOnAXFocus)) {
|
||||
// If the object is not natively focusable but can be focused using an ARIA
|
||||
// active descendant, perform a native click instead. This will enable Web
|
||||
1231
patches/chromium/bindings_refactor_domdatastore.patch
Normal file
1231
patches/chromium/bindings_refactor_domdatastore.patch
Normal file
File diff suppressed because it is too large
Load Diff
139
patches/chromium/cherry-pick-013961609785.patch
Normal file
139
patches/chromium/cherry-pick-013961609785.patch
Normal file
@@ -0,0 +1,139 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: John Stiles <johnstiles@google.com>
|
||||
Date: Thu, 28 Mar 2024 00:51:13 +0000
|
||||
Subject: Detect overflow in JPEG image size calculations.
|
||||
|
||||
Bug: 330756841
|
||||
Change-Id: Ib30493152e08fd2347f76de276c5805d6fef9a7d
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5402199
|
||||
Commit-Queue: John Stiles <johnstiles@google.com>
|
||||
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1279376}
|
||||
|
||||
diff --git a/ui/gfx/codec/jpeg_codec.cc b/ui/gfx/codec/jpeg_codec.cc
|
||||
index a4de17094e69b36bb2c34b3b4f70eece9c088cc3..595966d724146df33701b1225e659ec085ca115b 100644
|
||||
--- a/ui/gfx/codec/jpeg_codec.cc
|
||||
+++ b/ui/gfx/codec/jpeg_codec.cc
|
||||
@@ -6,10 +6,12 @@
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
+#include <climits>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#include "base/notreached.h"
|
||||
+#include "base/numerics/checked_math.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkColorPriv.h"
|
||||
#include "ui/gfx/codec/vector_wstream.h"
|
||||
@@ -244,16 +246,27 @@ bool JPEGCodec::Decode(const unsigned char* input, size_t input_size,
|
||||
|
||||
jpeg_start_decompress(cinfo.get());
|
||||
|
||||
- // FIXME(brettw) we may want to allow the capability for callers to request
|
||||
- // how to align row lengths as we do for the compressor.
|
||||
- int row_read_stride = cinfo->output_width * cinfo->output_components;
|
||||
+ // Confirm that the image width * height * component-size fits within an int.
|
||||
+ // Note that image width and height are unsigned ints (JDIMENSION) in memory,
|
||||
+ // but the file format only holds a uint16.
|
||||
+ base::CheckedNumeric<size_t> checked_output_size = cinfo->output_width;
|
||||
+ checked_output_size *= cinfo->output_components;
|
||||
|
||||
- // Create memory for a decoded image and write decoded lines to the memory
|
||||
- // without conversions same as JPEGCodec::Encode().
|
||||
- int row_write_stride = row_read_stride;
|
||||
- output->resize(row_write_stride * cinfo->output_height);
|
||||
+ // This shouldn't ever overflow a `size_t`; it's multiplying a uint16 by four.
|
||||
+ size_t row_write_stride = checked_output_size.ValueOrDie();
|
||||
|
||||
- for (int row = 0; row < static_cast<int>(cinfo->output_height); row++) {
|
||||
+ // Extremely large JPEGs could overflow here if `size_t` is 32 bits.
|
||||
+ checked_output_size *= cinfo->output_height;
|
||||
+ size_t output_size = checked_output_size.ValueOrDefault(INT_MAX);
|
||||
+ if (output_size >= INT_MAX) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Create memory for a decoded image.
|
||||
+ output->resize(output_size);
|
||||
+
|
||||
+ // Write decoded lines to the memory.
|
||||
+ for (unsigned int row = 0; row < cinfo->output_height; row++) {
|
||||
unsigned char* rowptr = &(*output)[row * row_write_stride];
|
||||
if (!jpeg_read_scanlines(cinfo.get(), &rowptr, 1))
|
||||
return false;
|
||||
diff --git a/ui/gfx/codec/jpeg_codec_unittest.cc b/ui/gfx/codec/jpeg_codec_unittest.cc
|
||||
index 9f1bee95e0e476b34382aceffbf6df3bdde095ea..b446fe77896e254820db77afe7ed4e48b8525c79 100644
|
||||
--- a/ui/gfx/codec/jpeg_codec_unittest.cc
|
||||
+++ b/ui/gfx/codec/jpeg_codec_unittest.cc
|
||||
@@ -61,6 +61,53 @@ const uint8_t kTopSitesMigrationTestImage[] =
|
||||
"\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00\xf9"
|
||||
"\xd2\x8a\x28\xaf\xc3\x0f\xf5\x4c\xff\xd9";
|
||||
|
||||
+// This is a copy of the above JPEG, with the Start-of-Frame header manually
|
||||
+// rewritten to indicate an image size of 25000x25000. An image this large would
|
||||
+// require more than 2GB of RAM to decode, so the decoder will reject the image
|
||||
+// as soon as the header is parsed.
|
||||
+const uint8_t kExtremelyLargeTestImage[] =
|
||||
+ "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01"
|
||||
+ "\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x03\x02\x02\x03\x02\x02\x03"
|
||||
+ "\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07"
|
||||
+ "\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d"
|
||||
+ "\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15\x15\x15\x0c\x0f"
|
||||
+ "\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04"
|
||||
+ "\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14"
|
||||
+ "\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
|
||||
+ "\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
|
||||
+ "\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\xff\xc0"
|
||||
+ "\x00\x11\x08\x61\xa8\x61\xa8\x03\x01\x22\x00\x02\x11\x01\x03\x11"
|
||||
+ // ^^ ^^ ^^ ^^ image size forced to 25000x25000
|
||||
+ "\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00"
|
||||
+ "\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"
|
||||
+ "\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05"
|
||||
+ "\x05\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21"
|
||||
+ "\x31\x41\x06\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23"
|
||||
+ "\x42\xb1\xc1\x15\x52\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17"
|
||||
+ "\x18\x19\x1a\x25\x26\x27\x28\x29\x2a\x34\x35\x36\x37\x38\x39\x3a"
|
||||
+ "\x43\x44\x45\x46\x47\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a"
|
||||
+ "\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75\x76\x77\x78\x79\x7a"
|
||||
+ "\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99"
|
||||
+ "\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7"
|
||||
+ "\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5"
|
||||
+ "\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1"
|
||||
+ "\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03"
|
||||
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01"
|
||||
+ "\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00"
|
||||
+ "\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02\x77\x00"
|
||||
+ "\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41\x51\x07\x61\x71\x13"
|
||||
+ "\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33\x52\xf0\x15"
|
||||
+ "\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26\x27"
|
||||
+ "\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49"
|
||||
+ "\x4a\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69"
|
||||
+ "\x6a\x73\x74\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88"
|
||||
+ "\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6"
|
||||
+ "\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4"
|
||||
+ "\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2"
|
||||
+ "\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9"
|
||||
+ "\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00\xf9"
|
||||
+ "\xd2\x8a\x28\xaf\xc3\x0f\xf5\x4c\xff\xd9";
|
||||
+
|
||||
} // namespace
|
||||
|
||||
namespace gfx {
|
||||
@@ -211,4 +258,15 @@ TEST(JPEGCodec, ParallelEncoding) {
|
||||
encode_loop.Run();
|
||||
}
|
||||
|
||||
+TEST(JPEGCodec, ExtremelyLargeImage) {
|
||||
+ std::vector<unsigned char> output;
|
||||
+ int outw, outh;
|
||||
+ bool ok = JPEGCodec::Decode(kExtremelyLargeTestImage,
|
||||
+ std::size(kExtremelyLargeTestImage),
|
||||
+ JPEGCodec::FORMAT_RGBA, &output, &outw, &outh);
|
||||
+ EXPECT_FALSE(ok);
|
||||
+ EXPECT_EQ(outw, 25000);
|
||||
+ EXPECT_EQ(outh, 25000);
|
||||
+}
|
||||
+
|
||||
} // namespace gfx
|
||||
117
patches/chromium/cherry-pick-03609e39be8c.patch
Normal file
117
patches/chromium/cherry-pick-03609e39be8c.patch
Normal file
@@ -0,0 +1,117 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: David Benjamin <davidben@chromium.org>
|
||||
Date: Fri, 10 May 2024 15:10:48 +0000
|
||||
Subject: Fix size calculations in V8StringToUTF8
|
||||
|
||||
While I'm here, remove the unnecessary use of base::WriteInto, which is
|
||||
a remnant of C++03 copy-on-write strings. Also ask V8 not to write a
|
||||
NUL terminator because std::(u16)string already owns that byte.
|
||||
|
||||
(cherry picked from commit f414dc31032a453f4a6c88977d7894fcb3cba44e)
|
||||
|
||||
Bug: 338574384
|
||||
Change-Id: I5c6eaa99093925db799736f321eab92d35f5acbb
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5515743
|
||||
Reviewed-by: mmenke <mmenke@chromium.org>
|
||||
Commit-Queue: David Benjamin <davidben@chromium.org>
|
||||
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1297196}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5527764
|
||||
Auto-Submit: David Benjamin <davidben@chromium.org>
|
||||
Commit-Queue: mmenke <mmenke@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/6367@{#1148}
|
||||
Cr-Branched-From: d158c6dc6e3604e6f899041972edf26087a49740-refs/heads/main@{#1274542}
|
||||
|
||||
diff --git a/services/proxy_resolver/proxy_resolver_v8.cc b/services/proxy_resolver/proxy_resolver_v8.cc
|
||||
index eca8014359b60f54d47c46aa7e106da83ec8a85f..da8232b229f3c6387d5ec063b197650ea24ec292 100644
|
||||
--- a/services/proxy_resolver/proxy_resolver_v8.cc
|
||||
+++ b/services/proxy_resolver/proxy_resolver_v8.cc
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/notreached.h"
|
||||
+#include "base/numerics/safe_conversions.h"
|
||||
#include "base/strings/string_tokenizer.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
@@ -148,25 +149,22 @@ const size_t kMaxStringBytesForCopy = 256;
|
||||
|
||||
// Converts a V8 String to a UTF8 std::string.
|
||||
std::string V8StringToUTF8(v8::Isolate* isolate, v8::Local<v8::String> s) {
|
||||
- int len = s->Length();
|
||||
- std::string result;
|
||||
- if (len > 0)
|
||||
- s->WriteUtf8(isolate, base::WriteInto(&result, len + 1));
|
||||
- return result;
|
||||
+ int len = s->Utf8Length(isolate);
|
||||
+ std::string str(base::checked_cast<size_t>(len), '\0');
|
||||
+ s->WriteUtf8(isolate, str.data(), len, /*nchars_ref=*/nullptr,
|
||||
+ v8::String::NO_NULL_TERMINATION);
|
||||
+ return str;
|
||||
}
|
||||
|
||||
// Converts a V8 String to a UTF16 std::u16string.
|
||||
std::u16string V8StringToUTF16(v8::Isolate* isolate, v8::Local<v8::String> s) {
|
||||
int len = s->Length();
|
||||
- std::u16string result;
|
||||
- // Note that the reinterpret cast is because on Windows string16 is an alias
|
||||
- // to wstring, and hence has character type wchar_t not uint16_t.
|
||||
- if (len > 0) {
|
||||
- s->Write(isolate,
|
||||
- reinterpret_cast<uint16_t*>(base::WriteInto(&result, len + 1)), 0,
|
||||
- len);
|
||||
- }
|
||||
- return result;
|
||||
+ std::u16string str(base::checked_cast<size_t>(len), '\0');
|
||||
+ // `char16_t` and `uint16_t` are not the same type, but we build with strict
|
||||
+ // aliasing off. See https://crbug.com/42209752.
|
||||
+ s->Write(isolate, reinterpret_cast<uint16_t*>(str.data()), /*start=*/0, len,
|
||||
+ v8::String::NO_NULL_TERMINATION);
|
||||
+ return str;
|
||||
}
|
||||
|
||||
// Converts an ASCII std::string to a V8 string.
|
||||
diff --git a/services/proxy_resolver/test/data/proxy_resolver_v8_unittest/pac_library_unittest.js b/services/proxy_resolver/test/data/proxy_resolver_v8_unittest/pac_library_unittest.js
|
||||
index 3414dc0b4a33a64c8fe09b0d2a5cd48123a5d9ac..1b8bc17862f0c3cfa4fdc97121619afac8777a9d 100644
|
||||
--- a/services/proxy_resolver/test/data/proxy_resolver_v8_unittest/pac_library_unittest.js
|
||||
+++ b/services/proxy_resolver/test/data/proxy_resolver_v8_unittest/pac_library_unittest.js
|
||||
@@ -69,6 +69,11 @@ Tests.testIsPlainHostName = function(t) {
|
||||
t.expectFalse(isPlainHostName("."));
|
||||
t.expectFalse(isPlainHostName(".:"));
|
||||
|
||||
+ // These are not really hostnames, but `isPlainHostName` accepts any dotless,
|
||||
+ // non-IP string.
|
||||
+ t.expectTrue(isPlainHostName("\uffff".repeat(256)));
|
||||
+ t.expectTrue(isPlainHostName(""));
|
||||
+
|
||||
// Valid IPv6 address
|
||||
t.expectFalse(isPlainHostName("::1"));
|
||||
|
||||
@@ -178,6 +183,7 @@ Tests.testSortIpAddressList = function(t) {
|
||||
t.expectEquals(null, sortIpAddressList());
|
||||
t.expectEquals(null, sortIpAddressList(null));
|
||||
t.expectEquals(null, sortIpAddressList(null, null));
|
||||
+ t.expectEquals(null, sortIpAddressList("\uffff".repeat(256)));
|
||||
};
|
||||
|
||||
Tests.testIsInNetEx = function(t) {
|
||||
@@ -223,10 +229,14 @@ Tests.testIsInNetEx = function(t) {
|
||||
// Invalid IP address.
|
||||
t.expectFalse(isInNetEx("256.0.0.1", "198.95.249.79"));
|
||||
t.expectFalse(isInNetEx("127.0.0.1 ", "127.0.0.1/32")); // Extra space.
|
||||
+ t.expectFalse(isInNetEx("\uffff".repeat(256), "127.0.0.1/32"));
|
||||
+ t.expectFalse(isInNetEx("", "127.0.0.1/32"));
|
||||
|
||||
// Invalid prefix.
|
||||
t.expectFalse(isInNetEx("198.95.115.10", "198.95.0.0/34"));
|
||||
t.expectFalse(isInNetEx("127.0.0.1", "127.0.0.1")); // Missing '/' in prefix.
|
||||
+ t.expectFalse(isInNetEx("127.0.0.1", "\uffff".repeat(256)));
|
||||
+ t.expectFalse(isInNetEx("127.0.0.1", ""));
|
||||
};
|
||||
|
||||
Tests.testWeekdayRange = function(t) {
|
||||
@@ -465,4 +475,3 @@ MockDate.setCurrent = function(currentDateString) {
|
||||
|
||||
// Bind the methods to proxy requests to the wrapped Date().
|
||||
MockDate.init();
|
||||
-
|
||||
42
patches/chromium/cherry-pick-1b1f34234346.patch
Normal file
42
patches/chromium/cherry-pick-1b1f34234346.patch
Normal file
@@ -0,0 +1,42 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kylechar <kylechar@chromium.org>
|
||||
Date: Tue, 9 Apr 2024 17:14:26 +0000
|
||||
Subject: Validate buffer length
|
||||
|
||||
The BitmapInSharedMemory mojo traits were only validating row length and
|
||||
not total buffer length.
|
||||
|
||||
(cherry picked from commit 1a19ff70bd54847d818566bd7a1e7c384c419746)
|
||||
|
||||
(cherry picked from commit f15315f1cb7897e208947a40d538aac693283d7f)
|
||||
|
||||
Bug: 331237485
|
||||
Change-Id: Ia2318899c44e9e7ac72fc7183954e6ce2c702179
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5396796
|
||||
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
|
||||
Cr-Original-Original-Commit-Position: refs/heads/main@{#1278417}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5420432
|
||||
Commit-Queue: danakj <danakj@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/branch-heads/6312@{#786}
|
||||
Cr-Original-Branched-From: 6711dcdae48edaf98cbc6964f90fac85b7d9986e-refs/heads/main@{#1262506}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5433678
|
||||
Reviewed-by: danakj <danakj@chromium.org>
|
||||
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/6099@{#2003}
|
||||
Cr-Branched-From: e6ee4500f7d6549a9ac1354f8d056da49ef406be-refs/heads/main@{#1217362}
|
||||
|
||||
diff --git a/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc b/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc
|
||||
index a6e5f45d9e72b9ac48e536c3a7756966b3c263cf..519d554055e5182cdcbae44fafdac339a64a923b 100644
|
||||
--- a/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc
|
||||
+++ b/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc
|
||||
@@ -76,6 +76,10 @@ bool StructTraits<viz::mojom::BitmapInSharedMemoryDataView, SkBitmap>::Read(
|
||||
if (!mapping_ptr->IsValid())
|
||||
return false;
|
||||
|
||||
+ if (mapping_ptr->size() < image_info.computeByteSize(data.row_bytes())) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
if (!sk_bitmap->installPixels(image_info, mapping_ptr->memory(),
|
||||
data.row_bytes(), &DeleteSharedMemoryMapping,
|
||||
mapping_ptr.get())) {
|
||||
102
patches/chromium/cherry-pick-98bcf9ef5cdd.patch
Normal file
102
patches/chromium/cherry-pick-98bcf9ef5cdd.patch
Normal file
@@ -0,0 +1,102 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tommy Steimel <steimel@chromium.org>
|
||||
Date: Tue, 23 Apr 2024 19:29:23 +0000
|
||||
Subject: Don't assume the enter event window is a LocalDOMWindow
|
||||
|
||||
This CL changes DocumentPictureInPictureEvent to store a DOMWindow
|
||||
instead of a LocalDOMWindow to prevent crashes when the window it gets
|
||||
is actually a RemoteDOMWindow.
|
||||
|
||||
(cherry picked from commit 2314741cdf2c4a6e11234dda7006ec0dd9005bbb)
|
||||
|
||||
Bug: 335003891
|
||||
Change-Id: I86a0ec5a89b51a26d5dd89559f86e6e4d6c3e8fe
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5467978
|
||||
Commit-Queue: Tommy Steimel <steimel@chromium.org>
|
||||
Reviewed-by: Frank Liberato <liberato@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1290122}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5477908
|
||||
Auto-Submit: Tommy Steimel <steimel@chromium.org>
|
||||
Commit-Queue: Frank Liberato <liberato@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/6367@{#974}
|
||||
Cr-Branched-From: d158c6dc6e3604e6f899041972edf26087a49740-refs/heads/main@{#1274542}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.cc b/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.cc
|
||||
index 037813c62c2f0dfc78b3451320a799a349ffde23..572d0803c25a99ef5dfd631e7872b05a681f0444 100644
|
||||
--- a/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.cc
|
||||
+++ b/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.cc
|
||||
@@ -8,7 +8,7 @@ namespace blink {
|
||||
|
||||
DocumentPictureInPictureEvent* DocumentPictureInPictureEvent::Create(
|
||||
const AtomicString& type,
|
||||
- LocalDOMWindow* document_picture_in_picture_window) {
|
||||
+ DOMWindow* document_picture_in_picture_window) {
|
||||
return MakeGarbageCollected<DocumentPictureInPictureEvent>(
|
||||
type, document_picture_in_picture_window);
|
||||
}
|
||||
@@ -19,13 +19,13 @@ DocumentPictureInPictureEvent* DocumentPictureInPictureEvent::Create(
|
||||
return MakeGarbageCollected<DocumentPictureInPictureEvent>(type, initializer);
|
||||
}
|
||||
|
||||
-LocalDOMWindow* DocumentPictureInPictureEvent::window() const {
|
||||
+DOMWindow* DocumentPictureInPictureEvent::window() const {
|
||||
return document_picture_in_picture_window_.Get();
|
||||
}
|
||||
|
||||
DocumentPictureInPictureEvent::DocumentPictureInPictureEvent(
|
||||
AtomicString const& type,
|
||||
- LocalDOMWindow* document_picture_in_picture_window)
|
||||
+ DOMWindow* document_picture_in_picture_window)
|
||||
: Event(type, Bubbles::kYes, Cancelable::kNo),
|
||||
document_picture_in_picture_window_(document_picture_in_picture_window) {}
|
||||
|
||||
@@ -33,8 +33,7 @@ DocumentPictureInPictureEvent::DocumentPictureInPictureEvent(
|
||||
AtomicString const& type,
|
||||
const DocumentPictureInPictureEventInit* initializer)
|
||||
: Event(type, initializer),
|
||||
- document_picture_in_picture_window_(
|
||||
- static_cast<LocalDOMWindow*>(initializer->window())) {}
|
||||
+ document_picture_in_picture_window_(initializer->window()) {}
|
||||
|
||||
void DocumentPictureInPictureEvent::Trace(Visitor* visitor) const {
|
||||
visitor->Trace(document_picture_in_picture_window_);
|
||||
diff --git a/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.h b/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.h
|
||||
index 7af2022146905a3c3d71e1420aaa68da30e6a9ce..59cd8cb7a2e3a2e2a81db1d146f8075b13755c0e 100644
|
||||
--- a/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.h
|
||||
+++ b/third_party/blink/renderer/modules/document_picture_in_picture/document_picture_in_picture_event.h
|
||||
@@ -6,7 +6,7 @@
|
||||
#define THIRD_PARTY_BLINK_RENDERER_MODULES_DOCUMENT_PICTURE_IN_PICTURE_DOCUMENT_PICTURE_IN_PICTURE_EVENT_H_
|
||||
|
||||
#include "third_party/blink/renderer/bindings/modules/v8/v8_document_picture_in_picture_event_init.h"
|
||||
-#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
+#include "third_party/blink/renderer/core/frame/dom_window.h"
|
||||
#include "third_party/blink/renderer/modules/event_modules.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
|
||||
|
||||
@@ -18,22 +18,21 @@ class MODULES_EXPORT DocumentPictureInPictureEvent final : public Event {
|
||||
DEFINE_WRAPPERTYPEINFO();
|
||||
|
||||
public:
|
||||
- static DocumentPictureInPictureEvent* Create(const AtomicString&,
|
||||
- LocalDOMWindow*);
|
||||
+ static DocumentPictureInPictureEvent* Create(const AtomicString&, DOMWindow*);
|
||||
static DocumentPictureInPictureEvent* Create(
|
||||
const AtomicString&,
|
||||
const DocumentPictureInPictureEventInit*);
|
||||
|
||||
- DocumentPictureInPictureEvent(AtomicString const&, LocalDOMWindow*);
|
||||
+ DocumentPictureInPictureEvent(AtomicString const&, DOMWindow*);
|
||||
DocumentPictureInPictureEvent(AtomicString const&,
|
||||
const DocumentPictureInPictureEventInit*);
|
||||
|
||||
- LocalDOMWindow* window() const;
|
||||
+ DOMWindow* window() const;
|
||||
|
||||
void Trace(Visitor*) const override;
|
||||
|
||||
private:
|
||||
- Member<LocalDOMWindow> document_picture_in_picture_window_;
|
||||
+ Member<DOMWindow> document_picture_in_picture_window_;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
99
patches/chromium/cherry-pick-b2cc7b7ac538.patch
Normal file
99
patches/chromium/cherry-pick-b2cc7b7ac538.patch
Normal file
@@ -0,0 +1,99 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ken Rockot <rockot@google.com>
|
||||
Date: Wed, 8 May 2024 15:32:48 +0000
|
||||
Subject: Viz: Tolerate SinkGroup destruction during submit
|
||||
|
||||
Fixed: 339266700
|
||||
Change-Id: I8c0ea8c540948016346b00db64fe33260d2446f0
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5523748
|
||||
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
|
||||
Reviewed-by: Jonathan Ross <jonross@chromium.org>
|
||||
Commit-Queue: Ken Rockot <rockot@google.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1298119}
|
||||
|
||||
diff --git a/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc b/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
|
||||
index a43e274a920a7cc189652c29eb2fe4a09ab66ded..9fefc2446d9c95964db512e4c98654c3fcc4e8b4 100644
|
||||
--- a/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
|
||||
+++ b/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
|
||||
@@ -4,12 +4,15 @@
|
||||
|
||||
#include "components/viz/service/frame_sinks/frame_sink_bundle_impl.h"
|
||||
|
||||
+#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/functional/bind.h"
|
||||
+#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ref.h"
|
||||
+#include "base/memory/weak_ptr.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/viz/service/frame_sinks/compositor_frame_sink_impl.h"
|
||||
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
|
||||
@@ -45,6 +48,10 @@ class FrameSinkBundleImpl::SinkGroup : public BeginFrameObserver {
|
||||
|
||||
bool IsEmpty() const { return frame_sinks_.empty(); }
|
||||
|
||||
+ base::WeakPtr<SinkGroup> GetWeakPtr() {
|
||||
+ return weak_ptr_factory_.GetWeakPtr();
|
||||
+ }
|
||||
+
|
||||
void AddFrameSink(uint32_t sink_id) {
|
||||
frame_sinks_.insert(sink_id);
|
||||
|
||||
@@ -206,6 +213,8 @@ class FrameSinkBundleImpl::SinkGroup : public BeginFrameObserver {
|
||||
std::set<uint32_t> unacked_submissions_;
|
||||
|
||||
BeginFrameArgs last_used_begin_frame_args_;
|
||||
+
|
||||
+ base::WeakPtrFactory<SinkGroup> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
FrameSinkBundleImpl::FrameSinkBundleImpl(
|
||||
@@ -282,8 +291,9 @@ void FrameSinkBundleImpl::SetWantsBeginFrameAcks(uint32_t sink_id) {
|
||||
|
||||
void FrameSinkBundleImpl::Submit(
|
||||
std::vector<mojom::BundledFrameSubmissionPtr> submissions) {
|
||||
- std::set<SinkGroup*> groups;
|
||||
- std::set<SinkGroup*> affected_groups;
|
||||
+ std::map<raw_ptr<SinkGroup>, base::WeakPtr<SinkGroup>> groups;
|
||||
+ std::map<raw_ptr<SinkGroup>, base::WeakPtr<SinkGroup>> affected_groups;
|
||||
+
|
||||
// Count the frame submissions before processing anything. This ensures that
|
||||
// any frames submitted here will be acked together in a batch, and not acked
|
||||
// individually in case they happen to ack synchronously within
|
||||
@@ -294,10 +304,10 @@ void FrameSinkBundleImpl::Submit(
|
||||
// through to the client without batching.
|
||||
for (auto& submission : submissions) {
|
||||
if (auto* group = GetSinkGroup(submission->sink_id)) {
|
||||
- groups.insert(group);
|
||||
+ groups.emplace(group, group->GetWeakPtr());
|
||||
if (submission->data->is_frame()) {
|
||||
group->WillSubmitFrame(submission->sink_id);
|
||||
- affected_groups.insert(group);
|
||||
+ affected_groups.emplace(group, group->GetWeakPtr());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,12 +337,16 @@ void FrameSinkBundleImpl::Submit(
|
||||
}
|
||||
}
|
||||
|
||||
- for (auto* group : groups) {
|
||||
- group->DidFinishFrame();
|
||||
+ for (const auto& [unsafe_group, weak_group] : groups) {
|
||||
+ if (weak_group) {
|
||||
+ weak_group->DidFinishFrame();
|
||||
+ }
|
||||
}
|
||||
|
||||
- for (auto* group : affected_groups) {
|
||||
- group->FlushMessages();
|
||||
+ for (const auto& [unsafe_group, weak_group] : affected_groups) {
|
||||
+ if (weak_group) {
|
||||
+ weak_group->FlushMessages();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
27
patches/chromium/cherry-pick-c1f25647c2fc.patch
Normal file
27
patches/chromium/cherry-pick-c1f25647c2fc.patch
Normal file
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Johannes Kron <kron@chromium.org>
|
||||
Date: Tue, 13 Feb 2024 23:22:49 +0000
|
||||
Subject: Update thumbnail size when sources are selected
|
||||
|
||||
(cherry picked from commit c1f25647c2fcbf5e5a2d574feb284bc7284b944d)
|
||||
|
||||
Bug: b/40281869
|
||||
Change-Id: I2d5a3954886bc6a5742321aeab415362da19c0ce
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5285701
|
||||
Commit-Queue: Johannes Kron <kron@chromium.org>
|
||||
Reviewed-by: Elad Alon <eladalon@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1260137}
|
||||
|
||||
diff --git a/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm b/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
|
||||
index ebe194e9d4a77d74996cb575364a32b61ccfac52..b27f6348d338d8952716b51825586e5d29b75651 100644
|
||||
--- a/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
|
||||
+++ b/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
|
||||
@@ -365,6 +365,8 @@ void API_AVAILABLE(macos(14.0))
|
||||
gfx::Size thumbnail_size) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
+ thumbnail_size_ = thumbnail_size;
|
||||
+
|
||||
// The iteration is in reverse order so that the sources
|
||||
// first in the list are captured first. This way we make sure that the first
|
||||
// thumbnails in the view are captured first.
|
||||
@@ -0,0 +1,243 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Sun, 5 May 2024 09:17:17 +0000
|
||||
Subject: feat: add support for missing dialog features to //shell_dialogs
|
||||
|
||||
This CL adds support for the following features to //shell_dialogs:
|
||||
* buttonLabel - Custom label for the confirmation button.
|
||||
* showHiddenFiles - Show hidden files in dialog.
|
||||
* showOverwriteConfirmation - Whether the user will be presented a confirmation dialog if the user types a file name that already exists.
|
||||
|
||||
This may be partially upstreamed to Chromium in the future.
|
||||
|
||||
diff --git a/ui/gtk/select_file_dialog_linux_gtk.cc b/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
index 38f8b2fa5ef4a8c86b9efa93d34dfb5ab1099c76..c1b5bd6cc2f31eee1f84cc57da58eba48576f139 100644
|
||||
--- a/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
+++ b/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
@@ -244,6 +244,10 @@ void SelectFileDialogLinuxGtk::SelectFileImpl(
|
||||
|
||||
std::string title_string = base::UTF16ToUTF8(title);
|
||||
|
||||
+ ExtraSettings extra_settings;
|
||||
+ if (params)
|
||||
+ extra_settings = *(static_cast<ExtraSettings*>(params));
|
||||
+
|
||||
set_file_type_index(file_type_index);
|
||||
if (file_types)
|
||||
set_file_types(*file_types);
|
||||
@@ -262,23 +266,23 @@ void SelectFileDialogLinuxGtk::SelectFileImpl(
|
||||
case SELECT_UPLOAD_FOLDER:
|
||||
case SELECT_EXISTING_FOLDER:
|
||||
dialog = CreateSelectFolderDialog(type, title_string, default_path,
|
||||
- owning_window);
|
||||
+ owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse);
|
||||
break;
|
||||
case SELECT_OPEN_FILE:
|
||||
- dialog = CreateFileOpenDialog(title_string, default_path, owning_window);
|
||||
+ dialog = CreateFileOpenDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFileDialogResponse);
|
||||
break;
|
||||
case SELECT_OPEN_MULTI_FILE:
|
||||
dialog =
|
||||
- CreateMultiFileOpenDialog(title_string, default_path, owning_window);
|
||||
+ CreateMultiFileOpenDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectMultiFileDialogResponse);
|
||||
break;
|
||||
case SELECT_SAVEAS_FILE:
|
||||
- dialog = CreateSaveAsDialog(title_string, default_path, owning_window);
|
||||
+ dialog = CreateSaveAsDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFileDialogResponse);
|
||||
break;
|
||||
@@ -413,10 +417,14 @@ void SelectFileDialogLinuxGtk::FileNotSelected(GtkWidget* dialog) {
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
+ const char* button_label = settings.button_label.empty()
|
||||
+ ? GetOpenLabel()
|
||||
+ : settings.button_label.c_str();
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, GetCancelLabel(),
|
||||
- GTK_RESPONSE_CANCEL, GetOpenLabel(), GTK_RESPONSE_ACCEPT);
|
||||
+ GTK_RESPONSE_CANCEL, button_label, GTK_RESPONSE_ACCEPT);
|
||||
SetGtkTransientForAura(dialog, parent);
|
||||
AddFilters(GTK_FILE_CHOOSER(dialog));
|
||||
|
||||
@@ -432,6 +440,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper(
|
||||
GtkFileChooserSetCurrentFolder(GTK_FILE_CHOOSER(dialog),
|
||||
*last_opened_path());
|
||||
}
|
||||
+ gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),
|
||||
+ settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@@ -439,7 +449,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
Type type,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string = title;
|
||||
if (title_string.empty()) {
|
||||
title_string =
|
||||
@@ -447,11 +458,14 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
? l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE)
|
||||
: l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
|
||||
}
|
||||
- std::string accept_button_label =
|
||||
- (type == SELECT_UPLOAD_FOLDER)
|
||||
- ? l10n_util::GetStringUTF8(
|
||||
- IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
|
||||
- : GetOpenLabel();
|
||||
+
|
||||
+ std::string accept_button_label = settings.button_label;
|
||||
+ if (accept_button_label.empty()) {
|
||||
+ accept_button_label = (type == SELECT_UPLOAD_FOLDER)
|
||||
+ ? l10n_util::GetStringUTF8(
|
||||
+ IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
|
||||
+ : GetOpenLabel();
|
||||
+ }
|
||||
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
@@ -473,19 +487,21 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
gtk_file_filter_add_mime_type(only_folders, "inode/directory");
|
||||
gtk_file_filter_add_mime_type(only_folders, "text/directory");
|
||||
gtk_file_chooser_add_filter(chooser, only_folders);
|
||||
- gtk_file_chooser_set_select_multiple(chooser, FALSE);
|
||||
+ gtk_file_chooser_set_select_multiple(chooser, settings.allow_multiple_selection);
|
||||
+ gtk_file_chooser_set_show_hidden(chooser, settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE);
|
||||
|
||||
- GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
|
||||
+ GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent, settings);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
||||
return dialog;
|
||||
}
|
||||
@@ -493,12 +509,14 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenDialog(
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateMultiFileOpenDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE);
|
||||
|
||||
- GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
|
||||
+ GtkWidget* dialog =
|
||||
+ CreateFileOpenHelper(title_string, default_path, parent, settings);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
|
||||
return dialog;
|
||||
}
|
||||
@@ -506,14 +524,17 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateMultiFileOpenDialog(
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE);
|
||||
-
|
||||
+ const char* button_label = settings.button_label.empty()
|
||||
+ ? GetSaveLabel()
|
||||
+ : settings.button_label.c_str();
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
- GetCancelLabel(), GTK_RESPONSE_CANCEL, GetSaveLabel(),
|
||||
+ GetCancelLabel(), GTK_RESPONSE_CANCEL, button_label,
|
||||
GTK_RESPONSE_ACCEPT);
|
||||
SetGtkTransientForAura(dialog, parent);
|
||||
|
||||
@@ -539,9 +560,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog(
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
||||
// Overwrite confirmation is always enabled in GTK4.
|
||||
if (!GtkCheckVersion(4)) {
|
||||
- gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
|
||||
- TRUE);
|
||||
+ gtk_file_chooser_set_do_overwrite_confirmation(
|
||||
+ GTK_FILE_CHOOSER(dialog), settings.show_overwrite_confirmation);
|
||||
}
|
||||
+ gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
diff --git a/ui/gtk/select_file_dialog_linux_gtk.h b/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
index 53ae15f14c45ee72abdae172fc4555c9e4b3ff9a..af181afd9db1351cd886ba24dd651c7bf2f8a716 100644
|
||||
--- a/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
+++ b/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
@@ -15,6 +15,13 @@
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+struct ExtraSettings {
|
||||
+ std::string button_label;
|
||||
+ bool show_overwrite_confirmation = true;
|
||||
+ bool show_hidden = false;
|
||||
+ bool allow_multiple_selection = false;
|
||||
+};
|
||||
+
|
||||
// Implementation of SelectFileDialog that shows a Gtk common dialog for
|
||||
// choosing a file or folder. This acts as a modal dialog.
|
||||
class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
@@ -90,19 +97,23 @@ class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
GtkWidget* CreateSelectFolderDialog(Type type,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateFileOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateMultiFileOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateSaveAsDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
// Removes and returns the |params| associated with |dialog| from
|
||||
// |params_map_|.
|
||||
@@ -121,7 +132,8 @@ class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
// Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog.
|
||||
GtkWidget* CreateFileOpenHelper(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
// Callback for when the user responds to a Save As or Open File dialog.
|
||||
void OnSelectSingleFileDialogResponse(GtkWidget* dialog, int response_id);
|
||||
@@ -1,13 +1,13 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGtk and gtk::GetLibGdkPixbuf public
|
||||
Subject: Make gtk::GetLibGdkPixbuf public
|
||||
|
||||
Allows embedders to get a handle to the gtk and
|
||||
gdk_pixbuf libraries already loaded in the process.
|
||||
Allows embedders to get a handle to the gdk_pixbuf
|
||||
library already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index d196e304a43191b6dc82f25b0b4bf24d242edb3c..85d618efa0574b00fc0cb1e5bde5ed725b2a90ab 100644
|
||||
index d196e304a43191b6dc82f25b0b4bf24d242edb3c..0156ed1c3e8e1de8ce875ca9a17e69358074743e 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -66,11 +66,6 @@ void* GetLibGio() {
|
||||
@@ -22,20 +22,7 @@ index d196e304a43191b6dc82f25b0b4bf24d242edb3c..85d618efa0574b00fc0cb1e5bde5ed72
|
||||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
return libgtk4;
|
||||
}
|
||||
|
||||
-void* GetLibGtk() {
|
||||
- if (GtkCheckVersion(4))
|
||||
- return GetLibGtk4();
|
||||
- return GetLibGtk3();
|
||||
-}
|
||||
-
|
||||
bool LoadGtk3() {
|
||||
if (!GetLibGtk3(false))
|
||||
return false;
|
||||
@@ -134,6 +123,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -134,6 +129,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -43,29 +30,20 @@ index d196e304a43191b6dc82f25b0b4bf24d242edb3c..85d618efa0574b00fc0cb1e5bde5ed72
|
||||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGtk() {
|
||||
+ if (GtkCheckVersion(4))
|
||||
+ return GetLibGtk4();
|
||||
+ return GetLibGtk3();
|
||||
+}
|
||||
+
|
||||
bool LoadGtk() {
|
||||
static bool loaded = LoadGtkImpl();
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 409e385fc952662c9887d9a810bb3c547c5be282..1518b681f98b3bc051aed74713a23c016c7b755e 100644
|
||||
index 409e385fc952662c9887d9a810bb3c547c5be282..579ac4f6a39654f1e58ed0eb165c2773e5620475 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -40,6 +40,12 @@ using SkColor = uint32_t;
|
||||
@@ -40,6 +40,9 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gtk library in the process.
|
||||
+void* GetLibGtk();
|
||||
+
|
||||
// Loads libgtk and related libraries and returns true on success.
|
||||
bool LoadGtk();
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Marja=20H=C3=B6ltt=C3=A4?= <marja@google.com>
|
||||
Date: Tue, 9 Apr 2024 08:32:27 +0000
|
||||
Subject: Merge "Fix DOMArrayBuffer::IsDetached()" and "Comment out a CHECK
|
||||
that a DOMAB has maximally one non-detached JSAB"
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
1)
|
||||
|
||||
A DOMArrayBuffer was maintaining its own "is_detached_" state, and
|
||||
would consider itself non-detached even if the corresponding
|
||||
JSArrayBuffer (or, all of them, in case there are several) was
|
||||
detached.
|
||||
|
||||
Piping in the v8::Isolate would be a too big change for this fix, so this is using v8::Isolate::GetCurrent() for now.
|
||||
|
||||
2)
|
||||
|
||||
Comment out a CHECK that a DOMAB has maximally one non-detached JSAB
|
||||
|
||||
Based on crash reports, this assumption is not true and has to be
|
||||
investigated.
|
||||
|
||||
Removing this newly introduced CHECK to be able to merge fixes in this
|
||||
area - we still violate this invariant but the fixes are a step into
|
||||
the right direction.
|
||||
|
||||
Fix in question:
|
||||
https://chromium-review.googlesource.com/5387887
|
||||
which also introduced this CHECK.
|
||||
|
||||
(cherry picked from commit 04e7550d7aa3bf4ac4e49d7074972d357de139e6)
|
||||
|
||||
Change-Id: I6a46721e24c6f04fe8252bc4a5e94caeec3a8b51
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5435035
|
||||
Commit-Queue: Marja Hölttä <marja@chromium.org>
|
||||
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/6367@{#667}
|
||||
Cr-Branched-From: d158c6dc6e3604e6f899041972edf26087a49740-refs/heads/main@{#1274542}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
|
||||
index 44e2849c8e4dfc38834c88937dc561858c13e726..f26b6de5baded254d1ae5c3db458d0405e9751cb 100644
|
||||
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
|
||||
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
|
||||
@@ -46,8 +46,19 @@ const WrapperTypeInfo& DOMArrayBuffer::wrapper_type_info_ =
|
||||
|
||||
static void AccumulateArrayBuffersForAllWorlds(
|
||||
v8::Isolate* isolate,
|
||||
- DOMArrayBuffer* object,
|
||||
+ const DOMArrayBuffer* object,
|
||||
v8::LocalVector<v8::ArrayBuffer>& buffers) {
|
||||
+ if (!object->has_non_main_world_wrappers() && IsMainThread()) {
|
||||
+ const DOMWrapperWorld& world = DOMWrapperWorld::MainWorld(isolate);
|
||||
+ v8::Local<v8::Object> wrapper;
|
||||
+ if (world.DomDataStore()
|
||||
+ .Get</*entered_context=*/false>(isolate, object)
|
||||
+ .ToLocal(&wrapper)) {
|
||||
+ buffers.push_back(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
Vector<scoped_refptr<DOMWrapperWorld>> worlds;
|
||||
DOMWrapperWorld::AllWorldsInIsolate(isolate, worlds);
|
||||
for (const auto& world : worlds) {
|
||||
@@ -259,6 +270,52 @@ v8::Local<v8::Value> DOMArrayBuffer::Wrap(ScriptState* script_state) {
|
||||
wrapper);
|
||||
}
|
||||
|
||||
+bool DOMArrayBuffer::IsDetached() const {
|
||||
+ if (contents_.BackingStore() == nullptr) {
|
||||
+ return is_detached_;
|
||||
+ }
|
||||
+ if (is_detached_) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
+ v8::HandleScope handle_scope(isolate);
|
||||
+ v8::LocalVector<v8::ArrayBuffer> buffer_handles(isolate);
|
||||
+ AccumulateArrayBuffersForAllWorlds(isolate, this, buffer_handles);
|
||||
+
|
||||
+ // There may be several v8::ArrayBuffers corresponding to the DOMArrayBuffer,
|
||||
+ // but at most one of them may be non-detached.
|
||||
+ int nondetached_count = 0;
|
||||
+ int detached_count = 0;
|
||||
+
|
||||
+ for (const auto& buffer_handle : buffer_handles) {
|
||||
+ if (buffer_handle->WasDetached()) {
|
||||
+ ++detached_count;
|
||||
+ } else {
|
||||
+ ++nondetached_count;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // This CHECK fires even though it should not. TODO(330759272): Investigate
|
||||
+ // under which conditions we end up with multiple non-detached JSABs for the
|
||||
+ // same DOMAB and potentially restore this check.
|
||||
+
|
||||
+ // CHECK_LE(nondetached_count, 1);
|
||||
+
|
||||
+ return nondetached_count == 0 && detached_count > 0;
|
||||
+}
|
||||
+
|
||||
+v8::Local<v8::Object> DOMArrayBuffer::AssociateWithWrapper(
|
||||
+ v8::Isolate* isolate,
|
||||
+ const WrapperTypeInfo* wrapper_type_info,
|
||||
+ v8::Local<v8::Object> wrapper) {
|
||||
+ if (!DOMWrapperWorld::Current(isolate).IsMainWorld()) {
|
||||
+ has_non_main_world_wrappers_ = true;
|
||||
+ }
|
||||
+ return ScriptWrappable::AssociateWithWrapper(isolate, wrapper_type_info,
|
||||
+ wrapper);
|
||||
+}
|
||||
+
|
||||
DOMArrayBuffer* DOMArrayBuffer::Slice(size_t begin, size_t end) const {
|
||||
begin = std::min(begin, ByteLength());
|
||||
end = std::min(end, ByteLength());
|
||||
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
|
||||
index a7d4bac99e608bcfaa02dc9bfcef20b5a29d0ddc..9d4827f548ca7db3be85011c68d8346fc8dfa909 100644
|
||||
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
|
||||
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
|
||||
@@ -91,6 +91,17 @@ class CORE_EXPORT DOMArrayBuffer : public DOMArrayBufferBase {
|
||||
|
||||
void Trace(Visitor*) const override;
|
||||
|
||||
+ bool IsDetached() const override;
|
||||
+
|
||||
+ v8::Local<v8::Object> AssociateWithWrapper(
|
||||
+ v8::Isolate* isolate,
|
||||
+ const WrapperTypeInfo* wrapper_type_info,
|
||||
+ v8::Local<v8::Object> wrapper) override;
|
||||
+
|
||||
+ bool has_non_main_world_wrappers() const {
|
||||
+ return has_non_main_world_wrappers_;
|
||||
+ }
|
||||
+
|
||||
private:
|
||||
v8::Maybe<bool> TransferDetachable(v8::Isolate*,
|
||||
v8::Local<v8::Value> detach_key,
|
||||
@@ -101,6 +112,8 @@ class CORE_EXPORT DOMArrayBuffer : public DOMArrayBufferBase {
|
||||
// support only v8::String as the detach key type. It's also convenient that
|
||||
// we can write `array_buffer->SetDetachKey(isolate, "my key")`.
|
||||
TraceWrapperV8Reference<v8::String> detach_key_;
|
||||
+
|
||||
+ bool has_non_main_world_wrappers_ = false;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h
|
||||
index 90c4c70755babdc8c88a7c6bf02803c5858c2194..43618b8ef4b831678b45c72ca47f5729c4f2aaef 100644
|
||||
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h
|
||||
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h
|
||||
@@ -27,7 +27,9 @@ class CORE_EXPORT DOMArrayBufferBase : public ScriptWrappable {
|
||||
|
||||
size_t ByteLength() const { return contents_.DataLength(); }
|
||||
|
||||
- bool IsDetached() const { return is_detached_; }
|
||||
+ // TODO(331348222): It doesn't make sense to detach DomSharedArrayBuffers,
|
||||
+ // remove that possibility.
|
||||
+ virtual bool IsDetached() const { return is_detached_; }
|
||||
|
||||
void Detach() { is_detached_ = true; }
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/gamepad/BUILD.gn b/third_party/blink/renderer/modules/gamepad/BUILD.gn
|
||||
index 572d8ce27fa808707ae17ed05f14e2ed103f131e..a871cbd002795bf49ad48f0002ac4996135f8b10 100644
|
||||
--- a/third_party/blink/renderer/modules/gamepad/BUILD.gn
|
||||
+++ b/third_party/blink/renderer/modules/gamepad/BUILD.gn
|
||||
@@ -55,6 +55,7 @@ source_set("unit_tests") {
|
||||
"//testing/gtest",
|
||||
"//third_party/blink/renderer/modules",
|
||||
"//third_party/blink/renderer/platform",
|
||||
+ "//third_party/blink/renderer/platform:test_support",
|
||||
"//third_party/blink/renderer/platform/wtf",
|
||||
]
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
|
||||
index e0a7f48630ba423b19641232c026d72ba71dfc4b..b9f422e6fff36da62575d803f132573a24d03c05 100644
|
||||
--- a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
|
||||
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
#include "third_party/blink/renderer/modules/gamepad/gamepad_comparisons.h"
|
||||
|
||||
+#include "base/test/task_environment.h"
|
||||
#include "device/gamepad/public/cpp/gamepad.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/blink/renderer/modules/gamepad/gamepad.h"
|
||||
+#include "third_party/blink/renderer/platform/testing/main_thread_isolate.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -241,6 +243,11 @@ class GamepadComparisonsTest : public testing::Test {
|
||||
list[0] = gamepad;
|
||||
return list;
|
||||
}
|
||||
+
|
||||
+ private:
|
||||
+ // Needed so we can do v8::Isolate::GetCurrent().
|
||||
+ base::test::TaskEnvironment task_environment_;
|
||||
+ blink::test::MainThreadIsolate isolate_;
|
||||
};
|
||||
|
||||
TEST_F(GamepadComparisonsTest, EmptyListCausesNoActivation) {
|
||||
@@ -11,5 +11,7 @@
|
||||
{ "patch_dir": "src/electron/patches/Mantle", "repo": "src/third_party/squirrel.mac/vendor/Mantle" },
|
||||
{ "patch_dir": "src/electron/patches/ReactiveObjC", "repo": "src/third_party/squirrel.mac/vendor/ReactiveObjC" },
|
||||
{ "patch_dir": "src/electron/patches/webrtc", "repo": "src/third_party/webrtc" },
|
||||
{ "patch_dir": "src/electron/patches/reclient-configs", "repo": "src/third_party/engflow-reclient-configs" }
|
||||
{ "patch_dir": "src/electron/patches/reclient-configs", "repo": "src/third_party/engflow-reclient-configs" },
|
||||
{ "patch_dir": "src/electron/patches/angle", "repo": "src/third_party/angle" },
|
||||
{ "patch_dir": "src/electron/patches/DirectXShaderCompiler", "repo": "src/third_party/dawn/third_party/dxc" }
|
||||
]
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
|
||||
fix_support_for_worklet_targets.patch
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Kosyakov <caseq@chromium.org>
|
||||
Date: Tue, 12 Mar 2024 19:58:44 -0700
|
||||
Subject: Fix support for worklet targets
|
||||
|
||||
Fixes an issue where console.log() did not work in AudioWorkletGlobalScope.
|
||||
|
||||
Bug: 327027138
|
||||
Change-Id: I051565c591645f0a4ccc297825d299c0764501ca
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5367245
|
||||
Reviewed-by: Danil Somsikov <dsv@chromium.org>
|
||||
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
|
||||
|
||||
diff --git a/front_end/core/sdk/ChildTargetManager.ts b/front_end/core/sdk/ChildTargetManager.ts
|
||||
index 1d790363b8a45655140353863b422fce35f2ae0e..9305546e8777abf582437e0978c114ad5db514ca 100644
|
||||
--- a/front_end/core/sdk/ChildTargetManager.ts
|
||||
+++ b/front_end/core/sdk/ChildTargetManager.ts
|
||||
@@ -173,6 +173,8 @@ export class ChildTargetManager extends SDKModel<EventTypes> implements Protocol
|
||||
type = Type.Frame;
|
||||
} else if (targetInfo.type === 'worker') {
|
||||
type = Type.Worker;
|
||||
+ } else if (targetInfo.type === 'worklet') {
|
||||
+ type = Type.Worklet;
|
||||
} else if (targetInfo.type === 'shared_worker') {
|
||||
type = Type.SharedWorker;
|
||||
} else if (targetInfo.type === 'shared_storage_worklet') {
|
||||
0
patches/libvpx/.patches
Normal file
0
patches/libvpx/.patches
Normal file
@@ -53,3 +53,6 @@ fix_undici_incorrectly_copies_headers_onto_fetches.patch
|
||||
fix_revert_src_lb_reducing_c_calls_of_esm_legacy_main_resolve.patch
|
||||
src_preload_function_for_environment.patch
|
||||
fs_fix_wtf-8_decoding_issue.patch
|
||||
stream_do_not_defer_construction_by_one_microtick.patch
|
||||
deps_disable_io_uring_support_in_libuv_by_default.patch
|
||||
src_deps_disable_setuid_etc_if_io_uring_enabled.patch
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= <tniessen@tnie.de>
|
||||
Date: Tue, 19 Sep 2023 16:01:49 +0000
|
||||
Subject: deps: disable io_uring support in libuv by default
|
||||
|
||||
setuid() does not affect libuv's internal io_uring operations if
|
||||
initialized before the call to setuid(). This potentially allows the
|
||||
process to perform privileged operations despite presumably having
|
||||
dropped such privileges through a call to setuid(). Similar concerns
|
||||
apply to other functions that modify the process's user identity.
|
||||
|
||||
This commit changes libuv's io_uring behavior from opt-out (through
|
||||
UV_USE_IO_URING=0) to opt-in (through UV_USE_IO_URING=1) until we figure
|
||||
out a better long-term solution.
|
||||
|
||||
PR-URL: https://github.com/nodejs-private/node-private/pull/529
|
||||
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||
CVE-ID: CVE-2024-22017
|
||||
|
||||
diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c
|
||||
index 48b9c2c43e104079d3ccb5d830d1d79f891fb1a3..656206c6ca8e808a840e006d776eeb64b4b14923 100644
|
||||
--- a/deps/uv/src/unix/linux.c
|
||||
+++ b/deps/uv/src/unix/linux.c
|
||||
@@ -431,8 +431,9 @@ static int uv__use_io_uring(void) {
|
||||
use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
|
||||
|
||||
if (use == 0) {
|
||||
+ /* Disable io_uring by default due to CVE-2024-22017. */
|
||||
val = getenv("UV_USE_IO_URING");
|
||||
- use = val == NULL || atoi(val) ? 1 : -1;
|
||||
+ use = val != NULL && atoi(val) ? 1 : -1;
|
||||
atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
|
||||
}
|
||||
|
||||
diff --git a/doc/api/cli.md b/doc/api/cli.md
|
||||
index f50b22f729c28386823d64ef8c9d5fc36c0bf9b1..053c0f94aef5b1681d1ab0513bcc76063ab3a2a6 100644
|
||||
--- a/doc/api/cli.md
|
||||
+++ b/doc/api/cli.md
|
||||
@@ -2596,6 +2596,22 @@ threadpool by setting the `'UV_THREADPOOL_SIZE'` environment variable to a value
|
||||
greater than `4` (its current default value). For more information, see the
|
||||
[libuv threadpool documentation][].
|
||||
|
||||
+### `UV_USE_IO_URING=value`
|
||||
+
|
||||
+Enable or disable libuv's use of `io_uring` on supported platforms.
|
||||
+
|
||||
+On supported platforms, `io_uring` can significantly improve the performance of
|
||||
+various asynchronous I/O operations.
|
||||
+
|
||||
+`io_uring` is disabled by default due to security concerns. When `io_uring`
|
||||
+is enabled, applications must not change the user identity of the process at
|
||||
+runtime, neither through JavaScript functions such as [`process.setuid()`][] nor
|
||||
+through native addons that can invoke system functions such as [`setuid(2)`][].
|
||||
+
|
||||
+This environment variable is implemented by a dependency of Node.js and may be
|
||||
+removed in future versions of Node.js. No stability guarantees are provided for
|
||||
+the behavior of this environment variable.
|
||||
+
|
||||
## Useful V8 options
|
||||
|
||||
V8 has its own set of CLI options. Any V8 CLI option that is provided to `node`
|
||||
@@ -2693,6 +2709,8 @@ done
|
||||
[`dnsPromises.lookup()`]: dns.md#dnspromiseslookuphostname-options
|
||||
[`import` specifier]: esm.md#import-specifiers
|
||||
[`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn
|
||||
+[`process.setuid()`]: process.md#processsetuidid
|
||||
+[`setuid(2)`]: https://man7.org/linux/man-pages/man2/setuid.2.html
|
||||
[`tls.DEFAULT_MAX_VERSION`]: tls.md#tlsdefault_max_version
|
||||
[`tls.DEFAULT_MIN_VERSION`]: tls.md#tlsdefault_min_version
|
||||
[`unhandledRejection`]: process.md#event-unhandledrejection
|
||||
@@ -0,0 +1,217 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= <tniessen@tnie.de>
|
||||
Date: Mon, 9 Oct 2023 08:10:00 +0000
|
||||
Subject: src,deps: disable setuid() etc if io_uring enabled
|
||||
|
||||
Within Node.js, attempt to determine if libuv is using io_uring. If it
|
||||
is, disable process.setuid() and other user identity setters.
|
||||
|
||||
We cannot fully prevent users from changing the process's user identity,
|
||||
but this should still prevent some accidental, dangerous scenarios.
|
||||
|
||||
PR-URL: https://github.com/nodejs-private/node-private/pull/529
|
||||
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||
CVE-ID: CVE-2024-22017
|
||||
|
||||
diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c
|
||||
index 656206c6ca8e808a840e006d776eeb64b4b14923..0c99718510fe1ca449ec685f323171475ab16552 100644
|
||||
--- a/deps/uv/src/unix/linux.c
|
||||
+++ b/deps/uv/src/unix/linux.c
|
||||
@@ -442,6 +442,14 @@ static int uv__use_io_uring(void) {
|
||||
}
|
||||
|
||||
|
||||
+UV_EXTERN int uv__node_patch_is_using_io_uring(void) {
|
||||
+ // This function exists only in the modified copy of libuv in the Node.js
|
||||
+ // repository. Node.js checks if this function exists and, if it does, uses it
|
||||
+ // to determine whether libuv is using io_uring or not.
|
||||
+ return uv__use_io_uring();
|
||||
+}
|
||||
+
|
||||
+
|
||||
static void uv__iou_init(int epollfd,
|
||||
struct uv__iou* iou,
|
||||
uint32_t entries,
|
||||
diff --git a/doc/api/cli.md b/doc/api/cli.md
|
||||
index 053c0f94aef5b1681d1ab0513bcc76063ab3a2a6..9b32639532bf6377aade96b86faccf050a396e63 100644
|
||||
--- a/doc/api/cli.md
|
||||
+++ b/doc/api/cli.md
|
||||
@@ -2605,8 +2605,9 @@ various asynchronous I/O operations.
|
||||
|
||||
`io_uring` is disabled by default due to security concerns. When `io_uring`
|
||||
is enabled, applications must not change the user identity of the process at
|
||||
-runtime, neither through JavaScript functions such as [`process.setuid()`][] nor
|
||||
-through native addons that can invoke system functions such as [`setuid(2)`][].
|
||||
+runtime. In this case, JavaScript functions such as [`process.setuid()`][] are
|
||||
+unavailable, and native addons must not invoke system functions such as
|
||||
+[`setuid(2)`][].
|
||||
|
||||
This environment variable is implemented by a dependency of Node.js and may be
|
||||
removed in future versions of Node.js. No stability guarantees are provided for
|
||||
diff --git a/src/node_credentials.cc b/src/node_credentials.cc
|
||||
index c1f7a4f2acbdf66a45230e2b9fdd860d9d3b4458..3b5eec8244024f58713c668c9dcd84617a18db88 100644
|
||||
--- a/src/node_credentials.cc
|
||||
+++ b/src/node_credentials.cc
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "env-inl.h"
|
||||
+#include "node_errors.h"
|
||||
#include "node_external_reference.h"
|
||||
#include "node_internals.h"
|
||||
#include "util-inl.h"
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <unistd.h> // setuid, getuid
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
+#include <dlfcn.h> // dlsym()
|
||||
#include <linux/capability.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/syscall.h>
|
||||
@@ -232,6 +234,45 @@ static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
|
||||
}
|
||||
}
|
||||
|
||||
+#ifdef __linux__
|
||||
+extern "C" {
|
||||
+int uv__node_patch_is_using_io_uring(void);
|
||||
+
|
||||
+int uv__node_patch_is_using_io_uring(void) __attribute__((weak));
|
||||
+
|
||||
+typedef int (*is_using_io_uring_fn)(void);
|
||||
+}
|
||||
+#endif // __linux__
|
||||
+
|
||||
+static bool UvMightBeUsingIoUring() {
|
||||
+#ifdef __linux__
|
||||
+ // Support for io_uring is only included in libuv 1.45.0 and later, and only
|
||||
+ // on Linux (and Android, but there it is always disabled). The patch that we
|
||||
+ // apply to libuv to work around the io_uring security issue adds a function
|
||||
+ // that tells us whether io_uring is being used. If that function is not
|
||||
+ // present, we assume that we are dynamically linking against an unpatched
|
||||
+ // version.
|
||||
+ static std::atomic<is_using_io_uring_fn> check =
|
||||
+ uv__node_patch_is_using_io_uring;
|
||||
+ if (check == nullptr) {
|
||||
+ check = reinterpret_cast<is_using_io_uring_fn>(
|
||||
+ dlsym(RTLD_DEFAULT, "uv__node_patch_is_using_io_uring"));
|
||||
+ }
|
||||
+ return uv_version() >= 0x012d00u && (check == nullptr || (*check)());
|
||||
+#else
|
||||
+ return false;
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+static bool ThrowIfUvMightBeUsingIoUring(Environment* env, const char* fn) {
|
||||
+ if (UvMightBeUsingIoUring()) {
|
||||
+ node::THROW_ERR_INVALID_STATE(
|
||||
+ env, "%s() disabled: io_uring may be enabled. See CVE-2024-22017.", fn);
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static void GetUid(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(env->has_run_bootstrapping_code());
|
||||
@@ -267,6 +308,8 @@ static void SetGid(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||
|
||||
+ if (ThrowIfUvMightBeUsingIoUring(env, "setgid")) return;
|
||||
+
|
||||
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
||||
|
||||
if (gid == gid_not_found) {
|
||||
@@ -286,6 +329,8 @@ static void SetEGid(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||
|
||||
+ if (ThrowIfUvMightBeUsingIoUring(env, "setegid")) return;
|
||||
+
|
||||
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
||||
|
||||
if (gid == gid_not_found) {
|
||||
@@ -305,6 +350,8 @@ static void SetUid(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||
|
||||
+ if (ThrowIfUvMightBeUsingIoUring(env, "setuid")) return;
|
||||
+
|
||||
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
||||
|
||||
if (uid == uid_not_found) {
|
||||
@@ -324,6 +371,8 @@ static void SetEUid(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||
|
||||
+ if (ThrowIfUvMightBeUsingIoUring(env, "seteuid")) return;
|
||||
+
|
||||
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
||||
|
||||
if (uid == uid_not_found) {
|
||||
@@ -364,6 +413,8 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsArray());
|
||||
|
||||
+ if (ThrowIfUvMightBeUsingIoUring(env, "setgroups")) return;
|
||||
+
|
||||
Local<Array> groups_list = args[0].As<Array>();
|
||||
size_t size = groups_list->Length();
|
||||
MaybeStackBuffer<gid_t, 64> groups(size);
|
||||
@@ -395,6 +446,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||
CHECK(args[1]->IsUint32() || args[1]->IsString());
|
||||
|
||||
+ if (ThrowIfUvMightBeUsingIoUring(env, "initgroups")) return;
|
||||
+
|
||||
Utf8Value arg0(env->isolate(), args[0]);
|
||||
gid_t extra_group;
|
||||
bool must_free;
|
||||
diff --git a/test/parallel/test-process-setuid-io-uring.js b/test/parallel/test-process-setuid-io-uring.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..93193ac2f8ab99f0cf8f2de368bc27958c92be76
|
||||
--- /dev/null
|
||||
+++ b/test/parallel/test-process-setuid-io-uring.js
|
||||
@@ -0,0 +1,43 @@
|
||||
+'use strict';
|
||||
+const common = require('../common');
|
||||
+
|
||||
+const assert = require('node:assert');
|
||||
+const { execFileSync } = require('node:child_process');
|
||||
+
|
||||
+if (!common.isLinux) {
|
||||
+ common.skip('test is Linux specific');
|
||||
+}
|
||||
+
|
||||
+if (process.arch !== 'x64' && process.arch !== 'arm64') {
|
||||
+ common.skip('io_uring support on this architecture is uncertain');
|
||||
+}
|
||||
+
|
||||
+const kv = /^(\d+)\.(\d+)\.(\d+)/.exec(execFileSync('uname', ['-r'])).slice(1).map((n) => parseInt(n, 10));
|
||||
+if (((kv[0] << 16) | (kv[1] << 8) | kv[2]) < 0x050ABA) {
|
||||
+ common.skip('io_uring is likely buggy due to old kernel');
|
||||
+}
|
||||
+
|
||||
+const userIdentitySetters = [
|
||||
+ ['setuid', [1000]],
|
||||
+ ['seteuid', [1000]],
|
||||
+ ['setgid', [1000]],
|
||||
+ ['setegid', [1000]],
|
||||
+ ['setgroups', [[1000]]],
|
||||
+ ['initgroups', ['nodeuser', 1000]],
|
||||
+];
|
||||
+
|
||||
+for (const [fnName, args] of userIdentitySetters) {
|
||||
+ const call = `process.${fnName}(${args.map((a) => JSON.stringify(a)).join(', ')})`;
|
||||
+ const code = `try { ${call}; } catch (err) { console.log(err); }`;
|
||||
+
|
||||
+ const stdout = execFileSync(process.execPath, ['-e', code], {
|
||||
+ encoding: 'utf8',
|
||||
+ env: { ...process.env, UV_USE_IO_URING: '1' },
|
||||
+ });
|
||||
+
|
||||
+ const msg = new RegExp(`^Error: ${fnName}\\(\\) disabled: io_uring may be enabled\\. See CVE-[X0-9]{4}-`);
|
||||
+ assert.match(stdout, msg);
|
||||
+ assert.match(stdout, /code: 'ERR_INVALID_STATE'/);
|
||||
+
|
||||
+ console.log(call, stdout.slice(0, stdout.indexOf('\n')));
|
||||
+}
|
||||
@@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Matteo Collina <hello@matteocollina.com>
|
||||
Date: Thu, 7 Mar 2024 17:28:25 +0100
|
||||
Subject: stream: do not defer construction by one microtick
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Fixes: https://github.com/nodejs/node/issues/51993
|
||||
PR-URL: https://github.com/nodejs/node/pull/52005
|
||||
Reviewed-By: Robert Nagy <ronagy@icloud.com>
|
||||
Reviewed-By: Michaël Zasso <targos@protonmail.com>
|
||||
|
||||
diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js
|
||||
index cfb49f2c7c72730356e530e22cb13adca38f5991..08f1fca0d5de26c80839e30b33e96ff2a59561eb 100644
|
||||
--- a/lib/internal/streams/destroy.js
|
||||
+++ b/lib/internal/streams/destroy.js
|
||||
@@ -267,7 +267,7 @@ function constructNT(stream) {
|
||||
} else if (err) {
|
||||
errorOrDestroy(stream, err, true);
|
||||
} else {
|
||||
- process.nextTick(emitConstructNT, stream);
|
||||
+ stream.emit(kConstruct);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,10 +280,6 @@ function constructNT(stream) {
|
||||
}
|
||||
}
|
||||
|
||||
-function emitConstructNT(stream) {
|
||||
- stream.emit(kConstruct);
|
||||
-}
|
||||
-
|
||||
function isRequest(stream) {
|
||||
return stream?.setHeader && typeof stream.abort === 'function';
|
||||
}
|
||||
@@ -1,2 +1,6 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
deps_add_v8_object_setinternalfieldfornodecore.patch
|
||||
merged_wasm_gc_scan_the_code_field_of_the_wasminternalfunction.patch
|
||||
cherry-pick-f320600cd1f4.patch
|
||||
cherry-pick-b3c01ac1e60a.patch
|
||||
cherry-pick-6503a987d966.patch
|
||||
|
||||
56
patches/v8/cherry-pick-6503a987d966.patch
Normal file
56
patches/v8/cherry-pick-6503a987d966.patch
Normal file
@@ -0,0 +1,56 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Victor Gomes <victorgomes@chromium.org>
|
||||
Date: Thu, 21 Mar 2024 09:59:19 +0100
|
||||
Subject: Deal with large strings in NoSideEffectsErrorToString
|
||||
|
||||
If name is too big, StringBuilder will fail to even add
|
||||
"<a very large string>" suffix.
|
||||
|
||||
In this case, we truncate name first.
|
||||
|
||||
Bug: 329699609
|
||||
Change-Id: I6e4440c07eae84371f44b54f88127e2c70af0db5
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5378286
|
||||
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
|
||||
Reviewed-by: Patrick Thier <pthier@chromium.org>
|
||||
Auto-Submit: Victor Gomes <victorgomes@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#92932}
|
||||
|
||||
diff --git a/src/objects/objects.cc b/src/objects/objects.cc
|
||||
index ed640e14dd43b3a1268afd196db8d7a0cb8601da..06e81c96c504e27eb218ca60d67a7bd4b4f4487f 100644
|
||||
--- a/src/objects/objects.cc
|
||||
+++ b/src/objects/objects.cc
|
||||
@@ -469,14 +469,27 @@ Handle<String> NoSideEffectsErrorToString(Isolate* isolate,
|
||||
if (name_str->length() == 0) return msg_str;
|
||||
if (msg_str->length() == 0) return name_str;
|
||||
|
||||
- IncrementalStringBuilder builder(isolate);
|
||||
- builder.AppendString(name_str);
|
||||
- builder.AppendCStringLiteral(": ");
|
||||
+ constexpr const char error_suffix[] = "<a very large string>";
|
||||
+ constexpr int error_suffix_size = sizeof(error_suffix);
|
||||
+ int suffix_size = std::min(error_suffix_size, msg_str->length());
|
||||
|
||||
- if (builder.Length() + msg_str->length() <= String::kMaxLength) {
|
||||
- builder.AppendString(msg_str);
|
||||
+ IncrementalStringBuilder builder(isolate);
|
||||
+ if (name_str->length() + suffix_size + 2 /* ": " */ > String::kMaxLength) {
|
||||
+ constexpr const char connector[] = "... : ";
|
||||
+ int connector_size = sizeof(connector);
|
||||
+ Handle<String> truncated_name = isolate->factory()->NewProperSubString(
|
||||
+ name_str, 0, name_str->length() - error_suffix_size - connector_size);
|
||||
+ builder.AppendString(truncated_name);
|
||||
+ builder.AppendCStringLiteral(connector);
|
||||
+ builder.AppendCStringLiteral(error_suffix);
|
||||
} else {
|
||||
- builder.AppendCStringLiteral("<a very large string>");
|
||||
+ builder.AppendString(name_str);
|
||||
+ builder.AppendCStringLiteral(": ");
|
||||
+ if (builder.Length() + msg_str->length() <= String::kMaxLength) {
|
||||
+ builder.AppendString(msg_str);
|
||||
+ } else {
|
||||
+ builder.AppendCStringLiteral(error_suffix);
|
||||
+ }
|
||||
}
|
||||
|
||||
return builder.Finish().ToHandleChecked();
|
||||
76
patches/v8/cherry-pick-b3c01ac1e60a.patch
Normal file
76
patches/v8/cherry-pick-b3c01ac1e60a.patch
Normal file
@@ -0,0 +1,76 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shu-yu Guo <syg@chromium.org>
|
||||
Date: Mon, 13 May 2024 11:23:20 -0700
|
||||
Subject: Don't build AccessInfo for storing to module exports
|
||||
|
||||
Bug: 340221135
|
||||
Change-Id: I5af35be6ebf6a69db1c4687107503575b23973c4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5534518
|
||||
Reviewed-by: Adam Klein <adamk@chromium.org>
|
||||
Commit-Queue: Shu-yu Guo <syg@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#93872}
|
||||
|
||||
diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc
|
||||
index 7cff878839c85cd9c6571ee48f1a0fce081f4471..9d022ba402d7bfdd5ca745a4193ee0de0e3d755e 100644
|
||||
--- a/src/compiler/access-info.cc
|
||||
+++ b/src/compiler/access-info.cc
|
||||
@@ -526,6 +526,14 @@ PropertyAccessInfo AccessorAccessInfoHelper(
|
||||
Cell::cast(module_namespace->module()->exports()->Lookup(
|
||||
isolate, name.object(),
|
||||
Smi::ToInt(Object::GetHash(*name.object())))));
|
||||
+ if (IsAnyStore(access_mode)) {
|
||||
+ // ES#sec-module-namespace-exotic-objects-set-p-v-receiver
|
||||
+ // ES#sec-module-namespace-exotic-objects-defineownproperty-p-desc
|
||||
+ //
|
||||
+ // Storing to a module namespace object is always an error or a no-op in
|
||||
+ // JS.
|
||||
+ return PropertyAccessInfo::Invalid(zone);
|
||||
+ }
|
||||
if (IsTheHole(cell->value(kRelaxedLoad), isolate)) {
|
||||
// This module has not been fully initialized yet.
|
||||
return PropertyAccessInfo::Invalid(zone);
|
||||
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
|
||||
index 8a5b4483d148192d3106e75874f042ccc6b36e56..728105c0aaccbaeb018eb9b9a660b5acba4650d8 100644
|
||||
--- a/src/maglev/maglev-graph-builder.cc
|
||||
+++ b/src/maglev/maglev-graph-builder.cc
|
||||
@@ -3914,19 +3914,28 @@ ReduceResult MaglevGraphBuilder::TryBuildPropertyStore(
|
||||
access_info.holder().value());
|
||||
}
|
||||
|
||||
- if (access_info.IsFastAccessorConstant()) {
|
||||
- return TryBuildPropertySetterCall(access_info, receiver,
|
||||
- GetAccumulatorTagged());
|
||||
- } else {
|
||||
- DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
|
||||
- ReduceResult res = TryBuildStoreField(access_info, receiver, access_mode);
|
||||
- if (res.IsDone()) {
|
||||
- RecordKnownProperty(receiver, name,
|
||||
- current_interpreter_frame_.accumulator(),
|
||||
- AccessInfoGuaranteedConst(access_info), access_mode);
|
||||
- return res;
|
||||
+ switch (access_info.kind()) {
|
||||
+ case compiler::PropertyAccessInfo::kFastAccessorConstant:
|
||||
+ return TryBuildPropertySetterCall(access_info, receiver,
|
||||
+ GetAccumulatorTagged());
|
||||
+ case compiler::PropertyAccessInfo::kDataField:
|
||||
+ case compiler::PropertyAccessInfo::kFastDataConstant: {
|
||||
+ ReduceResult res = TryBuildStoreField(access_info, receiver, access_mode);
|
||||
+ if (res.IsDone()) {
|
||||
+ RecordKnownProperty(
|
||||
+ receiver, name, current_interpreter_frame_.accumulator(),
|
||||
+ AccessInfoGuaranteedConst(access_info), access_mode);
|
||||
+ return res;
|
||||
+ }
|
||||
+ return ReduceResult::Fail();
|
||||
}
|
||||
- return ReduceResult::Fail();
|
||||
+ case compiler::PropertyAccessInfo::kInvalid:
|
||||
+ case compiler::PropertyAccessInfo::kNotFound:
|
||||
+ case compiler::PropertyAccessInfo::kDictionaryProtoDataConstant:
|
||||
+ case compiler::PropertyAccessInfo::kDictionaryProtoAccessorConstant:
|
||||
+ case compiler::PropertyAccessInfo::kModuleExport:
|
||||
+ case compiler::PropertyAccessInfo::kStringLength:
|
||||
+ UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
27
patches/v8/cherry-pick-f320600cd1f4.patch
Normal file
27
patches/v8/cherry-pick-f320600cd1f4.patch
Normal file
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shu-yu Guo <syg@chromium.org>
|
||||
Date: Thu, 9 May 2024 12:03:28 -0700
|
||||
Subject: Only normalize JSObject targets in SetOrCopyDataProperties
|
||||
|
||||
Bug: 339458194
|
||||
Change-Id: I4d6eebdd921971fa28d7c474535d978900ba633f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5527397
|
||||
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
|
||||
Commit-Queue: Shu-yu Guo <syg@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#93811}
|
||||
|
||||
diff --git a/src/objects/js-objects.cc b/src/objects/js-objects.cc
|
||||
index 32e1e89fc53217fe188c1f764bd97c03dadf35a2..0381a9f65a9e6cdfb30881077e4a442cd2a10802 100644
|
||||
--- a/src/objects/js-objects.cc
|
||||
+++ b/src/objects/js-objects.cc
|
||||
@@ -429,9 +429,7 @@ Maybe<bool> JSReceiver::SetOrCopyDataProperties(
|
||||
Nothing<bool>());
|
||||
|
||||
if (!from->HasFastProperties() && target->HasFastProperties() &&
|
||||
- !IsJSGlobalProxy(*target)) {
|
||||
- // JSProxy is always in slow-mode.
|
||||
- DCHECK(!IsJSProxy(*target));
|
||||
+ IsJSObject(*target) && !IsJSGlobalProxy(*target)) {
|
||||
// Convert to slow properties if we're guaranteed to overflow the number of
|
||||
// descriptors.
|
||||
int source_length;
|
||||
@@ -0,0 +1,65 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andreas Haas <ahaas@chromium.org>
|
||||
Date: Mon, 18 Mar 2024 15:25:15 +0100
|
||||
Subject: Merged: [wasm][gc] Scan the code field of the WasmInternalFunction
|
||||
|
||||
The code field in the WasmInternalFunction is a code pointer since
|
||||
https://crrev.com/c/5110559, so it has to be scanned explicitly.
|
||||
|
||||
Bug: 329130358
|
||||
(cherry picked from commit b93975a48c722c2e5fe9b39437738eb2e23dac74)
|
||||
|
||||
Change-Id: I0795d2188a8af3480c513d1dbaccfcef1da04473
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5410311
|
||||
Reviewed-by: Deepti Gandluri <gdeepti@chromium.org>
|
||||
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
|
||||
Auto-Submit: Shu-yu Guo <syg@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/12.2@{#54}
|
||||
Cr-Branched-From: 6eb5a9616aa6f8c705217aeb7c7ab8c037a2f676-refs/heads/12.2.281@{#1}
|
||||
Cr-Branched-From: 44cf56d850167c6988522f8981730462abc04bcc-refs/heads/main@{#91934}
|
||||
|
||||
diff --git a/src/objects/objects-body-descriptors-inl.h b/src/objects/objects-body-descriptors-inl.h
|
||||
index 4041a10b3d0e22fa95f263528f331dad1eadaf8b..e9ecda50718a85847ad1205bdcf2f275208fbc12 100644
|
||||
--- a/src/objects/objects-body-descriptors-inl.h
|
||||
+++ b/src/objects/objects-body-descriptors-inl.h
|
||||
@@ -791,6 +791,7 @@ class WasmInternalFunction::BodyDescriptor final : public BodyDescriptorBase {
|
||||
v->VisitExternalPointer(
|
||||
obj, obj->RawExternalPointerField(kCallTargetOffset,
|
||||
kWasmInternalFunctionCallTargetTag));
|
||||
+ IterateCodePointer(obj, kCodeOffset, v, IndirectPointerMode::kStrong);
|
||||
}
|
||||
|
||||
static inline int SizeOf(Tagged<Map> map, Tagged<HeapObject> object) {
|
||||
diff --git a/test/mjsunit/regress/wasm/regress-329130358.js b/test/mjsunit/regress/wasm/regress-329130358.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..d5231768989d9c709da7102bdd6be5702a89f2f8
|
||||
--- /dev/null
|
||||
+++ b/test/mjsunit/regress/wasm/regress-329130358.js
|
||||
@@ -0,0 +1,27 @@
|
||||
+// Copyright 2024 the V8 project authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+// Flags: --expose-gc --wasm-wrapper-tiering-budget=1 --experimental-wasm-type-reflection
|
||||
+
|
||||
+d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
+
|
||||
+const builder = new WasmModuleBuilder();
|
||||
+const type = builder.addType(kSig_i_i);
|
||||
+const global = builder.addImportedGlobal('m', 'val', kWasmAnyFunc);
|
||||
+
|
||||
+builder.addFunction('main', type)
|
||||
+ .addBody([
|
||||
+ kExprLocalGet, 0, kExprGlobalGet, global, kGCPrefix, kExprRefCast, type,
|
||||
+ kExprCallRef, type
|
||||
+ ])
|
||||
+ .exportFunc();
|
||||
+
|
||||
+function foo() {
|
||||
+ gc();
|
||||
+}
|
||||
+const func =
|
||||
+ new WebAssembly.Function({parameters: ['i32'], results: ['i32']}, foo);
|
||||
+
|
||||
+let instance = builder.instantiate({m: {val: func}});
|
||||
+instance.exports.main(3);
|
||||
+instance.exports.main(3);
|
||||
@@ -75,7 +75,8 @@ function spawnAndCheckExitCode (cmd, args, opts) {
|
||||
|
||||
function cpplint (args) {
|
||||
args.unshift(`--root=${SOURCE_ROOT}`);
|
||||
const result = childProcess.spawnSync(IS_WINDOWS ? 'cpplint.bat' : 'cpplint.py', args, { encoding: 'utf8', shell: true });
|
||||
const cmd = IS_WINDOWS ? 'cpplint.bat' : 'cpplint.py';
|
||||
const result = childProcess.spawnSync(cmd, args, { encoding: 'utf8', shell: true });
|
||||
// cpplint.py writes EVERYTHING to stderr, including status messages
|
||||
if (result.stderr) {
|
||||
for (const line of result.stderr.split(/[\r\n]+/)) {
|
||||
|
||||
@@ -93,7 +93,8 @@ async function main () {
|
||||
const { status: buildStatus } = cp.spawnSync(NPX_CMD, ['node-gyp', 'rebuild', '--verbose', '--directory', 'test', '-j', 'max'], {
|
||||
env,
|
||||
cwd: NAN_DIR,
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
if (buildStatus !== 0) {
|
||||
@@ -104,7 +105,8 @@ async function main () {
|
||||
const { status: installStatus } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], {
|
||||
env,
|
||||
cwd: NAN_DIR,
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
if (installStatus !== 0) {
|
||||
console.error('Failed to install nan node_modules');
|
||||
|
||||
@@ -10,7 +10,8 @@ if (fs.existsSync(checkPath)) {
|
||||
command.slice(1),
|
||||
{
|
||||
stdio: 'inherit',
|
||||
cwd: checkPath
|
||||
cwd: checkPath,
|
||||
shell: process.platform === 'win32'
|
||||
}
|
||||
);
|
||||
child.on('exit', code => process.exit(code));
|
||||
|
||||
@@ -221,7 +221,8 @@ async function installSpecModules (dir) {
|
||||
const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], {
|
||||
env,
|
||||
cwd: dir,
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
if (status !== 0 && !process.env.IGNORE_YARN_INSTALL_ERROR) {
|
||||
console.log(`${fail} Failed to yarn install in '${dir}'`);
|
||||
|
||||
@@ -11,7 +11,8 @@ if (require.main === module) {
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_yes: 'true'
|
||||
}
|
||||
},
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
child.on('exit', code => process.exit(code));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "content/public/common/color_parser.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "gin/dictionary.h"
|
||||
#include "shell/browser/api/electron_api_browser_view.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "shell/browser/ui/win/taskbar_host.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#endif
|
||||
@@ -1134,6 +1136,63 @@ void BaseWindow::SetAppDetails(const gin_helper::Dictionary& options) {
|
||||
relaunch_command, relaunch_display_name,
|
||||
window_->GetAcceleratedWidget());
|
||||
}
|
||||
|
||||
void BaseWindow::SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
||||
gin_helper::Arguments* args) {
|
||||
// Ensure WCO is already enabled on this window
|
||||
if (!window_->titlebar_overlay_enabled()) {
|
||||
args->ThrowError("Titlebar overlay is not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* window = static_cast<NativeWindowViews*>(window_.get());
|
||||
bool updated = false;
|
||||
|
||||
// Check and update the button color
|
||||
std::string btn_color;
|
||||
if (options.Get(options::kOverlayButtonColor, &btn_color)) {
|
||||
// Parse the string as a CSS color
|
||||
SkColor color;
|
||||
if (!content::ParseCssColorString(btn_color, &color)) {
|
||||
args->ThrowError("Could not parse color as CSS color");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view
|
||||
window->set_overlay_button_color(color);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Check and update the symbol color
|
||||
std::string symbol_color;
|
||||
if (options.Get(options::kOverlaySymbolColor, &symbol_color)) {
|
||||
// Parse the string as a CSS color
|
||||
SkColor color;
|
||||
if (!content::ParseCssColorString(symbol_color, &color)) {
|
||||
args->ThrowError("Could not parse symbol color as CSS color");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view
|
||||
window->set_overlay_symbol_color(color);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Check and update the height
|
||||
int height = 0;
|
||||
if (options.Get(options::kOverlayHeight, &height)) {
|
||||
window->set_titlebar_overlay_height(height);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// If anything was updated, invalidate the layout and schedule a paint of the
|
||||
// window's frame view
|
||||
if (updated) {
|
||||
auto* frame_view = static_cast<WinFrameView*>(
|
||||
window->widget()->non_client_view()->frame_view());
|
||||
frame_view->InvalidateCaptionButtons();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t BaseWindow::GetID() const {
|
||||
@@ -1351,6 +1410,7 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("setThumbnailClip", &BaseWindow::SetThumbnailClip)
|
||||
.SetMethod("setThumbnailToolTip", &BaseWindow::SetThumbnailToolTip)
|
||||
.SetMethod("setAppDetails", &BaseWindow::SetAppDetails)
|
||||
.SetMethod("setTitleBarOverlay", &BaseWindow::SetTitleBarOverlay)
|
||||
#endif
|
||||
.SetProperty("id", &BaseWindow::GetID);
|
||||
}
|
||||
|
||||
@@ -251,6 +251,8 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
bool SetThumbnailClip(const gfx::Rect& region);
|
||||
bool SetThumbnailToolTip(const std::string& tooltip);
|
||||
void SetAppDetails(const gin_helper::Dictionary& options);
|
||||
void SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
||||
gin_helper::Arguments* args);
|
||||
#endif
|
||||
int32_t GetID() const;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "content/browser/web_contents/web_contents_impl.h" // nogncheck
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/common/color_parser.h"
|
||||
#include "shell/browser/api/electron_api_web_contents_view.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/native_browser_view.h"
|
||||
@@ -28,10 +27,6 @@
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#endif
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||
@@ -332,65 +327,6 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
|
||||
return v8::Local<v8::Value>::New(isolate, web_contents_);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
void BrowserWindow::SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
||||
gin_helper::Arguments* args) {
|
||||
// Ensure WCO is already enabled on this window
|
||||
if (!window_->titlebar_overlay_enabled()) {
|
||||
args->ThrowError("Titlebar overlay is not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* window = static_cast<NativeWindowViews*>(window_.get());
|
||||
bool updated = false;
|
||||
|
||||
// Check and update the button color
|
||||
std::string btn_color;
|
||||
if (options.Get(options::kOverlayButtonColor, &btn_color)) {
|
||||
// Parse the string as a CSS color
|
||||
SkColor color;
|
||||
if (!content::ParseCssColorString(btn_color, &color)) {
|
||||
args->ThrowError("Could not parse color as CSS color");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view
|
||||
window->set_overlay_button_color(color);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Check and update the symbol color
|
||||
std::string symbol_color;
|
||||
if (options.Get(options::kOverlaySymbolColor, &symbol_color)) {
|
||||
// Parse the string as a CSS color
|
||||
SkColor color;
|
||||
if (!content::ParseCssColorString(symbol_color, &color)) {
|
||||
args->ThrowError("Could not parse symbol color as CSS color");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view
|
||||
window->set_overlay_symbol_color(color);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Check and update the height
|
||||
int height = 0;
|
||||
if (options.Get(options::kOverlayHeight, &height)) {
|
||||
window->set_titlebar_overlay_height(height);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// If anything was updated, invalidate the layout and schedule a paint of the
|
||||
// window's frame view
|
||||
if (updated) {
|
||||
auto* frame_view = static_cast<WinFrameView*>(
|
||||
window->widget()->non_client_view()->frame_view());
|
||||
frame_view->InvalidateCaptionButtons();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void BrowserWindow::ScheduleUnresponsiveEvent(int ms) {
|
||||
if (!window_unresponsive_closure_.IsCancelled())
|
||||
return;
|
||||
@@ -448,9 +384,6 @@ void BrowserWindow::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("focusOnWebView", &BrowserWindow::FocusOnWebView)
|
||||
.SetMethod("blurWebView", &BrowserWindow::BlurWebView)
|
||||
.SetMethod("isWebViewFocused", &BrowserWindow::IsWebViewFocused)
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
.SetMethod("setTitleBarOverlay", &BrowserWindow::SetTitleBarOverlay)
|
||||
#endif
|
||||
.SetProperty("webContents", &BrowserWindow::GetWebContents);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,10 +79,6 @@ class BrowserWindow : public BaseWindow,
|
||||
void BlurWebView();
|
||||
bool IsWebViewFocused();
|
||||
v8::Local<v8::Value> GetWebContents(v8::Isolate* isolate);
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
void SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
||||
gin_helper::Arguments* args);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Helpers.
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
#include "mojo/public/cpp/system/platform_handle.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
#include "shell/browser/api/electron_api_browser_window.h"
|
||||
@@ -173,10 +172,10 @@
|
||||
#include "components/printing/browser/print_manager_utils.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
|
||||
#include "printing/backend/print_backend.h" // nogncheck
|
||||
#include "printing/mojom/print.mojom.h" // nogncheck
|
||||
#include "printing/mojom/print.mojom.h" // nogncheck
|
||||
#include "printing/page_range.h"
|
||||
#include "shell/browser/printing/print_view_manager_electron.h"
|
||||
#include "shell/browser/printing/printing_utils.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "printing/backend/win_helper.h"
|
||||
@@ -352,6 +351,20 @@ struct Converter<scoped_refptr<content::DevToolsAgentHost>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<content::NavigationEntry*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
content::NavigationEntry* entry) {
|
||||
if (!entry) {
|
||||
return v8::Null(isolate);
|
||||
}
|
||||
gin_helper::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
dict.Set("url", entry->GetURL().spec());
|
||||
dict.Set("title", entry->GetTitleForDisplay());
|
||||
return dict.GetHandle();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
namespace electron::api {
|
||||
@@ -516,96 +529,6 @@ std::optional<base::TimeDelta> GetCursorBlinkInterval() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
// This will return false if no printer with the provided device_name can be
|
||||
// found on the network. We need to check this because Chromium does not do
|
||||
// sanity checking of device_name validity and so will crash on invalid names.
|
||||
bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
||||
base::SysUTF16ToCFStringRef(device_name));
|
||||
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
||||
bool printer_exists = new_printer != nullptr;
|
||||
PMRelease(new_printer);
|
||||
return printer_exists;
|
||||
#else
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function returns a validated device name.
|
||||
// If the user passed one to webContents.print(), we check that it's valid and
|
||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||
// pass a device name, we first try to return the system default printer. If one
|
||||
// isn't set, then pull all the printers and use the first one or fail if none
|
||||
// exist.
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||
// not thread-safe and have to be accessed on the UI thread.
|
||||
ScopedAllowBlockingForElectron allow_blocking;
|
||||
#endif
|
||||
|
||||
if (!device_name.empty()) {
|
||||
if (!IsDeviceNameValid(device_name))
|
||||
return std::make_pair("Invalid deviceName provided", std::u16string());
|
||||
return std::make_pair(std::string(), device_name);
|
||||
}
|
||||
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
std::string printer_name;
|
||||
printing::mojom::ResultCode code =
|
||||
print_backend->GetDefaultPrinterName(printer_name);
|
||||
|
||||
// We don't want to return if this fails since some devices won't have a
|
||||
// default printer.
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(ERROR) << "Failed to get default printer name";
|
||||
|
||||
if (printer_name.empty()) {
|
||||
printing::PrinterList printers;
|
||||
if (print_backend->EnumeratePrinters(printers) !=
|
||||
printing::mojom::ResultCode::kSuccess)
|
||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||
if (printers.empty())
|
||||
return std::make_pair("No printers available on the network",
|
||||
std::u16string());
|
||||
|
||||
printer_name = printers.front().printer_name;
|
||||
}
|
||||
|
||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||
}
|
||||
|
||||
// Copied from
|
||||
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
||||
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
||||
#if !BUILDFLAG(IS_WIN)
|
||||
static constexpr base::TaskTraits kTraits = {
|
||||
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
||||
#endif
|
||||
|
||||
#if defined(USE_CUPS)
|
||||
// CUPS is thread safe.
|
||||
return base::ThreadPool::CreateTaskRunner(kTraits);
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
// Windows drivers are likely not thread-safe and need to be accessed on the
|
||||
// UI thread.
|
||||
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
||||
#else
|
||||
// Be conservative on unsupported platforms.
|
||||
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
struct UserDataLink : public base::SupportsUserData::Data {
|
||||
explicit UserDataLink(base::WeakPtr<WebContents> contents)
|
||||
: web_contents(contents) {}
|
||||
@@ -2533,6 +2456,11 @@ int WebContents::GetActiveIndex() const {
|
||||
return web_contents()->GetController().GetCurrentEntryIndex();
|
||||
}
|
||||
|
||||
content::NavigationEntry* WebContents::GetNavigationEntryAtIndex(
|
||||
int index) const {
|
||||
return web_contents()->GetController().GetEntryAtIndex(index);
|
||||
}
|
||||
|
||||
void WebContents::ClearHistory() {
|
||||
// In some rare cases (normally while there is no real history) we are in a
|
||||
// state where we can't prune navigation entries
|
||||
@@ -2926,6 +2854,12 @@ void WebContents::OnGetDeviceNameToUse(
|
||||
// If the user has passed a deviceName use it, otherwise use default printer.
|
||||
print_settings.Set(printing::kSettingDeviceName, info.second);
|
||||
|
||||
if (!print_settings.FindInt(printing::kSettingDpiHorizontal)) {
|
||||
gfx::Size dpi = GetDefaultPrinterDPI(info.second);
|
||||
print_settings.Set(printing::kSettingDpiHorizontal, dpi.width());
|
||||
print_settings.Set(printing::kSettingDpiVertical, dpi.height());
|
||||
}
|
||||
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||
if (!print_view_manager)
|
||||
@@ -3087,7 +3021,6 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
|
||||
// Set custom dots per inch (dpi)
|
||||
gin_helper::Dictionary dpi_settings;
|
||||
int dpi = 72;
|
||||
if (options.Get("dpi", &dpi_settings)) {
|
||||
int horizontal = 72;
|
||||
dpi_settings.Get("horizontal", &horizontal);
|
||||
@@ -3095,9 +3028,6 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
int vertical = 72;
|
||||
dpi_settings.Get("vertical", &vertical);
|
||||
settings.Set(printing::kSettingDpiVertical, vertical);
|
||||
} else {
|
||||
settings.Set(printing::kSettingDpiHorizontal, dpi);
|
||||
settings.Set(printing::kSettingDpiVertical, dpi);
|
||||
}
|
||||
|
||||
print_task_runner_->PostTaskAndReplyWithResult(
|
||||
@@ -4304,9 +4234,11 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
|
||||
.SetMethod("goToOffset", &WebContents::GoToOffset)
|
||||
.SetMethod("canGoToIndex", &WebContents::CanGoToIndex)
|
||||
.SetMethod("goToIndex", &WebContents::GoToIndex)
|
||||
.SetMethod("getActiveIndex", &WebContents::GetActiveIndex)
|
||||
.SetMethod("_getActiveIndex", &WebContents::GetActiveIndex)
|
||||
.SetMethod("_getNavigationEntryAtIndex",
|
||||
&WebContents::GetNavigationEntryAtIndex)
|
||||
.SetMethod("_historyLength", &WebContents::GetHistoryLength)
|
||||
.SetMethod("clearHistory", &WebContents::ClearHistory)
|
||||
.SetMethod("length", &WebContents::GetHistoryLength)
|
||||
.SetMethod("isCrashed", &WebContents::IsCrashed)
|
||||
.SetMethod("forcefullyCrashRenderer",
|
||||
&WebContents::ForcefullyCrashRenderer)
|
||||
|
||||
@@ -190,6 +190,7 @@ class WebContents : public ExclusiveAccessContext,
|
||||
bool CanGoToIndex(int index) const;
|
||||
void GoToIndex(int index);
|
||||
int GetActiveIndex() const;
|
||||
content::NavigationEntry* GetNavigationEntryAtIndex(int index) const;
|
||||
void ClearHistory();
|
||||
int GetHistoryLength() const;
|
||||
const std::string GetWebRTCIPHandlingPolicy() const;
|
||||
|
||||
@@ -455,7 +455,15 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
base::CommandLine* command_line,
|
||||
int process_id) {
|
||||
// Make sure we're about to launch a known executable
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// On Linux, do not perform this check for /proc/self/exe. It will always
|
||||
// point to the currently running executable so this check is not
|
||||
// necessary, and if the executable has been deleted it will return a fake
|
||||
// name that causes this check to fail.
|
||||
if (command_line->GetProgram() != base::FilePath(base::kProcSelfExe)) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
ScopedAllowBlockingForElectron allow_blocking;
|
||||
base::FilePath child_path;
|
||||
base::FilePath program =
|
||||
|
||||
@@ -398,12 +398,6 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
|
||||
CHECK(linux_ui);
|
||||
linux_ui_getter_ = std::make_unique<LinuxUiGetterImpl>();
|
||||
|
||||
// Try loading gtk symbols used by Electron.
|
||||
electron::InitializeElectron_gtk(gtk::GetLibGtk());
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
electron::UninitializeElectron_gtk();
|
||||
}
|
||||
|
||||
electron::InitializeElectron_gdk_pixbuf(gtk::GetLibGdkPixbuf());
|
||||
CHECK(electron::IsElectron_gdk_pixbufInitialized())
|
||||
<< "Failed to initialize libgdk_pixbuf-2.0.so.0";
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/dcheck_is_on.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
std::string EnablePlatformSpecificFeatures() {
|
||||
@@ -19,8 +21,12 @@ std::string EnablePlatformSpecificFeatures() {
|
||||
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
|
||||
// kThumbnailCapturerMac,
|
||||
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
|
||||
#if DCHECK_IS_ON()
|
||||
return "";
|
||||
#else
|
||||
return "ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma,"
|
||||
"ThumbnailCapturerMac:capture_mode/sc_screenshot_manager";
|
||||
#endif
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -159,21 +159,21 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
|
||||
|
||||
void LibnotifyNotification::Dismiss() {
|
||||
if (!notification_) {
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
on_dismissing_ = true;
|
||||
libnotify_loader_.notify_notification_close(notification_, &error);
|
||||
if (error) {
|
||||
log_and_clear_error(error, "notify_notification_close");
|
||||
Destroy();
|
||||
}
|
||||
on_dismissing_ = false;
|
||||
}
|
||||
|
||||
void LibnotifyNotification::OnNotificationClosed(
|
||||
NotifyNotification* notification) {
|
||||
NotificationDismissed();
|
||||
NotificationDismissed(!on_dismissing_);
|
||||
}
|
||||
|
||||
void LibnotifyNotification::OnNotificationView(NotifyNotification* notification,
|
||||
|
||||
@@ -33,6 +33,7 @@ class LibnotifyNotification : public Notification {
|
||||
RAW_PTR_EXCLUSION NotifyNotification* notification_ = nullptr;
|
||||
|
||||
ScopedGSignal signal_;
|
||||
bool on_dismissing_ = false;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "content/browser/renderer_host/render_widget_host_delegate.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck
|
||||
#include "content/common/input/synthetic_gesture.h" // nogncheck
|
||||
#include "content/common/input/synthetic_gesture_target.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
@@ -229,6 +230,7 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
|
||||
|
||||
ResizeRootLayer(false);
|
||||
render_widget_host_->SetView(this);
|
||||
render_widget_host_->render_frame_metadata_provider()->AddObserver(this);
|
||||
|
||||
if (content::GpuDataManager::GetInstance()->HardwareAccelerationEnabled()) {
|
||||
video_consumer_ = std::make_unique<OffScreenVideoConsumer>(
|
||||
@@ -239,7 +241,21 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
|
||||
}
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::OnLocalSurfaceIdChanged(
|
||||
const cc::RenderFrameMetadata& metadata) {
|
||||
if (metadata.local_surface_id) {
|
||||
bool changed = delegated_frame_host_allocator_.UpdateFromChild(
|
||||
*metadata.local_surface_id);
|
||||
|
||||
if (changed) {
|
||||
ResizeRootLayer(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OffScreenRenderWidgetHostView::~OffScreenRenderWidgetHostView() {
|
||||
render_widget_host_->render_frame_metadata_provider()->RemoveObserver(this);
|
||||
|
||||
// Marking the DelegatedFrameHost as removed from the window hierarchy is
|
||||
// necessary to remove all connections to its old ui::Compositor.
|
||||
if (is_showing_)
|
||||
|
||||
@@ -59,9 +59,11 @@ typedef base::RepeatingCallback<void(const gfx::Rect&, const SkBitmap&)>
|
||||
OnPaintCallback;
|
||||
typedef base::RepeatingCallback<void(const gfx::Rect&)> OnPopupPaintCallback;
|
||||
|
||||
class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
|
||||
public ui::CompositorDelegate,
|
||||
public OffscreenViewProxyObserver {
|
||||
class OffScreenRenderWidgetHostView
|
||||
: public content::RenderWidgetHostViewBase,
|
||||
public content::RenderFrameMetadataProvider::Observer,
|
||||
public ui::CompositorDelegate,
|
||||
public OffscreenViewProxyObserver {
|
||||
public:
|
||||
OffScreenRenderWidgetHostView(bool transparent,
|
||||
bool painting,
|
||||
@@ -174,6 +176,15 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
|
||||
RenderWidgetHostViewBase* target_view,
|
||||
gfx::PointF* transformed_point) override;
|
||||
|
||||
// RenderFrameMetadataProvider::Observer:
|
||||
void OnRenderFrameMetadataChangedBeforeActivation(
|
||||
const cc::RenderFrameMetadata& metadata) override {}
|
||||
void OnRenderFrameMetadataChangedAfterActivation(
|
||||
base::TimeTicks activation_time) override {}
|
||||
void OnRenderFrameSubmission() override {}
|
||||
void OnLocalSurfaceIdChanged(
|
||||
const cc::RenderFrameMetadata& metadata) override;
|
||||
|
||||
// ui::CompositorDelegate:
|
||||
bool IsOffscreen() const override;
|
||||
std::unique_ptr<viz::HostDisplayClient> CreateHostDisplayClient(
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/printing/print_preview_message_handler.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/memory/read_only_shared_memory_region.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
#include "components/printing/browser/print_composite_client.h"
|
||||
#include "components/printing/browser/print_manager_utils.h"
|
||||
#include "components/services/print_compositor/public/cpp/print_service_mojo_types.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
#include "mojo/public/cpp/bindings/callback_helpers.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
|
||||
#include "shell/common/node_includes.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
void StopWorker(int document_cookie) {
|
||||
if (document_cookie <= 0)
|
||||
return;
|
||||
scoped_refptr<printing::PrintQueriesQueue> queue =
|
||||
g_browser_process->print_job_manager()->queue();
|
||||
std::unique_ptr<printing::PrinterQuery> printer_query =
|
||||
queue->PopPrinterQuery(document_cookie);
|
||||
if (printer_query.get()) {
|
||||
content::GetIOThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(&printing::PrinterQuery::StopWorker,
|
||||
std::move(printer_query)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PrintPreviewMessageHandler::PrintPreviewMessageHandler(
|
||||
content::WebContents* web_contents)
|
||||
: content::WebContentsUserData<PrintPreviewMessageHandler>(*web_contents),
|
||||
web_contents_(web_contents) {
|
||||
DCHECK(web_contents);
|
||||
}
|
||||
|
||||
PrintPreviewMessageHandler::~PrintPreviewMessageHandler() = default;
|
||||
|
||||
void PrintPreviewMessageHandler::MetafileReadyForPrinting(
|
||||
printing::mojom::DidPreviewDocumentParamsPtr params,
|
||||
int32_t request_id) {
|
||||
// Always try to stop the worker.
|
||||
StopWorker(params->document_cookie);
|
||||
|
||||
if (params->expected_pages_count == 0) {
|
||||
RejectPromise(request_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const base::ReadOnlySharedMemoryRegion& metafile =
|
||||
params->content->metafile_data_region;
|
||||
|
||||
if (printing::IsOopifEnabled()) {
|
||||
auto* client =
|
||||
printing::PrintCompositeClient::FromWebContents(web_contents_);
|
||||
DCHECK(client);
|
||||
|
||||
auto callback = base::BindOnce(
|
||||
&PrintPreviewMessageHandler::OnCompositeDocumentToPdfDone,
|
||||
weak_ptr_factory_.GetWeakPtr(), request_id);
|
||||
|
||||
client->DoCompleteDocumentToPdf(
|
||||
params->document_cookie, params->expected_pages_count,
|
||||
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
std::move(callback),
|
||||
printing::mojom::PrintCompositor::Status::kCompositingFailure,
|
||||
base::ReadOnlySharedMemoryRegion()));
|
||||
} else {
|
||||
ResolvePromise(
|
||||
request_id,
|
||||
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(metafile));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::OnPrepareForDocumentToPdfDone(
|
||||
int32_t request_id,
|
||||
printing::mojom::PrintCompositor::Status status) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
|
||||
LOG(ERROR) << "Preparing document for pdf failed with error " << status;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::DidPrepareDocumentForPreview(
|
||||
int32_t document_cookie,
|
||||
int32_t request_id) {
|
||||
if (printing::IsOopifEnabled()) {
|
||||
auto* client =
|
||||
printing::PrintCompositeClient::FromWebContents(web_contents_);
|
||||
DCHECK(client);
|
||||
|
||||
if (client->GetIsDocumentConcurrentlyComposited(document_cookie))
|
||||
return;
|
||||
|
||||
auto* focused_frame = web_contents_->GetFocusedFrame();
|
||||
auto* rfh = focused_frame && focused_frame->HasSelection()
|
||||
? focused_frame
|
||||
: web_contents_->GetPrimaryMainFrame();
|
||||
|
||||
client->DoPrepareForDocumentToPdf(
|
||||
document_cookie, rfh,
|
||||
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
base::BindOnce(
|
||||
&PrintPreviewMessageHandler::OnPrepareForDocumentToPdfDone,
|
||||
weak_ptr_factory_.GetWeakPtr(), request_id),
|
||||
printing::mojom::PrintCompositor::Status::kCompositingFailure));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::OnCompositeDocumentToPdfDone(
|
||||
int32_t request_id,
|
||||
printing::mojom::PrintCompositor::Status status,
|
||||
base::ReadOnlySharedMemoryRegion region) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
|
||||
LOG(ERROR) << "Compositing pdf failed with error " << status;
|
||||
RejectPromise(request_id);
|
||||
return;
|
||||
}
|
||||
|
||||
ResolvePromise(
|
||||
request_id,
|
||||
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::OnCompositePdfPageDone(
|
||||
int page_number,
|
||||
int document_cookie,
|
||||
int32_t request_id,
|
||||
printing::mojom::PrintCompositor::Status status,
|
||||
base::ReadOnlySharedMemoryRegion region) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
|
||||
LOG(ERROR) << "Compositing pdf failed on page: " << page_number
|
||||
<< " with error: " << status;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::DidPreviewPage(
|
||||
printing::mojom::DidPreviewPageParamsPtr params,
|
||||
int32_t request_id) {
|
||||
int page_number = params->page_number;
|
||||
const printing::mojom::DidPrintContentParams& content = *(params->content);
|
||||
|
||||
if (page_number < printing::FIRST_PAGE_INDEX ||
|
||||
!content.metafile_data_region.IsValid()) {
|
||||
RejectPromise(request_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (printing::IsOopifEnabled()) {
|
||||
auto* client =
|
||||
printing::PrintCompositeClient::FromWebContents(web_contents_);
|
||||
DCHECK(client);
|
||||
|
||||
auto* focused_frame = web_contents_->GetFocusedFrame();
|
||||
auto* rfh = focused_frame && focused_frame->HasSelection()
|
||||
? focused_frame
|
||||
: web_contents_->GetPrimaryMainFrame();
|
||||
|
||||
// Use utility process to convert skia metafile to pdf.
|
||||
client->DoCompositePageToPdf(
|
||||
params->document_cookie, rfh, content,
|
||||
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfPageDone,
|
||||
weak_ptr_factory_.GetWeakPtr(), page_number,
|
||||
params->document_cookie, request_id),
|
||||
printing::mojom::PrintCompositor::Status::kCompositingFailure,
|
||||
base::ReadOnlySharedMemoryRegion()));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::PrintPreviewFailed(int32_t document_cookie,
|
||||
int32_t request_id) {
|
||||
StopWorker(document_cookie);
|
||||
|
||||
RejectPromise(request_id);
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::PrintPreviewCancelled(int32_t document_cookie,
|
||||
int32_t request_id) {
|
||||
StopWorker(document_cookie);
|
||||
|
||||
RejectPromise(request_id);
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::PrintToPDF(
|
||||
base::Value::Dict options,
|
||||
gin_helper::Promise<v8::Local<v8::Value>> promise) {
|
||||
int request_id;
|
||||
options.Get(printing::kPreviewRequestID, &request_id);
|
||||
promise_map_.emplace(request_id, std::move(promise));
|
||||
|
||||
auto* focused_frame = web_contents_->GetFocusedFrame();
|
||||
auto* rfh = focused_frame && focused_frame->HasSelection()
|
||||
? focused_frame
|
||||
: web_contents_->GetPrimaryMainFrame();
|
||||
|
||||
if (!print_render_frame_.is_bound()) {
|
||||
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&print_render_frame_);
|
||||
}
|
||||
if (!receiver_.is_bound()) {
|
||||
print_render_frame_->SetPrintPreviewUI(
|
||||
receiver_.BindNewEndpointAndPassRemote());
|
||||
}
|
||||
print_render_frame_->PrintPreview(options.GetDict().Clone());
|
||||
}
|
||||
|
||||
gin_helper::Promise<v8::Local<v8::Value>>
|
||||
PrintPreviewMessageHandler::GetPromise(int request_id) {
|
||||
auto it = promise_map_.find(request_id);
|
||||
DCHECK(it != promise_map_.end());
|
||||
|
||||
gin_helper::Promise<v8::Local<v8::Value>> promise = std::move(it->second);
|
||||
promise_map_.erase(it);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::ResolvePromise(
|
||||
int request_id,
|
||||
scoped_refptr<base::RefCountedMemory> data_bytes) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
gin_helper::Promise<v8::Local<v8::Value>> promise = GetPromise(request_id);
|
||||
|
||||
v8::Isolate* isolate = promise.isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(
|
||||
v8::Local<v8::Context>::New(isolate, promise.GetContext()));
|
||||
|
||||
v8::Local<v8::Value> buffer =
|
||||
node::Buffer::Copy(isolate,
|
||||
reinterpret_cast<const char*>(data_bytes->front()),
|
||||
data_bytes->size())
|
||||
.ToLocalChecked();
|
||||
|
||||
promise.Resolve(buffer);
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::RejectPromise(int request_id) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
gin_helper::Promise<v8::Local<v8::Value>> promise = GetPromise(request_id);
|
||||
promise.RejectWithErrorMessage("Failed to generate PDF");
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(PrintPreviewMessageHandler);
|
||||
|
||||
} // namespace electron
|
||||
134
shell/browser/printing/printing_utils.cc
Normal file
134
shell/browser/printing/printing_utils.cc
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2024 Microsoft, GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/printing/printing_utils.h"
|
||||
|
||||
#include "base/apple/scoped_typeref.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "printing/backend/print_backend.h" // nogncheck
|
||||
#include "printing/units.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
|
||||
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
return gfx::Size(printing::kDefaultMacDpi, printing::kDefaultMacDpi);
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
HDC hdc =
|
||||
CreateDC(L"WINSPOOL", base::as_wcstr(device_name), nullptr, nullptr);
|
||||
if (hdc == nullptr)
|
||||
return gfx::Size(printing::kDefaultPdfDpi, printing::kDefaultPdfDpi);
|
||||
|
||||
int dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
int dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||
DeleteDC(hdc);
|
||||
|
||||
return gfx::Size(dpi_x, dpi_y);
|
||||
#else
|
||||
GtkPrintSettings* print_settings = gtk_print_settings_new();
|
||||
int dpi = gtk_print_settings_get_resolution(print_settings);
|
||||
g_object_unref(print_settings);
|
||||
return gfx::Size(dpi, dpi);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
||||
base::SysUTF16ToCFStringRef(device_name));
|
||||
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
||||
bool printer_exists = new_printer != nullptr;
|
||||
PMRelease(new_printer);
|
||||
return printer_exists;
|
||||
#else
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||
// not thread-safe and have to be accessed on the UI thread.
|
||||
ScopedAllowBlockingForElectron allow_blocking;
|
||||
#endif
|
||||
|
||||
if (!device_name.empty()) {
|
||||
if (!IsDeviceNameValid(device_name))
|
||||
return std::make_pair("Invalid deviceName provided", std::u16string());
|
||||
return std::make_pair(std::string(), device_name);
|
||||
}
|
||||
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
std::string printer_name;
|
||||
printing::mojom::ResultCode code =
|
||||
print_backend->GetDefaultPrinterName(printer_name);
|
||||
|
||||
// We don't want to return if this fails since some devices won't have a
|
||||
// default printer.
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(ERROR) << "Failed to get default printer name";
|
||||
|
||||
if (printer_name.empty()) {
|
||||
printing::PrinterList printers;
|
||||
if (print_backend->EnumeratePrinters(printers) !=
|
||||
printing::mojom::ResultCode::kSuccess)
|
||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||
if (printers.empty())
|
||||
return std::make_pair("No printers available on the network",
|
||||
std::u16string());
|
||||
|
||||
printer_name = printers.front().printer_name;
|
||||
}
|
||||
|
||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||
}
|
||||
|
||||
// Copied from
|
||||
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
||||
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
||||
#if !BUILDFLAG(IS_WIN)
|
||||
static constexpr base::TaskTraits kTraits = {
|
||||
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
||||
#endif
|
||||
|
||||
#if defined(USE_CUPS)
|
||||
// CUPS is thread safe.
|
||||
return base::ThreadPool::CreateTaskRunner(kTraits);
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
// Windows drivers are likely not thread-safe and need to be accessed on the
|
||||
// UI thread.
|
||||
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
||||
#else
|
||||
// Be conservative on unsupported platforms.
|
||||
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
41
shell/browser/printing/printing_utils.h
Normal file
41
shell/browser/printing/printing_utils.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2024 Microsoft, GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||
#define ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/task/task_runner.h"
|
||||
|
||||
namespace gfx {
|
||||
class Size;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
|
||||
// This function returns the per-platform default printer's DPI.
|
||||
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name);
|
||||
|
||||
// This will return false if no printer with the provided device_name can be
|
||||
// found on the network. We need to check this because Chromium does not do
|
||||
// sanity checking of device_name validity and so will crash on invalid names.
|
||||
bool IsDeviceNameValid(const std::u16string& device_name);
|
||||
|
||||
// This function returns a validated device name.
|
||||
// If the user passed one to webContents.print(), we check that it's valid and
|
||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||
// pass a device name, we first try to return the system default printer. If one
|
||||
// isn't set, then pull all the printers and use the first one or fail if none
|
||||
// exist.
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name);
|
||||
|
||||
// This function creates a task runner for use with printing tasks.
|
||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner();
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||
@@ -35,7 +35,9 @@ std::unique_ptr<content::SerialChooser> ElectronSerialDelegate::RunChooser(
|
||||
if (controller) {
|
||||
DeleteControllerForFrame(frame);
|
||||
}
|
||||
AddControllerForFrame(frame, std::move(filters), std::move(callback));
|
||||
AddControllerForFrame(frame, std::move(filters),
|
||||
std::move(allowed_bluetooth_service_class_ids),
|
||||
std::move(callback));
|
||||
|
||||
// Return a nullptr because the return value isn't used for anything, eg
|
||||
// there is no mechanism to cancel navigator.serial.requestPort(). The return
|
||||
@@ -106,12 +108,14 @@ SerialChooserController* ElectronSerialDelegate::ControllerForFrame(
|
||||
SerialChooserController* ElectronSerialDelegate::AddControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback) {
|
||||
auto* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
auto controller = std::make_unique<SerialChooserController>(
|
||||
render_frame_host, std::move(filters), std::move(callback), web_contents,
|
||||
weak_factory_.GetWeakPtr());
|
||||
render_frame_host, std::move(filters),
|
||||
std::move(allowed_bluetooth_service_class_ids), std::move(callback),
|
||||
web_contents, weak_factory_.GetWeakPtr());
|
||||
controller_map_.insert(
|
||||
std::make_pair(render_frame_host, std::move(controller)));
|
||||
return ControllerForFrame(render_frame_host);
|
||||
|
||||
@@ -65,6 +65,7 @@ class ElectronSerialDelegate : public content::SerialDelegate,
|
||||
SerialChooserController* AddControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback);
|
||||
|
||||
base::ScopedObservation<SerialChooserContext,
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
|
||||
#include "services/device/public/cpp/bluetooth/bluetooth_utils.h"
|
||||
#include "services/device/public/mojom/serial.mojom.h"
|
||||
#include "shell/browser/api/electron_api_session.h"
|
||||
#include "shell/browser/serial/serial_chooser_context.h"
|
||||
#include "shell/browser/serial/serial_chooser_context_factory.h"
|
||||
@@ -58,14 +61,57 @@ struct Converter<device::mojom::SerialPortInfoPtr> {
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::device::mojom::SerialPortType;
|
||||
|
||||
bool FilterMatchesPort(const blink::mojom::SerialPortFilter& filter,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (filter.bluetooth_service_class_id) {
|
||||
if (!port.bluetooth_service_class_id) {
|
||||
return false;
|
||||
}
|
||||
return device::BluetoothUUID(*port.bluetooth_service_class_id) ==
|
||||
device::BluetoothUUID(*filter.bluetooth_service_class_id);
|
||||
}
|
||||
if (!filter.has_vendor_id) {
|
||||
return true;
|
||||
}
|
||||
if (!port.has_vendor_id || port.vendor_id != filter.vendor_id) {
|
||||
return false;
|
||||
}
|
||||
if (!filter.has_product_id) {
|
||||
return true;
|
||||
}
|
||||
return port.has_product_id && port.product_id == filter.product_id;
|
||||
}
|
||||
|
||||
bool BluetoothPortIsAllowed(
|
||||
const std::vector<::device::BluetoothUUID>& allowed_ids,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (!port.bluetooth_service_class_id) {
|
||||
return true;
|
||||
}
|
||||
// Serial Port Profile is allowed by default.
|
||||
if (*port.bluetooth_service_class_id == device::GetSerialPortProfileUUID()) {
|
||||
return true;
|
||||
}
|
||||
return base::Contains(allowed_ids, port.bluetooth_service_class_id.value());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SerialChooserController::SerialChooserController(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronSerialDelegate> serial_delegate)
|
||||
: WebContentsObserver(web_contents),
|
||||
filters_(std::move(filters)),
|
||||
allowed_bluetooth_service_class_ids_(
|
||||
std::move(allowed_bluetooth_service_class_ids)),
|
||||
callback_(std::move(callback)),
|
||||
serial_delegate_(serial_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
@@ -93,10 +139,11 @@ api::Session* SerialChooserController::GetSession() {
|
||||
|
||||
void SerialChooserController::OnPortAdded(
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (!FilterMatchesAny(port))
|
||||
if (!DisplayDevice(port))
|
||||
return;
|
||||
|
||||
ports_.push_back(port.Clone());
|
||||
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
session->Emit("serial-port-added", port.Clone(), web_contents());
|
||||
@@ -105,9 +152,8 @@ void SerialChooserController::OnPortAdded(
|
||||
|
||||
void SerialChooserController::OnPortRemoved(
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
const auto it = std::find_if(
|
||||
ports_.begin(), ports_.end(),
|
||||
[&port](const auto& ptr) { return ptr->token == port.token; });
|
||||
const auto it = base::ranges::find(ports_, port.token,
|
||||
&device::mojom::SerialPortInfo::token);
|
||||
if (it != ports_.end()) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
@@ -152,7 +198,7 @@ void SerialChooserController::OnGetDevices(
|
||||
});
|
||||
|
||||
for (auto& port : ports) {
|
||||
if (FilterMatchesAny(*port))
|
||||
if (DisplayDevice(*port))
|
||||
ports_.push_back(std::move(port));
|
||||
}
|
||||
|
||||
@@ -169,21 +215,17 @@ void SerialChooserController::OnGetDevices(
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialChooserController::FilterMatchesAny(
|
||||
bool SerialChooserController::DisplayDevice(
|
||||
const device::mojom::SerialPortInfo& port) const {
|
||||
if (filters_.empty())
|
||||
return true;
|
||||
if (filters_.empty()) {
|
||||
return BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port);
|
||||
}
|
||||
|
||||
for (const auto& filter : filters_) {
|
||||
if (filter->has_vendor_id &&
|
||||
(!port.has_vendor_id || filter->vendor_id != port.vendor_id)) {
|
||||
continue;
|
||||
if (FilterMatchesPort(*filter, port) &&
|
||||
BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port)) {
|
||||
return true;
|
||||
}
|
||||
if (filter->has_product_id &&
|
||||
(!port.has_product_id || filter->product_id != port.product_id)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -34,6 +34,7 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
|
||||
SerialChooserController(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronSerialDelegate> serial_delegate);
|
||||
@@ -53,11 +54,12 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
|
||||
private:
|
||||
api::Session* GetSession();
|
||||
void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports);
|
||||
bool FilterMatchesAny(const device::mojom::SerialPortInfo& port) const;
|
||||
bool DisplayDevice(const device::mojom::SerialPortInfo& port) const;
|
||||
void RunCallback(device::mojom::SerialPortInfoPtr port);
|
||||
void OnDeviceChosen(const std::string& port_id);
|
||||
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters_;
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids_;
|
||||
content::SerialChooser::Callback callback_;
|
||||
url::Origin origin_;
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ class ElectronMenuModel;
|
||||
@protected
|
||||
base::WeakPtr<electron::ElectronMenuModel> model_;
|
||||
NSMenu* __strong menu_;
|
||||
NSMenuItem* __strong recentDocumentsMenuItem_;
|
||||
NSMenu* __strong recentDocumentsMenuSwap_;
|
||||
BOOL isMenuOpen_;
|
||||
BOOL useDefaultAccelerator_;
|
||||
base::OnceClosure closeCallback;
|
||||
|
||||
@@ -31,10 +31,14 @@ using SharingItem = electron::ElectronMenuModel::SharingItem;
|
||||
|
||||
namespace {
|
||||
|
||||
static NSMenuItem* __strong recentDocumentsMenuItem_;
|
||||
static NSMenu* __strong recentDocumentsMenuSwap_;
|
||||
|
||||
struct Role {
|
||||
SEL selector;
|
||||
const char* role;
|
||||
};
|
||||
|
||||
Role kRolesMap[] = {
|
||||
{@selector(orderFrontStandardAboutPanel:), "about"},
|
||||
{@selector(hide:), "hide"},
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/socket/tcp_server_socket.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/common/electron_paths.h"
|
||||
#include "third_party/inspector_protocol/crdtp/dispatch.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
@@ -139,4 +140,8 @@ bool DevToolsManagerDelegate::HasBundledFrontendResources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
content::BrowserContext* DevToolsManagerDelegate::GetDefaultBrowserContext() {
|
||||
return ElectronBrowserContext::From("", false);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "base/compiler_specific.h"
|
||||
#include "content/public/browser/devtools_manager_delegate.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
|
||||
class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||
@@ -33,6 +37,7 @@ class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||
TargetType target_type) override;
|
||||
std::string GetDiscoveryPageHTML() override;
|
||||
bool HasBundledFrontendResources() override;
|
||||
content::BrowserContext* GetDefaultBrowserContext() override;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/file_dialog.h"
|
||||
#include "shell/browser/ui/gtk_util.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
#include "ui/base/glib/scoped_gsignal.h"
|
||||
#include "ui/gtk/gtk_ui.h" // nogncheck
|
||||
#include "ui/gtk/gtk_util.h" // nogncheck
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
DialogSettings::DialogSettings() = default;
|
||||
DialogSettings::DialogSettings(const DialogSettings&) = default;
|
||||
DialogSettings::~DialogSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
static const int kPreviewWidth = 256;
|
||||
static const int kPreviewHeight = 512;
|
||||
|
||||
std::string MakeCaseInsensitivePattern(const std::string& extension) {
|
||||
// If the extension is the "all files" extension, no change needed.
|
||||
if (extension == "*")
|
||||
return extension;
|
||||
|
||||
std::string pattern("*.");
|
||||
for (char ch : extension) {
|
||||
if (!base::IsAsciiAlpha(ch)) {
|
||||
pattern.push_back(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
pattern.push_back('[');
|
||||
pattern.push_back(base::ToLowerASCII(ch));
|
||||
pattern.push_back(base::ToUpperASCII(ch));
|
||||
pattern.push_back(']');
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
class FileChooserDialog {
|
||||
public:
|
||||
FileChooserDialog(GtkFileChooserAction action, const DialogSettings& settings)
|
||||
: parent_(
|
||||
static_cast<electron::NativeWindowViews*>(settings.parent_window)),
|
||||
filters_(settings.filters) {
|
||||
auto label = settings.button_label;
|
||||
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
dialog_ = GTK_FILE_CHOOSER(gtk_file_chooser_native_new(
|
||||
settings.title.c_str(), nullptr, action,
|
||||
label.empty() ? nullptr : label.c_str(), nullptr));
|
||||
} else {
|
||||
const char* confirm_text = gtk_util::GetOkLabel();
|
||||
if (!label.empty())
|
||||
confirm_text = label.c_str();
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
confirm_text = gtk_util::GetSaveLabel();
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
confirm_text = gtk_util::GetOpenLabel();
|
||||
|
||||
dialog_ = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new(
|
||||
settings.title.c_str(), nullptr, action, gtk_util::GetCancelLabel(),
|
||||
GTK_RESPONSE_CANCEL, confirm_text, GTK_RESPONSE_ACCEPT, nullptr));
|
||||
}
|
||||
|
||||
if (parent_) {
|
||||
parent_->SetEnabled(false);
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_set_modal(GTK_NATIVE_DIALOG(dialog_), TRUE);
|
||||
} else {
|
||||
gtk::SetGtkTransientForAura(GTK_WIDGET(dialog_),
|
||||
parent_->GetNativeWindow());
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(dialog_, TRUE);
|
||||
if (action != GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
gtk_file_chooser_set_create_folders(dialog_, TRUE);
|
||||
|
||||
if (!settings.default_path.empty()) {
|
||||
electron::ScopedAllowBlockingForElectron allow_blocking;
|
||||
if (base::DirectoryExists(settings.default_path)) {
|
||||
gtk_file_chooser_set_current_folder(
|
||||
dialog_, settings.default_path.value().c_str());
|
||||
} else {
|
||||
if (settings.default_path.IsAbsolute()) {
|
||||
gtk_file_chooser_set_current_folder(
|
||||
dialog_, settings.default_path.DirName().value().c_str());
|
||||
}
|
||||
|
||||
gtk_file_chooser_set_current_name(
|
||||
GTK_FILE_CHOOSER(dialog_),
|
||||
settings.default_path.BaseName().value().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!settings.filters.empty())
|
||||
AddFilters(settings.filters);
|
||||
|
||||
// GtkFileChooserNative does not support preview widgets through the
|
||||
// org.freedesktop.portal.FileChooser portal. In the case of running through
|
||||
// the org.freedesktop.portal.FileChooser portal, anything having to do with
|
||||
// the update-preview signal or the preview widget will just be ignored.
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
preview_ = gtk_image_new();
|
||||
signals_.emplace_back(
|
||||
dialog_, "update-preview",
|
||||
base::BindRepeating(&FileChooserDialog::OnUpdatePreview,
|
||||
base::Unretained(this)));
|
||||
gtk_file_chooser_set_preview_widget(dialog_, preview_);
|
||||
}
|
||||
}
|
||||
|
||||
~FileChooserDialog() {
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_destroy(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_destroy(GTK_WIDGET(dialog_));
|
||||
}
|
||||
|
||||
if (parent_)
|
||||
parent_->SetEnabled(true);
|
||||
}
|
||||
|
||||
// disable copy
|
||||
FileChooserDialog(const FileChooserDialog&) = delete;
|
||||
FileChooserDialog& operator=(const FileChooserDialog&) = delete;
|
||||
|
||||
void SetupOpenProperties(int properties) {
|
||||
const auto hasProp = [properties](OpenFileDialogProperty prop) {
|
||||
return gboolean((properties & prop) != 0);
|
||||
};
|
||||
auto* file_chooser = dialog();
|
||||
gtk_file_chooser_set_select_multiple(file_chooser,
|
||||
hasProp(OPEN_DIALOG_MULTI_SELECTIONS));
|
||||
gtk_file_chooser_set_show_hidden(file_chooser,
|
||||
hasProp(OPEN_DIALOG_SHOW_HIDDEN_FILES));
|
||||
}
|
||||
|
||||
void SetupSaveProperties(int properties) {
|
||||
const auto hasProp = [properties](SaveFileDialogProperty prop) {
|
||||
return gboolean((properties & prop) != 0);
|
||||
};
|
||||
auto* file_chooser = dialog();
|
||||
gtk_file_chooser_set_show_hidden(file_chooser,
|
||||
hasProp(SAVE_DIALOG_SHOW_HIDDEN_FILES));
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(
|
||||
file_chooser, hasProp(SAVE_DIALOG_SHOW_OVERWRITE_CONFIRMATION));
|
||||
}
|
||||
|
||||
void RunAsynchronous() {
|
||||
signals_.emplace_back(
|
||||
GTK_WIDGET(dialog_), "response",
|
||||
base::BindRepeating(&FileChooserDialog::OnFileDialogResponse,
|
||||
base::Unretained(this)));
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_show(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_show_all(GTK_WIDGET(dialog_));
|
||||
gtk::GtkUi::GetPlatform()->ShowGtkWindow(GTK_WINDOW(dialog_));
|
||||
}
|
||||
}
|
||||
|
||||
void RunSaveAsynchronous(
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
save_promise_ =
|
||||
std::make_unique<gin_helper::Promise<gin_helper::Dictionary>>(
|
||||
std::move(promise));
|
||||
RunAsynchronous();
|
||||
}
|
||||
|
||||
void RunOpenAsynchronous(
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
open_promise_ =
|
||||
std::make_unique<gin_helper::Promise<gin_helper::Dictionary>>(
|
||||
std::move(promise));
|
||||
RunAsynchronous();
|
||||
}
|
||||
|
||||
base::FilePath GetFileName() const {
|
||||
gchar* filename = gtk_file_chooser_get_filename(dialog_);
|
||||
const base::FilePath path(filename);
|
||||
g_free(filename);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<base::FilePath> GetFileNames() const {
|
||||
std::vector<base::FilePath> paths;
|
||||
auto* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog_));
|
||||
for (auto* iter = filenames; iter != nullptr; iter = iter->next) {
|
||||
auto* filename = static_cast<char*>(iter->data);
|
||||
paths.emplace_back(filename);
|
||||
g_free(filename);
|
||||
}
|
||||
g_slist_free(filenames);
|
||||
return paths;
|
||||
}
|
||||
|
||||
void OnFileDialogResponse(GtkWidget* widget, int response);
|
||||
|
||||
GtkFileChooser* dialog() const { return dialog_; }
|
||||
|
||||
private:
|
||||
void AddFilters(const Filters& filters);
|
||||
|
||||
raw_ptr<electron::NativeWindowViews> parent_;
|
||||
|
||||
RAW_PTR_EXCLUSION GtkFileChooser* dialog_;
|
||||
RAW_PTR_EXCLUSION GtkWidget* preview_;
|
||||
|
||||
Filters filters_;
|
||||
std::unique_ptr<gin_helper::Promise<gin_helper::Dictionary>> save_promise_;
|
||||
std::unique_ptr<gin_helper::Promise<gin_helper::Dictionary>> open_promise_;
|
||||
|
||||
// Callback for when we update the preview for the selection.
|
||||
void OnUpdatePreview(GtkFileChooser* chooser);
|
||||
|
||||
std::vector<ScopedGSignal> signals_;
|
||||
};
|
||||
|
||||
void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_hide(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_hide(GTK_WIDGET(dialog_));
|
||||
}
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
if (save_promise_) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(save_promise_->isolate());
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePath", GetFileName());
|
||||
} else {
|
||||
dict.Set("canceled", true);
|
||||
dict.Set("filePath", base::FilePath());
|
||||
}
|
||||
save_promise_->Resolve(dict);
|
||||
} else if (open_promise_) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(open_promise_->isolate());
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePaths", GetFileNames());
|
||||
} else {
|
||||
dict.Set("canceled", true);
|
||||
dict.Set("filePaths", std::vector<base::FilePath>());
|
||||
}
|
||||
open_promise_->Resolve(dict);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileChooserDialog::AddFilters(const Filters& filters) {
|
||||
for (const auto& filter : filters) {
|
||||
GtkFileFilter* gtk_filter = gtk_file_filter_new();
|
||||
|
||||
for (const auto& extension : filter.second) {
|
||||
std::string pattern = MakeCaseInsensitivePattern(extension);
|
||||
gtk_file_filter_add_pattern(gtk_filter, pattern.c_str());
|
||||
}
|
||||
|
||||
gtk_file_filter_set_name(gtk_filter, filter.first.c_str());
|
||||
gtk_file_chooser_add_filter(dialog_, gtk_filter);
|
||||
}
|
||||
}
|
||||
|
||||
bool CanPreview(const struct stat& st) {
|
||||
// Only preview regular files; pipes may hang.
|
||||
// See https://crbug.com/534754.
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't preview huge files; they may crash.
|
||||
// https://github.com/electron/electron/issues/31630
|
||||
// Setting an arbitrary filesize max t at 100 MB here.
|
||||
constexpr off_t ArbitraryMax = 100000000ULL;
|
||||
return st.st_size < ArbitraryMax;
|
||||
}
|
||||
|
||||
void FileChooserDialog::OnUpdatePreview(GtkFileChooser* chooser) {
|
||||
CHECK(!electron::IsElectron_gtkInitialized());
|
||||
gchar* filename = gtk_file_chooser_get_preview_filename(chooser);
|
||||
if (!filename) {
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (stat(filename, &sb) != 0 || !CanPreview(sb)) {
|
||||
g_free(filename);
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will preserve the image's aspect ratio.
|
||||
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth,
|
||||
kPreviewHeight, nullptr);
|
||||
g_free(filename);
|
||||
if (pixbuf) {
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, pixbuf ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowFileDialog(const FileChooserDialog& dialog) {
|
||||
// gtk_native_dialog_run() will call gtk_native_dialog_show() for us.
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
gtk_widget_show_all(GTK_WIDGET(dialog.dialog()));
|
||||
}
|
||||
}
|
||||
|
||||
int RunFileDialog(const FileChooserDialog& dialog) {
|
||||
int response = 0;
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
response = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.dialog()));
|
||||
} else {
|
||||
response = gtk_dialog_run(GTK_DIALOG(dialog.dialog()));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (settings.properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog open_dialog(action, settings);
|
||||
open_dialog.SetupOpenProperties(settings.properties);
|
||||
|
||||
ShowFileDialog(open_dialog);
|
||||
|
||||
const int response = RunFileDialog(open_dialog);
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
*paths = open_dialog.GetFileNames();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (settings.properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog* open_dialog = new FileChooserDialog(action, settings);
|
||||
open_dialog->SetupOpenProperties(settings.properties);
|
||||
open_dialog->RunOpenAsynchronous(std::move(promise));
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||
save_dialog.SetupSaveProperties(settings.properties);
|
||||
|
||||
ShowFileDialog(save_dialog);
|
||||
|
||||
const int response = RunFileDialog(save_dialog);
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
*path = save_dialog.GetFileName();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* save_dialog =
|
||||
new FileChooserDialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||
save_dialog->RunSaveAsynchronous(std::move(promise));
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
||||
258
shell/browser/ui/file_dialog_linux.cc
Normal file
258
shell/browser/ui/file_dialog_linux.cc
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2024 Microsoft, GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/file_dialog.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "ui/gtk/select_file_dialog_linux_gtk.h" // nogncheck
|
||||
#include "ui/shell_dialogs/select_file_dialog.h"
|
||||
#include "ui/shell_dialogs/selected_file_info.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
DialogSettings::DialogSettings() = default;
|
||||
DialogSettings::DialogSettings(const DialogSettings&) = default;
|
||||
DialogSettings::~DialogSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
ui::SelectFileDialog::Type GetDialogType(int properties) {
|
||||
if (properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
return ui::SelectFileDialog::SELECT_FOLDER;
|
||||
|
||||
if (properties & OPEN_DIALOG_MULTI_SELECTIONS)
|
||||
return ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
|
||||
|
||||
return ui::SelectFileDialog::SELECT_OPEN_FILE;
|
||||
}
|
||||
|
||||
ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) {
|
||||
ui::SelectFileDialog::FileTypeInfo file_type_info;
|
||||
|
||||
for (const auto& [name, extension_group] : filters) {
|
||||
file_type_info.extension_description_overrides.push_back(
|
||||
base::UTF8ToUTF16(name));
|
||||
|
||||
const bool has_all_files_wildcard = base::ranges::any_of(
|
||||
extension_group, [](const auto& ext) { return ext == "*"; });
|
||||
if (has_all_files_wildcard) {
|
||||
file_type_info.include_all_files = true;
|
||||
} else {
|
||||
file_type_info.extensions.emplace_back(extension_group);
|
||||
}
|
||||
}
|
||||
|
||||
return file_type_info;
|
||||
}
|
||||
|
||||
class FileChooserDialog : public ui::SelectFileDialog::Listener {
|
||||
public:
|
||||
enum class DialogType { OPEN, SAVE };
|
||||
|
||||
FileChooserDialog() { dialog_ = ui::SelectFileDialog::Create(this, nullptr); }
|
||||
|
||||
~FileChooserDialog() override = default;
|
||||
|
||||
gtk::ExtraSettings GetExtraSettings(const DialogSettings& settings) {
|
||||
gtk::ExtraSettings extra;
|
||||
extra.button_label = settings.button_label;
|
||||
extra.show_overwrite_confirmation =
|
||||
settings.properties & SAVE_DIALOG_SHOW_OVERWRITE_CONFIRMATION;
|
||||
extra.allow_multiple_selection =
|
||||
settings.properties & OPEN_DIALOG_MULTI_SELECTIONS;
|
||||
if (type_ == DialogType::SAVE) {
|
||||
extra.show_hidden = settings.properties & SAVE_DIALOG_SHOW_HIDDEN_FILES;
|
||||
} else {
|
||||
extra.show_hidden = settings.properties & OPEN_DIALOG_SHOW_HIDDEN_FILES;
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
void RunSaveDialogImpl(const DialogSettings& settings) {
|
||||
type_ = DialogType::SAVE;
|
||||
ui::SelectFileDialog::FileTypeInfo file_info =
|
||||
GetFilterInfo(settings.filters);
|
||||
auto extra_settings = GetExtraSettings(settings);
|
||||
dialog_->SelectFile(
|
||||
ui::SelectFileDialog::SELECT_SAVEAS_FILE,
|
||||
base::UTF8ToUTF16(settings.title), settings.default_path,
|
||||
&file_info /* file_types */, 0 /* file_type_index */,
|
||||
base::FilePath::StringType() /* default_extension */,
|
||||
settings.parent_window ? settings.parent_window->GetNativeWindow()
|
||||
: nullptr,
|
||||
static_cast<void*>(&extra_settings));
|
||||
}
|
||||
|
||||
void RunSaveDialog(gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
const DialogSettings& settings) {
|
||||
promise_ = std::move(promise);
|
||||
RunSaveDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunSaveDialog(base::OnceCallback<void(gin_helper::Dictionary)> callback,
|
||||
const DialogSettings& settings) {
|
||||
callback_ = std::move(callback);
|
||||
RunSaveDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunOpenDialogImpl(const DialogSettings& settings) {
|
||||
type_ = DialogType::OPEN;
|
||||
ui::SelectFileDialog::FileTypeInfo file_info =
|
||||
GetFilterInfo(settings.filters);
|
||||
auto extra_settings = GetExtraSettings(settings);
|
||||
dialog_->SelectFile(
|
||||
GetDialogType(settings.properties), base::UTF8ToUTF16(settings.title),
|
||||
settings.default_path, &file_info, 0 /* file_type_index */,
|
||||
base::FilePath::StringType() /* default_extension */,
|
||||
settings.parent_window ? settings.parent_window->GetNativeWindow()
|
||||
: nullptr,
|
||||
static_cast<void*>(&extra_settings));
|
||||
}
|
||||
|
||||
void RunOpenDialog(gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
const DialogSettings& settings) {
|
||||
promise_ = std::move(promise);
|
||||
RunOpenDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunOpenDialog(base::OnceCallback<void(gin_helper::Dictionary)> callback,
|
||||
const DialogSettings& settings) {
|
||||
callback_ = std::move(callback);
|
||||
RunOpenDialogImpl(settings);
|
||||
}
|
||||
|
||||
void FileSelected(const ui::SelectedFileInfo& file,
|
||||
int index,
|
||||
void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", false);
|
||||
if (type_ == DialogType::SAVE) {
|
||||
dict.Set("filePath", file.file_path);
|
||||
} else {
|
||||
dict.Set("filePaths", std::vector<base::FilePath>{file.file_path});
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MultiFilesSelected(const std::vector<ui::SelectedFileInfo>& files,
|
||||
void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePaths", ui::SelectedFileInfoListToFilePathList(files));
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileSelectionCanceled(void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", true);
|
||||
if (type_ == DialogType::SAVE) {
|
||||
dict.Set("filePath", base::FilePath());
|
||||
} else {
|
||||
dict.Set("filePaths", std::vector<base::FilePath>());
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
DialogType type_;
|
||||
scoped_refptr<ui::SelectFileDialog> dialog_;
|
||||
base::OnceCallback<void(gin_helper::Dictionary)> callback_;
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
|
||||
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, std::vector<base::FilePath>* file_paths,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePaths", file_paths);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), paths);
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunOpenDialog(std::move(cb), settings);
|
||||
|
||||
run_loop.Run();
|
||||
return !paths->empty();
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunOpenDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, base::FilePath* file_path,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePath", file_path);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), path);
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(promise), settings);
|
||||
run_loop.Run();
|
||||
return !path->empty();
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
||||
@@ -156,36 +156,26 @@ void WinCaptionButtonContainer::UpdateBackground() {
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::UpdateButtons() {
|
||||
const bool is_maximized = frame_view_->frame()->IsMaximized();
|
||||
restore_button_->SetVisible(is_maximized);
|
||||
maximize_button_->SetVisible(!is_maximized);
|
||||
|
||||
const bool minimizable = frame_view_->window()->IsMinimizable();
|
||||
minimize_button_->SetEnabled(minimizable);
|
||||
minimize_button_->SetVisible(minimizable);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled.
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
const bool is_maximized = frame_view_->frame()->IsMaximized();
|
||||
const bool maximizable = frame_view_->window()->IsMaximizable();
|
||||
restore_button_->SetVisible(is_maximized && maximizable);
|
||||
maximize_button_->SetVisible(!is_maximized && maximizable);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled, unless the window is not
|
||||
// maximized.
|
||||
const bool maximizable = frame_view_->window()->IsMaximizable();
|
||||
maximize_button_->SetEnabled(!(is_touch && is_maximized) && maximizable);
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
maximize_button_->SetEnabled(!is_touch || !is_maximized);
|
||||
|
||||
// If the window isn't closable, the close button should be disabled.
|
||||
const bool closable = frame_view_->window()->IsClosable();
|
||||
close_button_->SetEnabled(closable);
|
||||
|
||||
// If all three of closable, maximizable, and minimizable are disabled,
|
||||
// Windows natively only shows the disabled closable button. Copy that
|
||||
// behavior here.
|
||||
if (!maximizable && !closable && !minimizable) {
|
||||
minimize_button_->SetVisible(false);
|
||||
maximize_button_->SetVisible(false);
|
||||
}
|
||||
|
||||
InvalidateLayout();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "base/hash/hash.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "shell/common/api/electron_api_key_weak_map.h"
|
||||
@@ -17,18 +16,6 @@
|
||||
#include "url/origin.h"
|
||||
#include "v8/include/v8-profiler.h"
|
||||
|
||||
namespace std {
|
||||
|
||||
// The hash function used by DoubleIDWeakMap.
|
||||
template <typename Type1, typename Type2>
|
||||
struct hash<std::pair<Type1, Type2>> {
|
||||
std::size_t operator()(std::pair<Type1, Type2> value) const {
|
||||
return base::HashInts(base::Hash(value.first), value.second);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
|
||||
@@ -177,6 +177,38 @@ describe('debugger module', () => {
|
||||
await loadingFinished;
|
||||
});
|
||||
|
||||
it('can get and set cookies using the Storage API', async () => {
|
||||
await w.webContents.loadURL('about:blank');
|
||||
w.webContents.debugger.attach('1.1');
|
||||
|
||||
await w.webContents.debugger.sendCommand('Storage.clearCookies', {});
|
||||
await w.webContents.debugger.sendCommand('Storage.setCookies', {
|
||||
cookies: [
|
||||
{
|
||||
name: 'cookieOne',
|
||||
value: 'cookieValueOne',
|
||||
url: 'https://cookieone.com'
|
||||
},
|
||||
{
|
||||
name: 'cookieTwo',
|
||||
value: 'cookieValueTwo',
|
||||
url: 'https://cookietwo.com'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { cookies } = await w.webContents.debugger.sendCommand('Storage.getCookies', {});
|
||||
expect(cookies).to.have.lengthOf(2);
|
||||
|
||||
const cookieOne = cookies.find((cookie: any) => cookie.name === 'cookieOne');
|
||||
expect(cookieOne.domain).to.equal('cookieone.com');
|
||||
expect(cookieOne.value).to.equal('cookieValueOne');
|
||||
|
||||
const cookieTwo = cookies.find((cookie: any) => cookie.name === 'cookieTwo');
|
||||
expect(cookieTwo.domain).to.equal('cookietwo.com');
|
||||
expect(cookieTwo.value).to.equal('cookieValueTwo');
|
||||
});
|
||||
|
||||
it('uses empty sessionId by default', async () => {
|
||||
w.webContents.loadURL('about:blank');
|
||||
w.webContents.debugger.attach();
|
||||
|
||||
@@ -935,12 +935,11 @@ describe('net module', () => {
|
||||
response.end();
|
||||
});
|
||||
const serverUrl = url.parse(serverUrlUnparsed);
|
||||
const options = {
|
||||
const urlRequest = net.request({
|
||||
port: serverUrl.port ? parseInt(serverUrl.port, 10) : undefined,
|
||||
hostname: '127.0.0.1',
|
||||
headers: { [customHeaderName]: customHeaderValue }
|
||||
};
|
||||
const urlRequest = net.request(options);
|
||||
});
|
||||
const response = await getResponse(urlRequest);
|
||||
expect(response.statusCode).to.be.equal(200);
|
||||
await collectStreamBody(response);
|
||||
|
||||
@@ -537,6 +537,79 @@ describe('webContents module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigationHistory', () => {
|
||||
let w: BrowserWindow;
|
||||
const urlPage1 = 'data:text/html,<html><head><script>document.title = "Page 1";</script></head><body></body></html>';
|
||||
const urlPage2 = 'data:text/html,<html><head><script>document.title = "Page 2";</script></head><body></body></html>';
|
||||
const urlPage3 = 'data:text/html,<html><head><script>document.title = "Page 3";</script></head><body></body></html>';
|
||||
|
||||
beforeEach(async () => {
|
||||
w = new BrowserWindow({ show: false });
|
||||
});
|
||||
afterEach(closeAllWindows);
|
||||
describe('navigationHistory.getEntryAtIndex(index) API ', () => {
|
||||
it('should fetch default navigation entry when no urls are loaded', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: '', title: '' });
|
||||
});
|
||||
it('should fetch navigation entry given a valid index', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: urlPage1, title: 'Page 1' });
|
||||
});
|
||||
it('should return null given an invalid index larger than history length', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(5);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
it('should return null given an invalid negative index', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(-1);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigationHistory.getActiveIndex() API', () => {
|
||||
it('should return valid active index after a single page visit', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
|
||||
});
|
||||
|
||||
it('should return valid active index after a multiple page visits', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(2);
|
||||
});
|
||||
|
||||
it('should return valid active index given no page visits', async () => {
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigationHistory.length() API', () => {
|
||||
it('should return valid history length after a single page visit', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
});
|
||||
|
||||
it('should return valid history length after a multiple page visits', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(3);
|
||||
});
|
||||
|
||||
it('should return valid history length given no page visits', async () => {
|
||||
// Note: Even if no navigation has committed, the history list will always start with an initial navigation entry
|
||||
// Ref: https://source.chromium.org/chromium/chromium/src/+/main:ceontent/public/browser/navigation_controller.h;l=381
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFocusedWebContents() API', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
|
||||
@@ -1383,6 +1383,138 @@ describe('chromium features', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Storage Access API', () => {
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => {
|
||||
session.defaultSession.setPermissionCheckHandler(null);
|
||||
session.defaultSession.setPermissionRequestHandler(null);
|
||||
});
|
||||
|
||||
it('can determine if a permission is granted for "storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission === 'storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({ name: 'storage-access' })
|
||||
.then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can determine if a permission is denied for "storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission !== 'storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({ name: 'storage-access' })
|
||||
.then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
|
||||
it('can determine if a permission is granted for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission === 'top-level-storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({
|
||||
name: 'top-level-storage-access',
|
||||
requestedOrigin: "https://www.example.com",
|
||||
}).then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can determine if a permission is denied for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission !== 'top-level-storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({
|
||||
name: 'top-level-storage-access',
|
||||
requestedOrigin: "https://www.example.com",
|
||||
}).then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
|
||||
it('can grant a permission request for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionRequestHandler(
|
||||
(_wc, permission, callback) => {
|
||||
callback(permission === 'top-level-storage-access');
|
||||
}
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'button.html'));
|
||||
|
||||
// requestStorageAccessFor returns a Promise that fulfills with undefined
|
||||
// if the access to third-party cookies was granted and rejects if access was denied.
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const button = document.getElementById('button');
|
||||
button.addEventListener("click", () => {
|
||||
document.requestStorageAccessFor('https://myfakesite').then(
|
||||
(res) => { resolve('granted') },
|
||||
(err) => { resolve('denied') },
|
||||
);
|
||||
});
|
||||
button.click();
|
||||
});
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can deny a permission request for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionRequestHandler(
|
||||
(_wc, permission, callback) => {
|
||||
callback(permission !== 'top-level-storage-access');
|
||||
}
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'button.html'));
|
||||
|
||||
// requestStorageAccessFor returns a Promise that fulfills with undefined
|
||||
// if the access to third-party cookies was granted and rejects if access was denied.
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const button = document.getElementById('button');
|
||||
button.addEventListener("click", () => {
|
||||
document.requestStorageAccessFor('https://myfakesite').then(
|
||||
(res) => { resolve('granted') },
|
||||
(err) => { resolve('denied') },
|
||||
);
|
||||
});
|
||||
button.click();
|
||||
});
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
});
|
||||
|
||||
describe('IdleDetection', () => {
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => {
|
||||
@@ -2068,13 +2200,13 @@ describe('chromium features', () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
|
||||
// History should have current page by now.
|
||||
expect(w.webContents.length()).to.equal(1);
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
|
||||
const waitCommit = once(w.webContents, 'navigation-entry-committed');
|
||||
w.webContents.executeJavaScript('window.history.pushState({}, "")');
|
||||
await waitCommit;
|
||||
// Initial page + pushed state.
|
||||
expect(w.webContents.length()).to.equal(2);
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2102,7 +2234,7 @@ describe('chromium features', () => {
|
||||
});
|
||||
await w.webContents.mainFrame.frames[0].executeJavaScript('window.history.back()');
|
||||
expect(await w.webContents.executeJavaScript('window.history.state')).to.equal(1);
|
||||
expect(w.webContents.getActiveIndex()).to.equal(1);
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,5 +7,7 @@
|
||||
"session module ses.cookies should set cookie for standard scheme",
|
||||
"webFrameMain module WebFrame.visibilityState should match window state",
|
||||
"reporting api sends a report for a deprecation",
|
||||
"chromium features SpeechSynthesis should emit lifecycle events"
|
||||
]
|
||||
"chromium features SpeechSynthesis should emit lifecycle events",
|
||||
"BrowserWindow module draggable regions should allow the window to be dragged when enabled",
|
||||
"BrowserWindow module draggable regions should allow the window to be dragged when no WCO and --webkit-app-region: drag enabled"
|
||||
]
|
||||
|
||||
@@ -38,9 +38,6 @@
|
||||
"ws": "^7.4.6",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@nut-tree/nut-js": "^3.1.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"nan": "file:../../third_party/nan",
|
||||
"dbus-native/optimist/minimist": "1.2.7",
|
||||
|
||||
775
spec/yarn.lock
775
spec/yarn.lock
File diff suppressed because it is too large
Load Diff
5
typings/internal-electron.d.ts
vendored
5
typings/internal-electron.d.ts
vendored
@@ -78,9 +78,10 @@ declare namespace Electron {
|
||||
_print(options: any, callback?: (success: boolean, failureReason: string) => void): void;
|
||||
_getPrintersAsync(): Promise<Electron.PrinterInfo[]>;
|
||||
_init(): void;
|
||||
_getNavigationEntryAtIndex(index: number): Electron.EntryAtIndex | null;
|
||||
_getActiveIndex(): number;
|
||||
_historyLength(): number;
|
||||
canGoToIndex(index: number): boolean;
|
||||
getActiveIndex(): number;
|
||||
length(): number;
|
||||
destroy(): void;
|
||||
// <webview>
|
||||
attachToIframe(embedderWebContents: Electron.WebContents, embedderFrameId: number): void;
|
||||
|
||||
@@ -199,10 +199,10 @@
|
||||
"@octokit/auth-app" "^4.0.13"
|
||||
"@octokit/rest" "^19.0.11"
|
||||
|
||||
"@electron/lint-roller@^1.9.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.10.0.tgz#278dfb285018869faabd5c3f616c64da7f2b26c1"
|
||||
integrity sha512-dDJDy5MANWNIxv7OqQ0NwPzqwMmgTFYtjR/KKeEEdCQj1k21TT61+Zjdsm+IKbA2LSPujpujsyGKUQtb4oLLCA==
|
||||
"@electron/lint-roller@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.12.1.tgz#3152b9a68815b2ab51cc5a4d462ae6769d5052ce"
|
||||
integrity sha512-TGgVcHUAooM9/dV3iJTxhmKl35x/gzStsClz2/LWtBQZ59cRK+0YwWF5HWhtydGFIpOLEQGzCvUrty5zZLyd4w==
|
||||
dependencies:
|
||||
"@dsanders11/vscode-markdown-languageservice" "^0.3.0"
|
||||
balanced-match "^2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user