mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9453e8bfe1 | ||
|
|
a4eb213a04 | ||
|
|
81c08e80f6 | ||
|
|
5f630c7de7 | ||
|
|
2dc82ea1f3 | ||
|
|
f57d6f92b6 | ||
|
|
744142fe54 | ||
|
|
b200b8d6c0 | ||
|
|
cdaf0e96b6 | ||
|
|
981df181c1 | ||
|
|
218300e57f | ||
|
|
6ccee512e4 | ||
|
|
b6e4f514d8 | ||
|
|
ade4c00984 | ||
|
|
d8687cfc9d | ||
|
|
2ab4489447 | ||
|
|
ab9b156113 | ||
|
|
35a531953b |
@@ -2,7 +2,7 @@ version: '3'
|
||||
|
||||
services:
|
||||
buildtools:
|
||||
image: ghcr.io/electron/devcontainer:933c7d6ff6802706875270bec2e3c891cf8add3f
|
||||
image: ghcr.io/electron/devcontainer:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
|
||||
volumes:
|
||||
- ..:/workspaces/gclient/src/electron:cached
|
||||
|
||||
@@ -15,7 +15,7 @@ runs:
|
||||
git config --global core.preloadindex true
|
||||
git config --global core.longpaths true
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=a5d9f9052dcc36ee88bef5c8b13acbefd87b7d8d
|
||||
export BUILD_TOOLS_SHA=4430e4a505e0f4fa2a41b707a10a36f780bbdd26
|
||||
npm i -g @electron/build-tools
|
||||
# Update depot_tools to ensure python
|
||||
e d update_depot_tools
|
||||
|
||||
6
.github/workflows/build-git-cache.yml
vendored
6
.github/workflows/build-git-cache.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
||||
volumes:
|
||||
- /mnt/win-cache:/mnt/win-cache
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
# This job updates the same git cache as linux, so it needs to run after the linux one.
|
||||
needs: build-git-cache-linux
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
skip-macos:
|
||||
type: boolean
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
id: set-output
|
||||
run: |
|
||||
if [ -z "${{ inputs.build-image-sha }}" ]; then
|
||||
echo "build-image-sha=933c7d6ff6802706875270bec2e3c891cf8add3f" >> "$GITHUB_OUTPUT"
|
||||
echo "build-image-sha=a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
2
.github/workflows/linux-publish.yml
vendored
2
.github/workflows/linux-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
required: false
|
||||
|
||||
2
.github/workflows/macos-publish.yml
vendored
2
.github/workflows/macos-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
|
||||
2
.github/workflows/windows-publish.yml
vendored
2
.github/workflows/windows-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'142.0.7444.235',
|
||||
'142.0.7444.265',
|
||||
'node_version':
|
||||
'v22.21.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -102,9 +102,9 @@
|
||||
* `trafficLightPosition` [Point](point.md) (optional) _macOS_ -
|
||||
Set a custom position for the traffic light buttons in frameless windows.
|
||||
* `roundedCorners` boolean (optional) _macOS_ _Windows_ - Whether frameless window
|
||||
should have rounded corners. Default is `true`. Setting this property
|
||||
to `false` will prevent the window from being fullscreenable on macOS.
|
||||
On Windows versions older than Windows 11 Build 22000 this property has no effect, and frameless windows will not have rounded corners.
|
||||
should have rounded corners. Default is `true`. On Windows versions older than
|
||||
Windows 11 Build 22000 this property has no effect, and frameless windows will
|
||||
not have rounded corners.
|
||||
* `thickFrame` boolean (optional) _Windows_ - Use `WS_THICKFRAME` style for
|
||||
frameless windows on Windows, which adds the standard window frame. Setting it
|
||||
to `false` will remove window shadow and window animations, and disable window
|
||||
|
||||
@@ -2410,7 +2410,8 @@ A [`NavigationHistory`](navigation-history.md) used by this webContents.
|
||||
|
||||
#### `contents.hostWebContents` _Readonly_
|
||||
|
||||
A [`WebContents`](web-contents.md) instance that might own this `WebContents`.
|
||||
A `WebContents | null` property that represents a [`WebContents`](web-contents.md)
|
||||
instance that might own this `WebContents`.
|
||||
|
||||
#### `contents.devToolsWebContents` _Readonly_
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"name": "@electron-ci/dev-root",
|
||||
"version": "0.0.0-development",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
@@ -25,7 +25,6 @@
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
"check-for-leaks": "^1.2.1",
|
||||
"dugite": "^2.7.1",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
@@ -148,9 +147,6 @@
|
||||
"dependenciesMeta": {
|
||||
"abstract-socket": {
|
||||
"built": true
|
||||
},
|
||||
"dugite": {
|
||||
"built": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
turboshaft_avoid_introducing_too_many_variables.patch
|
||||
runtime_setprototypeproperties_handling_of.patch
|
||||
runtime_correcting_setprototypeproperties.patch
|
||||
reduce_stack_memory_consumption_in_bytecodegenerator.patch
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jakob Kummerow <jkummerow@chromium.org>
|
||||
Date: Fri, 21 Nov 2025 11:52:15 +0100
|
||||
Subject: Reduce stack memory consumption in BytecodeGenerator
|
||||
|
||||
Using a SmallVector with 64 stack-allocated entries is not great
|
||||
for functions that can be used in deep recursions, so this patch
|
||||
replaces that with a ZoneVector.
|
||||
By allocating any backing store only when it's actually needed,
|
||||
and presizing that initial backing store when that happens, this
|
||||
should have similar performance while using a lot less stack space,
|
||||
allowing us to compile more deeply nested expressions.
|
||||
|
||||
For the highly artificial example of a function full of nested
|
||||
empty blocks `function f() {{{{{...}}}}}`, this increases the max
|
||||
nesting level from around 670 to around 2600. With more aggressive
|
||||
inlining in official builds, it can have similar effects on patterns
|
||||
that don't even call `VisitStatements()` recursively.
|
||||
|
||||
Bug: 429332174
|
||||
Change-Id: Id11e15ba0fd9bc39efa68bf83e1181fe0e7274a7
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7180480
|
||||
Reviewed-by: Raphael Herouart <rherouart@chromium.org>
|
||||
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#103870}
|
||||
|
||||
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
||||
index 63400abcc7ca0bd5369d566dd37e2ae93d7c3efe..a1cf142edc4c0224731ebab988bcbf9272acd57e 100644
|
||||
--- a/src/interpreter/bytecode-generator.cc
|
||||
+++ b/src/interpreter/bytecode-generator.cc
|
||||
@@ -2306,10 +2306,7 @@ void BytecodeGenerator::VisitDeclarations(Declaration::List* declarations) {
|
||||
// The subsequent ones must be about the same <var> to return true.
|
||||
|
||||
bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
- Statement* stmt, Variable** var, HoleCheckMode* hole_check_mode,
|
||||
- SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
- std::unordered_set<const AstRawString*>& duplicates) {
|
||||
+ Statement* stmt, std::unique_ptr<PrototypeAssignments>* assignments) {
|
||||
// The expression Statement is an assignment
|
||||
// ========================================
|
||||
ExpressionStatement* expr_stmt = stmt->AsExpressionStatement();
|
||||
@@ -2381,41 +2378,46 @@ bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (!duplicates.insert(prop_str).second) {
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- if (*var == nullptr) {
|
||||
- // This is the first proto assignment in the sequence
|
||||
- *var = tmp_var;
|
||||
- *hole_check_mode = proto_prop->obj()->AsVariableProxy()->hole_check_mode();
|
||||
- } else if (*var != tmp_var) {
|
||||
- // This prototype assignment is about another var
|
||||
- return false;
|
||||
+ if (!*assignments) {
|
||||
+ // This is the first prototype assignment in the sequence.
|
||||
+ *assignments = std::make_unique<PrototypeAssignments>(
|
||||
+ tmp_var, proto_prop->obj()->AsVariableProxy()->hole_check_mode(),
|
||||
+ ZoneVector<PrototypeAssignment>(zone()));
|
||||
+ (*assignments)->properties.reserve(8);
|
||||
+ (*assignments)->duplicates.insert(prop_str);
|
||||
+ } else {
|
||||
+ if (!(*assignments)->duplicates.insert(prop_str).second) return false;
|
||||
+ if ((*assignments)->var != tmp_var) {
|
||||
+ // This prototype assignment is about another var.
|
||||
+ return false;
|
||||
+ }
|
||||
+ DCHECK_EQ((*assignments)->hole_check_mode,
|
||||
+ proto_prop->obj()->AsVariableProxy()->hole_check_mode());
|
||||
}
|
||||
|
||||
- // Success
|
||||
- properties.push_back(std::make_pair(
|
||||
- prop_str,
|
||||
- value)); // This will be reused as part of an ObjectLiteral
|
||||
+ // Success!
|
||||
+ (*assignments)
|
||||
+ ->properties.push_back(std::make_pair(
|
||||
+ prop_str,
|
||||
+ value)); // This will be reused as part of an ObjectLiteral.
|
||||
|
||||
- DCHECK_EQ(*hole_check_mode,
|
||||
- proto_prop->obj()->AsVariableProxy()->hole_check_mode());
|
||||
return true;
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitConsecutivePrototypeAssignments(
|
||||
- const SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
- Variable* var, HoleCheckMode hole_check_mode) {
|
||||
+ std::unique_ptr<PrototypeAssignments> assignments) {
|
||||
// Create a boiler plate object in the constant pool to be merged into the
|
||||
- // proto
|
||||
+ // proto.
|
||||
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
|
||||
- proto_assign_seq_.push_back(std::make_pair(
|
||||
- zone()->New<ProtoAssignmentSeqBuilder>(properties), entry));
|
||||
+ proto_assign_seq_.push_back(
|
||||
+ std::make_pair(zone()->New<ProtoAssignmentSeqBuilder>(
|
||||
+ std::move(assignments->properties)),
|
||||
+ entry));
|
||||
+ const ZoneVector<PrototypeAssignment>* props =
|
||||
+ proto_assign_seq_.back().first->properties();
|
||||
|
||||
int first_idx = -1;
|
||||
- for (auto& p : properties) {
|
||||
+ for (auto& p : *props) {
|
||||
auto func = p.second->AsFunctionLiteral();
|
||||
if (func) {
|
||||
int idx = GetNewClosureSlot(func);
|
||||
@@ -2426,13 +2428,13 @@ void BytecodeGenerator::VisitConsecutivePrototypeAssignments(
|
||||
}
|
||||
}
|
||||
|
||||
- // We need it to be valid, even if unused
|
||||
+ // We need {first_idx} to be valid, even if it's unused.
|
||||
if (first_idx == -1) {
|
||||
first_idx = 0;
|
||||
}
|
||||
- // Load the variable whose prototype is to be set into the Accumulator
|
||||
- BuildVariableLoad(var, hole_check_mode);
|
||||
- // Merge in-place proto-def boilerplate object into Accumulator
|
||||
+ // Load the variable whose prototype is to be set into the Accumulator.
|
||||
+ BuildVariableLoad(assignments->var, assignments->hole_check_mode);
|
||||
+ // Merge in-place proto-def boilerplate object into the Accumulator.
|
||||
builder()->SetPrototypeProperties(entry, first_idx);
|
||||
}
|
||||
|
||||
@@ -2440,25 +2442,23 @@ void BytecodeGenerator::VisitStatements(
|
||||
const ZonePtrList<Statement>* statements, int start) {
|
||||
for (int stmt_idx = start; stmt_idx < statements->length(); stmt_idx++) {
|
||||
if (v8_flags.proto_assign_seq_opt) {
|
||||
- Variable* var = nullptr;
|
||||
- HoleCheckMode hole_check_mode;
|
||||
-
|
||||
int proto_assign_idx = stmt_idx;
|
||||
- SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>
|
||||
- properties(zone());
|
||||
- std::unordered_set<const AstRawString*> duplicates;
|
||||
+ // {VisitStatements} can be used for deep recursions, so this is a
|
||||
+ // stack-friendly design: statically we only need one {unique_ptr}, and
|
||||
+ // the actual storage is heap-allocated when it is needed.
|
||||
+ std::unique_ptr<PrototypeAssignments> assignments;
|
||||
while (proto_assign_idx < statements->length() &&
|
||||
- IsPrototypeAssignment(statements->at(proto_assign_idx), &var,
|
||||
- &hole_check_mode, properties, duplicates)) {
|
||||
+ IsPrototypeAssignment(statements->at(proto_assign_idx),
|
||||
+ &assignments)) {
|
||||
++proto_assign_idx;
|
||||
}
|
||||
|
||||
if (proto_assign_idx - stmt_idx > 1) {
|
||||
- DCHECK_EQ((size_t)(proto_assign_idx - stmt_idx), properties.size());
|
||||
- VisitConsecutivePrototypeAssignments(properties, var, hole_check_mode);
|
||||
- stmt_idx = proto_assign_idx - 1; // the outer loop should now ignore
|
||||
- // these statements
|
||||
+ DCHECK_EQ(static_cast<size_t>(proto_assign_idx - stmt_idx),
|
||||
+ assignments->properties.size());
|
||||
+ VisitConsecutivePrototypeAssignments(std::move(assignments));
|
||||
+ stmt_idx = proto_assign_idx - 1; // The outer loop should now ignore
|
||||
+ // these statements.
|
||||
DCHECK(!builder()->RemainderOfBlockIsDead());
|
||||
continue;
|
||||
}
|
||||
diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h
|
||||
index 5dd136a01848aaf4a182dd31aa05a6ebe845fae8..b2e4cf6d40c3cd66042f5f52533b4883ff159418 100644
|
||||
--- a/src/interpreter/bytecode-generator.h
|
||||
+++ b/src/interpreter/bytecode-generator.h
|
||||
@@ -30,6 +30,13 @@ class LoopBuilder;
|
||||
class BlockCoverageBuilder;
|
||||
class BytecodeJumpTable;
|
||||
|
||||
+struct PrototypeAssignments {
|
||||
+ Variable* var;
|
||||
+ HoleCheckMode hole_check_mode;
|
||||
+ ZoneVector<PrototypeAssignment> properties;
|
||||
+ std::unordered_set<const AstRawString*> duplicates;
|
||||
+};
|
||||
+
|
||||
class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
public:
|
||||
enum TypeHint : uint8_t {
|
||||
@@ -69,18 +76,12 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
#define DECLARE_VISIT(type) void Visit##type(type* node);
|
||||
AST_NODE_LIST(DECLARE_VISIT)
|
||||
#undef DECLARE_VISIT
|
||||
- static constexpr int kInitialPropertyCount = 64;
|
||||
|
||||
bool IsPrototypeAssignment(
|
||||
- Statement* stmt, Variable** var, HoleCheckMode* hole_check_mode,
|
||||
- SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
- std::unordered_set<const AstRawString*>& duplicate);
|
||||
-
|
||||
- void VisitConsecutivePrototypeAssignments(
|
||||
- const SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
- Variable* var, HoleCheckMode hole_check_mode);
|
||||
+ Statement* stmt, std::unique_ptr<PrototypeAssignments>* assignments);
|
||||
+ V8_NOINLINE void VisitConsecutivePrototypeAssignments(
|
||||
+ std::unique_ptr<PrototypeAssignments> assignments);
|
||||
+
|
||||
// Visiting function for declarations list and statements are overridden.
|
||||
void VisitModuleDeclarations(Declaration::List* declarations);
|
||||
void VisitGlobalDeclarations(Declaration::List* declarations);
|
||||
diff --git a/src/interpreter/prototype-assignment-sequence-builder.cc b/src/interpreter/prototype-assignment-sequence-builder.cc
|
||||
index a7c05b6e8555fde41ba6a81523437fd03b16c3e6..fb427c825c2753d3e12f557279a5230985a1ec44 100644
|
||||
--- a/src/interpreter/prototype-assignment-sequence-builder.cc
|
||||
+++ b/src/interpreter/prototype-assignment-sequence-builder.cc
|
||||
@@ -25,7 +25,7 @@ void ProtoAssignmentSeqBuilder::BuildBoilerplateDescription(
|
||||
|
||||
int position = 0;
|
||||
for (size_t i = 0; i < properties_.size(); i++) {
|
||||
- auto pair = properties_.at(i);
|
||||
+ PrototypeAssignment pair = properties_.at(i);
|
||||
const AstRawString* key_str = pair.first;
|
||||
|
||||
DirectHandle<Object> key = Cast<Object>(key_str->string());
|
||||
diff --git a/src/interpreter/prototype-assignment-sequence-builder.h b/src/interpreter/prototype-assignment-sequence-builder.h
|
||||
index 0c29f4f4bf0843877b2bd568866d8e290d4f3b51..6583022aa43a30df707325e6b7a14891f97a4ef1 100644
|
||||
--- a/src/interpreter/prototype-assignment-sequence-builder.h
|
||||
+++ b/src/interpreter/prototype-assignment-sequence-builder.h
|
||||
@@ -7,17 +7,17 @@
|
||||
|
||||
#include "src/ast/ast.h"
|
||||
#include "src/objects/literal-objects.h"
|
||||
+#include "src/zone/zone-containers.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
+using PrototypeAssignment = std::pair<const AstRawString*, Expression*>;
|
||||
+
|
||||
class ProtoAssignmentSeqBuilder final : public ZoneObject {
|
||||
public:
|
||||
- static constexpr int kInitialPropertyCount = 64;
|
||||
-
|
||||
explicit ProtoAssignmentSeqBuilder(
|
||||
- const SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties)
|
||||
+ ZoneVector<PrototypeAssignment>&& properties)
|
||||
: properties_(std::move(properties)) {}
|
||||
|
||||
~ProtoAssignmentSeqBuilder() = default;
|
||||
@@ -53,10 +53,10 @@ class ProtoAssignmentSeqBuilder final : public ZoneObject {
|
||||
IsolateT* isolate,
|
||||
Handle<Script> script);
|
||||
|
||||
+ const ZoneVector<PrototypeAssignment>* properties() { return &properties_; }
|
||||
+
|
||||
private:
|
||||
- SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>
|
||||
- properties_;
|
||||
+ ZoneVector<PrototypeAssignment> properties_;
|
||||
IndirectHandle<ObjectBoilerplateDescription> boilerplate_description_;
|
||||
};
|
||||
|
||||
148
patches/v8/runtime_correcting_setprototypeproperties.patch
Normal file
148
patches/v8/runtime_correcting_setprototypeproperties.patch
Normal file
@@ -0,0 +1,148 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Raphael Herouart <rherouart@chromium.org>
|
||||
Date: Tue, 18 Nov 2025 11:00:26 +0000
|
||||
Subject: [runtime] Correcting SetPrototypeProperties:
|
||||
|
||||
- Off by One Error in code generation
|
||||
- Attributes should be preserved when setting properties
|
||||
- SmallVector Memory Leak
|
||||
|
||||
Bug: 429332174
|
||||
Change-Id: I8a669a98f44777ad54ea7e7bb06fc4f8c552c865
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7160576
|
||||
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
|
||||
Commit-Queue: Raphael Herouart <rherouart@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#103784}
|
||||
|
||||
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
||||
index bf874d94ec34056d499fea9cbd034f959d506b58..63400abcc7ca0bd5369d566dd37e2ae93d7c3efe 100644
|
||||
--- a/src/interpreter/bytecode-generator.cc
|
||||
+++ b/src/interpreter/bytecode-generator.cc
|
||||
@@ -2307,8 +2307,8 @@ void BytecodeGenerator::VisitDeclarations(Declaration::List* declarations) {
|
||||
|
||||
bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
Statement* stmt, Variable** var, HoleCheckMode* hole_check_mode,
|
||||
- base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
+ SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>& properties,
|
||||
std::unordered_set<const AstRawString*>& duplicates) {
|
||||
// The expression Statement is an assignment
|
||||
// ========================================
|
||||
@@ -2405,8 +2405,8 @@ bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitConsecutivePrototypeAssignments(
|
||||
- const base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
+ const SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>& properties,
|
||||
Variable* var, HoleCheckMode hole_check_mode) {
|
||||
// Create a boiler plate object in the constant pool to be merged into the
|
||||
// proto
|
||||
@@ -2444,9 +2444,9 @@ void BytecodeGenerator::VisitStatements(
|
||||
HoleCheckMode hole_check_mode;
|
||||
|
||||
int proto_assign_idx = stmt_idx;
|
||||
- base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>
|
||||
- properties;
|
||||
+ SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>
|
||||
+ properties(zone());
|
||||
std::unordered_set<const AstRawString*> duplicates;
|
||||
while (proto_assign_idx < statements->length() &&
|
||||
IsPrototypeAssignment(statements->at(proto_assign_idx), &var,
|
||||
@@ -2457,10 +2457,10 @@ void BytecodeGenerator::VisitStatements(
|
||||
if (proto_assign_idx - stmt_idx > 1) {
|
||||
DCHECK_EQ((size_t)(proto_assign_idx - stmt_idx), properties.size());
|
||||
VisitConsecutivePrototypeAssignments(properties, var, hole_check_mode);
|
||||
- stmt_idx = proto_assign_idx; // the outer loop should now ignore these
|
||||
- // statements
|
||||
+ stmt_idx = proto_assign_idx - 1; // the outer loop should now ignore
|
||||
+ // these statements
|
||||
DCHECK(!builder()->RemainderOfBlockIsDead());
|
||||
- if (stmt_idx == statements->length()) break;
|
||||
+ continue;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h
|
||||
index 41c5cda1ca6dd4017b329e41bad8af3bcc4ee242..5dd136a01848aaf4a182dd31aa05a6ebe845fae8 100644
|
||||
--- a/src/interpreter/bytecode-generator.h
|
||||
+++ b/src/interpreter/bytecode-generator.h
|
||||
@@ -73,13 +73,13 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
|
||||
bool IsPrototypeAssignment(
|
||||
Statement* stmt, Variable** var, HoleCheckMode* hole_check_mode,
|
||||
- base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
+ SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>& properties,
|
||||
std::unordered_set<const AstRawString*>& duplicate);
|
||||
|
||||
void VisitConsecutivePrototypeAssignments(
|
||||
- const base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties,
|
||||
+ const SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>& properties,
|
||||
Variable* var, HoleCheckMode hole_check_mode);
|
||||
// Visiting function for declarations list and statements are overridden.
|
||||
void VisitModuleDeclarations(Declaration::List* declarations);
|
||||
diff --git a/src/interpreter/prototype-assignment-sequence-builder.h b/src/interpreter/prototype-assignment-sequence-builder.h
|
||||
index 880ed4dcec8399e9b52e6d1924b8773bff1db063..0c29f4f4bf0843877b2bd568866d8e290d4f3b51 100644
|
||||
--- a/src/interpreter/prototype-assignment-sequence-builder.h
|
||||
+++ b/src/interpreter/prototype-assignment-sequence-builder.h
|
||||
@@ -16,8 +16,8 @@ class ProtoAssignmentSeqBuilder final : public ZoneObject {
|
||||
static constexpr int kInitialPropertyCount = 64;
|
||||
|
||||
explicit ProtoAssignmentSeqBuilder(
|
||||
- const base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>& properties)
|
||||
+ const SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>& properties)
|
||||
: properties_(std::move(properties)) {}
|
||||
|
||||
~ProtoAssignmentSeqBuilder() = default;
|
||||
@@ -54,8 +54,8 @@ class ProtoAssignmentSeqBuilder final : public ZoneObject {
|
||||
Handle<Script> script);
|
||||
|
||||
private:
|
||||
- base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
- kInitialPropertyCount>
|
||||
+ SmallZoneVector<std::pair<const AstRawString*, Expression*>,
|
||||
+ kInitialPropertyCount>
|
||||
properties_;
|
||||
IndirectHandle<ObjectBoilerplateDescription> boilerplate_description_;
|
||||
};
|
||||
diff --git a/src/runtime/runtime-literals.cc b/src/runtime/runtime-literals.cc
|
||||
index ded3b466625c99da6f27936ae4e5e0c879baec1b..961b9ee5b7d329bb018300fe2f18bea60e1cd883 100644
|
||||
--- a/src/runtime/runtime-literals.cc
|
||||
+++ b/src/runtime/runtime-literals.cc
|
||||
@@ -730,6 +730,13 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
|
||||
feedback_cell_array, current_slot));
|
||||
}
|
||||
|
||||
+ if (IsSpecialReceiverMap(js_proto->map())) {
|
||||
+ RETURN_RESULT_OR_FAILURE(
|
||||
+ isolate, SetPrototypePropertiesSlow(isolate, context, obj,
|
||||
+ object_boilerplate_description,
|
||||
+ feedback_cell_array, current_slot));
|
||||
+ }
|
||||
+
|
||||
bool is_default_func_prototype =
|
||||
IsDefaultFunctionPrototype(js_proto, isolate);
|
||||
|
||||
@@ -782,7 +789,11 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
|
||||
value = InstantiateIfSharedFunctionInfo(
|
||||
context, isolate, value, feedback_cell_array, current_slot);
|
||||
DirectHandle<String> name = Cast<String>(key);
|
||||
- JSObject::SetOwnPropertyIgnoreAttributes(js_proto, name, value, NONE)
|
||||
+ Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
|
||||
+ PropertyAttributes attr = maybe.ToChecked();
|
||||
+ if (attr == ABSENT) attr = NONE;
|
||||
+
|
||||
+ JSObject::SetOwnPropertyIgnoreAttributes(js_proto, name, value, attr)
|
||||
.Check();
|
||||
|
||||
result = value;
|
||||
153
patches/v8/runtime_setprototypeproperties_handling_of.patch
Normal file
153
patches/v8/runtime_setprototypeproperties_handling_of.patch
Normal file
@@ -0,0 +1,153 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Raphael Herouart <rherouart@chromium.org>
|
||||
Date: Tue, 21 Oct 2025 14:49:06 +0000
|
||||
Subject: [runtime] SetPrototypeProperties handling of
|
||||
Hole/Undefined/Non-Object Values
|
||||
|
||||
Bug: 451750214
|
||||
Change-Id: Ida29afe0b92ed4a6acff97214005ed3589093294
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7062734
|
||||
Commit-Queue: Raphael Herouart <rherouart@chromium.org>
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#103261}
|
||||
|
||||
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
||||
index 6d444622bacbb71c4754bc055da5cae39ed213b7..bf874d94ec34056d499fea9cbd034f959d506b58 100644
|
||||
--- a/src/interpreter/bytecode-generator.cc
|
||||
+++ b/src/interpreter/bytecode-generator.cc
|
||||
@@ -2306,7 +2306,7 @@ void BytecodeGenerator::VisitDeclarations(Declaration::List* declarations) {
|
||||
// The subsequent ones must be about the same <var> to return true.
|
||||
|
||||
bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
- Statement* stmt, Variable** var,
|
||||
+ Statement* stmt, Variable** var, HoleCheckMode* hole_check_mode,
|
||||
base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
kInitialPropertyCount>& properties,
|
||||
std::unordered_set<const AstRawString*>& duplicates) {
|
||||
@@ -2388,6 +2388,7 @@ bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
if (*var == nullptr) {
|
||||
// This is the first proto assignment in the sequence
|
||||
*var = tmp_var;
|
||||
+ *hole_check_mode = proto_prop->obj()->AsVariableProxy()->hole_check_mode();
|
||||
} else if (*var != tmp_var) {
|
||||
// This prototype assignment is about another var
|
||||
return false;
|
||||
@@ -2398,13 +2399,15 @@ bool BytecodeGenerator::IsPrototypeAssignment(
|
||||
prop_str,
|
||||
value)); // This will be reused as part of an ObjectLiteral
|
||||
|
||||
+ DCHECK_EQ(*hole_check_mode,
|
||||
+ proto_prop->obj()->AsVariableProxy()->hole_check_mode());
|
||||
return true;
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitConsecutivePrototypeAssignments(
|
||||
const base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
kInitialPropertyCount>& properties,
|
||||
- Variable* var) {
|
||||
+ Variable* var, HoleCheckMode hole_check_mode) {
|
||||
// Create a boiler plate object in the constant pool to be merged into the
|
||||
// proto
|
||||
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
|
||||
@@ -2428,7 +2431,7 @@ void BytecodeGenerator::VisitConsecutivePrototypeAssignments(
|
||||
first_idx = 0;
|
||||
}
|
||||
// Load the variable whose prototype is to be set into the Accumulator
|
||||
- BuildVariableLoad(var, HoleCheckMode::kElided);
|
||||
+ BuildVariableLoad(var, hole_check_mode);
|
||||
// Merge in-place proto-def boilerplate object into Accumulator
|
||||
builder()->SetPrototypeProperties(entry, first_idx);
|
||||
}
|
||||
@@ -2438,6 +2441,8 @@ void BytecodeGenerator::VisitStatements(
|
||||
for (int stmt_idx = start; stmt_idx < statements->length(); stmt_idx++) {
|
||||
if (v8_flags.proto_assign_seq_opt) {
|
||||
Variable* var = nullptr;
|
||||
+ HoleCheckMode hole_check_mode;
|
||||
+
|
||||
int proto_assign_idx = stmt_idx;
|
||||
base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
kInitialPropertyCount>
|
||||
@@ -2445,13 +2450,13 @@ void BytecodeGenerator::VisitStatements(
|
||||
std::unordered_set<const AstRawString*> duplicates;
|
||||
while (proto_assign_idx < statements->length() &&
|
||||
IsPrototypeAssignment(statements->at(proto_assign_idx), &var,
|
||||
- properties, duplicates)) {
|
||||
+ &hole_check_mode, properties, duplicates)) {
|
||||
++proto_assign_idx;
|
||||
}
|
||||
|
||||
if (proto_assign_idx - stmt_idx > 1) {
|
||||
DCHECK_EQ((size_t)(proto_assign_idx - stmt_idx), properties.size());
|
||||
- VisitConsecutivePrototypeAssignments(properties, var);
|
||||
+ VisitConsecutivePrototypeAssignments(properties, var, hole_check_mode);
|
||||
stmt_idx = proto_assign_idx; // the outer loop should now ignore these
|
||||
// statements
|
||||
DCHECK(!builder()->RemainderOfBlockIsDead());
|
||||
diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h
|
||||
index b6421819c353febe283069852645a6b85e4c855a..41c5cda1ca6dd4017b329e41bad8af3bcc4ee242 100644
|
||||
--- a/src/interpreter/bytecode-generator.h
|
||||
+++ b/src/interpreter/bytecode-generator.h
|
||||
@@ -72,7 +72,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
static constexpr int kInitialPropertyCount = 64;
|
||||
|
||||
bool IsPrototypeAssignment(
|
||||
- Statement* stmt, Variable** var,
|
||||
+ Statement* stmt, Variable** var, HoleCheckMode* hole_check_mode,
|
||||
base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
kInitialPropertyCount>& properties,
|
||||
std::unordered_set<const AstRawString*>& duplicate);
|
||||
@@ -80,7 +80,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void VisitConsecutivePrototypeAssignments(
|
||||
const base::SmallVector<std::pair<const AstRawString*, Expression*>,
|
||||
kInitialPropertyCount>& properties,
|
||||
- Variable* var);
|
||||
+ Variable* var, HoleCheckMode hole_check_mode);
|
||||
// Visiting function for declarations list and statements are overridden.
|
||||
void VisitModuleDeclarations(Declaration::List* declarations);
|
||||
void VisitGlobalDeclarations(Declaration::List* declarations);
|
||||
diff --git a/src/runtime/runtime-literals.cc b/src/runtime/runtime-literals.cc
|
||||
index 83074253314c438eed3006332c63c6bf7dfe4421..ded3b466625c99da6f27936ae4e5e0c879baec1b 100644
|
||||
--- a/src/runtime/runtime-literals.cc
|
||||
+++ b/src/runtime/runtime-literals.cc
|
||||
@@ -613,7 +613,7 @@ static DirectHandle<Object> InstantiateIfSharedFunctionInfo(
|
||||
}
|
||||
|
||||
static MaybeDirectHandle<Object> SetPrototypePropertiesSlow(
|
||||
- Isolate* isolate, DirectHandle<Context> context, DirectHandle<JSObject> obj,
|
||||
+ Isolate* isolate, DirectHandle<Context> context, DirectHandle<JSAny> obj,
|
||||
Handle<ObjectBoilerplateDescription> object_boilerplate_description,
|
||||
DirectHandle<ClosureFeedbackCellArray> feedback_cell_array,
|
||||
int& current_slot, int start_index = 0) {
|
||||
@@ -684,7 +684,7 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(4, args.length());
|
||||
DirectHandle<Context> context(isolate->context(), isolate);
|
||||
- DirectHandle<JSObject> obj = args.at<JSObject>(0); // acc JS Object
|
||||
+ DirectHandle<JSAny> obj = args.at<JSAny>(0); // acc JS Object
|
||||
Handle<ObjectBoilerplateDescription> object_boilerplate_description =
|
||||
args.at<ObjectBoilerplateDescription>(1);
|
||||
DirectHandle<ClosureFeedbackCellArray> feedback_cell_array =
|
||||
diff --git a/test/unittests/interpreter/bytecode_expectations/SetPrototypePropertiesOptimization.golden b/test/unittests/interpreter/bytecode_expectations/SetPrototypePropertiesOptimization.golden
|
||||
index 84ebbb755f8a5432c2e8da3d26ceb648bee2cf2d..bb9d697caec6e79810fa757abec4b71ad4cf3f2f 100644
|
||||
--- a/test/unittests/interpreter/bytecode_expectations/SetPrototypePropertiesOptimization.golden
|
||||
+++ b/test/unittests/interpreter/bytecode_expectations/SetPrototypePropertiesOptimization.golden
|
||||
@@ -50,15 +50,17 @@ snippet: "
|
||||
"
|
||||
frame size: 0
|
||||
parameter count: 1
|
||||
-bytecode array length: 7
|
||||
+bytecode array length: 9
|
||||
bytecodes: [
|
||||
B(LdaImmutableCurrentContextSlot), U8(2),
|
||||
- /* 109 E> */ B(SetPrototypeProperties), U8(0), U8(0),
|
||||
+ /* 109 E> */ B(ThrowReferenceErrorIfHole), U8(1),
|
||||
+ B(SetPrototypeProperties), U8(0), U8(0),
|
||||
B(LdaUndefined),
|
||||
/* 245 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
OBJECT_BOILERPLATE_DESCRIPTION_TYPE,
|
||||
+ INTERNALIZED_ONE_BYTE_STRING_TYPE ["MyClass"],
|
||||
]
|
||||
handlers: [
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
const chalk = require('chalk');
|
||||
const { GitProcess } = require('dugite');
|
||||
|
||||
const childProcess = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
@@ -61,13 +61,16 @@ function getAbsoluteElectronExec () {
|
||||
return path.resolve(SRC_DIR, getElectronExec());
|
||||
}
|
||||
|
||||
async function handleGitCall (args, gitDir) {
|
||||
const details = await GitProcess.exec(args, gitDir);
|
||||
if (details.exitCode === 0) {
|
||||
return details.stdout.replace(/^\*|\s+|\s+$/, '');
|
||||
function handleGitCall (args, gitDir) {
|
||||
const result = childProcess.spawnSync('git', args, {
|
||||
cwd: gitDir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (result.status === 0) {
|
||||
return result.stdout.replace(/^\*|\s+|\s+$/, '');
|
||||
} else {
|
||||
const error = GitProcess.parseError(details.stderr);
|
||||
console.log(`${fail} couldn't parse git process call: `, error);
|
||||
console.log(`${fail} couldn't parse git process call: `, result.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { GitProcess } = require('dugite');
|
||||
const { ESLint } = require('eslint');
|
||||
const minimist = require('minimist');
|
||||
|
||||
@@ -431,9 +430,13 @@ function populateLinterWithArgs (linter, opts) {
|
||||
}
|
||||
|
||||
async function findChangedFiles (top) {
|
||||
const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top);
|
||||
if (result.exitCode !== 0) {
|
||||
console.log('Failed to find changed files', GitProcess.parseError(result.stderr));
|
||||
const result = childProcess.spawnSync('git', ['diff', '--name-only', '--cached'], {
|
||||
cwd: top,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
console.log('Failed to find changed files', result.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
const relativePaths = result.stdout.split(/\r\n|\r|\n/g);
|
||||
|
||||
@@ -13,6 +13,7 @@ import { createGitHubTokenStrategy } from '../github-token';
|
||||
import { ELECTRON_ORG, ELECTRON_REPO, ElectronReleaseRepo, NIGHTLY_REPO } from '../types';
|
||||
|
||||
const rootPackageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../../package.json'), 'utf-8'));
|
||||
rootPackageJson.name = 'electron';
|
||||
|
||||
if (!process.env.ELECTRON_NPM_OTP) {
|
||||
console.error('Please set ELECTRON_NPM_OTP');
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { GitProcess } from 'dugite';
|
||||
import { valid, compare, gte, lte } from 'semver';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { basename } from 'node:path';
|
||||
import { parseArgs } from 'node:util';
|
||||
|
||||
@@ -20,8 +20,12 @@ const semverify = (version: string) => version.replace(/^origin\//, '').replace(
|
||||
|
||||
const runGit = async (args: string[]) => {
|
||||
console.info(`Running: git ${args.join(' ')}`);
|
||||
const response = await GitProcess.exec(args, ELECTRON_DIR);
|
||||
if (response.exitCode !== 0) {
|
||||
const response = spawnSync('git', args, {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (response.status !== 0) {
|
||||
throw new Error(response.stderr.trim());
|
||||
}
|
||||
return response.stdout.trim();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { GitProcess } from 'dugite';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { resolve as _resolve } from 'node:path';
|
||||
|
||||
@@ -105,8 +105,13 @@ class Pool {
|
||||
**/
|
||||
|
||||
const runGit = async (dir: string, args: string[]) => {
|
||||
const response = await GitProcess.exec(args, dir);
|
||||
if (response.exitCode !== 0) {
|
||||
const response = spawnSync('git', args, {
|
||||
cwd: dir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
maxBuffer: 100 * 1024 * 1024 // 100MB buffer to handle large git outputs
|
||||
});
|
||||
if (response.status !== 0) {
|
||||
throw new Error(response.stderr.trim());
|
||||
}
|
||||
return response.stdout.trim();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import * as chalk from 'chalk';
|
||||
import { GitProcess } from 'dugite';
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { createGitHubTokenStrategy } from './github-token';
|
||||
@@ -166,11 +165,12 @@ async function createRelease (
|
||||
}
|
||||
|
||||
async function pushRelease (branch: string) {
|
||||
const pushDetails = await GitProcess.exec(
|
||||
['push', 'origin', `HEAD:${branch}`, '--follow-tags'],
|
||||
ELECTRON_DIR
|
||||
);
|
||||
if (pushDetails.exitCode === 0) {
|
||||
const pushDetails = spawnSync('git', ['push', 'origin', `HEAD:${branch}`, '--follow-tags'], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (pushDetails.status === 0) {
|
||||
console.log(
|
||||
`${pass} Successfully pushed the release. Wait for ` +
|
||||
'release builds to finish before running "npm run release".'
|
||||
@@ -191,11 +191,12 @@ async function runReleaseBuilds (branch: string, newVersion: string) {
|
||||
|
||||
async function tagRelease (version: string) {
|
||||
console.log(`Tagging release ${version}.`);
|
||||
const checkoutDetails = await GitProcess.exec(
|
||||
['tag', '-a', '-m', version, version],
|
||||
ELECTRON_DIR
|
||||
);
|
||||
if (checkoutDetails.exitCode === 0) {
|
||||
const checkoutDetails = spawnSync('git', ['tag', '-a', '-m', version, version], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (checkoutDetails.status === 0) {
|
||||
console.log(`${pass} Successfully tagged ${version}.`);
|
||||
} else {
|
||||
console.log(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GitProcess } from 'dugite';
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
import { ELECTRON_DIR } from '../lib/utils';
|
||||
|
||||
export enum PreType {
|
||||
@@ -27,7 +28,11 @@ export const isStable = (v: string) => {
|
||||
|
||||
export async function nextAlpha (v: string) {
|
||||
const next = semver.coerce(semver.clean(v));
|
||||
const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-alpha.*`], ELECTRON_DIR);
|
||||
const tagBlob = spawnSync('git', ['tag', '--list', '-l', `v${next}-alpha.*`], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
const tags = tagBlob.stdout.split('\n').filter(e => e !== '');
|
||||
tags.sort((t1, t2) => {
|
||||
const a = parseInt(t1.split('.').pop()!, 10);
|
||||
@@ -41,7 +46,11 @@ export async function nextAlpha (v: string) {
|
||||
|
||||
export async function nextBeta (v: string) {
|
||||
const next = semver.coerce(semver.clean(v));
|
||||
const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR);
|
||||
const tagBlob = spawnSync('git', ['tag', '--list', '-l', `v${next}-beta.*`], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
const tags = tagBlob.stdout.split('\n').filter(e => e !== '');
|
||||
tags.sort((t1, t2) => {
|
||||
const a = parseInt(t1.split('.').pop()!, 10);
|
||||
@@ -57,7 +66,11 @@ export async function nextNightly (v: string) {
|
||||
let next = semver.valid(semver.coerce(v));
|
||||
const pre = `nightly.${getCurrentDate()}`;
|
||||
|
||||
const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim();
|
||||
const branch = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
}).stdout.trim();
|
||||
if (branch === 'main') {
|
||||
next = semver.inc(await getLastMajorForMain(), 'major');
|
||||
} else if (isStable(v)) {
|
||||
@@ -69,8 +82,12 @@ export async function nextNightly (v: string) {
|
||||
|
||||
async function getLastMajorForMain () {
|
||||
let branchNames;
|
||||
const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR);
|
||||
if (result.exitCode === 0) {
|
||||
const result = spawnSync('git', ['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (result.status === 0) {
|
||||
branchNames = result.stdout.trim().split('\n');
|
||||
const filtered = branchNames.map(b => b.replace('origin/', ''));
|
||||
return getNextReleaseBranch(filtered);
|
||||
|
||||
@@ -114,11 +114,14 @@ async function runClangTidy (
|
||||
outDir: string,
|
||||
filenames: string[],
|
||||
checks: string = '',
|
||||
jobs: number = 1
|
||||
jobs: number = 1,
|
||||
fix: boolean = false
|
||||
): Promise<boolean> {
|
||||
const cmd = path.resolve(LLVM_BIN, 'clang-tidy');
|
||||
const args = [`-p=${outDir}`, '--use-color'];
|
||||
const args = [`-p=${outDir}`];
|
||||
|
||||
if (!process.env.CI) args.push('--use-color');
|
||||
if (fix) args.push('--fix');
|
||||
if (checks) args.push(`--checks=${checks}`);
|
||||
|
||||
// Remove any files that aren't in the compilation database to prevent
|
||||
@@ -209,7 +212,7 @@ function parseCommandLine () {
|
||||
if (!arg || arg.startsWith('-')) {
|
||||
console.log(
|
||||
'Usage: script/run-clang-tidy.ts [-h|--help] [--jobs|-j] ' +
|
||||
'[--checks] --out-dir OUTDIR [file1 file2]'
|
||||
'[--fix] [--checks] --out-dir OUTDIR [file1 file2]'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -218,7 +221,7 @@ function parseCommandLine () {
|
||||
};
|
||||
|
||||
const opts = minimist(process.argv.slice(2), {
|
||||
boolean: ['help'],
|
||||
boolean: ['fix', 'help'],
|
||||
string: ['checks', 'out-dir'],
|
||||
default: { jobs: 1 },
|
||||
alias: { help: 'h', jobs: 'j' },
|
||||
@@ -270,17 +273,23 @@ async function main (): Promise<boolean> {
|
||||
const filenames = [];
|
||||
|
||||
if (opts._.length > 0) {
|
||||
if (opts._.some((filename) => filename.endsWith('.h'))) {
|
||||
throw new ErrorWithExitCode(
|
||||
'Filenames must be for translation units, not headers', 3
|
||||
);
|
||||
}
|
||||
|
||||
filenames.push(...opts._.map((filename) => path.resolve(filename)));
|
||||
} else {
|
||||
filenames.push(
|
||||
...(await findMatchingFiles(
|
||||
path.resolve(SOURCE_ROOT, 'shell'),
|
||||
(filename: string) => /.*\.(?:cc|h|mm)$/.test(filename)
|
||||
(filename: string) => /.*\.(?:cc|mm)$/.test(filename)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
return runClangTidy(outDir, filenames, opts.checks, opts.jobs);
|
||||
return runClangTidy(outDir, filenames, opts.checks, opts.jobs, opts.fix);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@@ -1037,6 +1037,9 @@ void WebContents::InitWithWebContents(
|
||||
}
|
||||
|
||||
WebContents::~WebContents() {
|
||||
if (inspectable_web_contents_)
|
||||
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
||||
|
||||
if (owner_window_) {
|
||||
owner_window_->RemoveBackgroundThrottlingSource(this);
|
||||
}
|
||||
@@ -1051,8 +1054,6 @@ WebContents::~WebContents() {
|
||||
return;
|
||||
}
|
||||
|
||||
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
||||
|
||||
// This event is only for internal use, which is emitted when WebContents is
|
||||
// being destroyed.
|
||||
Emit("will-destroy");
|
||||
@@ -2189,8 +2190,8 @@ void WebContents::DevToolsOpened() {
|
||||
// Inherit owner window in devtools when it doesn't have one.
|
||||
auto* devtools = inspectable_web_contents_->GetDevToolsWebContents();
|
||||
bool has_window = devtools->GetUserData(NativeWindowRelay::UserDataKey());
|
||||
if (owner_window_ && !has_window) {
|
||||
DCHECK(!owner_window_.WasInvalidated());
|
||||
if (owner_window() && !has_window) {
|
||||
CHECK(!owner_window_.WasInvalidated());
|
||||
DCHECK_EQ(handle->owner_window(), nullptr);
|
||||
handle->SetOwnerWindow(devtools, owner_window());
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
#include "shell/browser/api/electron_api_web_request.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "base/containers/fixed_flat_map.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
@@ -25,6 +27,7 @@
|
||||
#include "shell/browser/api/electron_api_web_frame_main.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/login_handler.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/frame_converter.h"
|
||||
#include "shell/common/gin_converters/gurl_converter.h"
|
||||
@@ -108,7 +111,7 @@ v8::Local<v8::Value> HttpResponseHeadersToV8(
|
||||
|
||||
// Overloaded by multiple types to fill the |details| object.
|
||||
void ToDictionary(gin_helper::Dictionary* details,
|
||||
extensions::WebRequestInfo* info) {
|
||||
const extensions::WebRequestInfo* info) {
|
||||
details->Set("id", info->id);
|
||||
details->Set("url", info->url);
|
||||
details->Set("method", info->method);
|
||||
@@ -255,7 +258,7 @@ bool WebRequest::RequestFilter::MatchesType(
|
||||
}
|
||||
|
||||
bool WebRequest::RequestFilter::MatchesRequest(
|
||||
extensions::WebRequestInfo* info) const {
|
||||
const extensions::WebRequestInfo* info) const {
|
||||
// Matches URL and type, and does not match exclude URL.
|
||||
return MatchesURL(info->url, include_url_patterns_) &&
|
||||
!MatchesURL(info->url, exclude_url_patterns_) &&
|
||||
@@ -287,6 +290,10 @@ struct WebRequest::BlockedRequest {
|
||||
net::CompletionOnceCallback callback;
|
||||
// Only used for onBeforeSendHeaders.
|
||||
BeforeSendHeadersCallback before_send_headers_callback;
|
||||
// The callback to invoke for auth. If |auth_callback.is_null()| is false,
|
||||
// |callback| must be NULL.
|
||||
// Only valid for OnAuthRequired.
|
||||
AuthCallback auth_callback;
|
||||
// Only used for onBeforeSendHeaders.
|
||||
raw_ptr<net::HttpRequestHeaders> request_headers = nullptr;
|
||||
// Only used for onHeadersReceived.
|
||||
@@ -297,6 +304,8 @@ struct WebRequest::BlockedRequest {
|
||||
std::string status_line;
|
||||
// Only used for onBeforeRequest.
|
||||
raw_ptr<GURL> new_url = nullptr;
|
||||
// Owns the LoginHandler while waiting for auth credentials.
|
||||
std::unique_ptr<LoginHandler> login_handler;
|
||||
};
|
||||
|
||||
WebRequest::SimpleListenerInfo::SimpleListenerInfo(RequestFilter filter_,
|
||||
@@ -603,6 +612,36 @@ void WebRequest::OnSendHeaders(extensions::WebRequestInfo* info,
|
||||
HandleSimpleEvent(SimpleEvent::kOnSendHeaders, info, request, headers);
|
||||
}
|
||||
|
||||
WebRequest::AuthRequiredResponse WebRequest::OnAuthRequired(
|
||||
const extensions::WebRequestInfo* request_info,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
WebRequest::AuthCallback callback,
|
||||
net::AuthCredentials* credentials) {
|
||||
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
|
||||
request_info->render_process_id, request_info->frame_routing_id);
|
||||
content::WebContents* web_contents = nullptr;
|
||||
if (rfh)
|
||||
web_contents = content::WebContents::FromRenderFrameHost(rfh);
|
||||
|
||||
BlockedRequest blocked_request;
|
||||
blocked_request.auth_callback = std::move(callback);
|
||||
blocked_requests_[request_info->id] = std::move(blocked_request);
|
||||
|
||||
auto login_callback =
|
||||
base::BindOnce(&WebRequest::OnLoginAuthResult, base::Unretained(this),
|
||||
request_info->id, credentials);
|
||||
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers =
|
||||
request_info->response_headers;
|
||||
blocked_requests_[request_info->id].login_handler =
|
||||
std::make_unique<LoginHandler>(
|
||||
auth_info, web_contents,
|
||||
static_cast<base::ProcessId>(request_info->render_process_id),
|
||||
request_info->url, response_headers, std::move(login_callback));
|
||||
|
||||
return AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_IO_PENDING;
|
||||
}
|
||||
|
||||
void WebRequest::OnBeforeRedirect(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const GURL& new_location) {
|
||||
@@ -732,6 +771,26 @@ void WebRequest::HandleSimpleEvent(SimpleEvent event,
|
||||
info.listener.Run(gin::ConvertToV8(isolate, details));
|
||||
}
|
||||
|
||||
void WebRequest::OnLoginAuthResult(
|
||||
uint64_t id,
|
||||
net::AuthCredentials* credentials,
|
||||
const std::optional<net::AuthCredentials>& maybe_creds) {
|
||||
auto iter = blocked_requests_.find(id);
|
||||
if (iter == blocked_requests_.end())
|
||||
NOTREACHED();
|
||||
|
||||
AuthRequiredResponse action =
|
||||
AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_NO_ACTION;
|
||||
if (maybe_creds.has_value()) {
|
||||
*credentials = maybe_creds.value();
|
||||
action = AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_SET_AUTH;
|
||||
}
|
||||
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
|
||||
FROM_HERE, base::BindOnce(std::move(iter->second.auth_callback), action));
|
||||
blocked_requests_.erase(iter);
|
||||
}
|
||||
|
||||
// static
|
||||
gin_helper::Handle<WebRequest> WebRequest::FromOrCreate(
|
||||
v8::Isolate* isolate,
|
||||
|
||||
@@ -82,6 +82,11 @@ class WebRequest final : public gin_helper::DeprecatedWrappable<WebRequest>,
|
||||
void OnSendHeaders(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const net::HttpRequestHeaders& headers) override;
|
||||
AuthRequiredResponse OnAuthRequired(
|
||||
const extensions::WebRequestInfo* info,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
AuthCallback callback,
|
||||
net::AuthCredentials* credentials) override;
|
||||
void OnBeforeRedirect(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const GURL& new_location) override;
|
||||
@@ -157,6 +162,12 @@ class WebRequest final : public gin_helper::DeprecatedWrappable<WebRequest>,
|
||||
v8::Local<v8::Value> response);
|
||||
void OnHeadersReceivedListenerResult(uint64_t id,
|
||||
v8::Local<v8::Value> response);
|
||||
// Callback invoked by LoginHandler when auth credentials are supplied via
|
||||
// the unified 'login' event. Bridges back into WebRequest's AuthCallback.
|
||||
void OnLoginAuthResult(
|
||||
uint64_t id,
|
||||
net::AuthCredentials* credentials,
|
||||
const std::optional<net::AuthCredentials>& maybe_creds);
|
||||
|
||||
class RequestFilter {
|
||||
public:
|
||||
@@ -174,7 +185,7 @@ class WebRequest final : public gin_helper::DeprecatedWrappable<WebRequest>,
|
||||
bool is_match_pattern = true);
|
||||
void AddType(extensions::WebRequestResourceType type);
|
||||
|
||||
bool MatchesRequest(extensions::WebRequestInfo* info) const;
|
||||
bool MatchesRequest(const extensions::WebRequestInfo* info) const;
|
||||
|
||||
private:
|
||||
bool MatchesURL(const GURL& url,
|
||||
|
||||
@@ -42,6 +42,23 @@ LoginHandler::LoginHandler(
|
||||
response_headers, first_auth_attempt));
|
||||
}
|
||||
|
||||
LoginHandler::LoginHandler(
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
content::WebContents* web_contents,
|
||||
base::ProcessId process_id,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
content::LoginDelegate::LoginAuthRequiredCallback auth_required_callback)
|
||||
: LoginHandler(auth_info,
|
||||
web_contents,
|
||||
/*is_request_for_primary_main_frame=*/false,
|
||||
/*is_request_for_navigation=*/false,
|
||||
process_id,
|
||||
url,
|
||||
std::move(response_headers),
|
||||
/*first_auth_attempt=*/false,
|
||||
std::move(auth_required_callback)) {}
|
||||
|
||||
void LoginHandler::EmitEvent(
|
||||
net::AuthChallengeInfo auth_info,
|
||||
content::WebContents* web_contents,
|
||||
|
||||
@@ -32,6 +32,13 @@ class LoginHandler : public content::LoginDelegate {
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool first_auth_attempt,
|
||||
content::LoginDelegate::LoginAuthRequiredCallback auth_required_callback);
|
||||
LoginHandler(
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
content::WebContents* web_contents,
|
||||
base::ProcessId process_id,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
content::LoginDelegate::LoginAuthRequiredCallback auth_required_callback);
|
||||
~LoginHandler() override;
|
||||
|
||||
// disable copy
|
||||
|
||||
@@ -55,9 +55,9 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
|
||||
proxied_loader_receiver_(this, std::move(loader_receiver)),
|
||||
target_client_(std::move(client)),
|
||||
current_response_(network::mojom::URLResponseHead::New()),
|
||||
// Always use "extraHeaders" mode to be compatible with old APIs, except
|
||||
// when the |request_id_| is zero, which is not supported in Chromium and
|
||||
// only happens in Electron when the request is started from net module.
|
||||
// Always use "extraHeaders" mode to be compatible with old APIs.
|
||||
// A non-zero request ID is required to use the TrustedHeaderClient path
|
||||
// which allows webRequest to modify headers like Proxy-Authorization.
|
||||
has_any_extra_headers_listeners_(network_service_request_id != 0) {
|
||||
// If there is a client error, clean up the request.
|
||||
target_client_.set_disconnect_handler(base::BindOnce(
|
||||
|
||||
@@ -372,19 +372,21 @@ void ProxyingWebSocket::OnHeadersReceivedComplete(int error_code) {
|
||||
ContinueToCompleted();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnAuthRequiredComplete(AuthRequiredResponse rv) {
|
||||
void ProxyingWebSocket::OnAuthRequiredComplete(
|
||||
WebRequestAPI::AuthRequiredResponse rv) {
|
||||
CHECK(auth_required_callback_);
|
||||
ResumeIncomingMethodCallProcessing();
|
||||
switch (rv) {
|
||||
case AuthRequiredResponse::kNoAction:
|
||||
case AuthRequiredResponse::kCancelAuth:
|
||||
case WebRequestAPI::AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_NO_ACTION:
|
||||
case WebRequestAPI::AuthRequiredResponse::
|
||||
AUTH_REQUIRED_RESPONSE_CANCEL_AUTH:
|
||||
std::move(auth_required_callback_).Run(std::nullopt);
|
||||
break;
|
||||
|
||||
case AuthRequiredResponse::kSetAuth:
|
||||
case WebRequestAPI::AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_SET_AUTH:
|
||||
std::move(auth_required_callback_).Run(auth_credentials_);
|
||||
break;
|
||||
case AuthRequiredResponse::kIoPending:
|
||||
case WebRequestAPI::AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_IO_PENDING:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
@@ -401,8 +403,13 @@ void ProxyingWebSocket::OnHeadersReceivedCompleteForAuth(
|
||||
|
||||
auto continuation = base::BindRepeating(
|
||||
&ProxyingWebSocket::OnAuthRequiredComplete, weak_factory_.GetWeakPtr());
|
||||
auto auth_rv = AuthRequiredResponse::kCancelAuth;
|
||||
auto auth_rv = web_request_api_->OnAuthRequired(
|
||||
&info_, auth_info, std::move(continuation), &auth_credentials_);
|
||||
PauseIncomingMethodCallProcessing();
|
||||
if (auth_rv ==
|
||||
WebRequestAPI::AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_IO_PENDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnAuthRequiredComplete(auth_rv);
|
||||
}
|
||||
|
||||
@@ -36,21 +36,6 @@ class ProxyingWebSocket : public network::mojom::WebSocketHandshakeClient,
|
||||
public:
|
||||
using WebSocketFactory = content::ContentBrowserClient::WebSocketFactory;
|
||||
|
||||
// AuthRequiredResponse indicates how an OnAuthRequired call is handled.
|
||||
enum class AuthRequiredResponse {
|
||||
// No credentials were provided.
|
||||
kNoAction,
|
||||
// AuthCredentials is filled in with a username and password, which should
|
||||
// be used in a response to the provided auth challenge.
|
||||
kSetAuth,
|
||||
// The request should be canceled.
|
||||
kCancelAuth,
|
||||
// The action will be decided asynchronously. |callback| will be invoked
|
||||
// when the decision is made, and one of the other AuthRequiredResponse
|
||||
// values will be passed in with the same semantics as described above.
|
||||
kIoPending,
|
||||
};
|
||||
|
||||
ProxyingWebSocket(
|
||||
WebRequestAPI* web_request_api,
|
||||
WebSocketFactory factory,
|
||||
@@ -119,7 +104,7 @@ class ProxyingWebSocket : public network::mojom::WebSocketHandshakeClient,
|
||||
void ContinueToStartRequest(int error_code);
|
||||
void OnHeadersReceivedComplete(int error_code);
|
||||
void ContinueToHeadersReceived();
|
||||
void OnAuthRequiredComplete(AuthRequiredResponse rv);
|
||||
void OnAuthRequiredComplete(WebRequestAPI::AuthRequiredResponse rv);
|
||||
void OnHeadersReceivedCompleteForAuth(const net::AuthChallengeInfo& auth_info,
|
||||
int rv);
|
||||
void ContinueToCompleted();
|
||||
|
||||
@@ -27,6 +27,23 @@ class WebRequestAPI {
|
||||
const std::set<std::string>& set_headers,
|
||||
int error_code)>;
|
||||
|
||||
// AuthRequiredResponse indicates how an OnAuthRequired call is handled.
|
||||
enum class AuthRequiredResponse {
|
||||
// No credentials were provided.
|
||||
AUTH_REQUIRED_RESPONSE_NO_ACTION,
|
||||
// AuthCredentials is filled in with a username and password, which should
|
||||
// be used in a response to the provided auth challenge.
|
||||
AUTH_REQUIRED_RESPONSE_SET_AUTH,
|
||||
// The request should be canceled.
|
||||
AUTH_REQUIRED_RESPONSE_CANCEL_AUTH,
|
||||
// The action will be decided asynchronously. |callback| will be invoked
|
||||
// when the decision is made, and one of the other AuthRequiredResponse
|
||||
// values will be passed in with the same semantics as described above.
|
||||
AUTH_REQUIRED_RESPONSE_IO_PENDING,
|
||||
};
|
||||
|
||||
using AuthCallback = base::OnceCallback<void(AuthRequiredResponse)>;
|
||||
|
||||
virtual bool HasListener() const = 0;
|
||||
virtual int OnBeforeRequest(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
@@ -36,6 +53,11 @@ class WebRequestAPI {
|
||||
const network::ResourceRequest& request,
|
||||
BeforeSendHeadersCallback callback,
|
||||
net::HttpRequestHeaders* headers) = 0;
|
||||
virtual AuthRequiredResponse OnAuthRequired(
|
||||
const extensions::WebRequestInfo* info,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
AuthCallback callback,
|
||||
net::AuthCredentials* credentials) = 0;
|
||||
virtual int OnHeadersReceived(
|
||||
extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
|
||||
@@ -285,6 +285,19 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
|
||||
return base::SysUTF8ToNSString(shell_ ? shell_->GetTitle() : "");
|
||||
}
|
||||
|
||||
- (NSString*)accessibilityDocument {
|
||||
// Prefer representedFilename set via Electron's setRepresentedFilename API.
|
||||
// This works around a Chromium change (https://crrev.com/c/6187085) where
|
||||
// NativeWidgetMacNSWindow's accessibilityDocument override doesn't fall back
|
||||
// to NSWindow's default behavior of returning the representedFilename.
|
||||
NSString* representedFilename = [self representedFilename];
|
||||
if (representedFilename.length > 0) {
|
||||
return [[NSURL fileURLWithPath:representedFilename] absoluteString];
|
||||
}
|
||||
// Fall back to Chromium's implementation for web content URLs.
|
||||
return [super accessibilityDocument];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeMainWindow {
|
||||
return !self.disableKeyOrMainWindow;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ui/gfx/geometry/skia_conversions.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/ozone/public/ozone_platform.h"
|
||||
#include "ui/platform_window/extensions/wayland_extension.h"
|
||||
#include "ui/platform_window/platform_window.h"
|
||||
#include "ui/platform_window/platform_window_init_properties.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
|
||||
@@ -58,6 +59,24 @@ bool ElectronDesktopWindowTreeHostLinux::IsShowingFrame() const {
|
||||
!native_window_view_->IsMinimized();
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::SetWindowIcons(
|
||||
const gfx::ImageSkia& window_icon,
|
||||
const gfx::ImageSkia& app_icon) {
|
||||
DesktopWindowTreeHostLinux::SetWindowIcons(window_icon, app_icon);
|
||||
|
||||
if (ui::GetWaylandToplevelExtension(*platform_window()))
|
||||
saved_window_icon_ = window_icon;
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::Show(
|
||||
ui::mojom::WindowShowState show_state,
|
||||
const gfx::Rect& restore_bounds) {
|
||||
DesktopWindowTreeHostLinux::Show(show_state, restore_bounds);
|
||||
|
||||
if (!saved_window_icon_.isNull())
|
||||
DesktopWindowTreeHostLinux::SetWindowIcons(saved_window_icon_, {});
|
||||
}
|
||||
|
||||
gfx::Insets ElectronDesktopWindowTreeHostLinux::CalculateInsetsInDIP(
|
||||
ui::PlatformWindowState window_state) const {
|
||||
// If we are not showing frame, the insets should be zero.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
#include "ui/linux/device_scale_factor_observer.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/native_theme/native_theme_observer.h"
|
||||
@@ -44,6 +45,10 @@ class ElectronDesktopWindowTreeHostLinux
|
||||
protected:
|
||||
// views::DesktopWindowTreeHostLinuxImpl:
|
||||
void OnWidgetInitDone() override;
|
||||
void SetWindowIcons(const gfx::ImageSkia& window_icon,
|
||||
const gfx::ImageSkia& app_icon) override;
|
||||
void Show(ui::mojom::WindowShowState show_state,
|
||||
const gfx::Rect& restore_bounds) override;
|
||||
|
||||
// ui::PlatformWindowDelegate
|
||||
gfx::Insets CalculateInsetsInDIP(
|
||||
@@ -71,6 +76,8 @@ class ElectronDesktopWindowTreeHostLinux
|
||||
|
||||
bool IsShowingFrame() const;
|
||||
|
||||
gfx::ImageSkia saved_window_icon_;
|
||||
|
||||
raw_ptr<NativeWindowViews> native_window_view_; // weak ref
|
||||
|
||||
base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "content/public/browser/global_request_id.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "mojo/public/cpp/system/data_pipe_producer.h"
|
||||
@@ -375,6 +376,13 @@ void SimpleURLLoaderWrapper::Start() {
|
||||
|
||||
loader_->SetAllowHttpErrorResults(true);
|
||||
loader_->SetURLLoaderFactoryOptions(request_options_);
|
||||
// Set a non-zero request ID so that the request can use the
|
||||
// TrustedHeaderClient code path for webRequest header modifications.
|
||||
// See proxying_url_loader_factory.cc for details.
|
||||
if (electron::IsBrowserProcess()) {
|
||||
loader_->SetRequestID(
|
||||
content::GlobalRequestID::MakeBrowserInitiated().request_id);
|
||||
}
|
||||
loader_->SetOnResponseStartedCallback(base::BindOnce(
|
||||
&SimpleURLLoaderWrapper::OnResponseStarted, weak_factory_.GetWeakPtr()));
|
||||
loader_->SetOnRedirectCallback(base::BindRepeating(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ipcMain, protocol, session, WebContents, webContents } from 'electron/main';
|
||||
import { ipcMain, net, protocol, session, WebContents, webContents } from 'electron/main';
|
||||
|
||||
import { expect } from 'chai';
|
||||
import * as WebSocket from 'ws';
|
||||
@@ -455,6 +455,35 @@ describe('webRequest module', () => {
|
||||
}));
|
||||
expect(onSendHeadersCalled).to.be.true();
|
||||
});
|
||||
|
||||
it('can inject Proxy-Authorization header for net module requests', async () => {
|
||||
// Proxy-Authorization is normally rejected by Chromium's network service
|
||||
// for security reasons. However, for Electron's trusted net module,
|
||||
// webRequest.onBeforeSendHeaders should be able to inject it via the
|
||||
// TrustedHeaderClient code path.
|
||||
const proxyAuthValue = 'Basic test-credentials';
|
||||
let receivedProxyAuth: string | undefined;
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
receivedProxyAuth = req.headers['proxy-authorization'];
|
||||
res.end('ok');
|
||||
});
|
||||
const { url: serverUrl } = await listen(server);
|
||||
|
||||
try {
|
||||
ses.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
const requestHeaders = details.requestHeaders;
|
||||
requestHeaders['Proxy-Authorization'] = proxyAuthValue;
|
||||
callback({ requestHeaders });
|
||||
});
|
||||
|
||||
const response = await net.fetch(serverUrl, { bypassCustomProtocolHandlers: true });
|
||||
expect(response.ok).to.be.true();
|
||||
expect(receivedProxyAuth).to.equal(proxyAuthValue);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('webRequest.onSendHeaders', () => {
|
||||
@@ -733,5 +762,80 @@ describe('webRequest module', () => {
|
||||
expect(reqHeaders['/websocket'].foo).to.equal('bar');
|
||||
expect(reqHeaders['/'].foo).to.equal('bar');
|
||||
});
|
||||
|
||||
it('authenticates a WebSocket via login event', async () => {
|
||||
const authServer = http.createServer();
|
||||
const wssAuth = new WebSocket.Server({ noServer: true });
|
||||
const expected = 'Basic ' + Buffer.from('user:pass').toString('base64');
|
||||
|
||||
wssAuth.on('connection', ws => {
|
||||
ws.send('Authenticated!');
|
||||
});
|
||||
|
||||
authServer.on('upgrade', (req, socket, head) => {
|
||||
const auth = req.headers.authorization || '';
|
||||
if (auth !== expected) {
|
||||
socket.write(
|
||||
'HTTP/1.1 401 Unauthorized\r\n' +
|
||||
'WWW-Authenticate: Basic realm="Test"\r\n' +
|
||||
'Content-Length: 0\r\n' +
|
||||
'\r\n'
|
||||
);
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
wssAuth.handleUpgrade(req, socket as Socket, head, ws => {
|
||||
wssAuth.emit('connection', ws, req);
|
||||
});
|
||||
});
|
||||
|
||||
const { port } = await listen(authServer);
|
||||
const ses = session.fromPartition(`WebRequestWSAuth-${Date.now()}`);
|
||||
|
||||
const contents = (webContents as typeof ElectronInternal.WebContents).create({
|
||||
session: ses,
|
||||
sandbox: true
|
||||
});
|
||||
|
||||
defer(() => {
|
||||
contents.destroy();
|
||||
authServer.close();
|
||||
wssAuth.close();
|
||||
});
|
||||
|
||||
ses.webRequest.onBeforeRequest({ urls: ['ws://*/*'] }, (details, callback) => {
|
||||
callback({});
|
||||
});
|
||||
|
||||
contents.on('login', (event, details: any, _: any, callback: (u: string, p: string) => void) => {
|
||||
if (details?.url?.startsWith(`ws://localhost:${port}`)) {
|
||||
event.preventDefault();
|
||||
callback('user', 'pass');
|
||||
}
|
||||
});
|
||||
|
||||
await contents.loadFile(path.join(fixturesPath, 'blank.html'));
|
||||
|
||||
const message = await contents.executeJavaScript(`new Promise((resolve, reject) => {
|
||||
let attempts = 0;
|
||||
function connect() {
|
||||
attempts++;
|
||||
const ws = new WebSocket('ws://localhost:${port}');
|
||||
ws.onmessage = e => resolve(e.data);
|
||||
ws.onerror = () => {
|
||||
if (attempts < 3) {
|
||||
setTimeout(connect, 50);
|
||||
} else {
|
||||
reject(new Error('WebSocket auth failed'));
|
||||
}
|
||||
};
|
||||
}
|
||||
connect();
|
||||
setTimeout(() => reject(new Error('timeout')), 5000);
|
||||
});`);
|
||||
|
||||
expect(message).to.equal('Authenticated!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { expect } from 'chai';
|
||||
import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { SpawnSyncReturns } from 'node:child_process';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import * as notes from '../script/release/notes/notes';
|
||||
|
||||
/* Fake a Dugite GitProcess that only returns the specific
|
||||
/* Fake a git spawnSync that only returns the specific
|
||||
commits that we want to test */
|
||||
|
||||
class Commit {
|
||||
@@ -43,10 +43,10 @@ class GitFake {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise<IGitResult> {
|
||||
exec (command: string, args: string[], options?: any): SpawnSyncReturns<string> {
|
||||
let stdout = '';
|
||||
const stderr = '';
|
||||
const exitCode = 0;
|
||||
const status = 0;
|
||||
|
||||
if (args.length === 3 && args[0] === 'merge-base') {
|
||||
// expected form: `git merge-base branchName1 branchName2`
|
||||
@@ -78,10 +78,10 @@ class GitFake {
|
||||
// git branch --all --contains ${ref} --sort version:refname
|
||||
stdout = args[3];
|
||||
} else {
|
||||
console.error('unhandled GitProcess.exec():', args);
|
||||
console.error('unhandled git spawnSync():', args);
|
||||
}
|
||||
|
||||
return Promise.resolve({ exitCode, stdout, stderr });
|
||||
return { status, stdout, stderr, pid: 0, output: [null, stdout, stderr], signal: null };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,14 @@ describe('release notes', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options);
|
||||
sandbox.replace(GitProcess, 'exec', wrapper);
|
||||
const wrapper = (command: unknown, args: unknown, options?: unknown) => {
|
||||
if (command === 'git' && Array.isArray(args)) {
|
||||
return gitFake.exec(command as string, args as string[], options);
|
||||
}
|
||||
// Default behavior for non-git commands
|
||||
return { status: 0, stdout: '', stderr: '', pid: 0, output: [null, '', ''], signal: null };
|
||||
};
|
||||
sandbox.stub(require('node:child_process'), 'spawnSync').callsFake(wrapper);
|
||||
|
||||
gitFake.setBranch(oldBranch, [...sharedHistory, oldFix]);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { expect } from 'chai';
|
||||
import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { SpawnSyncReturns } from 'node:child_process';
|
||||
|
||||
import { ifdescribe } from './lib/spec-helpers';
|
||||
import { nextVersion } from '../script/release/version-bumper';
|
||||
|
||||
@@ -33,10 +34,10 @@ class GitFake {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise<IGitResult> {
|
||||
exec (command: string, args: string[], options?: any): SpawnSyncReturns<string> {
|
||||
let stdout = '';
|
||||
const stderr = '';
|
||||
const exitCode = 0;
|
||||
const status = 0;
|
||||
|
||||
// handle for promoting from current master HEAD
|
||||
let branch = 'stable';
|
||||
@@ -48,7 +49,7 @@ class GitFake {
|
||||
if (!this.branches[branch]) this.setBranch(branch);
|
||||
|
||||
stdout = this.branches[branch].join('\n');
|
||||
return Promise.resolve({ exitCode, stdout, stderr });
|
||||
return { status, stdout, stderr, pid: 0, output: [null, stdout, stderr], signal: null };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +164,14 @@ describe('version-bumper', () => {
|
||||
const gitFake = new GitFake();
|
||||
|
||||
beforeEach(() => {
|
||||
const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options);
|
||||
sandbox.replace(GitProcess, 'exec', wrapper);
|
||||
const wrapper = (command: unknown, args: unknown, options?: unknown) => {
|
||||
if (command === 'git' && Array.isArray(args)) {
|
||||
return gitFake.exec(command as string, args as string[], options);
|
||||
}
|
||||
// Default behavior for non-git commands
|
||||
return { status: 0, stdout: '', stderr: '', pid: 0, output: [null, '', ''], signal: null };
|
||||
};
|
||||
sandbox.stub(require('node:child_process'), 'spawnSync').callsFake(wrapper);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
141
yarn.lock
141
yarn.lock
@@ -432,6 +432,70 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@electron-ci/dev-root@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@electron-ci/dev-root@workspace:."
|
||||
dependencies:
|
||||
"@azure/storage-blob": "npm:^12.28.0"
|
||||
"@datadog/datadog-ci": "npm:^4.1.2"
|
||||
"@electron/asar": "npm:^3.2.13"
|
||||
"@electron/docs-parser": "npm:^2.0.0"
|
||||
"@electron/fiddle-core": "npm:^1.3.4"
|
||||
"@electron/github-app-auth": "npm:^3.2.0"
|
||||
"@electron/lint-roller": "npm:^3.1.2"
|
||||
"@electron/typescript-definitions": "npm:^9.1.2"
|
||||
"@octokit/rest": "npm:^20.1.2"
|
||||
"@primer/octicons": "npm:^10.0.0"
|
||||
"@types/minimist": "npm:^1.2.5"
|
||||
"@types/node": "npm:^22.7.7"
|
||||
"@types/semver": "npm:^7.5.8"
|
||||
"@types/stream-json": "npm:^1.7.8"
|
||||
"@types/temp": "npm:^0.9.4"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^8.32.1"
|
||||
"@typescript-eslint/parser": "npm:^8.7.0"
|
||||
"@xmldom/xmldom": "npm:^0.8.11"
|
||||
buffer: "npm:^6.0.3"
|
||||
chalk: "npm:^4.1.0"
|
||||
check-for-leaks: "npm:^1.2.1"
|
||||
eslint: "npm:^8.57.1"
|
||||
eslint-config-standard: "npm:^17.1.0"
|
||||
eslint-plugin-import: "npm:^2.32.0"
|
||||
eslint-plugin-markdown: "npm:^5.1.0"
|
||||
eslint-plugin-mocha: "npm:^10.5.0"
|
||||
eslint-plugin-n: "npm:^16.6.2"
|
||||
eslint-plugin-node: "npm:^11.1.0"
|
||||
eslint-plugin-promise: "npm:^6.6.0"
|
||||
events: "npm:^3.2.0"
|
||||
folder-hash: "npm:^4.1.1"
|
||||
got: "npm:^11.8.5"
|
||||
husky: "npm:^9.1.7"
|
||||
lint-staged: "npm:^16.1.0"
|
||||
markdownlint-cli2: "npm:^0.18.0"
|
||||
minimist: "npm:^1.2.8"
|
||||
node-gyp: "npm:^11.4.2"
|
||||
null-loader: "npm:^4.0.1"
|
||||
pre-flight: "npm:^2.0.0"
|
||||
process: "npm:^0.11.10"
|
||||
remark-cli: "npm:^12.0.1"
|
||||
remark-preset-lint-markdown-style-guide: "npm:^6.0.1"
|
||||
semver: "npm:^7.6.3"
|
||||
stream-json: "npm:^1.9.1"
|
||||
tap-xunit: "npm:^2.4.1"
|
||||
temp: "npm:^0.9.4"
|
||||
timers-browserify: "npm:1.4.2"
|
||||
ts-loader: "npm:^8.0.2"
|
||||
ts-node: "npm:6.2.0"
|
||||
typescript: "npm:^5.6.2"
|
||||
url: "npm:^0.11.4"
|
||||
webpack: "npm:^5.95.0"
|
||||
webpack-cli: "npm:^5.1.4"
|
||||
wrapper-webpack-plugin: "npm:^2.2.0"
|
||||
dependenciesMeta:
|
||||
abstract-socket:
|
||||
built: true
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@electron-ci/echo@npm:*, @electron-ci/echo@workspace:spec/fixtures/native-addon/echo":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@electron-ci/echo@workspace:spec/fixtures/native-addon/echo"
|
||||
@@ -4452,16 +4516,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dugite@npm:^2.7.1":
|
||||
version: 2.7.1
|
||||
resolution: "dugite@npm:2.7.1"
|
||||
dependencies:
|
||||
progress: "npm:^2.0.3"
|
||||
tar: "npm:^6.1.11"
|
||||
checksum: 10c0/8b7992b91abc2473e43a4900c5b100f5a4b98785b9955275c72b1a621152b5df9ff794cda50daced5ee452f4dc95b313ce9c30af5dc9e302241ef9f661a593df
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "dunder-proto@npm:1.0.1"
|
||||
@@ -4569,73 +4623,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "electron@workspace:."
|
||||
dependencies:
|
||||
"@azure/storage-blob": "npm:^12.28.0"
|
||||
"@datadog/datadog-ci": "npm:^4.1.2"
|
||||
"@electron/asar": "npm:^3.2.13"
|
||||
"@electron/docs-parser": "npm:^2.0.0"
|
||||
"@electron/fiddle-core": "npm:^1.3.4"
|
||||
"@electron/github-app-auth": "npm:^3.2.0"
|
||||
"@electron/lint-roller": "npm:^3.1.2"
|
||||
"@electron/typescript-definitions": "npm:^9.1.2"
|
||||
"@octokit/rest": "npm:^20.1.2"
|
||||
"@primer/octicons": "npm:^10.0.0"
|
||||
"@types/minimist": "npm:^1.2.5"
|
||||
"@types/node": "npm:^22.7.7"
|
||||
"@types/semver": "npm:^7.5.8"
|
||||
"@types/stream-json": "npm:^1.7.8"
|
||||
"@types/temp": "npm:^0.9.4"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^8.32.1"
|
||||
"@typescript-eslint/parser": "npm:^8.7.0"
|
||||
"@xmldom/xmldom": "npm:^0.8.11"
|
||||
buffer: "npm:^6.0.3"
|
||||
chalk: "npm:^4.1.0"
|
||||
check-for-leaks: "npm:^1.2.1"
|
||||
dugite: "npm:^2.7.1"
|
||||
eslint: "npm:^8.57.1"
|
||||
eslint-config-standard: "npm:^17.1.0"
|
||||
eslint-plugin-import: "npm:^2.32.0"
|
||||
eslint-plugin-markdown: "npm:^5.1.0"
|
||||
eslint-plugin-mocha: "npm:^10.5.0"
|
||||
eslint-plugin-n: "npm:^16.6.2"
|
||||
eslint-plugin-node: "npm:^11.1.0"
|
||||
eslint-plugin-promise: "npm:^6.6.0"
|
||||
events: "npm:^3.2.0"
|
||||
folder-hash: "npm:^4.1.1"
|
||||
got: "npm:^11.8.5"
|
||||
husky: "npm:^9.1.7"
|
||||
lint-staged: "npm:^16.1.0"
|
||||
markdownlint-cli2: "npm:^0.18.0"
|
||||
minimist: "npm:^1.2.8"
|
||||
node-gyp: "npm:^11.4.2"
|
||||
null-loader: "npm:^4.0.1"
|
||||
pre-flight: "npm:^2.0.0"
|
||||
process: "npm:^0.11.10"
|
||||
remark-cli: "npm:^12.0.1"
|
||||
remark-preset-lint-markdown-style-guide: "npm:^6.0.1"
|
||||
semver: "npm:^7.6.3"
|
||||
stream-json: "npm:^1.9.1"
|
||||
tap-xunit: "npm:^2.4.1"
|
||||
temp: "npm:^0.9.4"
|
||||
timers-browserify: "npm:1.4.2"
|
||||
ts-loader: "npm:^8.0.2"
|
||||
ts-node: "npm:6.2.0"
|
||||
typescript: "npm:^5.6.2"
|
||||
url: "npm:^0.11.4"
|
||||
webpack: "npm:^5.95.0"
|
||||
webpack-cli: "npm:^5.1.4"
|
||||
wrapper-webpack-plugin: "npm:^2.2.0"
|
||||
dependenciesMeta:
|
||||
abstract-socket:
|
||||
built: true
|
||||
dugite:
|
||||
built: true
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"emoji-regex@npm:^10.2.1, emoji-regex@npm:^10.3.0":
|
||||
version: 10.4.0
|
||||
resolution: "emoji-regex@npm:10.4.0"
|
||||
|
||||
Reference in New Issue
Block a user