mirror of
https://github.com/electron/electron.git
synced 2026-03-19 03:02:02 -04:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44bc2c8cef | ||
|
|
4e9e7335bc | ||
|
|
cd88382756 | ||
|
|
105c5591d0 | ||
|
|
90b3a2341d | ||
|
|
21f9474f4f | ||
|
|
c3e397ed2d | ||
|
|
d84dca2818 | ||
|
|
bb20d0c352 | ||
|
|
c66fc559b2 | ||
|
|
76f34911f2 | ||
|
|
5d381dd27e |
2
.github/actions/checkout/action.yml
vendored
2
.github/actions/checkout/action.yml
vendored
@@ -43,7 +43,7 @@ runs:
|
||||
curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token
|
||||
- name: Save SAS Key
|
||||
if: ${{ inputs.generate-sas-token == 'true' }}
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: sas-token
|
||||
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
|
||||
|
||||
@@ -7,7 +7,7 @@ runs:
|
||||
shell: bash
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
||||
8
.github/workflows/apply-patches.yml
vendored
8
.github/workflows/apply-patches.yml
vendored
@@ -71,3 +71,11 @@ jobs:
|
||||
uses: ./src/electron/.github/actions/checkout
|
||||
with:
|
||||
target-platform: linux
|
||||
- name: Upload Patch Conflict Fix
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: update-patches
|
||||
path: patches/update-patches.patch
|
||||
if-no-files-found: ignore
|
||||
archive: false
|
||||
|
||||
16
CLAUDE.md
16
CLAUDE.md
@@ -127,6 +127,22 @@ patches/{target}/*.patch → [e sync --3] → target repo commits
|
||||
2. Create a git commit
|
||||
3. Run `e patches <target>` to export
|
||||
|
||||
**Fixing patch conflicts on an existing PR:**
|
||||
|
||||
If asked to fix a patch conflict on a branch that already has an open PR, check the PR's failed **Apply Patches** CI run for an `update-patches` artifact before running `e sync` locally. CI has already performed the 3-way merge and exported the resolved patch diff — applying it is much faster than a full local sync.
|
||||
|
||||
```bash
|
||||
# Find the failed Apply Patches run for the PR and download the artifact
|
||||
gh run list --repo electron/electron --branch <pr-branch> --workflow "Apply Patches" --limit 1
|
||||
gh run download <run-id> --repo electron/electron --name update-patches
|
||||
|
||||
# Apply the CI-generated fix, then push
|
||||
git am update-patches.patch
|
||||
git push
|
||||
```
|
||||
|
||||
If no artifact exists (e.g. the 3-way merge itself failed), fall back to `e sync --3` and resolve manually.
|
||||
|
||||
## Testing
|
||||
|
||||
**Test location:** `spec/` directory
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'146.0.7680.72',
|
||||
'146.0.7680.80',
|
||||
'node_version':
|
||||
'v24.14.0',
|
||||
'nan_version':
|
||||
|
||||
@@ -1485,6 +1485,11 @@ mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||
const browserView = new BrowserView(options)
|
||||
mainWindow.addBrowserView(browserView)
|
||||
browserView.setBounds({ x: 0, y: 0, width: 640, height: 480 })
|
||||
// For `background-tab` disposition (e.g., when middle-clicking or ctrl/cmd-clicking a link),
|
||||
// `options.webContents` is undefined because its creation can be deferred. So load the URL manually.
|
||||
if (details.disposition === 'background-tab') {
|
||||
browserView.webContents.loadURL(details.url)
|
||||
}
|
||||
return browserView.webContents
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,24 +21,33 @@
|
||||
|
||||
### Step 1: Fork
|
||||
|
||||
Fork the project [on GitHub](https://github.com/electron/electron) and clone your fork
|
||||
locally.
|
||||
|
||||
```sh
|
||||
$ git clone git@github.com:username/electron.git
|
||||
$ cd electron
|
||||
$ git remote add upstream https://github.com/electron/electron.git
|
||||
$ git fetch upstream
|
||||
```
|
||||
Fork Electron's [GitHub repository](https://github.com/electron/electron).
|
||||
|
||||
### Step 2: Build
|
||||
|
||||
Build steps and dependencies differ slightly depending on your operating system.
|
||||
See these detailed guides on building Electron locally:
|
||||
We recommend using [`@electron/build-tools`](https://github.com/electron/build-tools) to build
|
||||
Electron itself.
|
||||
|
||||
* [Building on macOS](build-instructions-macos.md)
|
||||
* [Building on Linux](build-instructions-linux.md)
|
||||
* [Building on Windows](build-instructions-windows.md)
|
||||
```sh
|
||||
# Install build-tools package globally:
|
||||
npm install -g @electron/build-tools
|
||||
# Run the init script where you want to clone the project and point it to your fork:
|
||||
e init --fork my-org/electron --bootstrap testing
|
||||
```
|
||||
|
||||
This will create a new `electron` folder in your working directory and initialize the project.
|
||||
Once the build completes, navigate to `electron/src/electron`, where your fork is actually cloned.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Your Electron project has a complex folder structure with nested repositories.
|
||||
> See the [Build Instructions](./build-instructions-gn.md) docs for detailed Build Tools
|
||||
> usage instructions (e.g. how to sync dependencies or how to recompile the binary)
|
||||
> and platform-specific notices.
|
||||
|
||||
There, you should have two `remote` URLs in git:
|
||||
|
||||
* `origin` will point to `electron/electron`
|
||||
* `fork` will point to your fork (`my-org/electron`)
|
||||
|
||||
Once you've built the project locally, you're ready to start making changes!
|
||||
|
||||
@@ -48,7 +57,7 @@ To keep your development environment organized, create local branches to
|
||||
hold your work. These should be branched directly off of the `main` branch.
|
||||
|
||||
```sh
|
||||
$ git checkout -b my-branch -t upstream/main
|
||||
git checkout -b my-branch
|
||||
```
|
||||
|
||||
## Making Changes
|
||||
@@ -60,7 +69,7 @@ changes to either the C/C++ code in the `shell/` folder,
|
||||
the JavaScript code in the `lib/` folder, the documentation in `docs/api/`
|
||||
or tests in the `spec/` folder.
|
||||
|
||||
Please be sure to run `npm run lint` from time to time on any code changes
|
||||
Please be sure to run `yarn lint` from time to time on any code changes
|
||||
to ensure that they follow the project's code style.
|
||||
|
||||
See [coding style](coding-style.md) for
|
||||
@@ -75,8 +84,8 @@ across multiple commits. There is no limit to the number of commits in a
|
||||
pull request.
|
||||
|
||||
```sh
|
||||
$ git add my/changed/files
|
||||
$ git commit
|
||||
git add my/changed/files
|
||||
git commit
|
||||
```
|
||||
|
||||
Note that multiple commits get squashed when they are landed.
|
||||
@@ -138,8 +147,8 @@ Once you have committed your changes, it is a good idea to use `git rebase`
|
||||
(not `git merge`) to synchronize your work with the main repository.
|
||||
|
||||
```sh
|
||||
$ git fetch upstream
|
||||
$ git rebase upstream/main
|
||||
git fetch origin
|
||||
git rebase origin/main
|
||||
```
|
||||
|
||||
This ensures that your working branch has the latest changes from `electron/electron`
|
||||
@@ -156,7 +165,7 @@ Before submitting your changes in a pull request, always run the full
|
||||
test suite. To run the tests:
|
||||
|
||||
```sh
|
||||
$ npm run test
|
||||
yarn test
|
||||
```
|
||||
|
||||
Make sure the linter does not report any issues and that all tests pass.
|
||||
@@ -165,7 +174,7 @@ Please do not submit patches that fail either check.
|
||||
If you are updating tests and want to run a single spec to check it:
|
||||
|
||||
```sh
|
||||
$ npm run test -match=menu
|
||||
yarn test -match=menu
|
||||
```
|
||||
|
||||
The above would only run spec modules matching `menu`, which is useful for
|
||||
@@ -179,7 +188,7 @@ begin the process of opening a pull request by pushing your working branch
|
||||
to your fork on GitHub.
|
||||
|
||||
```sh
|
||||
$ git push origin my-branch
|
||||
git push fork my-branch
|
||||
```
|
||||
|
||||
### Step 9: Opening the Pull Request
|
||||
@@ -203,9 +212,9 @@ branch, add a new commit with those changes, and push those to your fork.
|
||||
GitHub will automatically update the pull request.
|
||||
|
||||
```sh
|
||||
$ git add my/changed/files
|
||||
$ git commit
|
||||
$ git push origin my-branch
|
||||
git add my/changed/files
|
||||
git commit
|
||||
git push fork my-branch
|
||||
```
|
||||
|
||||
There are a number of more advanced mechanisms for managing commits using
|
||||
@@ -213,8 +222,8 @@ There are a number of more advanced mechanisms for managing commits using
|
||||
|
||||
Feel free to post a comment in the pull request to ping reviewers if you are
|
||||
awaiting an answer on something. If you encounter words or acronyms that
|
||||
seem unfamiliar, refer to this
|
||||
[glossary](https://sites.google.com/a/chromium.org/dev/glossary).
|
||||
seem unfamiliar, refer to the
|
||||
[Chromium glossary](https://sites.google.com/a/chromium.org/dev/glossary).
|
||||
|
||||
#### Approval and Request Changes Workflow
|
||||
|
||||
|
||||
@@ -1232,6 +1232,8 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
// has filesystem caching.
|
||||
overrideAPI(fs, 'copyFile');
|
||||
overrideAPISync(fs, 'copyFileSync');
|
||||
overrideAPI(fs, 'cp');
|
||||
overrideAPISync(fs, 'cpSync');
|
||||
|
||||
overrideAPI(fs, 'open');
|
||||
overrideAPISync(process, 'dlopen', 1);
|
||||
|
||||
@@ -12,6 +12,5 @@
|
||||
{ "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/skia", "repo": "src/third_party/skia/src" },
|
||||
{ "patch_dir": "src/electron/patches/sqlite", "repo": "src/third_party/sqlite/src" }
|
||||
]
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
|
||||
fix_prefer_browser_runtime_over_node_in_hostruntime_detection.patch
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Thu, 12 Mar 2026 17:03:29 +0100
|
||||
Subject: fix: prefer browser runtime over node in HostRuntime detection
|
||||
|
||||
In Electron, the `process` global is available in renderer processes,
|
||||
including the DevTools renderer. This causes the IS_NODE check to pass,
|
||||
leading DevTools to attempt importing the Node.js platform runtime
|
||||
(which uses `node:worker_threads`). However, DevTools Web Workers
|
||||
running under the `devtools://` protocol don't have access to Node.js
|
||||
built-in modules, resulting in a failed dynamic import.
|
||||
|
||||
Fix by checking IS_BROWSER first, since DevTools always runs in a
|
||||
browser-like environment. The Node.js runtime is only needed when
|
||||
DevTools runs under pure Node.js (e.g., CLI tooling or testing).
|
||||
|
||||
diff --git a/front_end/core/platform/HostRuntime.ts b/front_end/core/platform/HostRuntime.ts
|
||||
index 91adba7c966a9c4c0e5315d2cfee07f8f622b731..16822b8d4ea74a4ffd6870e5e95948d75918f5d2 100644
|
||||
--- a/front_end/core/platform/HostRuntime.ts
|
||||
+++ b/front_end/core/platform/HostRuntime.ts
|
||||
@@ -14,12 +14,12 @@ export const IS_BROWSER =
|
||||
typeof window !== 'undefined' || (typeof self !== 'undefined' && typeof self.postMessage === 'function');
|
||||
|
||||
export const HOST_RUNTIME = await (async(): Promise<Api.HostRuntime.HostRuntime> => {
|
||||
- if (IS_NODE) {
|
||||
- return (await import('./node/node.js')).HostRuntime.HOST_RUNTIME;
|
||||
- }
|
||||
if (IS_BROWSER) {
|
||||
return (await import('./browser/browser.js')).HostRuntime.HOST_RUNTIME;
|
||||
}
|
||||
+ if (IS_NODE) {
|
||||
+ return (await import('./node/node.js')).HostRuntime.HOST_RUNTIME;
|
||||
+ }
|
||||
|
||||
throw new Error('Unknown runtime!');
|
||||
})();
|
||||
@@ -17,7 +17,7 @@ Upstreams:
|
||||
- https://github.com/nodejs/node/pull/39136
|
||||
|
||||
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
|
||||
index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d67b0b9413 100644
|
||||
index 461819ce0fa732048e4365c40a86ef55d984c35f..f1c85e94cf526d0255f47c003664680d26413ec3 100644
|
||||
--- a/deps/ncrypto/ncrypto.cc
|
||||
+++ b/deps/ncrypto/ncrypto.cc
|
||||
@@ -11,6 +11,7 @@
|
||||
@@ -28,38 +28,6 @@ index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d6
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/params.h>
|
||||
@@ -1130,7 +1131,9 @@ int64_t X509View::getValidToTime() const {
|
||||
return tp;
|
||||
#else
|
||||
struct tm tp;
|
||||
- ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
|
||||
+#ifndef OPENSSL_IS_BORINGSSL
|
||||
+ ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
|
||||
+#endif
|
||||
return PortableTimeGM(&tp);
|
||||
#endif
|
||||
}
|
||||
@@ -1142,7 +1145,9 @@ int64_t X509View::getValidFromTime() const {
|
||||
return tp;
|
||||
#else
|
||||
struct tm tp;
|
||||
+#ifndef OPENSSL_IS_BORINGSSL
|
||||
ASN1_TIME_to_tm(X509_get0_notBefore(cert_), &tp);
|
||||
+#endif
|
||||
return PortableTimeGM(&tp);
|
||||
#endif
|
||||
}
|
||||
@@ -2886,10 +2891,6 @@ std::optional<uint32_t> SSLPointer::verifyPeerCertificate() const {
|
||||
const char* SSLPointer::getClientHelloAlpn() const {
|
||||
if (ssl_ == nullptr) return {};
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
- const unsigned char* buf;
|
||||
- size_t len;
|
||||
- size_t rem;
|
||||
-
|
||||
if (!SSL_client_hello_get0_ext(
|
||||
get(),
|
||||
TLSEXT_TYPE_application_layer_protocol_negotiation,
|
||||
@@ -3090,9 +3091,11 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
|
||||
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
|
||||
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
cherry-pick-248acd90d9a3.patch
|
||||
@@ -1,539 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Greg Daniel <egdaniel@google.com>
|
||||
Date: Wed, 11 Mar 2026 16:00:23 -0400
|
||||
Subject: Make sure we are getting the correct atlas for glyph mask format.
|
||||
|
||||
Bug: b/491421267
|
||||
Change-Id: I4eacd46599eca2df8c10a3fc894b9ce890fae1e2
|
||||
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184076
|
||||
Commit-Queue: Greg Daniel <egdaniel@google.com>
|
||||
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
|
||||
(cherry picked from commit 0cab3e4ee34b3bca6ba7df676639d73ffe4b2135)
|
||||
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184916
|
||||
|
||||
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
|
||||
index 6793512e216b00e1f8112f8e681eecf5beee8fe8..4fd0965185f8bab5a55ec63329bf6aa36ad56ed0 100644
|
||||
--- a/bench/GlyphQuadFillBench.cpp
|
||||
+++ b/bench/GlyphQuadFillBench.cpp
|
||||
@@ -68,7 +68,7 @@ class DirectMaskGlyphVertexFillBenchmark : public Benchmark {
|
||||
const sktext::gpu::AtlasSubRun* subRun =
|
||||
sktext::gpu::TextBlobTools::FirstSubRun(fBlob.get());
|
||||
SkASSERT_RELEASE(subRun);
|
||||
- subRun->testingOnly_packedGlyphIDToGlyph(&fCache);
|
||||
+ subRun->testingOnly_packedGlyphIDToGlyph(&fCache, subRun->maskFormat());
|
||||
fVertices.reset(new char[subRun->vertexStride(drawMatrix) * subRun->glyphCount() * 4]);
|
||||
}
|
||||
|
||||
diff --git a/gn/tests.gni b/gn/tests.gni
|
||||
index 346cf0ca3b7884d3fb599508d50cc07019322b36..93f4fe5d3281035d1ff469a144e20e00bd783d71 100644
|
||||
--- a/gn/tests.gni
|
||||
+++ b/gn/tests.gni
|
||||
@@ -428,6 +428,7 @@ pathops_tests_sources = [
|
||||
ganesh_tests_sources = [
|
||||
"$_tests/AdvancedBlendTest.cpp",
|
||||
"$_tests/ApplyGammaTest.cpp",
|
||||
+ "$_tests/AtlasOobTest.cpp",
|
||||
"$_tests/BackendAllocationTest.cpp",
|
||||
"$_tests/BackendSurfaceMutableStateTest.cpp",
|
||||
"$_tests/BlendTest.cpp",
|
||||
diff --git a/src/gpu/ganesh/text/GrAtlasManager.cpp b/src/gpu/ganesh/text/GrAtlasManager.cpp
|
||||
index 403bfe274e56293bfe2382b02525ae742ba541a7..1e7d9aa0ce14f19e09d79544730c6aa922ae37d6 100644
|
||||
--- a/src/gpu/ganesh/text/GrAtlasManager.cpp
|
||||
+++ b/src/gpu/ganesh/text/GrAtlasManager.cpp
|
||||
@@ -178,8 +178,7 @@ GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
|
||||
}
|
||||
SkASSERT(glyph != nullptr);
|
||||
|
||||
- MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
|
||||
- MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
|
||||
+ MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
|
||||
int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
|
||||
|
||||
int padding;
|
||||
@@ -299,7 +298,7 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
|
||||
|
||||
uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
|
||||
|
||||
- this->packedGlyphIDToGlyph(target->strikeCache());
|
||||
+ this->packedGlyphIDToGlyph(target->strikeCache(), maskFormat);
|
||||
|
||||
if (fAtlasGeneration != currentAtlasGen) {
|
||||
// Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
|
||||
@@ -316,9 +315,10 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
|
||||
for (const Variant& variant : glyphs) {
|
||||
Glyph* gpuGlyph = variant.glyph;
|
||||
SkASSERT(gpuGlyph != nullptr);
|
||||
-
|
||||
+ SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
|
||||
if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
|
||||
- const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
|
||||
+ const SkGlyph& skGlyph =
|
||||
+ *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
|
||||
auto code = atlasManager->addGlyphToAtlas(
|
||||
skGlyph, gpuGlyph, srcPadding, target->resourceProvider(), uploadTarget);
|
||||
if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
|
||||
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
|
||||
index ba8049369956f5ea00e09f947c4ed9067a99bb6c..1e7027ebcfaaf79e2c6bd17b65a046bdb8eef9ae 100644
|
||||
--- a/src/gpu/graphite/Device.cpp
|
||||
+++ b/src/gpu/graphite/Device.cpp
|
||||
@@ -1436,6 +1436,7 @@ void Device::drawAtlasSubRun(const sktext::gpu::AtlasSubRun* subRun,
|
||||
int padding) {
|
||||
return glyphs->regenerateAtlasForGraphite(begin, end, maskFormat, padding, fRecorder);
|
||||
};
|
||||
+
|
||||
for (int subRunCursor = 0; subRunCursor < subRunEnd;) {
|
||||
// For the remainder of the run, add any atlas uploads to the Recorder's TextAtlasManager
|
||||
auto[ok, glyphsRegenerated] = subRun->regenerateAtlas(subRunCursor, subRunEnd,
|
||||
diff --git a/src/gpu/graphite/text/TextAtlasManager.cpp b/src/gpu/graphite/text/TextAtlasManager.cpp
|
||||
index 6602a76c150bff077666fb91b990d3e45d528ce2..cbb51a66846922995912c3159afba879a2487313 100644
|
||||
--- a/src/gpu/graphite/text/TextAtlasManager.cpp
|
||||
+++ b/src/gpu/graphite/text/TextAtlasManager.cpp
|
||||
@@ -207,8 +207,7 @@ DrawAtlas::ErrorCode TextAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
|
||||
}
|
||||
SkASSERT(glyph != nullptr);
|
||||
|
||||
- MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
|
||||
- MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
|
||||
+ MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
|
||||
int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
|
||||
|
||||
int padding;
|
||||
@@ -359,7 +358,7 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
|
||||
|
||||
uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
|
||||
|
||||
- this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
|
||||
+ this->packedGlyphIDToGlyph(recorder->priv().strikeCache(), maskFormat);
|
||||
|
||||
if (fAtlasGeneration != currentAtlasGen) {
|
||||
// Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
|
||||
@@ -375,9 +374,10 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
|
||||
for (const Variant& variant : glyphs) {
|
||||
Glyph* gpuGlyph = variant.glyph;
|
||||
SkASSERT(gpuGlyph != nullptr);
|
||||
-
|
||||
+ SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
|
||||
if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
|
||||
- const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
|
||||
+ const SkGlyph& skGlyph =
|
||||
+ *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
|
||||
auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
|
||||
if (code != DrawAtlas::ErrorCode::kSucceeded) {
|
||||
success = code != DrawAtlas::ErrorCode::kError;
|
||||
diff --git a/src/text/gpu/Glyph.h b/src/text/gpu/Glyph.h
|
||||
index 821612d68cecfe9dae9518e376e09fdf233395ad..7942006a563bcab925ea2129ab6f6beea438a4c8 100644
|
||||
--- a/src/text/gpu/Glyph.h
|
||||
+++ b/src/text/gpu/Glyph.h
|
||||
@@ -14,6 +14,25 @@
|
||||
|
||||
namespace sktext::gpu {
|
||||
|
||||
+struct GlyphEntryKey {
|
||||
+ explicit GlyphEntryKey(SkPackedGlyphID id, skgpu::MaskFormat format)
|
||||
+ : fPackedID(id), fFormat(format) {}
|
||||
+
|
||||
+ const SkPackedGlyphID fPackedID;
|
||||
+ skgpu::MaskFormat fFormat;
|
||||
+
|
||||
+ bool operator==(const GlyphEntryKey& that) const {
|
||||
+ return fPackedID == that.fPackedID && fFormat == that.fFormat;
|
||||
+ }
|
||||
+ bool operator!=(const GlyphEntryKey& that) const {
|
||||
+ return !(*this == that);
|
||||
+ }
|
||||
+
|
||||
+ uint32_t hash() const {
|
||||
+ return fPackedID.hash();
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
class Glyph {
|
||||
public:
|
||||
static skgpu::MaskFormat FormatFromSkGlyph(SkMask::Format format) {
|
||||
@@ -34,10 +53,11 @@ public:
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
- explicit Glyph(SkPackedGlyphID packedGlyphID) : fPackedID(packedGlyphID) {}
|
||||
+ explicit Glyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format)
|
||||
+ : fGlyphEntryKey(packedGlyphID, format) {}
|
||||
|
||||
- const SkPackedGlyphID fPackedID;
|
||||
- skgpu::AtlasLocator fAtlasLocator;
|
||||
+ const GlyphEntryKey fGlyphEntryKey;
|
||||
+ skgpu::AtlasLocator fAtlasLocator;
|
||||
};
|
||||
|
||||
} // namespace sktext::gpu
|
||||
diff --git a/src/text/gpu/GlyphVector.cpp b/src/text/gpu/GlyphVector.cpp
|
||||
index 2a8e85f926aa547169f4b85372e9d3fb99816956..7bec7a0b77d8560d5ef978281edd7df6c45cb56f 100644
|
||||
--- a/src/text/gpu/GlyphVector.cpp
|
||||
+++ b/src/text/gpu/GlyphVector.cpp
|
||||
@@ -99,14 +99,14 @@ SkSpan<const Glyph*> GlyphVector::glyphs() const {
|
||||
|
||||
// packedGlyphIDToGlyph must be run in single-threaded mode.
|
||||
// If fSkStrike is not sk_sp<SkStrike> then the conversion to Glyph* has not happened.
|
||||
-void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache) {
|
||||
+void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache, MaskFormat maskFormat) {
|
||||
if (fTextStrike == nullptr) {
|
||||
SkStrike* strike = fStrikePromise.strike();
|
||||
fTextStrike = cache->findOrCreateStrike(strike->strikeSpec());
|
||||
|
||||
// Get all the atlas locations for each glyph.
|
||||
for (Variant& variant : fGlyphs) {
|
||||
- variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID);
|
||||
+ variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID, maskFormat);
|
||||
}
|
||||
|
||||
// This must be pinned for the Atlas filling to work.
|
||||
diff --git a/src/text/gpu/GlyphVector.h b/src/text/gpu/GlyphVector.h
|
||||
index 42b92a93f70cc6d86d0a87dd07c2244e0da1281c..1eec6327d38fb4472b027faae68eecb9ad7509d7 100644
|
||||
--- a/src/text/gpu/GlyphVector.h
|
||||
+++ b/src/text/gpu/GlyphVector.h
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
// the sub runs.
|
||||
int unflattenSize() const { return GlyphVectorSize(fGlyphs.size()); }
|
||||
|
||||
- void packedGlyphIDToGlyph(StrikeCache* cache);
|
||||
+ void packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat);
|
||||
|
||||
static size_t GlyphVectorSize(size_t count) {
|
||||
return sizeof(Variant) * count;
|
||||
diff --git a/src/text/gpu/StrikeCache.cpp b/src/text/gpu/StrikeCache.cpp
|
||||
index add3127c92fdbfe56d6b56209a2235ce5a9f5acb..19df48329fd500f8682669ec96eb883b58243fdd 100644
|
||||
--- a/src/text/gpu/StrikeCache.cpp
|
||||
+++ b/src/text/gpu/StrikeCache.cpp
|
||||
@@ -207,10 +207,11 @@ TextStrike::TextStrike(StrikeCache* strikeCache, const SkStrikeSpec& strikeSpec)
|
||||
: fStrikeCache(strikeCache)
|
||||
, fStrikeSpec{strikeSpec} {}
|
||||
|
||||
-Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
|
||||
- Glyph* glyph = fCache.findOrNull(packedGlyphID);
|
||||
+Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format) {
|
||||
+ GlyphEntryKey localKey(packedGlyphID, format);
|
||||
+ Glyph* glyph = fCache.findOrNull(localKey);
|
||||
if (glyph == nullptr) {
|
||||
- glyph = fAlloc.make<Glyph>(packedGlyphID);
|
||||
+ glyph = fAlloc.make<Glyph>(packedGlyphID, format);
|
||||
fCache.set(glyph);
|
||||
fMemoryUsed += sizeof(Glyph);
|
||||
if (!fRemoved) {
|
||||
@@ -220,11 +221,11 @@ Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
|
||||
return glyph;
|
||||
}
|
||||
|
||||
-const SkPackedGlyphID& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
|
||||
- return glyph->fPackedID;
|
||||
+const GlyphEntryKey& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
|
||||
+ return glyph->fGlyphEntryKey;
|
||||
}
|
||||
|
||||
-uint32_t TextStrike::HashTraits::Hash(SkPackedGlyphID key) {
|
||||
+uint32_t TextStrike::HashTraits::Hash(GlyphEntryKey key) {
|
||||
return key.hash();
|
||||
}
|
||||
|
||||
diff --git a/src/text/gpu/StrikeCache.h b/src/text/gpu/StrikeCache.h
|
||||
index 007c45c6c6feecba3ff031ba3939ad2402e082b9..014afd5286602e3e049d8e48ae328273e599dc41 100644
|
||||
--- a/src/text/gpu/StrikeCache.h
|
||||
+++ b/src/text/gpu/StrikeCache.h
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "src/core/SkDescriptor.h"
|
||||
#include "src/core/SkStrikeSpec.h"
|
||||
#include "src/core/SkTHash.h"
|
||||
+#include "src/gpu/AtlasTypes.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -32,6 +33,7 @@ struct SkPackedGlyphID;
|
||||
namespace sktext::gpu {
|
||||
|
||||
class Glyph;
|
||||
+struct GlyphEntryKey;
|
||||
class StrikeCache;
|
||||
|
||||
// The TextStrike manages an SkArenaAlloc for Glyphs. The SkStrike is what actually creates
|
||||
@@ -43,7 +45,7 @@ public:
|
||||
TextStrike(StrikeCache* strikeCache,
|
||||
const SkStrikeSpec& strikeSpec);
|
||||
|
||||
- Glyph* getGlyph(SkPackedGlyphID);
|
||||
+ Glyph* getGlyph(SkPackedGlyphID, skgpu::MaskFormat format);
|
||||
const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
|
||||
const SkDescriptor& getDescriptor() const { return fStrikeSpec.descriptor(); }
|
||||
|
||||
@@ -54,11 +56,11 @@ private:
|
||||
const SkStrikeSpec fStrikeSpec;
|
||||
|
||||
struct HashTraits {
|
||||
- static const SkPackedGlyphID& GetKey(const Glyph* glyph);
|
||||
- static uint32_t Hash(SkPackedGlyphID key);
|
||||
+ static const GlyphEntryKey& GetKey(const Glyph* glyph);
|
||||
+ static uint32_t Hash(GlyphEntryKey key);
|
||||
};
|
||||
// Map SkPackedGlyphID -> Glyph*.
|
||||
- skia_private::THashTable<Glyph*, SkPackedGlyphID, HashTraits> fCache;
|
||||
+ skia_private::THashTable<Glyph*, GlyphEntryKey, HashTraits> fCache;
|
||||
|
||||
// Store for the glyph information.
|
||||
SkArenaAlloc fAlloc{512};
|
||||
diff --git a/src/text/gpu/SubRunContainer.cpp b/src/text/gpu/SubRunContainer.cpp
|
||||
index 34b3f347aa404a964f06ff18d43eef99f3b25f6b..66531e030ddfabc2204c8e27c9478251c507b5ec 100644
|
||||
--- a/src/text/gpu/SubRunContainer.cpp
|
||||
+++ b/src/text/gpu/SubRunContainer.cpp
|
||||
@@ -651,8 +651,9 @@ public:
|
||||
|
||||
int glyphSrcPadding() const override { return 0; }
|
||||
|
||||
- void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override {
|
||||
- fGlyphs.packedGlyphIDToGlyph(cache);
|
||||
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache,
|
||||
+ skgpu::MaskFormat maskFormat) const override {
|
||||
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
|
||||
}
|
||||
|
||||
std::tuple<bool, SkRect> deviceRectAndNeedsTransform(
|
||||
@@ -755,8 +756,9 @@ public:
|
||||
|
||||
const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
|
||||
|
||||
- void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
|
||||
- fGlyphs.packedGlyphIDToGlyph(cache);
|
||||
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
|
||||
+ skgpu::MaskFormat maskFormat) const override {
|
||||
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
|
||||
}
|
||||
|
||||
int glyphSrcPadding() const override { return 1; }
|
||||
@@ -893,8 +895,9 @@ public:
|
||||
|
||||
const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
|
||||
|
||||
- void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
|
||||
- fGlyphs.packedGlyphIDToGlyph(cache);
|
||||
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
|
||||
+ skgpu::MaskFormat maskFormat) const override {
|
||||
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
|
||||
}
|
||||
|
||||
int glyphSrcPadding() const override { return SK_DistanceFieldInset; }
|
||||
diff --git a/src/text/gpu/SubRunContainer.h b/src/text/gpu/SubRunContainer.h
|
||||
index 2573dbb3964e9ab2cc0e276b60d4ab4f9804f0d9..4d1a3c8c2d55015d3d351d322ef039c45be2a398 100644
|
||||
--- a/src/text/gpu/SubRunContainer.h
|
||||
+++ b/src/text/gpu/SubRunContainer.h
|
||||
@@ -167,7 +167,7 @@ public:
|
||||
|
||||
const VertexFiller& vertexFiller() const { return fVertexFiller; }
|
||||
|
||||
- virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0;
|
||||
+ virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat) const = 0;
|
||||
|
||||
protected:
|
||||
const VertexFiller fVertexFiller;
|
||||
diff --git a/tests/AtlasOobTest.cpp b/tests/AtlasOobTest.cpp
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4e6fb02ee6af6543df285d8112f1a2ced5bd9ac9
|
||||
--- /dev/null
|
||||
+++ b/tests/AtlasOobTest.cpp
|
||||
@@ -0,0 +1,201 @@
|
||||
+/*
|
||||
+ * Copyright 2026 Google LLC
|
||||
+ *
|
||||
+ * Use of this source code is governed by a BSD-style license that can be
|
||||
+ * found in the LICENSE file.
|
||||
+ */
|
||||
+#include "include/core/SkCanvas.h"
|
||||
+#include "include/core/SkGraphics.h"
|
||||
+#include "include/core/SkSerialProcs.h"
|
||||
+#include "include/core/SkSurface.h"
|
||||
+#include "include/private/chromium/SkChromeRemoteGlyphCache.h"
|
||||
+#include "include/private/chromium/Slug.h"
|
||||
+#include "src/core/SkDescriptor.h"
|
||||
+#include "src/core/SkReadBuffer.h"
|
||||
+#include "src/core/SkTypeface_remote.h"
|
||||
+#include "src/core/SkWriteBuffer.h"
|
||||
+#include "src/gpu/AtlasTypes.h"
|
||||
+#include "tests/CtsEnforcement.h"
|
||||
+#include "tests/Test.h"
|
||||
+#include "tools/ToolUtils.h"
|
||||
+
|
||||
+#if defined(SK_GANESH)
|
||||
+#include "include/gpu/ganesh/GrDirectContext.h"
|
||||
+#include "include/gpu/ganesh/SkSurfaceGanesh.h"
|
||||
+#endif
|
||||
+
|
||||
+#if defined(SK_GRAPHITE)
|
||||
+#include "include/gpu/graphite/Context.h"
|
||||
+#include "include/gpu/graphite/Surface.h"
|
||||
+#include "tools/graphite/GraphiteTestContext.h"
|
||||
+#endif // defined(SK_GRAPHITE)
|
||||
+
|
||||
+#include <vector>
|
||||
+#include <cstring>
|
||||
+
|
||||
+namespace {
|
||||
+class FakeDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
|
||||
+public:
|
||||
+ bool deleteHandle(SkDiscardableHandleId) override { return false; }
|
||||
+ void notifyCacheMiss(SkStrikeClient::CacheMissType, int) override {}
|
||||
+ void notifyReadFailure(const ReadFailureData&) override {}
|
||||
+ void assertHandleValid(SkDiscardableHandleId) override {}
|
||||
+};
|
||||
+
|
||||
+unsigned char kStrikeData[] = {
|
||||
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
|
||||
+ 0x00, 0x00, 0x00, 0x65, 0xd8, 0x50, 0xda, 0x99, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x86, 0x07, 0xc2, 0x42,
|
||||
+ 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00
|
||||
+};
|
||||
+
|
||||
+unsigned char kDrawSlugOp[] = {
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x86, 0x07, 0xc2, 0x42, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73,
|
||||
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
+};
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+// TODO: We expect this test to correctly hit an SkUnreachable and then crash. That does not work
|
||||
+// with our current testing framework because we have no to "expect" a crash. So for now we will
|
||||
+// land this test with only the valid loop enabled, but to test this is working locally, you should
|
||||
+// change the loop to have both iterations.
|
||||
+static void run_atlas_oob_test(skiatest::Reporter* reporter, SkCanvas* canvas) {
|
||||
+ auto discardableManager = sk_make_sp<FakeDiscardableManager>();
|
||||
+ SkStrikeClient client(discardableManager, false);
|
||||
+
|
||||
+ // 1. Prepare Strike Data
|
||||
+ if (!client.readStrikeData(kStrikeData, sizeof(kStrikeData))) {
|
||||
+ REPORTER_ASSERT(reporter, false, "Failed to read initial strike data");
|
||||
+ }
|
||||
+
|
||||
+ // 2. Prepare and Execute DrawSlug ops
|
||||
+ SkPaint paint;
|
||||
+ for (int idx = 0; idx < 1; ++idx) {
|
||||
+// for (int idx = 0; idx < 2; ++idx) {
|
||||
+ if (idx == 0) {
|
||||
+ kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kARGB;
|
||||
+ } else if (idx == 1) {
|
||||
+ kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kA8;
|
||||
+ }
|
||||
+ kDrawSlugOp[0xd8] = SkMask::kARGB32_Format;
|
||||
+ kDrawSlugOp[0xe0] = 0x99;
|
||||
+
|
||||
+ auto slug = client.deserializeSlugForTest(kDrawSlugOp, sizeof(kDrawSlugOp));
|
||||
+ if (slug) {
|
||||
+ slug->draw(canvas, paint);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
+
|
||||
+#if defined(SK_GANESH)
|
||||
+DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_ganesh, reporter, ctxInfo, CtsEnforcement::kNextRelease) {
|
||||
+ auto dContext = ctxInfo.directContext();
|
||||
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
|
||||
+ auto surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
|
||||
+ if (!surface) return;
|
||||
+ auto canvas = surface->getCanvas();
|
||||
+
|
||||
+ run_atlas_oob_test(reporter, canvas);
|
||||
+
|
||||
+ dContext->flushAndSubmit();
|
||||
+}
|
||||
+#endif // defined(SK_GANESH)
|
||||
+
|
||||
+#if defined(SK_GRAPHITE)
|
||||
+DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_graphite, reporter, context, CtsEnforcement::kNextRelease) {
|
||||
+ using namespace skgpu::graphite;
|
||||
+ std::unique_ptr<Recorder> recorder = context->makeRecorder();
|
||||
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
|
||||
+ auto surface = SkSurfaces::RenderTarget(recorder.get(), info);
|
||||
+ if (!surface) return;
|
||||
+ auto canvas = surface->getCanvas();
|
||||
+
|
||||
+ run_atlas_oob_test(reporter, canvas);
|
||||
+
|
||||
+ std::unique_ptr<Recording> recording = recorder->snap();
|
||||
+ InsertRecordingInfo recordingInfo;
|
||||
+ recordingInfo.fRecording = recording.get();
|
||||
+ context->insertRecording(recordingInfo);
|
||||
+ context->submit();
|
||||
+}
|
||||
+#endif // defined(SK_GRAPHITE)
|
||||
@@ -1,2 +1 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
cherry-pick-d5b0cb2acffe.patch
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Darius Mercadier <dmercadier@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 12:56:18 +0100
|
||||
Subject: [M144 Merge] [maglev] fix CanElideWriteBarrier Smi recording for phis
|
||||
|
||||
Recording a Tagged use is not enough for 2 reasons:
|
||||
|
||||
* Tagged uses are sometimes ignored, in particular for loop phis
|
||||
where we distinguish in-loop and out-of-loop uses.
|
||||
|
||||
* This Tagged use could only prevent untagging of this specific phi,
|
||||
but none of its inputs. So we could have a Smi phi as input to the
|
||||
current phi which gets untagged and retagged to a non-Smi, all
|
||||
while the current phi doesn't get untagged.
|
||||
|
||||
(cherry picked from commit a54bf5cd45e5b119e2afe6019428e81c3d626fb3)
|
||||
|
||||
Change-Id: I9b3a2ea339f2c9d81dbb74a44425ba55d8c73871
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7604255
|
||||
Auto-Submit: Darius Mercadier <dmercadier@chromium.org>
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Commit-Queue: Darius Mercadier <dmercadier@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#105444}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7659106
|
||||
Auto-Submit: Srinivas Sista <srinivassista@chromium.org>
|
||||
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
|
||||
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
|
||||
Reviewed-by: Deepti Gandluri <gdeepti@chromium.org>
|
||||
Owners-Override: Srinivas Sista <srinivassista@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/14.4@{#64}
|
||||
Cr-Branched-From: 80acc26727d5a34e77dabeebe7c9213ec1bd4768-refs/heads/14.4.258@{#1}
|
||||
Cr-Branched-From: ce7e597e90f6df3fa4b6df224bc613b80c635450-refs/heads/main@{#104020}
|
||||
|
||||
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
|
||||
index e334c256779f224d66e2ec3c9bac2ecd88f6708f..e93c52ee51f3e80ce24af622f74ae3635446f6e9 100644
|
||||
--- a/src/maglev/maglev-graph-builder.cc
|
||||
+++ b/src/maglev/maglev-graph-builder.cc
|
||||
@@ -4458,7 +4458,11 @@ bool MaglevGraphBuilder::CanElideWriteBarrier(ValueNode* object,
|
||||
ValueNode* value) {
|
||||
if (value->Is<RootConstant>() || value->Is<ConsStringMap>()) return true;
|
||||
if (!IsEmptyNodeType(GetType(value)) && CheckType(value, NodeType::kSmi)) {
|
||||
- value->MaybeRecordUseReprHint(UseRepresentation::kTagged);
|
||||
+ if constexpr (SmiValuesAre31Bits()) {
|
||||
+ if (Phi* value_as_phi = value->TryCast<Phi>()) {
|
||||
+ value_as_phi->SetUseRequires31BitValue();
|
||||
+ }
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const args = minimist(process.argv.slice(2), {
|
||||
|
||||
const BASE = path.resolve(__dirname, '../..');
|
||||
|
||||
const ROOT_PACKAGE_JSON = path.resolve(BASE, 'package.json');
|
||||
const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node');
|
||||
const JUNIT_DIR = args.jUnitDir ? path.resolve(args.jUnitDir) : null;
|
||||
const TAP_FILE_NAME = 'test.tap';
|
||||
@@ -38,6 +39,18 @@ const defaultOptions = [
|
||||
'-J'
|
||||
];
|
||||
|
||||
// The root package.json is ESM, which breaks the test runner.
|
||||
// Temporarily change it to CommonJS while running the tests, then
|
||||
// change it back when done.
|
||||
const resetPackageJson = ({ useESM }) => {
|
||||
// This won't always exist in CI.
|
||||
if (!fs.existsSync(ROOT_PACKAGE_JSON)) { return; }
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync(ROOT_PACKAGE_JSON, 'utf-8'));
|
||||
packageJson.type = useESM ? 'module' : 'commonjs';
|
||||
fs.writeFileSync(ROOT_PACKAGE_JSON, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
};
|
||||
|
||||
const getCustomOptions = () => {
|
||||
let customOptions = ['tools/test.py'];
|
||||
|
||||
@@ -79,6 +92,8 @@ async function main () {
|
||||
|
||||
const options = args.default ? defaultOptions : getCustomOptions();
|
||||
|
||||
resetPackageJson({ useESM: false });
|
||||
|
||||
const testChild = cp.spawn('python3', options, {
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -88,7 +103,10 @@ async function main () {
|
||||
cwd: NODE_DIR,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
testChild.on('exit', (testCode) => {
|
||||
resetPackageJson({ useESM: true });
|
||||
|
||||
if (JUNIT_DIR) {
|
||||
fs.mkdirSync(JUNIT_DIR);
|
||||
const converterStream = require('tap-xunit')();
|
||||
|
||||
@@ -1017,17 +1017,13 @@ void NativeWindowViews::MoveTop() {
|
||||
|
||||
bool NativeWindowViews::CanResize() const {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
return resizable_ && thick_frame_;
|
||||
return has_frame() ? resizable_ && thick_frame_ : resizable_;
|
||||
#else
|
||||
return resizable_;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsResizable() const {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (has_frame())
|
||||
return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME;
|
||||
#endif
|
||||
return CanResize();
|
||||
}
|
||||
|
||||
|
||||
@@ -5550,7 +5550,7 @@ describe('BrowserWindow module', () => {
|
||||
thickFrame: true,
|
||||
transparent: true
|
||||
});
|
||||
expect(w.isResizable()).to.be.false('resizable');
|
||||
expect(w.isResizable()).to.be.true('resizable');
|
||||
w.maximize();
|
||||
expect(w.isMaximized()).to.be.true('maximized');
|
||||
const bounds = w.getBounds();
|
||||
|
||||
@@ -407,6 +407,41 @@ describe('asar package', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('fs.cpSync', function () {
|
||||
itremote('copies a normal file', function () {
|
||||
if (!fs.cpSync) return;
|
||||
const p = path.join(asarDir, 'a.asar', 'file1');
|
||||
const temp = require('temp').track();
|
||||
const dest = temp.path();
|
||||
fs.cpSync(p, dest);
|
||||
expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fs.cp', function () {
|
||||
itremote('copies a normal file', async function () {
|
||||
if (!fs.cp) return;
|
||||
const p = path.join(asarDir, 'a.asar', 'file1');
|
||||
const temp = require('temp').track();
|
||||
const dest = temp.path();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
fs.cp(p, dest, (err) => err ? reject(err) : resolve());
|
||||
});
|
||||
expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fs.promises.cp', function () {
|
||||
itremote('copies a normal file', async function () {
|
||||
if (!fs.promises.cp) return;
|
||||
const p = path.join(asarDir, 'a.asar', 'file1');
|
||||
const temp = require('temp').track();
|
||||
const dest = temp.path();
|
||||
await fs.promises.cp(p, dest);
|
||||
expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fs.lstatSync', function () {
|
||||
itremote('handles path with trailing slash correctly', function () {
|
||||
const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1');
|
||||
|
||||
Reference in New Issue
Block a user