mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2871c1d392 | ||
|
|
0d3f57f3de | ||
|
|
6247116f8d | ||
|
|
5a1bda2277 | ||
|
|
cca4a7388d | ||
|
|
a8dfe3687c | ||
|
|
a4955396e1 | ||
|
|
bd193de24b | ||
|
|
ef66db337e | ||
|
|
00827a2da4 | ||
|
|
5f020c4685 |
@@ -9,4 +9,8 @@ npmMinimalAgeGate: 10080
|
||||
npmPreapprovedPackages:
|
||||
- "@electron/*"
|
||||
|
||||
httpProxy: "${HTTP_PROXY:-}"
|
||||
|
||||
httpsProxy: "${HTTPS_PROXY:-}"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
|
||||
@@ -777,8 +777,7 @@ WebContents.prototype._init = function () {
|
||||
const originCounts = new Map<string, number>();
|
||||
const openDialogs = new Set<AbortController>();
|
||||
this.on('-run-dialog', async (info, callback) => {
|
||||
const originUrl = new URL(info.frame.url);
|
||||
const origin = originUrl.protocol === 'file:' ? originUrl.href : originUrl.origin;
|
||||
const origin = info.frame.origin === 'file://' ? info.frame.url : info.frame.origin;
|
||||
if ((originCounts.get(origin) ?? 0) < 0) return callback(false, '');
|
||||
|
||||
const prefs = this.getLastWebPreferences();
|
||||
|
||||
@@ -151,3 +151,9 @@ fix_update_dbus_signal_signature_for_xdg_globalshortcuts_portal.patch
|
||||
patch_osr_control_screen_info.patch
|
||||
cherry-pick-12f932985275.patch
|
||||
fix_mac_high_res_icons.patch
|
||||
cherry-pick-074d472db745.patch
|
||||
cherry-pick-50b057660b4d.patch
|
||||
cherry-pick-45c5a70d984d.patch
|
||||
cherry-pick-05e4b544803c.patch
|
||||
cherry-pick-5efc7a0127a6.patch
|
||||
feat_plumb_node_integration_in_worker_through_workersettings.patch
|
||||
|
||||
204
patches/chromium/cherry-pick-05e4b544803c.patch
Normal file
204
patches/chromium/cherry-pick-05e4b544803c.patch
Normal file
@@ -0,0 +1,204 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 03:24:31 -0800
|
||||
Subject: Stringify CSSUnparsedValues via toString, as normal
|
||||
|
||||
CSSUnparsedValue exposes a special stringification function
|
||||
ToUnparsedString() in addition to the regular toString().
|
||||
The documentation says it returns "tokens without substituting
|
||||
variables", but it's not clear what this means; we don't substitute
|
||||
any variables in CSSStyleValue::toString() either.
|
||||
|
||||
This CL makes ToUnparsedString() private (and renames it).
|
||||
Clients needing to serialize a CSSUnparsedValue can do so via
|
||||
the normal toString() function. (If ToUnparsedString() existed
|
||||
for performance reasons, that should have been documented.)
|
||||
|
||||
Also, the /**/-"fixup" pass over the value has been folded into
|
||||
ToStringInternal(). This is to make it easy to find the canonical string
|
||||
representation of this value within CSSUnparsedValue (without going
|
||||
through a CSSValue).
|
||||
|
||||
The main point of this CL is to prepare for validating
|
||||
the "argument grammar" of the value during the StyleValue-to-CSSValue
|
||||
conversion in StylePropertyMap (which requires item (2) above).
|
||||
|
||||
We now jump through additional hoops to ultimately get a string
|
||||
from the outside of CSSUnparsedValue, but there should otherwise
|
||||
be no behavior change.
|
||||
|
||||
Bug: 484751092
|
||||
Change-Id: I5db45ad85f780c67a2ea3ba8482c390ebab10068
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7600415
|
||||
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1590041}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
|
||||
index dcc2eccbc84e6cd5710ab51cee2dab49661467c1..86d42c87a6bd10838a3e059c9227868e5bfc0798 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
|
||||
@@ -19,12 +19,12 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
|
||||
-#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_color.h"
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread.h"
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
|
||||
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -152,8 +152,7 @@ TEST_F(CrossThreadStyleValueTest, CrossThreadUnparsedValueToCSSStyleValue) {
|
||||
CSSStyleValue* style_value = value->ToCSSStyleValue();
|
||||
EXPECT_EQ(style_value->GetType(),
|
||||
CSSStyleValue::StyleValueType::kUnparsedType);
|
||||
- EXPECT_EQ(static_cast<CSSUnparsedValue*>(style_value)->ToUnparsedString(),
|
||||
- "Unparsed");
|
||||
+ EXPECT_EQ(style_value->toString(), "Unparsed");
|
||||
}
|
||||
|
||||
TEST_F(CrossThreadStyleValueTest, PassKeywordValueCrossThread) {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
index 567d4fad7436c24d4c42bc36ebfd7ee3641e3b90..12d70ed096cb1c509a2acf14b7f421273d833d0e 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
@@ -137,16 +137,26 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
|
||||
}
|
||||
|
||||
const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
- String unparsed_string = ToUnparsedString();
|
||||
- CSSParserTokenStream stream(unparsed_string);
|
||||
+ String unparsed_string = ToStringInternal();
|
||||
|
||||
- if (stream.AtEnd()) {
|
||||
+ if (unparsed_string.IsNull()) {
|
||||
return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
MakeGarbageCollected<CSSVariableData>());
|
||||
}
|
||||
|
||||
- // The string we just parsed has /**/ inserted between every token
|
||||
- // to make sure we get back the correct sequence of tokens.
|
||||
+ // TODO(crbug.com/985028): We should probably propagate the CSSParserContext
|
||||
+ // to here.
|
||||
+ return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
+ CSSVariableData::Create(unparsed_string, false /* is_animation_tainted */,
|
||||
+ false /* is_attr_tainted */,
|
||||
+ false /* needs_variable_resolution */));
|
||||
+}
|
||||
+
|
||||
+String CSSUnparsedValue::ToStringInternal() const {
|
||||
+ String serialized = SerializeSegments();
|
||||
+
|
||||
+ // The serialization above defensively inserted /**/ between segments
|
||||
+ // to make sure that e.g. ['foo', 'bar'] does not collapse into 'foobar'.
|
||||
// The spec mentions nothing of the sort:
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#unparsedvalue-serialization
|
||||
//
|
||||
@@ -160,6 +170,10 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
// the original contents of any comments will be lost, but Typed OM does
|
||||
// not have anywhere to store that kind of data, so it is expected.
|
||||
StringBuilder builder;
|
||||
+ CSSParserTokenStream stream(serialized);
|
||||
+ if (stream.AtEnd()) {
|
||||
+ return g_null_atom;
|
||||
+ }
|
||||
CSSParserToken token = stream.ConsumeRaw();
|
||||
token.Serialize(builder);
|
||||
while (!stream.Peek().IsEOF()) {
|
||||
@@ -169,17 +183,10 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
token = stream.ConsumeRaw();
|
||||
token.Serialize(builder);
|
||||
}
|
||||
- String original_text = builder.ReleaseString();
|
||||
-
|
||||
- // TODO(crbug.com/985028): We should probably propagate the CSSParserContext
|
||||
- // to here.
|
||||
- return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
- CSSVariableData::Create(original_text, false /* is_animation_tainted */,
|
||||
- false /* is_attr_tainted */,
|
||||
- false /* needs_variable_resolution */));
|
||||
+ return builder.ReleaseString();
|
||||
}
|
||||
|
||||
-String CSSUnparsedValue::ToUnparsedString() const {
|
||||
+String CSSUnparsedValue::SerializeSegments() const {
|
||||
StringBuilder builder;
|
||||
HeapHashSet<Member<const CSSUnparsedValue>> values_on_stack;
|
||||
if (AppendUnparsedString(builder, values_on_stack)) {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
index 5d1961b170f14ae21ca8f69b3c3cd8af28f4478a..ec7e3ed708f406d7a61fdb370b2eed8a8297cffb 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
@@ -67,15 +67,9 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
CSSStyleValue::Trace(visitor);
|
||||
}
|
||||
|
||||
- // Unlike CSSStyleValue::toString(), this returns tokens without
|
||||
- // substituting variables. There are extra /**/ inserted between
|
||||
- // every token to ensure there are no ambiguities, which is fine
|
||||
- // because this value is never presented directly to the user
|
||||
- // (ToCSSValue() will parse to a token range and then re-serialize
|
||||
- // using extra /**/ only where needed).
|
||||
- String ToUnparsedString() const;
|
||||
-
|
||||
private:
|
||||
+ String ToStringInternal() const;
|
||||
+ String SerializeSegments() const;
|
||||
// Return 'false' if there is a cycle in the serialization.
|
||||
bool AppendUnparsedString(
|
||||
StringBuilder&,
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
|
||||
index f81fa39423a9235bc58e1600ca7a250affd3d9bb..2ee4dd7e591095b8460ca559b29b78e37ab71729 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h"
|
||||
|
||||
#include <memory>
|
||||
+
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@@ -13,7 +14,6 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_paint_worklet_input.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
|
||||
-#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_color.h"
|
||||
#include "third_party/blink/renderer/core/css/properties/longhands/custom_property.h"
|
||||
#include "third_party/blink/renderer/core/dom/element.h"
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
|
||||
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -66,8 +67,7 @@ class PaintWorkletStylePropertyMapTest : public PageTestBase {
|
||||
CSSStyleValue* style_value = data.at("--x")->ToCSSStyleValue();
|
||||
EXPECT_EQ(style_value->GetType(),
|
||||
CSSStyleValue::StyleValueType::kUnparsedType);
|
||||
- EXPECT_EQ(static_cast<CSSUnparsedValue*>(style_value)->ToUnparsedString(),
|
||||
- "50");
|
||||
+ EXPECT_EQ(style_value->toString(), "50");
|
||||
waitable_event->Signal();
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
|
||||
index 79b292f72efe32e6b56971ea577481710b0c750c..8b0c9f73656d664b04b640016391965009b667d6 100644
|
||||
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
|
||||
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
|
||||
@@ -5059,7 +5059,7 @@ ComputedStyleUtils::CrossThreadStyleValueFromCSSStyleValue(
|
||||
To<CSSUnsupportedColor>(style_value)->Value());
|
||||
case CSSStyleValue::StyleValueType::kUnparsedType:
|
||||
return std::make_unique<CrossThreadUnparsedValue>(
|
||||
- To<CSSUnparsedValue>(style_value)->ToUnparsedString());
|
||||
+ To<CSSUnparsedValue>(style_value)->toString());
|
||||
default:
|
||||
return std::make_unique<CrossThreadUnsupportedValue>(
|
||||
style_value->toString());
|
||||
296
patches/chromium/cherry-pick-074d472db745.patch
Normal file
296
patches/chromium/cherry-pick-074d472db745.patch
Normal file
@@ -0,0 +1,296 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mikel Astiz <mastiz@chromium.org>
|
||||
Date: Tue, 10 Mar 2026 13:22:17 -0700
|
||||
Subject: [M146][base] Fix UAF in base::OnceCallbackList on re-entrant Notify()
|
||||
|
||||
Before this patch, `base::OnceCallbackList` was susceptible to a
|
||||
heap-use-after-free when `Notify()` was called re-entrantly.
|
||||
|
||||
The UAF occurred because `OnceCallbackList::RunCallback()` immediately
|
||||
spliced executed nodes out of `callbacks_` and into `null_callbacks_`.
|
||||
If a nested `Notify()` executed a node that an outer `Notify()` loop was
|
||||
already holding an iterator to, and that node's subscription was
|
||||
subsequently destroyed during the re-entrant cycle, the node would be
|
||||
physically erased from `null_callbacks_`. When control returned to the
|
||||
outer loop, it would attempt to evaluate the now-dangling iterator.
|
||||
|
||||
This CL fixes the bug by deferring list mutations until the outermost
|
||||
iteration completes:
|
||||
1. `RunCallback()` no longer splices nodes during iteration.
|
||||
2. Cancellation logic is pushed down to the subclasses via a new
|
||||
`CancelCallback()` hook, which is an extension to the pre-existing
|
||||
`CancelNullCallback()` with increased responsibilities and clearer
|
||||
semantics.
|
||||
3. If a subscription is destroyed while `is_iterating` is true,
|
||||
`OnceCallbackList` resets the node and stashes its iterator in
|
||||
`pending_erasures_`.
|
||||
4. A new `CleanUpNullCallbacksPostIteration()` phase runs at the end
|
||||
of the outermost `Notify()`, which safely splices executed nodes
|
||||
into `null_callbacks_` and physically erases the pending dead nodes.
|
||||
|
||||
As a side effect, the type-trait hack in `Notify()` based on
|
||||
`is_instantiation<CallbackType, OnceCallback>` can be removed, because
|
||||
this information is exposed directly by
|
||||
`OnceCallbackList::CleanUpNullCallbacksPostIteration()`.
|
||||
|
||||
The newly-added unit-test
|
||||
CallbackListTest.OnceCallbackListCancelDuringReentrantNotify reproduces
|
||||
the scenario and crashed before this patch.
|
||||
|
||||
(cherry picked from commit 36acd49636845be2419269acbe9a5137da3d5d96)
|
||||
|
||||
Change-Id: I6b1e2bcb97be1bc8d6a15e5ca7511992e00e1772
|
||||
Fixed: 489381399
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7627506
|
||||
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
|
||||
Reviewed-by: Gabriel Charette <gab@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1594520}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7653916
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/7680@{#2287}
|
||||
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
|
||||
|
||||
diff --git a/base/callback_list.h b/base/callback_list.h
|
||||
index 82cb11dc0ee02906b009cc383c41a056861199d0..d5f99cf685486f1ea74718b4e6b228a5d83f0c29 100644
|
||||
--- a/base/callback_list.h
|
||||
+++ b/base/callback_list.h
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
+#include <vector>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/base_export.h"
|
||||
@@ -16,7 +17,6 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
-#include "base/types/is_instantiation.h"
|
||||
|
||||
// OVERVIEW:
|
||||
//
|
||||
@@ -240,17 +240,14 @@ class CallbackListBase {
|
||||
|
||||
// Any null callbacks remaining in the list were canceled due to
|
||||
// Subscription destruction during iteration, and can safely be erased now.
|
||||
- const size_t erased_callbacks =
|
||||
- std::erase_if(callbacks_, [](const auto& cb) { return cb.is_null(); });
|
||||
-
|
||||
- // Run |removal_callback_| if any callbacks were canceled. Note that we
|
||||
- // cannot simply compare list sizes before and after iterating, since
|
||||
- // notification may result in Add()ing new callbacks as well as canceling
|
||||
- // them. Also note that if this is a OnceCallbackList, the OnceCallbacks
|
||||
- // that were executed above have all been removed regardless of whether
|
||||
- // they're counted in |erased_callbacks_|.
|
||||
- if (removal_callback_ &&
|
||||
- (erased_callbacks || is_instantiation<CallbackType, OnceCallback>)) {
|
||||
+ const bool any_callbacks_erased = static_cast<CallbackListImpl*>(this)
|
||||
+ ->CleanUpNullCallbacksPostIteration();
|
||||
+
|
||||
+ // Run |removal_callback_| if any callbacks were canceled or executed. Note
|
||||
+ // that simply comparing list sizes before and after iterating cannot be
|
||||
+ // done, since notification may result in Add()ing new callbacks as well as
|
||||
+ // canceling them.
|
||||
+ if (removal_callback_ && any_callbacks_erased) {
|
||||
removal_callback_.Run(); // May delete |this|!
|
||||
}
|
||||
}
|
||||
@@ -264,21 +261,9 @@ class CallbackListBase {
|
||||
private:
|
||||
// Cancels the callback pointed to by |it|, which is guaranteed to be valid.
|
||||
void CancelCallback(const typename Callbacks::iterator& it) {
|
||||
- if (static_cast<CallbackListImpl*>(this)->CancelNullCallback(it)) {
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- if (iterating_) {
|
||||
- // Calling erase() here is unsafe, since the loop in Notify() may be
|
||||
- // referencing this same iterator, e.g. if adjacent callbacks'
|
||||
- // Subscriptions are both destroyed when the first one is Run(). Just
|
||||
- // reset the callback and let Notify() clean it up at the end.
|
||||
- it->Reset();
|
||||
- } else {
|
||||
- callbacks_.erase(it);
|
||||
- if (removal_callback_) {
|
||||
- removal_callback_.Run(); // May delete |this|!
|
||||
- }
|
||||
+ if (static_cast<CallbackListImpl*>(this)->CancelCallback(it, iterating_) &&
|
||||
+ removal_callback_) {
|
||||
+ removal_callback_.Run(); // May delete |this|!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,23 +289,71 @@ class OnceCallbackList
|
||||
// Runs the current callback, which may cancel it or any other callbacks.
|
||||
template <typename... RunArgs>
|
||||
void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) {
|
||||
- // OnceCallbacks still have Subscriptions with outstanding iterators;
|
||||
- // splice() removes them from |callbacks_| without invalidating those.
|
||||
- null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
|
||||
+ // Do not splice here. Splicing during iteration breaks re-entrant Notify()
|
||||
+ // by invalidating the outer loop's iterator. Splicing is deferred to
|
||||
+ // CleanUpNullCallbacksPostIteration(), which is called when the outermost
|
||||
+ // Notify() finishes.
|
||||
|
||||
// NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see
|
||||
// comments in Notify().
|
||||
std::move(*it).Run(args...);
|
||||
}
|
||||
|
||||
- // If |it| refers to an already-canceled callback, does any necessary cleanup
|
||||
- // and returns true. Otherwise returns false.
|
||||
- bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
|
||||
+ // Called during subscription destruction to cancel the callback. Returns true
|
||||
+ // if the callback was removed from the active list and the generic removal
|
||||
+ // callback should be executed. Returns false if the callback was already
|
||||
+ // executed, or if the erasure is deferred due to active iteration.
|
||||
+ bool CancelCallback(const typename Traits::Callbacks::iterator& it,
|
||||
+ bool is_iterating) {
|
||||
+ if (is_iterating) {
|
||||
+ // During iteration, nodes cannot be safely erased from |callbacks_|
|
||||
+ // without invalidating iterators. They also cannot be spliced into
|
||||
+ // |null_callbacks_| right now. Thus, the node is reset and tracked for
|
||||
+ // erasure in CleanUpNullCallbacksPostIteration().
|
||||
+ it->Reset();
|
||||
+ pending_erasures_.push_back(it);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
if (it->is_null()) {
|
||||
+ // The callback already ran, so it's safely sitting in |null_callbacks_|.
|
||||
null_callbacks_.erase(it);
|
||||
- return true;
|
||||
+ return false;
|
||||
}
|
||||
- return false;
|
||||
+
|
||||
+ // The callback hasn't run yet, so it's still in |callbacks_|.
|
||||
+ this->callbacks_.erase(it);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Performs post-iteration cleanup. Successfully executed callbacks (which
|
||||
+ // become null) are spliced into |null_callbacks_| to keep their
|
||||
+ // Subscriptions' iterators valid. Callbacks explicitly canceled during
|
||||
+ // iteration (tracked in |pending_erasures_|) are erased. Returns true if any
|
||||
+ // callbacks were erased or spliced out.
|
||||
+ bool CleanUpNullCallbacksPostIteration() {
|
||||
+ bool any_spliced = false;
|
||||
+ for (auto it = this->callbacks_.begin(); it != this->callbacks_.end();) {
|
||||
+ if (it->is_null()) {
|
||||
+ any_spliced = true;
|
||||
+ auto next = std::next(it);
|
||||
+ null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
|
||||
+ it = next;
|
||||
+ } else {
|
||||
+ ++it;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ bool any_erased = !pending_erasures_.empty();
|
||||
+ for (auto pending_it : pending_erasures_) {
|
||||
+ // Note: `pending_it` was originally an iterator into `callbacks_`, but
|
||||
+ // the node it points to has just been spliced into `null_callbacks_`. The
|
||||
+ // iterator itself remains valid and can now be used for erasure from
|
||||
+ // `null_callbacks_`.
|
||||
+ null_callbacks_.erase(pending_it);
|
||||
+ }
|
||||
+ pending_erasures_.clear();
|
||||
+ return any_spliced || any_erased;
|
||||
}
|
||||
|
||||
// Holds null callbacks whose Subscriptions are still alive, so the
|
||||
@@ -328,6 +361,11 @@ class OnceCallbackList
|
||||
// OnceCallbacks, since RepeatingCallbacks are not canceled except by
|
||||
// Subscription destruction.
|
||||
typename Traits::Callbacks null_callbacks_;
|
||||
+
|
||||
+ // Holds iterators for callbacks canceled during iteration.
|
||||
+ // Erasure is deferred to CleanUpNullCallbacksPostIteration() when iteration
|
||||
+ // completes to prevent invalidating iterators that an outer loop might hold.
|
||||
+ std::vector<typename Traits::Callbacks::iterator> pending_erasures_;
|
||||
};
|
||||
|
||||
template <typename Signature>
|
||||
@@ -344,14 +382,29 @@ class RepeatingCallbackList
|
||||
it->Run(args...);
|
||||
}
|
||||
|
||||
- // If |it| refers to an already-canceled callback, does any necessary cleanup
|
||||
- // and returns true. Otherwise returns false.
|
||||
- bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
|
||||
- // Because at most one Subscription can point to a given callback, and
|
||||
- // RepeatingCallbacks are only reset by CancelCallback(), no one should be
|
||||
- // able to request cancellation of a canceled RepeatingCallback.
|
||||
- DCHECK(!it->is_null());
|
||||
- return false;
|
||||
+ // Called during subscription destruction to cancel the callback. Returns true
|
||||
+ // if the callback was removed from the active list and the generic removal
|
||||
+ // callback should be executed. Returns false if the callback was already
|
||||
+ // executed, or if the erasure is deferred due to active iteration.
|
||||
+ bool CancelCallback(const typename Traits::Callbacks::iterator& it,
|
||||
+ bool is_iterating) {
|
||||
+ if (is_iterating) {
|
||||
+ // During iteration, nodes cannot be safely erased from |callbacks_|
|
||||
+ // without invalidating iterators. The node is reset and will be swept up
|
||||
+ // by CleanUpNullCallbacksPostIteration().
|
||||
+ it->Reset();
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ this->callbacks_.erase(it);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Performs post-iteration cleanup by erasing all canceled callbacks. Returns
|
||||
+ // true if any callbacks were erased.
|
||||
+ bool CleanUpNullCallbacksPostIteration() {
|
||||
+ return std::erase_if(this->callbacks_,
|
||||
+ [](const auto& cb) { return cb.is_null(); }) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
diff --git a/base/callback_list_unittest.cc b/base/callback_list_unittest.cc
|
||||
index 7474278525e5efecc0de903809a54d366896d524..a855443fbae862befbc3a2a484ea335632136e94 100644
|
||||
--- a/base/callback_list_unittest.cc
|
||||
+++ b/base/callback_list_unittest.cc
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
+#include "base/test/bind.h"
|
||||
#include "base/test/test_future.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
@@ -577,6 +578,30 @@ TEST(CallbackListTest, ReentrantNotify) {
|
||||
EXPECT_EQ(1, d.total());
|
||||
}
|
||||
|
||||
+// Regression test for crbug.com/489381399: Verifies Notify() can be called
|
||||
+// reentrantly for OnceCallbackList even if a callback is canceled during the
|
||||
+// reentrant notification.
|
||||
+TEST(CallbackListTest, OnceCallbackListCancelDuringReentrantNotify) {
|
||||
+ OnceClosureList cb_reg;
|
||||
+ CallbackListSubscription sub_a, sub_b;
|
||||
+
|
||||
+ auto cb_a = base::BindLambdaForTesting([&]() {
|
||||
+ // Re-entrant notification.
|
||||
+ cb_reg.Notify();
|
||||
+ // After re-entrant notification returns, sub_b has been run. Destroying it
|
||||
+ // now should be a no-op.
|
||||
+ sub_b = {};
|
||||
+ });
|
||||
+
|
||||
+ auto cb_b = base::DoNothing();
|
||||
+
|
||||
+ sub_a = cb_reg.Add(std::move(cb_a));
|
||||
+ sub_b = cb_reg.Add(std::move(cb_b));
|
||||
+
|
||||
+ // This should not crash.
|
||||
+ cb_reg.Notify();
|
||||
+}
|
||||
+
|
||||
TEST(CallbackListTest, ClearPreventsInvocation) {
|
||||
Listener listener;
|
||||
RepeatingClosureList cb_reg;
|
||||
199
patches/chromium/cherry-pick-45c5a70d984d.patch
Normal file
199
patches/chromium/cherry-pick-45c5a70d984d.patch
Normal file
@@ -0,0 +1,199 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 03:24:19 -0800
|
||||
Subject: Describe a vector of segments as "segments", not "tokens"
|
||||
|
||||
The specification uses the term "tokens" to refer to a sequence
|
||||
of V8CSSUnparsedSegment objects, and CSSUnparsedValue has adopted
|
||||
this terminology. While it is usually a good idea for Blink
|
||||
to mirror the language used in specifications, "tokens" is very
|
||||
confusing here, since it always means CSSParserTokens in every other
|
||||
place in the style code.
|
||||
|
||||
Bug: 487117772
|
||||
Change-Id: I2dc132c4e618e398e1f8bdabc03a8d2ab6c118e7
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7606599
|
||||
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1590040}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
index 486e9d10c1e0a682ec239f4df696f4133300eebb..567d4fad7436c24d4c42bc36ebfd7ee3641e3b90 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
@@ -28,12 +28,12 @@ String FindVariableName(CSSParserTokenStream& stream) {
|
||||
|
||||
V8CSSUnparsedSegment* VariableReferenceValue(
|
||||
const StringView& variable_name,
|
||||
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens) {
|
||||
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments) {
|
||||
CSSUnparsedValue* unparsed_value;
|
||||
- if (tokens.size() == 0) {
|
||||
+ if (segments.size() == 0) {
|
||||
unparsed_value = nullptr;
|
||||
} else {
|
||||
- unparsed_value = CSSUnparsedValue::Create(tokens);
|
||||
+ unparsed_value = CSSUnparsedValue::Create(segments);
|
||||
}
|
||||
|
||||
CSSStyleVariableReferenceValue* variable_reference =
|
||||
@@ -50,13 +50,13 @@ V8CSSUnparsedSegment* VariableReferenceValue(
|
||||
HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
|
||||
CSSParserTokenStream& stream) {
|
||||
int nesting_level = 0;
|
||||
- HeapVector<Member<V8CSSUnparsedSegment>> tokens;
|
||||
+ HeapVector<Member<V8CSSUnparsedSegment>> segments;
|
||||
StringBuilder builder;
|
||||
while (stream.Peek().GetType() != kEOFToken) {
|
||||
if (stream.Peek().FunctionId() == CSSValueID::kVar ||
|
||||
stream.Peek().FunctionId() == CSSValueID::kEnv) {
|
||||
if (!builder.empty()) {
|
||||
- tokens.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(
|
||||
+ segments.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(
|
||||
builder.ReleaseString()));
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
|
||||
if (!ref) {
|
||||
break;
|
||||
}
|
||||
- tokens.push_back(ref);
|
||||
+ segments.push_back(ref);
|
||||
} else {
|
||||
if (stream.Peek().GetBlockType() == CSSParserToken::kBlockStart) {
|
||||
++nesting_level;
|
||||
@@ -86,10 +86,10 @@ HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
|
||||
}
|
||||
}
|
||||
if (!builder.empty()) {
|
||||
- tokens.push_back(
|
||||
+ segments.push_back(
|
||||
MakeGarbageCollected<V8CSSUnparsedSegment>(builder.ReleaseString()));
|
||||
}
|
||||
- return tokens;
|
||||
+ return segments;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -109,8 +109,8 @@ CSSUnparsedValue* CSSUnparsedValue::FromCSSVariableData(
|
||||
V8CSSUnparsedSegment* CSSUnparsedValue::AnonymousIndexedGetter(
|
||||
uint32_t index,
|
||||
ExceptionState& exception_state) const {
|
||||
- if (index < tokens_.size()) {
|
||||
- return tokens_[index].Get();
|
||||
+ if (index < segments_.size()) {
|
||||
+ return segments_[index].Get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -119,20 +119,20 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
|
||||
uint32_t index,
|
||||
V8CSSUnparsedSegment* segment,
|
||||
ExceptionState& exception_state) {
|
||||
- if (index < tokens_.size()) {
|
||||
- tokens_[index] = segment;
|
||||
+ if (index < segments_.size()) {
|
||||
+ segments_[index] = segment;
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
- if (index == tokens_.size()) {
|
||||
- tokens_.push_back(segment);
|
||||
+ if (index == segments_.size()) {
|
||||
+ segments_.push_back(segment);
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
exception_state.ThrowRangeError(
|
||||
ExceptionMessages::IndexOutsideRange<unsigned>(
|
||||
- "index", index, 0, ExceptionMessages::kInclusiveBound, tokens_.size(),
|
||||
- ExceptionMessages::kInclusiveBound));
|
||||
+ "index", index, 0, ExceptionMessages::kInclusiveBound,
|
||||
+ segments_.size(), ExceptionMessages::kInclusiveBound));
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
@@ -195,14 +195,14 @@ bool CSSUnparsedValue::AppendUnparsedString(
|
||||
return false; // Cycle.
|
||||
}
|
||||
values_on_stack.insert(this);
|
||||
- for (unsigned i = 0; i < tokens_.size(); i++) {
|
||||
+ for (unsigned i = 0; i < segments_.size(); i++) {
|
||||
if (i) {
|
||||
builder.Append("/**/");
|
||||
}
|
||||
- switch (tokens_[i]->GetContentType()) {
|
||||
+ switch (segments_[i]->GetContentType()) {
|
||||
case V8CSSUnparsedSegment::ContentType::kCSSVariableReferenceValue: {
|
||||
const auto* reference_value =
|
||||
- tokens_[i]->GetAsCSSVariableReferenceValue();
|
||||
+ segments_[i]->GetAsCSSVariableReferenceValue();
|
||||
builder.Append("var(");
|
||||
builder.Append(reference_value->variable());
|
||||
if (reference_value->fallback()) {
|
||||
@@ -216,7 +216,7 @@ bool CSSUnparsedValue::AppendUnparsedString(
|
||||
break;
|
||||
}
|
||||
case V8CSSUnparsedSegment::ContentType::kString:
|
||||
- builder.Append(tokens_[i]->GetAsString());
|
||||
+ builder.Append(segments_[i]->GetAsString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
index c9dab7a0b3ffeaeb6b5d2ab50d876d40c38a760e..5d1961b170f14ae21ca8f69b3c3cd8af28f4478a 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
@@ -26,8 +26,8 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
|
||||
public:
|
||||
static CSSUnparsedValue* Create(
|
||||
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens) {
|
||||
- return MakeGarbageCollected<CSSUnparsedValue>(tokens);
|
||||
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments) {
|
||||
+ return MakeGarbageCollected<CSSUnparsedValue>(segments);
|
||||
}
|
||||
|
||||
// Blink-internal constructor
|
||||
@@ -37,14 +37,14 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
static CSSUnparsedValue* FromCSSValue(const CSSUnparsedDeclarationValue&);
|
||||
static CSSUnparsedValue* FromCSSVariableData(const CSSVariableData&);
|
||||
static CSSUnparsedValue* FromString(const String& string) {
|
||||
- HeapVector<Member<V8CSSUnparsedSegment>> tokens;
|
||||
- tokens.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(string));
|
||||
- return Create(tokens);
|
||||
+ HeapVector<Member<V8CSSUnparsedSegment>> segments;
|
||||
+ segments.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(string));
|
||||
+ return Create(segments);
|
||||
}
|
||||
|
||||
explicit CSSUnparsedValue(
|
||||
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens)
|
||||
- : tokens_(tokens) {}
|
||||
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments)
|
||||
+ : segments_(segments) {}
|
||||
CSSUnparsedValue(const CSSUnparsedValue&) = delete;
|
||||
CSSUnparsedValue& operator=(const CSSUnparsedValue&) = delete;
|
||||
|
||||
@@ -60,10 +60,10 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
V8CSSUnparsedSegment* segment,
|
||||
ExceptionState& exception_state);
|
||||
|
||||
- wtf_size_t length() const { return tokens_.size(); }
|
||||
+ wtf_size_t length() const { return segments_.size(); }
|
||||
|
||||
void Trace(Visitor* visitor) const override {
|
||||
- visitor->Trace(tokens_);
|
||||
+ visitor->Trace(segments_);
|
||||
CSSStyleValue::Trace(visitor);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
StringBuilder&,
|
||||
HeapHashSet<Member<const CSSUnparsedValue>>& values_on_stack) const;
|
||||
|
||||
- HeapVector<Member<V8CSSUnparsedSegment>> tokens_;
|
||||
+ HeapVector<Member<V8CSSUnparsedSegment>> segments_;
|
||||
|
||||
FRIEND_TEST_ALL_PREFIXES(CSSUnparsedDeclarationValueTest, MixedList);
|
||||
};
|
||||
149
patches/chromium/cherry-pick-50b057660b4d.patch
Normal file
149
patches/chromium/cherry-pick-50b057660b4d.patch
Normal file
@@ -0,0 +1,149 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Ninomiya <kainino@chromium.org>
|
||||
Date: Wed, 11 Mar 2026 14:52:44 -0700
|
||||
Subject: [M146] Increment WebGL context generation number on context restore
|
||||
|
||||
Objects created while the context is lost should not be valid to use
|
||||
after the context is restored.
|
||||
- Replace number_of_context_losses_ with a "context generation number"
|
||||
which increments on both context loss and context restore.
|
||||
- Technically, it would make sense to increment it only on context
|
||||
restore, but just in case any logic is relying on the current
|
||||
behavior, increment it in both places.
|
||||
- It's uint64_t just in case someone figures out how to increment it 4
|
||||
billion times.
|
||||
- Remove unused WebGLRenderingContextBase::number_of_context_losses_,
|
||||
left over from before it was moved into WebGLContextObjectSupport.
|
||||
|
||||
(cherry picked from commit c1433740f3ea902fd6b15d63c4865ad60a3761f9)
|
||||
|
||||
Bug: 485935305
|
||||
Change-Id: I1007217c8e69cfb8de4f117e0b7845ca574579c4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7630664
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1593726}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7658823
|
||||
Auto-Submit: Kai Ninomiya <kainino@chromium.org>
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/7680@{#2370}
|
||||
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
|
||||
index 6a3b1416354e7993e7a9ebd25c4ca08593105d9a..83941f8163a5e9425f2df8fd3bb98e1fd37537ad 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
|
||||
@@ -22,7 +22,10 @@ WebGLContextObjectSupport::WebGLContextObjectSupport(
|
||||
|
||||
void WebGLContextObjectSupport::OnContextLost() {
|
||||
DCHECK(!is_lost_);
|
||||
- number_of_context_losses_++;
|
||||
+ // Invalidate all past objects.
|
||||
+ // (It may not be strictly necessary to do this here, since it's also done in
|
||||
+ // OnContextRestored, but we did it historically, and there's no harm in it.)
|
||||
+ context_generation_++;
|
||||
is_lost_ = true;
|
||||
gles2_interface_ = nullptr;
|
||||
extensions_enabled_.reset();
|
||||
@@ -31,6 +34,8 @@ void WebGLContextObjectSupport::OnContextLost() {
|
||||
void WebGLContextObjectSupport::OnContextRestored(
|
||||
gpu::gles2::GLES2Interface* gl) {
|
||||
DCHECK(is_lost_);
|
||||
+ // Invalidate all past objects.
|
||||
+ context_generation_++;
|
||||
is_lost_ = false;
|
||||
gles2_interface_ = gl;
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
|
||||
index 907866bb21acf9647d1c0ecd791e642e96b734fc..ba8b79f8bb9db12058614982a625baaff5546af7 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
|
||||
@@ -33,10 +33,10 @@ class MODULES_EXPORT WebGLContextObjectSupport : public ScriptWrappable {
|
||||
bool IsWebGL2() const { return is_webgl2_; }
|
||||
bool IsLost() const { return is_lost_; }
|
||||
|
||||
- // How many context losses there were, to check whether a WebGLObject was
|
||||
- // created since the last context resoration or before that (and hence invalid
|
||||
- // to use).
|
||||
- uint32_t NumberOfContextLosses() const { return number_of_context_losses_; }
|
||||
+ // Which "generation" the context is on (essentially, how many times it has
|
||||
+ // been restored), to check whether a WebGLObject was created since the last
|
||||
+ // context restoration, or before that (and hence invalid to use).
|
||||
+ uint64_t GetContextGeneration() const { return context_generation_; }
|
||||
|
||||
bool ExtensionEnabled(WebGLExtensionName name) const {
|
||||
return extensions_enabled_.test(name);
|
||||
@@ -65,7 +65,7 @@ class MODULES_EXPORT WebGLContextObjectSupport : public ScriptWrappable {
|
||||
std::bitset<kWebGLExtensionNameCount> extensions_enabled_ = {};
|
||||
raw_ptr<gpu::gles2::GLES2Interface> gles2_interface_ = nullptr;
|
||||
|
||||
- uint32_t number_of_context_losses_ = 0;
|
||||
+ uint64_t context_generation_ = 0;
|
||||
bool is_lost_ = true;
|
||||
bool is_webgl2_;
|
||||
};
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.cc b/third_party/blink/renderer/modules/webgl/webgl_object.cc
|
||||
index 9d984de0073796f23a5038bfc0a51ec676179765..07e0a9a4aa3406a1298a677a3159edadc5f2cbb5 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_object.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_object.cc
|
||||
@@ -33,9 +33,9 @@ namespace blink {
|
||||
|
||||
WebGLObject::WebGLObject(WebGLContextObjectSupport* context)
|
||||
: context_(context),
|
||||
- cached_number_of_context_losses_(std::numeric_limits<uint32_t>::max()) {
|
||||
+ context_generation_at_creation_(std::numeric_limits<uint64_t>::max()) {
|
||||
if (context_) {
|
||||
- cached_number_of_context_losses_ = context->NumberOfContextLosses();
|
||||
+ context_generation_at_creation_ = context->GetContextGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ bool WebGLObject::Validate(const WebGLContextObjectSupport* context) const {
|
||||
// the objects they ever created, so there's no way to invalidate them
|
||||
// eagerly during context loss. The invalidation is discovered lazily.
|
||||
return (context == context_ && context_ != nullptr &&
|
||||
- cached_number_of_context_losses_ == context->NumberOfContextLosses());
|
||||
+ context_generation_at_creation_ == context->GetContextGeneration());
|
||||
}
|
||||
|
||||
void WebGLObject::SetObject(GLuint object) {
|
||||
@@ -71,7 +71,7 @@ void WebGLObject::DeleteObject(gpu::gles2::GLES2Interface* gl) {
|
||||
return;
|
||||
}
|
||||
|
||||
- if (context_->NumberOfContextLosses() != cached_number_of_context_losses_) {
|
||||
+ if (context_->GetContextGeneration() != context_generation_at_creation_) {
|
||||
// This object has been invalidated.
|
||||
return;
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.h b/third_party/blink/renderer/modules/webgl/webgl_object.h
|
||||
index bb56df0f99e8e8432e03442feb9302b8dde27d01..97caa90e34288911b1a827e60c2569544d2b8f69 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_object.h
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_object.h
|
||||
@@ -123,9 +123,9 @@ class WebGLObject : public ScriptWrappable {
|
||||
|
||||
GLuint object_ = 0;
|
||||
|
||||
- // This was the number of context losses of the object's associated
|
||||
- // WebGLContext at the time this object was created.
|
||||
- uint32_t cached_number_of_context_losses_;
|
||||
+ // The context generation number of the associated WebGLContext when the
|
||||
+ // object was created, to prevent reuse in later generations.
|
||||
+ uint64_t context_generation_at_creation_;
|
||||
|
||||
unsigned attachment_count_ = 0;
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
|
||||
index 060563a9955a8564d176177fc389c4f98aa64e9f..f24221cb2f47cfde515179ff945c13756487ebfc 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
|
||||
@@ -2073,8 +2073,6 @@ class MODULES_EXPORT WebGLRenderingContextBase
|
||||
|
||||
bool has_been_drawn_to_ = false;
|
||||
|
||||
- uint32_t number_of_context_losses_ = 0;
|
||||
-
|
||||
// Tracks if the context has ever called glBeginPixelLocalStorageANGLE. If it
|
||||
// has, we need to start using the pixel local storage interrupt mechanism
|
||||
// when we take over the client's context.
|
||||
219
patches/chromium/cherry-pick-5efc7a0127a6.patch
Normal file
219
patches/chromium/cherry-pick-5efc7a0127a6.patch
Normal file
@@ -0,0 +1,219 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 06:21:21 -0800
|
||||
Subject: Validate CSSUnparsedValues upon assignment
|
||||
|
||||
CSS Typed OM has a concept of a value "matching a grammar" (or not)
|
||||
upon assignment to a property [1]. For CSSUnparsedValues, we currently
|
||||
don't perform any significant validation, and as a consequence
|
||||
we allow "invalid" CSSUnparsedDeclarationValues to be created
|
||||
(causing DCHECKs later in the pipeline).
|
||||
|
||||
This CL makes sure values can be parsed using CSSVariableParser::
|
||||
ConsumeUnparsedDeclaration before assignment.
|
||||
|
||||
We're still not handling the value in the context of the destination
|
||||
property, which we probably should. This is also a problem with
|
||||
current state of things, however, so for now the goal is primarily
|
||||
to avoid the DCHECKs in Issue 484751092.
|
||||
|
||||
Finally, I opened an issue against the specification [2], which
|
||||
currently doesn't define any of this.
|
||||
|
||||
[1] https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
|
||||
[2] https://github.com/w3c/csswg-drafts/issues/13547
|
||||
|
||||
Fixed: 484751092
|
||||
Change-Id: Id7f888a6df8c02ade24910900f5d01909cb2dfad
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7595347
|
||||
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
|
||||
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1590110}
|
||||
|
||||
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
|
||||
index edfa73a57d30ebd4f9a7147702df42b836f7d82b..4442ba0872ca4c739596b546e6d3b600c5a31598 100644
|
||||
--- a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
|
||||
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
|
||||
+#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/cssom_keywords.h"
|
||||
#include "third_party/blink/renderer/core/css/properties/css_property.h"
|
||||
@@ -105,8 +106,8 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
|
||||
: CSSPropertyName(id);
|
||||
return unsupported_style_value->IsValidFor(name);
|
||||
}
|
||||
- if (value.GetType() == CSSStyleValue::kUnparsedType) {
|
||||
- return true;
|
||||
+ if (auto* unparsed_value = DynamicTo<CSSUnparsedValue>(value)) {
|
||||
+ return unparsed_value->IsValidDeclarationValue();
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
index 12d70ed096cb1c509a2acf14b7f421273d833d0e..5f9d6a39effe207e44dd84cececebdb6c666f011 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
|
||||
+#include "css_style_value.h"
|
||||
#include "third_party/blink/renderer/core/css/css_unparsed_declaration_value.h"
|
||||
#include "third_party/blink/renderer/core/css/css_variable_data.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_style_variable_reference_value.h"
|
||||
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
|
||||
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
|
||||
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
|
||||
#include "third_party/blink/renderer/core/css_value_keywords.h"
|
||||
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
|
||||
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
|
||||
@@ -136,6 +138,10 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
+bool CSSUnparsedValue::IsValidDeclarationValue() const {
|
||||
+ return IsValidDeclarationValue(ToStringInternal());
|
||||
+}
|
||||
+
|
||||
const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
String unparsed_string = ToStringInternal();
|
||||
|
||||
@@ -144,12 +150,40 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
MakeGarbageCollected<CSSVariableData>());
|
||||
}
|
||||
|
||||
+ CHECK(IsValidDeclarationValue(unparsed_string));
|
||||
+ // The call to IsValidDeclarationValue() above also creates a CSSVariableData
|
||||
+ // to carry out its check. It would be nice to use that here, but WPTs
|
||||
+ // expect leading whitespace to be preserved, even though it's not possible
|
||||
+ // to create such declaration values normally.
|
||||
+ CSSVariableData* variable_data =
|
||||
+ CSSVariableData::Create(unparsed_string,
|
||||
+ /*is_animation_tainted=*/false,
|
||||
+ /*is_attr_tainted=*/false,
|
||||
+ /*needs_variable_resolution=*/false);
|
||||
+
|
||||
// TODO(crbug.com/985028): We should probably propagate the CSSParserContext
|
||||
// to here.
|
||||
- return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
- CSSVariableData::Create(unparsed_string, false /* is_animation_tainted */,
|
||||
- false /* is_attr_tainted */,
|
||||
- false /* needs_variable_resolution */));
|
||||
+ return MakeGarbageCollected<CSSUnparsedDeclarationValue>(variable_data);
|
||||
+}
|
||||
+
|
||||
+bool CSSUnparsedValue::IsValidDeclarationValue(const String& string) {
|
||||
+ CSSParserTokenStream stream(string);
|
||||
+ bool important_unused;
|
||||
+ // This checks that the value does not violate the "argument grammar" [1]
|
||||
+ // of any substitution functions, and that it is a valid <declaration-value>
|
||||
+ // otherwise.
|
||||
+ //
|
||||
+ // [1] https://drafts.csswg.org/css-values-5/#argument-grammar
|
||||
+ //
|
||||
+ // TODO(andruud): 'restricted_value' depends on the destination property.
|
||||
+ return CSSVariableParser::ConsumeUnparsedDeclaration(
|
||||
+ stream,
|
||||
+ /*allow_important_annotation=*/false,
|
||||
+ /*is_animation_tainted=*/false,
|
||||
+ /*must_contain_variable_reference=*/false,
|
||||
+ /*restricted_value=*/false,
|
||||
+ /*comma_ends_declaration=*/false, important_unused,
|
||||
+ *StrictCSSParserContext(SecureContextMode::kInsecureContext));
|
||||
}
|
||||
|
||||
String CSSUnparsedValue::ToStringInternal() const {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
index ec7e3ed708f406d7a61fdb370b2eed8a8297cffb..7fd66aed677e31046a1bd206854b2cbeac07c25b 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
@@ -48,6 +48,14 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
CSSUnparsedValue(const CSSUnparsedValue&) = delete;
|
||||
CSSUnparsedValue& operator=(const CSSUnparsedValue&) = delete;
|
||||
|
||||
+ // True if this CSSUnparsedValue can be converted into
|
||||
+ // a CSSUnparsedDeclarationValue.
|
||||
+ //
|
||||
+ // We may want to ban some invalid values earlier, see:
|
||||
+ // https://github.com/w3c/csswg-drafts/issues/13547
|
||||
+ bool IsValidDeclarationValue() const;
|
||||
+
|
||||
+ // Requires IsValidDeclarationValue()==true.
|
||||
const CSSValue* ToCSSValue() const override;
|
||||
|
||||
StyleValueType GetType() const override { return kUnparsedType; }
|
||||
@@ -68,6 +76,7 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
}
|
||||
|
||||
private:
|
||||
+ static bool IsValidDeclarationValue(const String&);
|
||||
String ToStringInternal() const;
|
||||
String SerializeSegments() const;
|
||||
// Return 'false' if there is a cycle in the serialization.
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html
|
||||
deleted file mode 100644
|
||||
index b92bd62deb71f2623b0265bed099d739cd1fce3a..0000000000000000000000000000000000000000
|
||||
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html
|
||||
+++ /dev/null
|
||||
@@ -1,12 +0,0 @@
|
||||
-<!DOCTYPE html>
|
||||
-<title>Crash Test: Missing variable name in CSSUnparsedValue</title>
|
||||
-<link rel="help" href="https://issues.chromium.org/issues/484811719">
|
||||
-<div id="div"></div>
|
||||
-<script>
|
||||
- for (let i = 0; i < 5000; ++i) {
|
||||
- const bad = new CSSUnparsedValue(['var(,)']);
|
||||
- div.attributeStyleMap.set('--x', bad);
|
||||
- div.attributeStyleMap.get('--x');
|
||||
- }
|
||||
-</script>
|
||||
-<p>PASS if no crash</p>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ce618bf38fe651297b969ffdc16e212dee6a3688
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html
|
||||
@@ -0,0 +1,39 @@
|
||||
+<!DOCTYPE html>
|
||||
+<title>Crash when setting invalid CSSUnparsedValue</title>
|
||||
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/13547">
|
||||
+<div id=target></div>
|
||||
+<script>
|
||||
+ let examples = [
|
||||
+ 'var()',
|
||||
+ 'var(,)',
|
||||
+ 'var(0)',
|
||||
+ 'env()',
|
||||
+ 'env(,)',
|
||||
+ 'env(0)',
|
||||
+ 'attr()',
|
||||
+ 'attr(,)',
|
||||
+ 'attr(0)',
|
||||
+ 'if()',
|
||||
+ 'if(,)',
|
||||
+ 'if(0)',
|
||||
+ '--f()',
|
||||
+ '--f(,)',
|
||||
+ '--f(0)',
|
||||
+ 'thing!!!',
|
||||
+ 'var(--x) !important',
|
||||
+ ];
|
||||
+ // Some of the above cases may be valid. That's fine; just don't crash.
|
||||
+
|
||||
+ for (let e of examples) {
|
||||
+ try {
|
||||
+ let value = new CSSUnparsedValue([e]);
|
||||
+ target.attributeStyleMap.set('width', value);
|
||||
+ // One of the two above statements should likely throw an exception.
|
||||
+ // If they don't, then we should at least not crash on get():
|
||||
+ target.attributeStyleMap.get('width');
|
||||
+ } catch (e) {
|
||||
+ // Intentionally empty.
|
||||
+ }
|
||||
+ target.offsetTop;
|
||||
+ }
|
||||
+</script>
|
||||
@@ -0,0 +1,73 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@anthropic.com>
|
||||
Date: Sat, 7 Mar 2026 23:07:30 -0800
|
||||
Subject: feat: plumb node_integration_in_worker through WorkerSettings
|
||||
|
||||
Copy the node_integration_in_worker flag from the initiating frame's
|
||||
WebPreferences into WorkerSettings at dedicated worker creation time,
|
||||
so the value is readable per-worker on the worker thread rather than
|
||||
relying on a process-wide command line switch. The value is also
|
||||
propagated to nested workers via WorkerSettings::Copy.
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
|
||||
index a0f78583334fdf4912b897e88d8ce518773dbfb1..300c5a3b806222e46388d2f0d906737cf282e52e 100644
|
||||
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
|
||||
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
|
||||
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
|
||||
+#include "third_party/blink/renderer/core/exported/web_view_impl.h"
|
||||
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
|
||||
#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
|
||||
#include "third_party/blink/renderer/core/loader/document_loader.h"
|
||||
@@ -555,6 +556,12 @@ DedicatedWorker::CreateGlobalScopeCreationParams(
|
||||
auto* frame = window->GetFrame();
|
||||
parent_devtools_token = frame->GetDevToolsFrameToken();
|
||||
settings = std::make_unique<WorkerSettings>(frame->GetSettings());
|
||||
+ if (auto* web_local_frame = WebLocalFrameImpl::FromFrame(frame)) {
|
||||
+ if (auto* web_view = web_local_frame->ViewImpl()) {
|
||||
+ settings->SetNodeIntegrationInWorker(
|
||||
+ web_view->GetWebPreferences().node_integration_in_worker);
|
||||
+ }
|
||||
+ }
|
||||
agent_group_scheduler_compositor_task_runner =
|
||||
execution_context->GetScheduler()
|
||||
->ToFrameScheduler()
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_settings.cc b/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
index 45680c5f6ea0c7e89ccf43eb88f8a11e3318c02e..3fa3af62f4e7ba8186441c5e3184b1c04fe32d12 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
@@ -40,6 +40,8 @@ std::unique_ptr<WorkerSettings> WorkerSettings::Copy(
|
||||
old_settings->strictly_block_blockable_mixed_content_;
|
||||
new_settings->generic_font_family_settings_ =
|
||||
old_settings->generic_font_family_settings_;
|
||||
+ new_settings->node_integration_in_worker_ =
|
||||
+ old_settings->node_integration_in_worker_;
|
||||
return new_settings;
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_settings.h b/third_party/blink/renderer/core/workers/worker_settings.h
|
||||
index 45c60dd2c44b05fdd279f759069383479823c7f2..33a2a0337efb9a46293e11d0d09b3fc182ab9618 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_settings.h
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_settings.h
|
||||
@@ -43,6 +43,11 @@ class CORE_EXPORT WorkerSettings {
|
||||
return generic_font_family_settings_;
|
||||
}
|
||||
|
||||
+ bool NodeIntegrationInWorker() const { return node_integration_in_worker_; }
|
||||
+ void SetNodeIntegrationInWorker(bool value) {
|
||||
+ node_integration_in_worker_ = value;
|
||||
+ }
|
||||
+
|
||||
private:
|
||||
void CopyFlagValuesFromSettings(Settings*);
|
||||
|
||||
@@ -54,6 +59,7 @@ class CORE_EXPORT WorkerSettings {
|
||||
bool strict_mixed_content_checking_ = false;
|
||||
bool allow_running_of_insecure_content_ = false;
|
||||
bool strictly_block_blockable_mixed_content_ = false;
|
||||
+ bool node_integration_in_worker_ = false;
|
||||
|
||||
GenericFontFamilySettings generic_font_family_settings_;
|
||||
};
|
||||
@@ -32,7 +32,8 @@ async function main () {
|
||||
}));
|
||||
const hitRate = stats.CacheHit / (stats.Remote + stats.CacheHit + stats.LocalFallback);
|
||||
|
||||
console.log(`Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
|
||||
const messagePrefix = process.env.GITHUB_ACTIONS ? '::notice title=Build Stats::' : '';
|
||||
console.log(`${messagePrefix}Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
|
||||
|
||||
if (uploadStats) {
|
||||
if (!process.env.DD_API_KEY) {
|
||||
|
||||
@@ -317,6 +317,12 @@ void BaseWindow::OnWindowSheetEnd() {
|
||||
Emit("sheet-end");
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowIsKeyChanged(bool is_key) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
window()->SetActive(is_key);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowEnterHtmlFullScreen() {
|
||||
Emit("enter-html-full-screen");
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
void OnWindowRotateGesture(float rotation) override;
|
||||
void OnWindowSheetBegin() override;
|
||||
void OnWindowSheetEnd() override;
|
||||
void OnWindowIsKeyChanged(bool is_key) override;
|
||||
void OnWindowEnterFullScreen() override;
|
||||
void OnWindowLeaveFullScreen() override;
|
||||
void OnWindowEnterHtmlFullScreen() override;
|
||||
|
||||
@@ -280,16 +280,22 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowShow() {
|
||||
if (!web_contents_shown_) {
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
}
|
||||
BaseWindow::OnWindowShow();
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowHide() {
|
||||
web_contents()->WasOccluded();
|
||||
web_contents_shown_ = false;
|
||||
BaseWindow::OnWindowHide();
|
||||
}
|
||||
|
||||
void BrowserWindow::Show() {
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
BaseWindow::Show();
|
||||
}
|
||||
|
||||
@@ -298,6 +304,7 @@ void BrowserWindow::ShowInactive() {
|
||||
if (IsModal())
|
||||
return;
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
BaseWindow::ShowInactive();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class BrowserWindow : public BaseWindow,
|
||||
// Helpers.
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
bool web_contents_shown_ = false;
|
||||
v8::Global<v8::Value> web_contents_view_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ void UtilityProcessWrapper::OnServiceProcessLaunch(
|
||||
EmitWithoutEvent("spawn");
|
||||
}
|
||||
|
||||
void UtilityProcessWrapper::HandleTermination(uint64_t exit_code) {
|
||||
void UtilityProcessWrapper::HandleTermination(uint32_t exit_code) {
|
||||
// HandleTermination is called from multiple callsites,
|
||||
// we need to ensure we only process it for the first callsite.
|
||||
if (terminated_)
|
||||
@@ -326,7 +326,7 @@ void UtilityProcessWrapper::CloseConnectorPort() {
|
||||
}
|
||||
}
|
||||
|
||||
void UtilityProcessWrapper::Shutdown(uint64_t exit_code) {
|
||||
void UtilityProcessWrapper::Shutdown(uint32_t exit_code) {
|
||||
node_service_remote_.reset();
|
||||
HandleTermination(exit_code);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class UtilityProcessWrapper final
|
||||
static gin_helper::Handle<UtilityProcessWrapper> Create(gin::Arguments* args);
|
||||
static raw_ptr<UtilityProcessWrapper> FromProcessId(base::ProcessId pid);
|
||||
|
||||
void Shutdown(uint64_t exit_code);
|
||||
void Shutdown(uint32_t exit_code);
|
||||
|
||||
// gin_helper::Wrappable
|
||||
static gin::DeprecatedWrapperInfo kWrapperInfo;
|
||||
@@ -77,7 +77,7 @@ class UtilityProcessWrapper final
|
||||
void OnServiceProcessLaunch(const base::Process& process);
|
||||
void CloseConnectorPort();
|
||||
|
||||
void HandleTermination(uint64_t exit_code);
|
||||
void HandleTermination(uint32_t exit_code);
|
||||
|
||||
void PostMessage(gin::Arguments* args);
|
||||
bool Kill();
|
||||
|
||||
@@ -87,8 +87,8 @@ MouseDownImpl g_nsnextstepframe_mousedown;
|
||||
(electron::NativeWindowMac*)[(id)self.window shell];
|
||||
if (shell && !shell->has_frame())
|
||||
[self cr_mouseDownOnFrameView:event];
|
||||
g_nsthemeframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
g_nsthemeframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
|
||||
- (void)swiz_nsnextstepframe_mouseDown:(NSEvent*)event {
|
||||
@@ -98,8 +98,8 @@ MouseDownImpl g_nsnextstepframe_mousedown;
|
||||
if (shell && !shell->has_frame()) {
|
||||
[self cr_mouseDownOnFrameView:event];
|
||||
}
|
||||
g_nsnextstepframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
g_nsnextstepframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
|
||||
- (void)swiz_nsview_swipeWithEvent:(NSEvent*)event {
|
||||
|
||||
@@ -343,9 +343,6 @@ void WebContentsPreferences::AppendCommandLineSwitches(
|
||||
command_line->AppendSwitchASCII(::switches::kDisableBlinkFeatures,
|
||||
*disable_blink_features_);
|
||||
|
||||
if (node_integration_in_worker_)
|
||||
command_line->AppendSwitch(switches::kNodeIntegrationInWorker);
|
||||
|
||||
// We are appending args to a webContents so let's save the current state
|
||||
// of our preferences object so that during the lifetime of the WebContents
|
||||
// we can fetch the options used to initially configure the WebContents
|
||||
|
||||
@@ -279,10 +279,6 @@ inline constexpr base::cstring_view kAppPath = "app-path";
|
||||
// The command line switch versions of the options.
|
||||
inline constexpr base::cstring_view kScrollBounce = "scroll-bounce";
|
||||
|
||||
// Command switch passed to renderer process to control nodeIntegration.
|
||||
inline constexpr base::cstring_view kNodeIntegrationInWorker =
|
||||
"node-integration-in-worker";
|
||||
|
||||
// Widevine options
|
||||
// Path to Widevine CDM binaries.
|
||||
inline constexpr base::cstring_view kWidevineCdmPath = "widevine-cdm-path";
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "shell/renderer/electron_render_frame_observer.h"
|
||||
#include "shell/renderer/web_worker_observer.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worker_global_scope.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worker_settings.h" // nogncheck
|
||||
|
||||
#if BUILDFLAG(IS_LINUX) && (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64))
|
||||
#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX
|
||||
@@ -207,44 +209,54 @@ void ElectronRendererClient::WillReleaseScriptContext(
|
||||
electron_bindings_->EnvironmentDestroyed(env);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {
|
||||
namespace {
|
||||
|
||||
bool WorkerHasNodeIntegration(blink::ExecutionContext* ec) {
|
||||
// We do not create a Node.js environment in service or shared workers
|
||||
// owing to an inability to customize sandbox policies in these workers
|
||||
// given that they're run out-of-process.
|
||||
// Also avoid creating a Node.js environment for worklet global scope
|
||||
// created on the main thread.
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (ec->IsServiceWorkerGlobalScope() || ec->IsSharedWorkerGlobalScope() ||
|
||||
ec->IsMainThreadWorkletGlobalScope())
|
||||
return false;
|
||||
|
||||
auto* wgs = blink::DynamicTo<blink::WorkerGlobalScope>(ec);
|
||||
if (!wgs)
|
||||
return false;
|
||||
|
||||
// Read the nodeIntegrationInWorker preference from the worker's settings,
|
||||
// which were copied from the initiating frame's WebPreferences at worker
|
||||
// creation time. This ensures that in-process child windows with different
|
||||
// webPreferences get the correct per-frame value rather than a process-wide
|
||||
// value.
|
||||
auto* worker_settings = wgs->GetWorkerSettings();
|
||||
return worker_settings && worker_settings->NodeIntegrationInWorker();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (!WorkerHasNodeIntegration(ec))
|
||||
return;
|
||||
|
||||
// This won't be correct for in-process child windows with webPreferences
|
||||
// that have a different value for nodeIntegrationInWorker
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kNodeIntegrationInWorker)) {
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
return;
|
||||
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
return;
|
||||
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (ec->IsServiceWorkerGlobalScope() || ec->IsSharedWorkerGlobalScope() ||
|
||||
ec->IsMainThreadWorkletGlobalScope())
|
||||
if (!WorkerHasNodeIntegration(ec))
|
||||
return;
|
||||
|
||||
// TODO(loc): Note that this will not be correct for in-process child windows
|
||||
// with webPreferences that have a different value for nodeIntegrationInWorker
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kNodeIntegrationInWorker)) {
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
current->ContextWillDestroy(context);
|
||||
}
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
current->ContextWillDestroy(context);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::SetUpWebAssemblyTrapHandler() {
|
||||
|
||||
@@ -129,6 +129,22 @@ describe('utilityProcess module', () => {
|
||||
expect(code).to.equal(exitCode);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('emits correct exit code when high bit is set on Windows', async () => {
|
||||
// NTSTATUS code with high bit set should not be mangled by sign extension.
|
||||
const exitCode = 0xC0000005;
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'custom-exit.js'), [`--exitCode=${exitCode}`]);
|
||||
const [code] = await once(child, 'exit');
|
||||
expect(code).to.equal(exitCode);
|
||||
});
|
||||
|
||||
ifit(process.platform !== 'win32')('emits correct exit code when child process crashes on posix', async () => {
|
||||
// Crash exit codes should not be sign-extended to large 64-bit values.
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'crash.js'));
|
||||
const [code] = await once(child, 'exit');
|
||||
expect(code).to.not.equal(0);
|
||||
expect(code).to.be.lessThanOrEqual(0xFFFFFFFF);
|
||||
});
|
||||
|
||||
it('does not run JS after process.exit is called', async () => {
|
||||
const file = path.join(os.tmpdir(), `no-js-after-exit-log-${Math.random()}`);
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'no-js-after-exit.js'), [`--testPath=${file}`]);
|
||||
|
||||
@@ -1372,6 +1372,89 @@ describe('chromium features', () => {
|
||||
expect(data).to.equal('object function object function');
|
||||
});
|
||||
|
||||
it('Worker does not have node integration when nodeIntegrationInWorker is disabled via setWindowOpenHandler', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
|
||||
w.webContents.setWindowOpenHandler(() => ({
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
contextIsolation: true
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
await w.loadURL(`file://${fixturesPath}/pages/blank.html`);
|
||||
const childCreated = once(app, 'browser-window-created') as Promise<[any, BrowserWindow]>;
|
||||
w.webContents.executeJavaScript(`window.open(${JSON.stringify(`file://${fixturesPath}/pages/blank.html`)}); void 0;`);
|
||||
const [, child] = await childCreated;
|
||||
await once(child.webContents, 'did-finish-load');
|
||||
|
||||
const data = await child.webContents.executeJavaScript(`
|
||||
const worker = new Worker('../workers/worker_node.js');
|
||||
new Promise((resolve) => { worker.onmessage = e => resolve(e.data); })
|
||||
`);
|
||||
expect(data).to.equal('undefined undefined undefined undefined');
|
||||
});
|
||||
|
||||
it('Worker has node integration when nodeIntegrationInWorker is enabled via setWindowOpenHandler', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: false,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
|
||||
w.webContents.setWindowOpenHandler(() => ({
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
await w.loadURL(`file://${fixturesPath}/pages/blank.html`);
|
||||
|
||||
// Parent's workers should NOT have node integration.
|
||||
const parentData = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve) => {
|
||||
const worker = new Worker('../workers/worker_node.js');
|
||||
worker.onmessage = e => resolve(e.data);
|
||||
})
|
||||
`);
|
||||
expect(parentData).to.equal('undefined undefined undefined undefined');
|
||||
|
||||
const childCreated = once(app, 'browser-window-created') as Promise<[any, BrowserWindow]>;
|
||||
w.webContents.executeJavaScript(`window.open(${JSON.stringify(`file://${fixturesPath}/pages/blank.html`)}); void 0;`);
|
||||
const [, child] = await childCreated;
|
||||
await once(child.webContents, 'did-finish-load');
|
||||
|
||||
// Child's workers should have node integration.
|
||||
const childData = await child.webContents.executeJavaScript(`
|
||||
new Promise((resolve) => {
|
||||
const worker = new Worker('../workers/worker_node.js');
|
||||
worker.onmessage = e => resolve(e.data);
|
||||
})
|
||||
`);
|
||||
expect(childData).to.equal('object function object function');
|
||||
});
|
||||
|
||||
it('Worker has access to fetch-dependent interfaces with nodeIntegrationInWorker', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
|
||||
7
spec/fixtures/crash-cases/dialog-on-invalid-url/index.html
vendored
Normal file
7
spec/fixtures/crash-cases/dialog-on-invalid-url/index.html
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
window.open('javascript:alert()');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
22
spec/fixtures/crash-cases/dialog-on-invalid-url/index.js
vendored
Normal file
22
spec/fixtures/crash-cases/dialog-on-invalid-url/index.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
console.error(reason);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
window.webContents.once('did-frame-navigate', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const win = new BrowserWindow({ show: false });
|
||||
win.loadFile('index.html');
|
||||
});
|
||||
Reference in New Issue
Block a user