mirror of
https://github.com/electron/electron.git
synced 2026-05-02 03:00:22 -04:00
chore: bump chromium to 148.0.7778.40 (42-x-y) (#51126)
* chore: bump chromium in DEPS to 148.0.7778.40 * chore: update patches * chore: update patches after rebase --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
This commit is contained in:
committed by
GitHub
parent
2f3f6379bf
commit
4799b61742
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'148.0.7778.5',
|
||||
'148.0.7778.40',
|
||||
'node_version':
|
||||
'v24.15.0',
|
||||
'nan_version':
|
||||
|
||||
@@ -149,11 +149,4 @@ fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
|
||||
fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
|
||||
cherry-pick-b173791bf402.patch
|
||||
cherry-pick-be87466afecb.patch
|
||||
cherry-pick-c0390bcd64ba.patch
|
||||
cherry-pick-1b69067db7d2.patch
|
||||
cherry-pick-d513cd2fe668.patch
|
||||
cherry-pick-dc5e20c4c055.patch
|
||||
cherry-pick-847b11ad2fa3.patch
|
||||
cherry-pick-fc79e8cc2dfc.patch
|
||||
|
||||
@@ -8,7 +8,7 @@ categories in use are known / declared. This patch is required for us
|
||||
to introduce a new Electron category for Electron-specific tracing.
|
||||
|
||||
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
|
||||
index 440349df6c5767fe3f93b51f78b33bf9d3bb5c1a..85c6f973788938b6a48a7a89e9fa803dc1030580 100644
|
||||
index 8bc03cb77ebdfe991e0ebe05aa187dfdea8c2764..976221d0303036ecae500b7861931ff96e9ea0c1 100644
|
||||
--- a/base/trace_event/builtin_categories.h
|
||||
+++ b/base/trace_event/builtin_categories.h
|
||||
@@ -133,6 +133,7 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
|
||||
|
||||
@@ -28,7 +28,7 @@ index 0af4d4b75d0519fabcb5d48bd9d5bd465bc80e92..eb6b23655afaa268f25d99301a0853aa
|
||||
":chrome_dll",
|
||||
":chrome_exe_version",
|
||||
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
|
||||
index e91f97276866bd500720962c74acaca2c22fff7c..22867153821d2b1e83feb1a2a7a6b8c26ba776eb 100644
|
||||
index cf5a56ee6eeeba1b7060e558874612c44f564560..fc0914f7f1feb615516c2120a37329a106874633 100644
|
||||
--- a/chrome/test/BUILD.gn
|
||||
+++ b/chrome/test/BUILD.gn
|
||||
@@ -7737,6 +7737,10 @@ test("unit_tests") {
|
||||
@@ -42,7 +42,7 @@ index e91f97276866bd500720962c74acaca2c22fff7c..22867153821d2b1e83feb1a2a7a6b8c2
|
||||
deps += [
|
||||
"//chrome:other_version",
|
||||
"//chrome//services/util_win:unit_tests",
|
||||
@@ -8711,6 +8715,10 @@ test("unit_tests") {
|
||||
@@ -8712,6 +8716,10 @@ test("unit_tests") {
|
||||
"../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc",
|
||||
]
|
||||
|
||||
@@ -53,7 +53,7 @@ index e91f97276866bd500720962c74acaca2c22fff7c..22867153821d2b1e83feb1a2a7a6b8c2
|
||||
sources += [
|
||||
# The importer code is not used on Android.
|
||||
"../common/importer/firefox_importer_utils_unittest.cc",
|
||||
@@ -8767,7 +8775,7 @@ test("unit_tests") {
|
||||
@@ -8768,7 +8776,7 @@ test("unit_tests") {
|
||||
# TODO(crbug.com/417513088): Maybe merge with the non-android `deps` declaration above?
|
||||
deps += [
|
||||
"../browser/screen_ai:screen_ai_install_state",
|
||||
|
||||
@@ -9,10 +9,10 @@ potentially prevent a window from being created.
|
||||
TODO(loc): this patch is currently broken.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index ac474e220d411dec278c40448f038b25e6788d2a..e4ff8f11bed9e53f3134068492ac94b4c9bb4df2 100644
|
||||
index e416207b5f421b15827f0aa186234f425f9100df..d3a9d1b30dbbf7952203f0c93a068f493550fa79 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -10228,6 +10228,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
@@ -10242,6 +10242,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
last_committed_origin_, params->window_container_type,
|
||||
params->target_url, params->referrer.To<Referrer>(),
|
||||
params->frame_name, params->disposition, *params->features,
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Vasilii Sukhanov <vasilii@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 07:48:21 -0700
|
||||
Subject: Fix cross-domain password leak via manual-fallback preview
|
||||
|
||||
In PasswordManualFallbackFlow::DidSelectSuggestion, when a user selects
|
||||
a password suggestion, the browser process sends the cleartext password
|
||||
to the renderer for previewing. If the suggestion is cross-domain, this
|
||||
leak happens without consent or auth.
|
||||
|
||||
This CL fixes this by omitting the password in the preview message for
|
||||
all the cases by sending the fake string.
|
||||
|
||||
Fixed: 498269651
|
||||
Change-Id: Ic9546114c453f05de1030f05c7a9830b39d73038
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735152
|
||||
Commit-Queue: Vasilii Sukhanov <vasilii@chromium.org>
|
||||
Reviewed-by: Anna Tsvirchkova <atsvirchkova@google.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611490}
|
||||
|
||||
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow.cc b/components/password_manager/core/browser/password_manual_fallback_flow.cc
|
||||
index 6fd5468b061949c7f4a45a29b07e1325bde629e3..47bd86d5fd70a849a13b6837a98f3009fbc10ea6 100644
|
||||
--- a/components/password_manager/core/browser/password_manual_fallback_flow.cc
|
||||
+++ b/components/password_manager/core/browser/password_manual_fallback_flow.cc
|
||||
@@ -213,12 +213,13 @@ void PasswordManualFallbackFlow::DidSelectSuggestion(
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
+ const auto payload =
|
||||
+ suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>();
|
||||
password_manager_driver_->PreviewSuggestionById(
|
||||
form->username_element_renderer_id,
|
||||
form->password_element_renderer_id,
|
||||
GetUsernameFromLabel(suggestion.labels[0][0].value),
|
||||
- suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>()
|
||||
- .password);
|
||||
+ std::u16string(payload.password.length(), '*'));
|
||||
break;
|
||||
}
|
||||
case autofill::SuggestionType::kPasswordFieldByFieldFilling:
|
||||
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
|
||||
index 8b51bbcab5ec65562eed443ea9ba80dbaf8cba63..b99c6531aeb2d4c46e0a88cc0478e18c117c2bb6 100644
|
||||
--- a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
|
||||
+++ b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
|
||||
@@ -656,7 +656,7 @@ TEST_F(PasswordManualFallbackFlowTest,
|
||||
EXPECT_CALL(driver(), PreviewSuggestionById(form.username_element_renderer_id,
|
||||
form.password_element_renderer_id,
|
||||
std::u16string(u"username"),
|
||||
- std::u16string(u"password")));
|
||||
+ std::u16string(u"********")));
|
||||
Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
|
||||
SuggestionType::kPasswordEntry, u"google.com",
|
||||
CreateTestPasswordDetails());
|
||||
@@ -667,6 +667,40 @@ TEST_F(PasswordManualFallbackFlowTest,
|
||||
flow().DidSelectSuggestion(suggestion);
|
||||
}
|
||||
|
||||
+// Test that password manual fallback suggestion is previewed without password
|
||||
+// if the suggestion is cross-domain.
|
||||
+TEST_F(PasswordManualFallbackFlowTest,
|
||||
+ SelectFillFullFormSuggestion_CrossDomain_TriggeredOnAPasswordForm) {
|
||||
+ InitializeFlow();
|
||||
+ ProcessPasswordStoreUpdates();
|
||||
+
|
||||
+ PasswordForm form;
|
||||
+ form.username_element_renderer_id = MakeFieldRendererId();
|
||||
+ form.password_element_renderer_id = MakeFieldRendererId();
|
||||
+ // Simulate that the field is/isn't classified as target filling password.
|
||||
+ EXPECT_CALL(password_form_cache(),
|
||||
+ GetPasswordForm(_, form.username_element_renderer_id))
|
||||
+ .WillRepeatedly(Return(&form));
|
||||
+
|
||||
+ flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
|
||||
+ TextDirection::LEFT_TO_RIGHT);
|
||||
+
|
||||
+ // Expect that the password is empty in the preview call.
|
||||
+ EXPECT_CALL(driver(), PreviewSuggestionById(form.username_element_renderer_id,
|
||||
+ form.password_element_renderer_id,
|
||||
+ std::u16string(u"username"),
|
||||
+ std::u16string(u"********")));
|
||||
+ Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
|
||||
+ SuggestionType::kPasswordEntry, u"google.com",
|
||||
+ Suggestion::PasswordSuggestionDetails(u"username", u"password",
|
||||
+ "https://cross-domain.com/",
|
||||
+ u"cross-domain.com",
|
||||
+ /*is_cross_domain=*/true));
|
||||
+ suggestion.labels = {{Suggestion::Text(u"username")}};
|
||||
+ suggestion.acceptability = Suggestion::Acceptability::kAcceptable;
|
||||
+ flow().DidSelectSuggestion(suggestion);
|
||||
+}
|
||||
+
|
||||
// Test that only password field is previewed if the credential doesn't have
|
||||
// a username saved for it.
|
||||
TEST_F(PasswordManualFallbackFlowTest,
|
||||
@@ -687,7 +721,7 @@ TEST_F(PasswordManualFallbackFlowTest,
|
||||
EXPECT_CALL(driver(), PreviewSuggestionById(FieldRendererId(),
|
||||
form.password_element_renderer_id,
|
||||
std::u16string(),
|
||||
- std::u16string(u"password")));
|
||||
+ std::u16string(u"********")));
|
||||
Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
|
||||
SuggestionType::kPasswordEntry, u"google.com",
|
||||
CreateTestPasswordDetails());
|
||||
@@ -1,216 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Joey Arhar <jarhar@chromium.org>
|
||||
Date: Fri, 10 Apr 2026 12:19:11 -0700
|
||||
Subject: Fix crashes when restoring <selectedcontent> with <input>
|
||||
|
||||
When restoring form control state, the DOM could be modified to add or
|
||||
remove more listed elements to the form if a select element is being
|
||||
restored which has a selectedcontent element.
|
||||
|
||||
Fixed: 499384399
|
||||
Change-Id: I18f69c30ae25396c53625f7a3172626b79de3ae3
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7732030
|
||||
Reviewed-by: Joey Arhar <jarhar@chromium.org>
|
||||
Commit-Queue: Joey Arhar <jarhar@chromium.org>
|
||||
Reviewed-by: Dominic Farolino <dom@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1613032}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/html/forms/form_controller.cc b/third_party/blink/renderer/core/html/forms/form_controller.cc
|
||||
index abacf373d30debc17498eaeb7cd940972aed2a3a..c4cb59678eeacc2d3fb9f69a30df8674154dbf37 100644
|
||||
--- a/third_party/blink/renderer/core/html/forms/form_controller.cc
|
||||
+++ b/third_party/blink/renderer/core/html/forms/form_controller.cc
|
||||
@@ -492,8 +492,10 @@ void FormController::RestoreControlStateIn(HTMLFormElement& form) {
|
||||
if (!document_->HasFinishedParsing())
|
||||
return;
|
||||
EventQueueScope scope;
|
||||
- const ListedElement::List& elements = form.ListedElements();
|
||||
- for (const auto& control : elements) {
|
||||
+ // Make a copy of the list because the DOM could be modified during
|
||||
+ // restoration of a <select> with a <selectedcontent> element.
|
||||
+ ListedElement::List elements_copy(form.ListedElements());
|
||||
+ for (const auto& control : elements_copy) {
|
||||
if (!control->ClassSupportsStateRestore())
|
||||
continue;
|
||||
if (OwnerFormForState(*control) != &form)
|
||||
@@ -550,7 +552,11 @@ void FormController::RestoreAllControlsInDocumentOrder() {
|
||||
return;
|
||||
HeapHashSet<Member<HTMLFormElement>> finished_forms;
|
||||
EventQueueScope scope;
|
||||
- for (auto& control : document_state_->GetControlList()) {
|
||||
+ // Make a copy of the list because the DOM could be modified during
|
||||
+ // restoration of a <select> with a <selectedcontent> element.
|
||||
+ DocumentState::ControlList control_list_copy(
|
||||
+ document_state_->GetControlList());
|
||||
+ for (auto& control : control_list_copy) {
|
||||
auto* owner = OwnerFormForState(*control);
|
||||
if (!owner)
|
||||
RestoreControlStateFor(*control);
|
||||
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
|
||||
index ef47961c57cd15f8c59a4554165568c7f13c0d0a..54331107dd22c6b534327ffd5c282ca3220345de 100644
|
||||
--- a/third_party/blink/web_tests/VirtualTestSuites
|
||||
+++ b/third_party/blink/web_tests/VirtualTestSuites
|
||||
@@ -3067,7 +3067,7 @@
|
||||
],
|
||||
"bases": [
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-in-option-crash.html",
|
||||
- "external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html",
|
||||
+ "external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html",
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-color.html",
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-nested.html",
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontentelement-attr.tentative.html"
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/resources/selectedcontent-input.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/resources/selectedcontent-input.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..847f42ac304835c2049cf434a4dec68814ad533c
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/resources/selectedcontent-input.html
|
||||
@@ -0,0 +1,27 @@
|
||||
+<!DOCTYPE html>
|
||||
+<style>
|
||||
+select,::picker(select) {
|
||||
+ appearance: base-select;
|
||||
+}
|
||||
+</style>
|
||||
+<form action="blank.html">
|
||||
+ <select>
|
||||
+ <button>
|
||||
+ <selectedcontent></selectedcontent>
|
||||
+ </button>
|
||||
+ <option id=one>one</option>
|
||||
+ <option id=two>two</option>
|
||||
+ </select>
|
||||
+</form>
|
||||
+
|
||||
+<script>
|
||||
+window.createInput = () => {
|
||||
+ const selectedcontent = document.querySelector('selectedcontent');
|
||||
+ const input = document.createElement('input');
|
||||
+ window.input = input;
|
||||
+ input.name = 'input';
|
||||
+ selectedcontent.innerHTML = '';
|
||||
+ selectedcontent.appendChild(input);
|
||||
+};
|
||||
+window.createInput();
|
||||
+</script>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html
|
||||
deleted file mode 100644
|
||||
index da5fe450abbae0d19826021f114cc6388f97bc57..0000000000000000000000000000000000000000
|
||||
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html
|
||||
+++ /dev/null
|
||||
@@ -1,35 +0,0 @@
|
||||
-<!DOCTYPE html>
|
||||
-<link rel=author href="mailto:jarhar@chromium.org">
|
||||
-<link rel=help href="https://github.com/whatwg/html/issues/9799">
|
||||
-<script src="/resources/testharness.js"></script>
|
||||
-<script src="/resources/testharnessreport.js"></script>
|
||||
-<script src="/resources/testdriver.js"></script>
|
||||
-<script src="/resources/testdriver-vendor.js"></script>
|
||||
-
|
||||
-<iframe src="resources/selectedcontent-restore-iframe.html"></iframe>
|
||||
-
|
||||
-<script>
|
||||
-const iframe = document.querySelector('iframe');
|
||||
-promise_test(async () => {
|
||||
- await new Promise(resolve => iframe.onload = resolve);
|
||||
- await test_driver.bless();
|
||||
-
|
||||
- iframe.contentDocument.querySelector('select').value = 'two';
|
||||
- assert_equals(iframe.contentDocument.querySelector('select').value, 'two',
|
||||
- 'Assining two to select.value should work.');
|
||||
- iframe.contentDocument.querySelector('form').submit();
|
||||
- await new Promise(resolve => iframe.onload = resolve);
|
||||
-
|
||||
- await test_driver.bless();
|
||||
- iframe.contentWindow.history.back();
|
||||
- await new Promise(resolve => iframe.onload = resolve);
|
||||
- await new Promise(requestAnimationFrame);
|
||||
- await new Promise(requestAnimationFrame);
|
||||
-
|
||||
- assert_equals(iframe.contentDocument.querySelector('select').value, 'two',
|
||||
- 'The selects value should be restored after navigating back.');
|
||||
- assert_equals(iframe.contentDocument.querySelector('selectedcontent').innerHTML,
|
||||
- iframe.contentDocument.querySelector('option[value=two]').innerHTML,
|
||||
- 'selectedcontent.innerHTML should match the selected <option>');
|
||||
-}, '<selectedcontent> should be up to date after form restoration.');
|
||||
-</script>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1d0064659cd9df06d6267261bf0b39b3fb29aeef
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html
|
||||
@@ -0,0 +1,76 @@
|
||||
+<!DOCTYPE html>
|
||||
+<link rel=author href="mailto:jarhar@chromium.org">
|
||||
+<link rel=help href="https://github.com/whatwg/html/issues/9799">
|
||||
+<script src="/resources/testharness.js"></script>
|
||||
+<script src="/resources/testharnessreport.js"></script>
|
||||
+<script src="/resources/testdriver.js"></script>
|
||||
+<script src="/resources/testdriver-vendor.js"></script>
|
||||
+
|
||||
+<!-- This test is marked optional because form control restoration is not explicitly specified. -->
|
||||
+
|
||||
+<iframe id=iframe1 src="resources/selectedcontent-restore-iframe.html"></iframe>
|
||||
+<iframe id=iframe2 src="resources/selectedcontent-input.html"></iframe>
|
||||
+
|
||||
+<script>
|
||||
+const iframe1 = document.getElementById('iframe1');
|
||||
+const iframe2 = document.getElementById('iframe2');
|
||||
+const iframe1load = new Promise(resolve => iframe1.onload = resolve);
|
||||
+const iframe2load = new Promise(resolve => iframe2.onload = resolve);
|
||||
+
|
||||
+promise_test(async () => {
|
||||
+ await iframe1load;
|
||||
+ await test_driver.bless();
|
||||
+
|
||||
+ iframe1.contentDocument.querySelector('select').value = 'two';
|
||||
+ assert_equals(iframe1.contentDocument.querySelector('select').value, 'two',
|
||||
+ 'Assigning two to select.value should work.');
|
||||
+ iframe1.contentDocument.querySelector('form').submit();
|
||||
+ await new Promise(resolve => iframe1.onload = resolve);
|
||||
+
|
||||
+ await test_driver.bless();
|
||||
+ iframe1.contentWindow.history.back();
|
||||
+ // Form controls are restored immediately after the load event is fired, so
|
||||
+ // one rAF is added after awaiting the load event. See
|
||||
+ // LocalDOMWindow::DispatchLoadAndPageshowEvents.
|
||||
+ await new Promise(resolve => iframe1.onload = resolve);
|
||||
+ await new Promise(requestAnimationFrame);
|
||||
+
|
||||
+ assert_equals(iframe1.contentDocument.querySelector('select').value, 'two',
|
||||
+ 'The selects value should be restored after navigating back.');
|
||||
+ assert_equals(iframe1.contentDocument.querySelector('selectedcontent').innerHTML,
|
||||
+ iframe1.contentDocument.querySelector('option[value=two]').innerHTML,
|
||||
+ 'selectedcontent.innerHTML should match the selected <option>');
|
||||
+}, '<selectedcontent> should be up to date after form restoration.');
|
||||
+
|
||||
+promise_test(async () => {
|
||||
+ await iframe2load;
|
||||
+ await test_driver.bless();
|
||||
+
|
||||
+ iframe2.contentDocument.querySelector('select').value = 'two';
|
||||
+ iframe2.contentWindow.createInput();
|
||||
+ iframe2.contentDocument.querySelector('input').value = 'value';
|
||||
+ iframe2.contentDocument.querySelector('form').submit();
|
||||
+ await new Promise(resolve => iframe2.onload = resolve);
|
||||
+
|
||||
+ await test_driver.bless();
|
||||
+ iframe2.contentWindow.history.back();
|
||||
+ // Form controls are restored immediately after the load event is fired, so
|
||||
+ // one rAF is added after awaiting the load event. See
|
||||
+ // LocalDOMWindow::DispatchLoadAndPageshowEvents.
|
||||
+ await new Promise(resolve => iframe2.onload = resolve);
|
||||
+ await new Promise(requestAnimationFrame);
|
||||
+
|
||||
+ // A crash would happen here because the form restoration code would iterate
|
||||
+ // over all of the form controls and remove an input element to restore during
|
||||
+ // restoration of the selectedcontent element, then try to restore the
|
||||
+ // disconnected input.
|
||||
+
|
||||
+ assert_equals(iframe2.contentDocument.querySelector('select').value, 'two',
|
||||
+ 'The selects value should be restored after navigating back.');
|
||||
+ assert_equals(iframe2.contentDocument.querySelector('selectedcontent').innerHTML,
|
||||
+ iframe2.contentDocument.getElementById('two').innerHTML,
|
||||
+ 'selectedcontent.innerHTML should match the selected <option>');
|
||||
+ assert_equals(iframe2.contentWindow.input.value, '',
|
||||
+ 'The text inputs value should not be restored because it was removed before restoring.');
|
||||
+}, '<input> inside <selectedcontent> should be restored after form submission.');
|
||||
+</script>
|
||||
@@ -1,53 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: p0-tato <smartphonewithbear@gmail.com>
|
||||
Date: Mon, 13 Apr 2026 14:50:07 -0700
|
||||
Subject: Fix dangling pointers in OpenXrSpatialFrameworkManager
|
||||
|
||||
Pointers to vector elements were collected during emplace_back,
|
||||
which invalidates them on reallocation. Split into two loops
|
||||
and reserve the correct capacity.
|
||||
|
||||
Bug: 497724498
|
||||
Change-Id: I204534bc1bd1522fe03db86f03c2c3e0d285631c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735242
|
||||
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
|
||||
Reviewed-by: Brian Sheedy <bsheedy@chromium.org>
|
||||
Reviewed-by: Brandon Jones <bajones@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1613990}
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index 700ad1f495549eac39242e5d3b489245eb1b633d..5ef10597084e19b283209ee9833c56a7a0346187 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -736,6 +736,7 @@ Jihoon Chung <jihoon@gmail.com>
|
||||
Jihun Brent Kim <devgrapher@gmail.com>
|
||||
Jihwan Marc Kim <bluewhale.marc@gmail.com>
|
||||
Jihye Hyun <jijinny26@gmail.com>
|
||||
+Jihyeon Jeong <smartphonewithbear@gmail.com>
|
||||
Jihyeon Lee <wlgus7464@gmail.com>
|
||||
Jim Wu <lofoz.tw@gmail.com>
|
||||
Jin Yang <jin.a.yang@intel.com>
|
||||
diff --git a/device/vr/openxr/openxr_spatial_framework_manager.cc b/device/vr/openxr/openxr_spatial_framework_manager.cc
|
||||
index 2fd3609f277dc425d38a9acf9895b1ad02d64c72..b6f82c5c2999073aad9c43811fe9561c670992f9 100644
|
||||
--- a/device/vr/openxr/openxr_spatial_framework_manager.cc
|
||||
+++ b/device/vr/openxr/openxr_spatial_framework_manager.cc
|
||||
@@ -74,12 +74,15 @@ OpenXrSpatialFrameworkManager::OpenXrSpatialFrameworkManager(
|
||||
// to help abstract some of the details of creating the child structs, even
|
||||
// though at present we only have a configuration base.
|
||||
std::vector<OpenXrSpatialCapabilityConfigurationBase> capability_configs;
|
||||
- std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*>
|
||||
- capability_config_ptrs;
|
||||
+ capability_configs.reserve(capability_configuration.size());
|
||||
for (auto& [capability, components] : capability_configuration) {
|
||||
capability_configs.emplace_back(capability, components);
|
||||
- capability_config_ptrs.push_back(
|
||||
- capability_configs.back().GetAsBaseHeader());
|
||||
+ }
|
||||
+
|
||||
+ std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*>
|
||||
+ capability_config_ptrs;
|
||||
+ for (auto& config : capability_configs) {
|
||||
+ capability_config_ptrs.push_back(config.GetAsBaseHeader());
|
||||
}
|
||||
|
||||
XrSpatialContextCreateInfoEXT create_info = {
|
||||
@@ -1,224 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jonathan Ross <jonross@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 17:15:45 -0700
|
||||
Subject: gl: Make DCOMPSurfaceRegistry thread-safe
|
||||
|
||||
DCOMPSurfaceRegistry is accessed from both the GPU IO thread (via
|
||||
GpuServiceImpl) and the GPU main scheduler thread (via DCOMPTexture).
|
||||
The underlying base::flat_map is not thread-safe, leading to potential
|
||||
container corruption and crashes (UAF, BOf) during concurrent access.
|
||||
|
||||
This CL adds a base::Lock to protect all accesses to the map and
|
||||
includes a new multi-threaded stress test to verify the fix.
|
||||
|
||||
Bug: 493315759
|
||||
Change-Id: Ibb7ef5e602f222410fde06a61fb3f5e571e7a70f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737061
|
||||
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Commit-Queue: Jonathan Ross <jonross@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611867}
|
||||
|
||||
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
|
||||
index 3584b693370b5199456608a26ceb763f6e9c3446..1cb66199a0b8adf2035a05fecc411c67180f7e80 100644
|
||||
--- a/ui/gl/BUILD.gn
|
||||
+++ b/ui/gl/BUILD.gn
|
||||
@@ -552,6 +552,7 @@ test("gl_unittests") {
|
||||
if (is_win) {
|
||||
sources += [
|
||||
"dcomp_presenter_unittest.cc",
|
||||
+ "dcomp_surface_registry_unittest.cc",
|
||||
"delegated_ink_point_renderer_gpu_unittest.cc",
|
||||
"gl_fence_win_unittest.cc",
|
||||
"hdr_metadata_helper_win_unittest.cc",
|
||||
diff --git a/ui/gl/dcomp_surface_registry.cc b/ui/gl/dcomp_surface_registry.cc
|
||||
index 352cc298b9ea97361ae2a7d668b7d7e9eb455cd5..410f76f8980438abae32b6c89e7083ae48cf1699 100644
|
||||
--- a/ui/gl/dcomp_surface_registry.cc
|
||||
+++ b/ui/gl/dcomp_surface_registry.cc
|
||||
@@ -3,8 +3,11 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ui/gl/dcomp_surface_registry.h"
|
||||
+
|
||||
+#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
|
||||
namespace gl {
|
||||
|
||||
@@ -20,8 +23,11 @@ base::UnguessableToken DCOMPSurfaceRegistry::RegisterDCOMPSurfaceHandle(
|
||||
base::win::ScopedHandle surface) {
|
||||
DVLOG(1) << __func__;
|
||||
base::UnguessableToken token = base::UnguessableToken::Create();
|
||||
- DCHECK(surface_handle_map_.find(token) == surface_handle_map_.end());
|
||||
- surface_handle_map_[token] = std::move(surface);
|
||||
+ {
|
||||
+ base::AutoLock lock(lock_);
|
||||
+ DCHECK(surface_handle_map_.find(token) == surface_handle_map_.end());
|
||||
+ surface_handle_map_[token] = std::move(surface);
|
||||
+ }
|
||||
DVLOG(1) << __func__ << ": Surface handle registered with token " << token;
|
||||
return token;
|
||||
}
|
||||
@@ -29,12 +35,14 @@ base::UnguessableToken DCOMPSurfaceRegistry::RegisterDCOMPSurfaceHandle(
|
||||
void DCOMPSurfaceRegistry::UnregisterDCOMPSurfaceHandle(
|
||||
const base::UnguessableToken& token) {
|
||||
DVLOG(1) << __func__;
|
||||
+ base::AutoLock lock(lock_);
|
||||
surface_handle_map_.erase(token);
|
||||
}
|
||||
|
||||
base::win::ScopedHandle DCOMPSurfaceRegistry::TakeDCOMPSurfaceHandle(
|
||||
const base::UnguessableToken& token) {
|
||||
DVLOG(1) << __func__;
|
||||
+ base::AutoLock lock(lock_);
|
||||
auto surface_iter = surface_handle_map_.find(token);
|
||||
if (surface_iter != surface_handle_map_.end()) {
|
||||
// Take ownership.
|
||||
diff --git a/ui/gl/dcomp_surface_registry.h b/ui/gl/dcomp_surface_registry.h
|
||||
index 803a3cc6398f0777504063118920998869086d7f..7cd9fdbfe8669bc97d4b664fdb29573ec2ea26de 100644
|
||||
--- a/ui/gl/dcomp_surface_registry.h
|
||||
+++ b/ui/gl/dcomp_surface_registry.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/no_destructor.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
#include "base/unguessable_token.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "ui/gl/gl_export.h"
|
||||
@@ -44,7 +45,9 @@ class GL_EXPORT DCOMPSurfaceRegistry {
|
||||
~DCOMPSurfaceRegistry();
|
||||
|
||||
base::flat_map<base::UnguessableToken, base::win::ScopedHandle>
|
||||
- surface_handle_map_;
|
||||
+ surface_handle_map_ GUARDED_BY(lock_);
|
||||
+
|
||||
+ base::Lock lock_;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
diff --git a/ui/gl/dcomp_surface_registry_unittest.cc b/ui/gl/dcomp_surface_registry_unittest.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..595e2388e9f50df33214359ecef0c135d94610b8
|
||||
--- /dev/null
|
||||
+++ b/ui/gl/dcomp_surface_registry_unittest.cc
|
||||
@@ -0,0 +1,118 @@
|
||||
+// Copyright 2026 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "ui/gl/dcomp_surface_registry.h"
|
||||
+
|
||||
+#include <windows.h>
|
||||
+
|
||||
+#include <atomic>
|
||||
+#include <thread>
|
||||
+#include <vector>
|
||||
+
|
||||
+#include "base/memory/raw_ptr.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
+#include "base/unguessable_token.h"
|
||||
+#include "base/win/scoped_handle.h"
|
||||
+#include "testing/gtest/include/gtest/gtest.h"
|
||||
+
|
||||
+namespace gl {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+class DCOMPSurfaceRegistryTest : public testing::Test {
|
||||
+ public:
|
||||
+ void SetUp() override { registry_ = DCOMPSurfaceRegistry::GetInstance(); }
|
||||
+
|
||||
+ protected:
|
||||
+ raw_ptr<DCOMPSurfaceRegistry> registry_;
|
||||
+};
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+// Stress test for concurrent access to DCOMPSurfaceRegistry using the
|
||||
+// barrier pattern to ensure TSAN consistently catches data races.
|
||||
+//
|
||||
+// Without proper synchronization (e.g., base::Lock), this test would likely
|
||||
+// fail in the following ways:
|
||||
+// 1. Memory Corruption (UAF/HeapBOf): base::flat_map uses a contiguous
|
||||
+// std::vector. If one thread triggers a reallocation during an insertion
|
||||
+// while another thread is searching or erasing, the latter will hold an
|
||||
+// invalidated iterator or pointer.
|
||||
+// 2. Container Inconsistency: Concurrent insertions and erasures can leave
|
||||
+// the map in an unsorted or corrupted state, leading to failed lookups
|
||||
+// for valid tokens.
|
||||
+// 3. Sanitizer Triggers: ASan would detect container-overflow or
|
||||
+// heap-use-after-free, and TSan would flag a data race.
|
||||
+TEST_F(DCOMPSurfaceRegistryTest, ConcurrentRegisterAndTake) {
|
||||
+ const int kOpsPerThread = 100;
|
||||
+
|
||||
+ std::vector<base::UnguessableToken> tokens;
|
||||
+ base::Lock tokens_lock;
|
||||
+
|
||||
+ std::atomic<bool> start_flag{false};
|
||||
+ std::atomic<int> threads_ready{0};
|
||||
+
|
||||
+ auto register_worker = [&]() {
|
||||
+ threads_ready++;
|
||||
+ while (!start_flag.load(std::memory_order_acquire)) {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0; i < kOpsPerThread; ++i) {
|
||||
+ base::win::ScopedHandle handle(
|
||||
+ ::CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
+ base::UnguessableToken token =
|
||||
+ registry_->RegisterDCOMPSurfaceHandle(std::move(handle));
|
||||
+ {
|
||||
+ base::AutoLock lock(tokens_lock);
|
||||
+ tokens.push_back(token);
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ auto take_worker = [&]() {
|
||||
+ threads_ready++;
|
||||
+ while (!start_flag.load(std::memory_order_acquire)) {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+
|
||||
+ int taken = 0;
|
||||
+ while (taken < kOpsPerThread) {
|
||||
+ base::UnguessableToken token;
|
||||
+ {
|
||||
+ base::AutoLock lock(tokens_lock);
|
||||
+ if (!tokens.empty()) {
|
||||
+ token = tokens.back();
|
||||
+ tokens.pop_back();
|
||||
+ }
|
||||
+ }
|
||||
+ if (!token.is_empty()) {
|
||||
+ base::win::ScopedHandle handle =
|
||||
+ registry_->TakeDCOMPSurfaceHandle(token);
|
||||
+ taken++;
|
||||
+ } else {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ // With the barrier pattern, two threads are sufficient to trigger
|
||||
+ // the race condition for TSAN.
|
||||
+ std::thread t1(register_worker);
|
||||
+ std::thread t2(take_worker);
|
||||
+
|
||||
+ // Wait until both threads are ready at the starting line.
|
||||
+ while (threads_ready.load(std::memory_order_relaxed) < 2) {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+
|
||||
+ // Signal the staring flag to allow both threads to race from the initialized
|
||||
+ // state.
|
||||
+ start_flag.store(true, std::memory_order_release);
|
||||
+
|
||||
+ t1.join();
|
||||
+ t2.join();
|
||||
+}
|
||||
+
|
||||
+} // namespace gl
|
||||
@@ -197,10 +197,10 @@ index 0000000000000000000000000000000000000000..b0f15909bebda29fc2ec689a6d3b15d7
|
||||
+
|
||||
+} // namespace content
|
||||
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
|
||||
index 4521cc9e247c44248627c12b9eda0961f837d744..6ea61eb47fcde420261a9dd7e7e3feefa31f87d2 100644
|
||||
index be51e4f3e5de4ed82ba2752293ea58a9a672128c..8e2354d7918c3d86dde086c6647cb0c2656ab3c3 100644
|
||||
--- a/content/test/BUILD.gn
|
||||
+++ b/content/test/BUILD.gn
|
||||
@@ -2680,6 +2680,7 @@ test("content_unittests") {
|
||||
@@ -2681,6 +2681,7 @@ test("content_unittests") {
|
||||
"../browser/fenced_frame/redacted_fenced_frame_config_mojom_traits_unittest.cc",
|
||||
"../browser/file_system/browser_file_system_helper_unittest.cc",
|
||||
"../browser/file_system/file_system_operation_runner_unittest.cc",
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kenichi Ishibashi <bashi@chromium.org>
|
||||
Date: Fri, 10 Apr 2026 17:14:24 -0700
|
||||
Subject: [CORS] Block forbidden methods for no-cors requests
|
||||
|
||||
Previously, forbidden methods like TRACE and TRACK were allowed when
|
||||
the request mode was no-cors, and only CONNECT was unconditionally
|
||||
blocked.
|
||||
|
||||
This CL updates CorsURLLoaderFactory::IsValidRequest to block all
|
||||
forbidden methods regardless of the request mode. The unit test is
|
||||
also updated to reflect this new restriction.
|
||||
|
||||
Bug: 498765210
|
||||
Change-Id: Ie451a3c2b8fa7aafdebade8b3ba517be3ce255f8
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7743444
|
||||
Reviewed-by: mmenke <mmenke@chromium.org>
|
||||
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1613186}
|
||||
|
||||
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
|
||||
index 6a1eb075b0ed581bf81c43a0439da12eff20664c..bf02d6663b47413e47ebfe9ae4ba5799787f69ae 100644
|
||||
--- a/services/network/cors/cors_url_loader_factory.cc
|
||||
+++ b/services/network/cors/cors_url_loader_factory.cc
|
||||
@@ -910,13 +910,8 @@ bool CorsURLLoaderFactory::IsValidRequest(
|
||||
return false;
|
||||
}
|
||||
|
||||
- // Don't allow forbidden methods for any requests except RequestMode::kNoCors.
|
||||
- // Don't allow CONNECT method for any request.
|
||||
- if ((request.mode != mojom::RequestMode::kNoCors &&
|
||||
- cors::IsForbiddenMethod(request.method)) ||
|
||||
- (request.mode == mojom::RequestMode::kNoCors &&
|
||||
- base::EqualsCaseInsensitiveASCII(
|
||||
- request.method, net::HttpRequestHeaders::kConnectMethod))) {
|
||||
+ // Don't allow forbidden methods.
|
||||
+ if (cors::IsForbiddenMethod(request.method)) {
|
||||
mojo::ReportBadMessage("CorsURLLoaderFactory: Forbidden method");
|
||||
return false;
|
||||
}
|
||||
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
|
||||
index e9bbbc2013e6fb9498bec0982c045ea8b937a207..23a9e8093aa8d6cafb9a949d4a1dae86bd52a99d 100644
|
||||
--- a/services/network/cors/cors_url_loader_unittest.cc
|
||||
+++ b/services/network/cors/cors_url_loader_unittest.cc
|
||||
@@ -109,11 +109,10 @@ TEST_F(CorsURLLoaderTest, ForbiddenMethods) {
|
||||
std::string forbidden_method;
|
||||
bool expect_allowed_for_no_cors;
|
||||
} kTestCases[] = {
|
||||
- // CONNECT is never allowed, while TRACE and TRACK are allowed only with
|
||||
- // RequestMode::kNoCors.
|
||||
+ // CONNECT, TRACE and TRACK are not allowed for any mode.
|
||||
{"CONNECT", false},
|
||||
- {"TRACE", true},
|
||||
- {"TRACK", true},
|
||||
+ {"TRACE", false},
|
||||
+ {"TRACK", false},
|
||||
};
|
||||
for (const auto& test_case : kTestCases) {
|
||||
SCOPED_TRACE(test_case.forbidden_method);
|
||||
@@ -1,78 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 16:29:57 -0700
|
||||
Subject: [gpu] Fix OOB write due to unvalidated get_offset
|
||||
|
||||
A compromised GPU process can provide an invalid get_offset to the
|
||||
CommandBufferHelper (e.g., via shared memory). This offset is used to
|
||||
calculate available space and could lead to out-of-bounds writes in the
|
||||
Browser process if not validated.
|
||||
|
||||
This change adds a bounds check in
|
||||
CommandBufferHelper::UpdateCachedState to ensure that the cached
|
||||
get_offset is within the valid range [0, total_entry_count_]. If an
|
||||
invalid offset is detected, it forces a context loss, frees the ring
|
||||
buffer, and marks the helper as unusable, preventing further operations.
|
||||
|
||||
Bug: 498782145
|
||||
Test: CommandBufferHelperTest.*
|
||||
Change-Id: I8c64e546ecdc90a5a22d15e57ff762a86a6a6964
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7739951
|
||||
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
|
||||
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611853}
|
||||
|
||||
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc
|
||||
index ccda45b133c6a9f2ee60ccc8900bd4a4ce328394..5aea0c81b29b3507099f399c374f3cb372a3100e 100644
|
||||
--- a/gpu/command_buffer/client/cmd_buffer_helper.cc
|
||||
+++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
|
||||
@@ -158,6 +158,17 @@ void CommandBufferHelper::UpdateCachedState(const CommandBuffer::State& state) {
|
||||
service_on_old_buffer_ =
|
||||
(state.set_get_buffer_count != set_get_buffer_count_);
|
||||
cached_get_offset_ = service_on_old_buffer_ ? 0 : state.get_offset;
|
||||
+
|
||||
+ if (!service_on_old_buffer_ &&
|
||||
+ (cached_get_offset_ < 0 || cached_get_offset_ > total_entry_count_)) {
|
||||
+ command_buffer_->ForceLostContext(error::kGuilty);
|
||||
+ FreeRingBuffer();
|
||||
+ usable_ = false;
|
||||
+ context_lost_ = true;
|
||||
+ cached_get_offset_ = 0; // Safe fallback
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
cached_last_token_read_ = state.token;
|
||||
// Don't transition from a lost context to a working context.
|
||||
context_lost_ |= error::IsError(state.error);
|
||||
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
|
||||
index 5b1e5fae133ef75a99dab4ba1f8d2beddef68138..31e46714756ee30bf2fc3353693b6d49be8f6076 100644
|
||||
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
|
||||
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
|
||||
@@ -70,6 +70,8 @@ class CommandBufferHelperTest : public testing::Test {
|
||||
return helper_->immediate_entry_count_;
|
||||
}
|
||||
|
||||
+ int32_t TotalEntryCount() const { return helper_->total_entry_count_; }
|
||||
+
|
||||
// Adds a command to the buffer through the helper, while adding it as an
|
||||
// expected call on the API mock.
|
||||
void AddCommandWithExpect(error::Error _return,
|
||||
@@ -655,6 +657,17 @@ TEST_F(CommandBufferHelperTest, IsContextLost) {
|
||||
EXPECT_TRUE(helper_->IsContextLost());
|
||||
}
|
||||
|
||||
+TEST_F(CommandBufferHelperTest, TestInvalidGetOffset) {
|
||||
+ EXPECT_FALSE(helper_->IsContextLost());
|
||||
+ EXPECT_TRUE(helper_->usable());
|
||||
+
|
||||
+ command_buffer_->SetGetOffsetForTest(TotalEntryCount() + 1);
|
||||
+ helper_->RefreshCachedToken(); // calls UpdateCachedState internally.
|
||||
+
|
||||
+ EXPECT_TRUE(helper_->IsContextLost());
|
||||
+ EXPECT_FALSE(helper_->usable());
|
||||
+}
|
||||
+
|
||||
// Checks helper's 'flush generation' updates.
|
||||
TEST_F(CommandBufferHelperTest, TestFlushGeneration) {
|
||||
// Explicit flushing only.
|
||||
@@ -1,368 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Eugene Zemtsov <eugene@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 18:32:31 -0700
|
||||
Subject: media: Zero-copy VP9 alpha decoding in VpxVideoDecoder
|
||||
|
||||
Configures the VP9 alpha decoder to use `memory_pool_` for external
|
||||
frame buffers, eliminating the need for `libyuv::CopyPlane`.
|
||||
|
||||
The `VideoFrame` now wraps the alpha data directly from the pool using
|
||||
a second destruction observer. `AllocateAlphaPlaneForFrameBuffer` and
|
||||
`alpha_data` tracking are removed from `FrameBufferPool`.
|
||||
|
||||
Bug: 500066234
|
||||
Change-Id: I6e7cf13bcc8a5a1759acfd51961859c4c57fcbf2
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737984
|
||||
Reviewed-by: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
|
||||
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
|
||||
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611919}
|
||||
|
||||
diff --git a/media/base/frame_buffer_pool.cc b/media/base/frame_buffer_pool.cc
|
||||
index ceb0313c1fa36483ac545a6440737da035a1224c..59bc0790178aa2eb2e6960ef396c96ae18bbba09 100644
|
||||
--- a/media/base/frame_buffer_pool.cc
|
||||
+++ b/media/base/frame_buffer_pool.cc
|
||||
@@ -56,7 +56,6 @@ struct FrameBufferPool::FrameBuffer {
|
||||
// Not using std::vector<uint8_t> as resize() calls take a really long time
|
||||
// for large buffers.
|
||||
BytesArray data;
|
||||
- BytesArray alpha_data;
|
||||
bool held_by_library = false;
|
||||
// Needs to be a counter since a frame buffer might be used multiple times.
|
||||
int held_by_frame = 0;
|
||||
@@ -155,24 +154,6 @@ void FrameBufferPool::ReleaseFrameBuffer(void* fb_priv) {
|
||||
}
|
||||
}
|
||||
|
||||
-base::span<uint8_t> FrameBufferPool::AllocateAlphaPlaneForFrameBuffer(
|
||||
- size_t min_size,
|
||||
- void* fb_priv) {
|
||||
- base::AutoLock lock(lock_);
|
||||
- DCHECK(fb_priv);
|
||||
-
|
||||
- auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
|
||||
- DCHECK(IsUsedLocked(frame_buffer));
|
||||
- if (frame_buffer->alpha_data.size() < min_size) {
|
||||
- // Free the existing |alpha_data| first so that the memory can be reused,
|
||||
- // if possible.
|
||||
- frame_buffer->alpha_data = {};
|
||||
- frame_buffer->alpha_data = AllocateMemory(min_size, zero_initialize_memory_,
|
||||
- force_allocation_error_);
|
||||
- }
|
||||
- return frame_buffer->alpha_data;
|
||||
-}
|
||||
-
|
||||
base::OnceClosure FrameBufferPool::CreateFrameCallback(void* fb_priv) {
|
||||
base::AutoLock lock(lock_);
|
||||
|
||||
@@ -210,10 +191,9 @@ bool FrameBufferPool::OnMemoryDump(
|
||||
size_t bytes_reserved = 0;
|
||||
for (const auto& frame_buffer : frame_buffers_) {
|
||||
if (IsUsedLocked(frame_buffer.get())) {
|
||||
- bytes_used += frame_buffer->data.size() + frame_buffer->alpha_data.size();
|
||||
+ bytes_used += frame_buffer->data.size();
|
||||
}
|
||||
- bytes_reserved +=
|
||||
- frame_buffer->data.size() + frame_buffer->alpha_data.size();
|
||||
+ bytes_reserved += frame_buffer->data.size();
|
||||
}
|
||||
|
||||
memory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
|
||||
diff --git a/media/base/frame_buffer_pool.h b/media/base/frame_buffer_pool.h
|
||||
index ac839b8e8bfa00d2fea203be5248a56f04cecc71..2ccb01676b0e8e1e3ca1b3cb60f2883538f2f13c 100644
|
||||
--- a/media/base/frame_buffer_pool.h
|
||||
+++ b/media/base/frame_buffer_pool.h
|
||||
@@ -48,11 +48,6 @@ class MEDIA_EXPORT FrameBufferPool
|
||||
// Called when a frame buffer allocation is no longer needed.
|
||||
void ReleaseFrameBuffer(void* fb_priv);
|
||||
|
||||
- // Allocates (or reuses) room for an alpha plane on a given frame buffer.
|
||||
- // |fb_priv| must be a value previously returned by GetFrameBuffer().
|
||||
- base::span<uint8_t> AllocateAlphaPlaneForFrameBuffer(size_t min_size,
|
||||
- void* fb_priv);
|
||||
-
|
||||
// Generates a "no_longer_needed" closure that holds a reference to this pool;
|
||||
// |fb_priv| must be a value previously returned by GetFrameBuffer(). The
|
||||
// callback may be called on any thread.
|
||||
diff --git a/media/base/frame_buffer_pool_unittest.cc b/media/base/frame_buffer_pool_unittest.cc
|
||||
index 893e941f9f9b6d7eaff98b3e9ae4278861a2b0fd..8b50896e7544e34589614216373b566598b30ec6 100644
|
||||
--- a/media/base/frame_buffer_pool_unittest.cc
|
||||
+++ b/media/base/frame_buffer_pool_unittest.cc
|
||||
@@ -32,12 +32,6 @@ TEST(FrameBufferPool, BasicFunctionality) {
|
||||
EXPECT_NE(buf1.data(), buf2.data());
|
||||
std::ranges::fill(buf2, 0);
|
||||
|
||||
- auto alpha = pool->AllocateAlphaPlaneForFrameBuffer(kBufferSize, priv1);
|
||||
- ASSERT_FALSE(alpha.empty());
|
||||
- EXPECT_NE(alpha.data(), buf1.data());
|
||||
- EXPECT_NE(alpha.data(), buf2.data());
|
||||
- std::ranges::fill(alpha, 0);
|
||||
-
|
||||
EXPECT_EQ(2u, pool->get_pool_size_for_testing());
|
||||
|
||||
// Frames are not released immediately, so this should still show two frames.
|
||||
@@ -52,7 +46,6 @@ TEST(FrameBufferPool, BasicFunctionality) {
|
||||
EXPECT_EQ(1u, pool->get_pool_size_for_testing());
|
||||
|
||||
std::ranges::fill(buf1, 0);
|
||||
- std::ranges::fill(alpha, 0);
|
||||
|
||||
// This will release all memory since we're in the shutdown state.
|
||||
std::move(frame_release_cb).Run();
|
||||
@@ -132,13 +125,6 @@ TEST(FrameBufferPool, DoesClearAllocations) {
|
||||
}
|
||||
EXPECT_FALSE(nonzero);
|
||||
|
||||
- auto alpha_buf = pool->AllocateAlphaPlaneForFrameBuffer(kBufferSize, priv1);
|
||||
- nonzero = false;
|
||||
- for (size_t i = 0; i < kBufferSize; i++) {
|
||||
- nonzero |= !!alpha_buf[i];
|
||||
- }
|
||||
- EXPECT_FALSE(nonzero);
|
||||
-
|
||||
pool->Shutdown();
|
||||
}
|
||||
|
||||
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
|
||||
index ca1e45ed3cddcbf878f03c233f982b04425287f5..fe1b8b9b0686ed0b89737097087fca00cb1677c6 100644
|
||||
--- a/media/filters/vpx_video_decoder.cc
|
||||
+++ b/media/filters/vpx_video_decoder.cc
|
||||
@@ -250,7 +250,21 @@ bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
|
||||
|
||||
DCHECK(!vpx_codec_alpha_);
|
||||
vpx_codec_alpha_ = InitializeVpxContext(config);
|
||||
- return !!vpx_codec_alpha_;
|
||||
+ if (!vpx_codec_alpha_) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (config.codec() == VideoCodec::kVP9) {
|
||||
+ if (vpx_codec_set_frame_buffer_functions(
|
||||
+ vpx_codec_alpha_.get(), &GetVP9FrameBuffer, &ReleaseVP9FrameBuffer,
|
||||
+ memory_pool_.get())) {
|
||||
+ DLOG(ERROR) << "Failed to configure external buffers for alpha. "
|
||||
+ << vpx_codec_error(vpx_codec_alpha_.get());
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
}
|
||||
|
||||
void VpxVideoDecoder::CloseDecoder() {
|
||||
@@ -546,20 +560,13 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
|
||||
if (memory_pool_) {
|
||||
DCHECK_EQ(VideoCodec::kVP9, config_.codec());
|
||||
if (vpx_image_alpha) {
|
||||
+ CHECK_GT(vpx_image_alpha->stride[VPX_PLANE_Y], 0);
|
||||
size_t alpha_plane_size =
|
||||
vpx_image_alpha->stride[VPX_PLANE_Y] * vpx_image_alpha->d_h;
|
||||
- auto alpha_plane = memory_pool_->AllocateAlphaPlaneForFrameBuffer(
|
||||
- alpha_plane_size, vpx_image->fb_priv);
|
||||
- if (alpha_plane.empty()) {
|
||||
- error_status_ = DecoderStatus::Codes::kOutOfMemory;
|
||||
- // In case of OOM, abort copy.
|
||||
- return false;
|
||||
- }
|
||||
- libyuv::CopyPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
|
||||
- vpx_image_alpha->stride[VPX_PLANE_Y],
|
||||
- alpha_plane.data(),
|
||||
- vpx_image_alpha->stride[VPX_PLANE_Y],
|
||||
- vpx_image_alpha->d_w, vpx_image_alpha->d_h);
|
||||
+ // SAFETY: libvpx guarantees that the Y plane has at least `stride * d_h`
|
||||
+ // bytes available.
|
||||
+ auto alpha_plane = UNSAFE_BUFFERS(base::span<uint8_t>(
|
||||
+ vpx_image_alpha->planes[VPX_PLANE_Y], alpha_plane_size));
|
||||
*video_frame = VideoFrame::WrapExternalYuvaData(
|
||||
codec_format, coded_size, gfx::Rect(visible_size), natural_size,
|
||||
vpx_image->stride[VPX_PLANE_Y], vpx_image->stride[VPX_PLANE_U],
|
||||
@@ -575,8 +582,14 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
|
||||
if (!(*video_frame))
|
||||
return false;
|
||||
|
||||
- video_frame->get()->AddDestructionObserver(
|
||||
- memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
|
||||
+ (*video_frame)
|
||||
+ ->AddDestructionObserver(
|
||||
+ memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
|
||||
+ if (vpx_image_alpha) {
|
||||
+ (*video_frame)
|
||||
+ ->AddDestructionObserver(
|
||||
+ memory_pool_->CreateFrameCallback(vpx_image_alpha->fb_priv));
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
diff --git a/media/filters/vpx_video_decoder.h b/media/filters/vpx_video_decoder.h
|
||||
index f53da976ba4ba9dc39c6f03c99d5937b82650399..8f8f07e419b7d16c52f550edd97af6f235fbd2ac 100644
|
||||
--- a/media/filters/vpx_video_decoder.h
|
||||
+++ b/media/filters/vpx_video_decoder.h
|
||||
@@ -102,8 +102,8 @@ class MEDIA_EXPORT VpxVideoDecoder : public OffloadableVideoDecoder {
|
||||
std::unique_ptr<vpx_codec_ctx> vpx_codec_;
|
||||
std::unique_ptr<vpx_codec_ctx> vpx_codec_alpha_;
|
||||
|
||||
- // |memory_pool_| is a single-threaded memory pool used for VP9 decoding
|
||||
- // with no alpha. |frame_pool_| is used for all other cases.
|
||||
+ // |memory_pool_| is a thread-safe memory pool used for zero-copy VP9 decoding
|
||||
+ // (both with and without alpha). |frame_pool_| is used for VP8.
|
||||
scoped_refptr<FrameBufferPool> memory_pool_;
|
||||
VideoFramePool frame_pool_;
|
||||
|
||||
diff --git a/media/filters/vpx_video_decoder_unittest.cc b/media/filters/vpx_video_decoder_unittest.cc
|
||||
index 8fba2b469656c7bd60b555bf6dea02b7b37ee701..bb32fa8e7d59f5a3136a48a2ef14dda08a95fff0 100644
|
||||
--- a/media/filters/vpx_video_decoder_unittest.cc
|
||||
+++ b/media/filters/vpx_video_decoder_unittest.cc
|
||||
@@ -175,6 +175,28 @@ class VpxVideoDecoderTest : public testing::Test {
|
||||
output_frames_.push_back(std::move(frame));
|
||||
}
|
||||
|
||||
+ // Extracts the compressed video data from the AVPacket and also checks for
|
||||
+ // side data containing an alpha channel. If found, it copies the alpha data
|
||||
+ // into the DecoderBuffer's side data. This is necessary because FFmpeg
|
||||
+ // demuxes alpha channel data as side data associated with the video packet.
|
||||
+ static scoped_refptr<DecoderBuffer> CreateBufferWithAlphaFromPacket(
|
||||
+ const AVPacket* packet) {
|
||||
+ auto buffer = DecoderBuffer::CopyFrom(AVPacketData(*packet));
|
||||
+ size_t side_data_size = 0;
|
||||
+ uint8_t* side_data_ptr = av_packet_get_side_data(
|
||||
+ packet, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size);
|
||||
+ if (side_data_size > 8) {
|
||||
+ // SAFETY: The best we can do here is trust the size reported by ffmpeg.
|
||||
+ auto side_data =
|
||||
+ UNSAFE_BUFFERS(base::span(side_data_ptr, side_data_size));
|
||||
+ if (base::U64FromBigEndian(side_data.first<8u>()) == 1) {
|
||||
+ buffer->WritableSideData().alpha_data =
|
||||
+ base::HeapArray<uint8_t>::CopiedFrom(side_data.subspan(8u));
|
||||
+ }
|
||||
+ }
|
||||
+ return buffer;
|
||||
+ }
|
||||
+
|
||||
MOCK_METHOD1(DecodeDone, void(DecoderStatus));
|
||||
|
||||
base::test::TaskEnvironment task_env_;
|
||||
@@ -292,6 +314,68 @@ TEST_F(VpxVideoDecoderTest, SimpleFrameReuse) {
|
||||
EXPECT_EQ(old_y_data, output_frames_.back()->data(VideoFrame::Plane::kY));
|
||||
}
|
||||
|
||||
+TEST_F(VpxVideoDecoderTest, SimpleAlphaFrameReuse) {
|
||||
+ VideoDecoderConfig config = TestVideoConfig::Normal(VideoCodec::kVP9);
|
||||
+ config.Initialize(
|
||||
+ config.codec(), config.profile(),
|
||||
+ VideoDecoderConfig::AlphaMode::kHasAlpha, config.color_space_info(),
|
||||
+ config.video_transformation(), config.coded_size(), config.visible_rect(),
|
||||
+ config.natural_size(), config.extra_data(), config.encryption_scheme());
|
||||
+ InitializeWithConfig(config);
|
||||
+ scoped_refptr<DecoderBuffer> alpha_frame = ReadTestDataFile("bear-vp9a.webm");
|
||||
+
|
||||
+ // Read frames from the webm file.
|
||||
+ InMemoryUrlProtocol protocol(*alpha_frame, false);
|
||||
+ FFmpegGlue glue(&protocol);
|
||||
+ ASSERT_TRUE(glue.OpenContext());
|
||||
+
|
||||
+ auto packet = ScopedAVPacket::Allocate();
|
||||
+
|
||||
+ // Decode first frame
|
||||
+ ASSERT_GE(av_read_frame(glue.format_context(), packet.get()), 0);
|
||||
+ auto buffer = CreateBufferWithAlphaFromPacket(packet.get());
|
||||
+ Decode(buffer);
|
||||
+ av_packet_unref(packet.get());
|
||||
+
|
||||
+ ASSERT_EQ(1u, output_frames_.size());
|
||||
+ scoped_refptr<VideoFrame> frame = std::move(output_frames_.front());
|
||||
+ EXPECT_EQ(PIXEL_FORMAT_I420A, frame->format());
|
||||
+ const uint8_t* old_y_data = frame->data(VideoFrame::Plane::kY);
|
||||
+ const uint8_t* old_a_data = frame->data(VideoFrame::Plane::kA);
|
||||
+ output_frames_.pop_back();
|
||||
+
|
||||
+ // Clear frame reference to return the frame to the pool.
|
||||
+ frame = nullptr;
|
||||
+
|
||||
+ // Decode second frame.
|
||||
+ Decode(buffer);
|
||||
+ const uint8_t* mid_y_data =
|
||||
+ output_frames_.front()->data(VideoFrame::Plane::kY);
|
||||
+ const uint8_t* mid_a_data =
|
||||
+ output_frames_.front()->data(VideoFrame::Plane::kA);
|
||||
+ output_frames_.clear();
|
||||
+
|
||||
+ // Issuing another decode should reuse buffers from the pool.
|
||||
+ Decode(buffer);
|
||||
+
|
||||
+ ASSERT_EQ(1u, output_frames_.size());
|
||||
+ const uint8_t* new_y_data =
|
||||
+ output_frames_.back()->data(VideoFrame::Plane::kY);
|
||||
+ const uint8_t* new_a_data =
|
||||
+ output_frames_.back()->data(VideoFrame::Plane::kA);
|
||||
+
|
||||
+ // The pool is shared, so buffers might be reused in a different order (e.g. Y
|
||||
+ // might get the buffer previously used for A). Because libvpx allocates the
|
||||
+ // new frame before releasing the old reference frame, we need to check across
|
||||
+ // all previously allocated buffers.
|
||||
+ bool reused_y = new_y_data == old_y_data || new_y_data == old_a_data ||
|
||||
+ new_y_data == mid_y_data || new_y_data == mid_a_data;
|
||||
+ bool reused_a = new_a_data == old_y_data || new_a_data == old_a_data ||
|
||||
+ new_a_data == mid_y_data || new_a_data == mid_a_data;
|
||||
+ EXPECT_TRUE(reused_y);
|
||||
+ EXPECT_TRUE(reused_a);
|
||||
+}
|
||||
+
|
||||
TEST_F(VpxVideoDecoderTest, SimpleFormatChange) {
|
||||
scoped_refptr<DecoderBuffer> large_frame =
|
||||
ReadTestDataFile("vp9-I-frame-1280x720");
|
||||
@@ -311,10 +395,41 @@ TEST_F(VpxVideoDecoderTest, FrameValidAfterPoolDestruction) {
|
||||
|
||||
// Write to the Y plane. The memory tools should detect a
|
||||
// use-after-free if the storage was actually removed by pool destruction.
|
||||
- UNSAFE_TODO(
|
||||
- memset(output_frames_.front()->writable_data(VideoFrame::Plane::kY), 0xff,
|
||||
- output_frames_.front()->rows(VideoFrame::Plane::kY) *
|
||||
- output_frames_.front()->stride(VideoFrame::Plane::kY)));
|
||||
+ std::ranges::fill(
|
||||
+ output_frames_.front()->writable_span(VideoFrame::Plane::kY), 0xff);
|
||||
+}
|
||||
+
|
||||
+TEST_F(VpxVideoDecoderTest, AlphaFrameValidAfterPoolDestruction) {
|
||||
+ VideoDecoderConfig config = TestVideoConfig::Normal(VideoCodec::kVP9);
|
||||
+ config.Initialize(
|
||||
+ config.codec(), config.profile(),
|
||||
+ VideoDecoderConfig::AlphaMode::kHasAlpha, config.color_space_info(),
|
||||
+ config.video_transformation(), config.coded_size(), config.visible_rect(),
|
||||
+ config.natural_size(), config.extra_data(), config.encryption_scheme());
|
||||
+ InitializeWithConfig(config);
|
||||
+ scoped_refptr<DecoderBuffer> alpha_frame = ReadTestDataFile("bear-vp9a.webm");
|
||||
+
|
||||
+ InMemoryUrlProtocol protocol(*alpha_frame, false);
|
||||
+ FFmpegGlue glue(&protocol);
|
||||
+ ASSERT_TRUE(glue.OpenContext());
|
||||
+
|
||||
+ auto packet = ScopedAVPacket::Allocate();
|
||||
+ ASSERT_GE(av_read_frame(glue.format_context(), packet.get()), 0);
|
||||
+ auto buffer = CreateBufferWithAlphaFromPacket(packet.get());
|
||||
+ Decode(std::move(buffer));
|
||||
+ av_packet_unref(packet.get());
|
||||
+
|
||||
+ ASSERT_EQ(1u, output_frames_.size());
|
||||
+ EXPECT_EQ(PIXEL_FORMAT_I420A, output_frames_.front()->format());
|
||||
+
|
||||
+ Destroy();
|
||||
+
|
||||
+ // Write to the Y and A planes. The memory tools should detect a
|
||||
+ // use-after-free if the storage was actually removed by pool destruction.
|
||||
+ std::ranges::fill(
|
||||
+ output_frames_.front()->writable_span(VideoFrame::Plane::kY), 0xff);
|
||||
+ std::ranges::fill(
|
||||
+ output_frames_.front()->writable_span(VideoFrame::Plane::kA), 0xff);
|
||||
}
|
||||
|
||||
// The test stream uses profile 2, which needs high bit depth support in libvpx.
|
||||
@@ -362,8 +477,7 @@ TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) {
|
||||
Destroy();
|
||||
|
||||
// ASAN will be very unhappy with this line if the above is incorrect.
|
||||
- UNSAFE_TODO(memset(last_frame->writable_data(VideoFrame::Plane::kY), 0,
|
||||
- last_frame->row_bytes(VideoFrame::Plane::kY)));
|
||||
+ std::ranges::fill(last_frame->writable_span(VideoFrame::Plane::kY), 0);
|
||||
}
|
||||
#endif // !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY)
|
||||
|
||||
@@ -33,10 +33,10 @@ index 0ab8187b0db8ae6db46d81738f653a2bc4c566f6..de3d55e85c22317f7f9375eb94d0d5d4
|
||||
|
||||
} // namespace net
|
||||
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
|
||||
index cf1001557f2f59747ceb394ab2c93b4bf379dafb..2e16a8d19e1ccfbfc838ed33ecac3375f1e81b17 100644
|
||||
index f19c3cf6e7e50098c6fe3cda1f6db3bcc1fe7e90..fd0ef3275b205e94e3fa1914d4e64023cdf702dd 100644
|
||||
--- a/services/network/network_context.cc
|
||||
+++ b/services/network/network_context.cc
|
||||
@@ -1994,6 +1994,13 @@ void NetworkContext::SetNetworkConditions(
|
||||
@@ -1993,6 +1993,13 @@ void NetworkContext::SetNetworkConditions(
|
||||
std::move(network_conditions));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ index cf1001557f2f59747ceb394ab2c93b4bf379dafb..2e16a8d19e1ccfbfc838ed33ecac3375
|
||||
// This may only be called on NetworkContexts created with the constructor
|
||||
// that calls MakeURLRequestContext().
|
||||
diff --git a/services/network/network_context.h b/services/network/network_context.h
|
||||
index 04e6e884dccbb680a39f2b9c8a54de162e056a30..672dd48040586190b761e2db554ba0767d254f62 100644
|
||||
index 8d0a055792936286c96047b9a70670f93e2ae5b1..dad6a445ee3f6a0eb3d55d29fae643bde5bf706d 100644
|
||||
--- a/services/network/network_context.h
|
||||
+++ b/services/network/network_context.h
|
||||
@@ -332,6 +332,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
|
||||
|
||||
@@ -314,7 +314,7 @@ index 18f283e625101318ee14b50e6e765dfd1c9a1a44..44a3a55974c9e4b9e715574075f25661
|
||||
|
||||
auto DrawAsSinglePath = [&]() {
|
||||
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
|
||||
index 89301f737c6a1f4043a47366db604967159b7cb6..0f6417f6933f3f71f39b4a06ac229efd3ac7634b 100644
|
||||
index e35efdeaa064e185dcd31822be2d077bd4add7db..9c207b3927535ceeecb6f73a36f51566500c9ebb 100644
|
||||
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
|
||||
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
|
||||
@@ -215,6 +215,10 @@
|
||||
|
||||
@@ -619,7 +619,7 @@ index 2f462f0deb5fc8a637457243fb5d5849fc214d14..695869b83cefaa24af93a2e11b39de05
|
||||
+ Draw(gfx.mojom.Rect damage_rect) => ();
|
||||
};
|
||||
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
|
||||
index 97f26777528c3b21255b74ccf411ff2d3b243215..d45839ebc7b07ade2ca382e15b1d48dcf7d29106 100644
|
||||
index fbe5a8e6458970652723f91a426541220b394a18..c2e748b92103c582802af9b973ebe0c3770ba44d 100644
|
||||
--- a/ui/compositor/compositor.h
|
||||
+++ b/ui/compositor/compositor.h
|
||||
@@ -89,6 +89,7 @@ class DisplayPrivate;
|
||||
@@ -656,7 +656,7 @@ index 97f26777528c3b21255b74ccf411ff2d3b243215..d45839ebc7b07ade2ca382e15b1d48dc
|
||||
// Sets the root of the layer tree drawn by this Compositor. The root layer
|
||||
// must have no parent. The compositor's root layer is reset if the root layer
|
||||
// is destroyed. NULL can be passed to reset the root layer, in which case the
|
||||
@@ -632,6 +645,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
@@ -631,6 +644,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
simple_begin_frame_observers_;
|
||||
std::unique_ptr<ui::HostBeginFrameObserver> host_begin_frame_observer_;
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ The patch should be removed in favor of either:
|
||||
Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397.
|
||||
|
||||
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
|
||||
index 7532fbb742624d86c342df29a7621867fa99180b..45cbb6bcaf16307a6949473ddb62a2be95031199 100644
|
||||
index e321b9acde92b212dd9591a2e72868a2ad64406b..1150972064845f46717e1c184e4dde911bc1a019 100644
|
||||
--- a/content/browser/renderer_host/navigation_request.cc
|
||||
+++ b/content/browser/renderer_host/navigation_request.cc
|
||||
@@ -11915,6 +11915,11 @@ url::Origin NavigationRequest::GetOriginForURLLoaderFactoryUnchecked() {
|
||||
@@ -11929,6 +11929,11 @@ url::Origin NavigationRequest::GetOriginForURLLoaderFactoryUnchecked() {
|
||||
target_rph_id);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ index 7532fbb742624d86c342df29a7621867fa99180b..45cbb6bcaf16307a6949473ddb62a2be
|
||||
// origin of |common_params.url| and/or |common_params.initiator_origin|.
|
||||
url::Origin resolved_origin = url::Origin::Resolve(
|
||||
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
|
||||
index 1fd466b975fc5347e01bf5f6368e0b4fe89f7c5f..b77eb4ae99103d84ac6f1cb53fd1fe94814c7d05 100644
|
||||
index b98cf42ff36bbdbb65e59cd6cae1d0db02472319..ab65d53255cfea81a1a27849cd41f2d37e2d514d 100644
|
||||
--- a/third_party/blink/renderer/core/loader/document_loader.cc
|
||||
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
|
||||
@@ -2357,6 +2357,7 @@ Frame* DocumentLoader::CalculateOwnerFrame() {
|
||||
|
||||
@@ -53,10 +53,10 @@ index 3aec78e17de2012689c813145f78226867ac879a..5cb8f51c9ef6ac098629970d195eb593
|
||||
void Compositor::SetSeamlessRefreshRates(
|
||||
const std::vector<float>& seamless_refresh_rates) {
|
||||
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
|
||||
index d45839ebc7b07ade2ca382e15b1d48dcf7d29106..2c24993f1a509856966bb4a1302db97d976ad4ba 100644
|
||||
index c2e748b92103c582802af9b973ebe0c3770ba44d..b36dfec6bec2009b7761ec4ce3739a7f50f1196b 100644
|
||||
--- a/ui/compositor/compositor.h
|
||||
+++ b/ui/compositor/compositor.h
|
||||
@@ -516,6 +516,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
@@ -515,6 +515,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
|
||||
const cc::LayerTreeSettings& GetLayerTreeSettings() const;
|
||||
|
||||
@@ -67,7 +67,7 @@ index d45839ebc7b07ade2ca382e15b1d48dcf7d29106..2c24993f1a509856966bb4a1302db97d
|
||||
size_t saved_events_metrics_count_for_testing() const {
|
||||
return host_->saved_events_metrics_count_for_testing();
|
||||
}
|
||||
@@ -732,6 +736,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
@@ -731,6 +735,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
// See go/report-ux-metrics-at-painting for details.
|
||||
bool animation_started_ = false;
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ index f2c94689450f0333a144ccf82cf147c194896e6b..1c2e9fe36c297f7d614d9ca290e4d13c
|
||||
const mojom::blink::UserActivationOption user_activation_option_;
|
||||
const mojom::blink::LoadEventBlockingOption blocking_option_;
|
||||
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
|
||||
index 7d4039de028e6c7ef87e93792961c338032b261d..41d0a336dc4c91c74c57383f9203f7bca7675b66 100644
|
||||
index 9107414c2c689b7d1cb51b0eb861425d275d9cf1..b7fed34978a207d195af371a9c5bde02d19ee664 100644
|
||||
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
|
||||
@@ -300,6 +300,7 @@ void ExecuteScriptsInMainWorld(
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: frame_host_manager.patch
|
||||
Allows embedder to intercept site instances created by chromium.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
|
||||
index f9179c9f42b16b5c73b7102700410f2d1b83aeab..abce85bb4fb63d1662bbc9ad28a1047f97c6d3c4 100644
|
||||
index 25ee8cf3da9ffbef6798548cc458f74e41b97fdd..32dcee38dde1587596d24d1433fcdeb310fca4b6 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_manager.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
|
||||
@@ -4913,6 +4913,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest(
|
||||
@@ -4915,6 +4915,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest(
|
||||
request->ResetStateForSiteInstanceChange();
|
||||
}
|
||||
|
||||
|
||||
@@ -1209,7 +1209,7 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe
|
||||
|
||||
} // namespace content
|
||||
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
|
||||
index ec792aa7e050550721cd221bfe8ff335e22e90ef..4521cc9e247c44248627c12b9eda0961f837d744 100644
|
||||
index 19f17632aecf606bf7b39cf1a2fda068e10ddfec..be51e4f3e5de4ed82ba2752293ea58a9a672128c 100644
|
||||
--- a/content/test/BUILD.gn
|
||||
+++ b/content/test/BUILD.gn
|
||||
@@ -701,6 +701,7 @@ static_library("test_support") {
|
||||
@@ -1229,7 +1229,7 @@ index ec792aa7e050550721cd221bfe8ff335e22e90ef..4521cc9e247c44248627c12b9eda0961
|
||||
}
|
||||
|
||||
mojom("content_test_mojo_bindings") {
|
||||
@@ -2078,6 +2081,7 @@ test("content_browsertests") {
|
||||
@@ -2079,6 +2082,7 @@ test("content_browsertests") {
|
||||
"//ui/shell_dialogs",
|
||||
"//ui/snapshot",
|
||||
"//ui/webui:test_support",
|
||||
@@ -1237,7 +1237,7 @@ index ec792aa7e050550721cd221bfe8ff335e22e90ef..4521cc9e247c44248627c12b9eda0961
|
||||
]
|
||||
|
||||
if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) {
|
||||
@@ -3446,6 +3450,7 @@ test("content_unittests") {
|
||||
@@ -3447,6 +3451,7 @@ test("content_unittests") {
|
||||
"//ui/shell_dialogs",
|
||||
"//ui/webui:test_support",
|
||||
"//url",
|
||||
|
||||
@@ -7,7 +7,7 @@ This adds a callback from the network service that's used to implement
|
||||
session.setCertificateVerifyCallback.
|
||||
|
||||
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
|
||||
index 68b3acc5a4e67fee975fd982f252cbf46c28ccac..cf1001557f2f59747ceb394ab2c93b4bf379dafb 100644
|
||||
index 1f070b2d00e2b0848939433172066b735702c596..f19c3cf6e7e50098c6fe3cda1f6db3bcc1fe7e90 100644
|
||||
--- a/services/network/network_context.cc
|
||||
+++ b/services/network/network_context.cc
|
||||
@@ -173,6 +173,11 @@
|
||||
@@ -148,7 +148,7 @@ index 68b3acc5a4e67fee975fd982f252cbf46c28ccac..cf1001557f2f59747ceb394ab2c93b4b
|
||||
void NetworkContext::CreateURLLoaderFactory(
|
||||
mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
|
||||
mojom::URLLoaderFactoryParamsPtr params) {
|
||||
@@ -2800,6 +2917,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext(
|
||||
@@ -2799,6 +2916,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext(
|
||||
cert_verifier = std::make_unique<net::CachingCertVerifier>(
|
||||
std::make_unique<net::CoalescingCertVerifier>(
|
||||
std::move(cert_verifier)));
|
||||
@@ -160,7 +160,7 @@ index 68b3acc5a4e67fee975fd982f252cbf46c28ccac..cf1001557f2f59747ceb394ab2c93b4b
|
||||
|
||||
builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier(
|
||||
diff --git a/services/network/network_context.h b/services/network/network_context.h
|
||||
index 54a5feca1fbec658039826304f27283bd9d9c15a..04e6e884dccbb680a39f2b9c8a54de162e056a30 100644
|
||||
index 911b03040d084dc14c9b1f4b8ebe79dfff3e4ead..8d0a055792936286c96047b9a70670f93e2ae5b1 100644
|
||||
--- a/services/network/network_context.h
|
||||
+++ b/services/network/network_context.h
|
||||
@@ -122,6 +122,7 @@ class SimpleUrlPatternMatcher;
|
||||
@@ -180,7 +180,7 @@ index 54a5feca1fbec658039826304f27283bd9d9c15a..04e6e884dccbb680a39f2b9c8a54de16
|
||||
void ResetURLLoaderFactories() override;
|
||||
void GetViaObliviousHttp(
|
||||
mojom::ObliviousHttpRequestPtr request,
|
||||
@@ -1002,6 +1005,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
|
||||
@@ -1004,6 +1007,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
|
||||
std::vector<base::OnceClosure> dismount_closures_;
|
||||
#endif // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
|
||||
|
||||
|
||||
@@ -133,10 +133,10 @@ index 9bf238e64af483294ae3c3f18a4e9aed49a8658d..b9b2a4c8c387b8e8b4eb1f02fc0f891c
|
||||
const GURL& document_url,
|
||||
const WeakDocumentPtr& weak_document_ptr,
|
||||
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
|
||||
index bdf3d9ca4192818a33438dde4dd23ba40996bf00..3d7ccd3becf0ff3cc7003b9159dec7765d5f82c6 100644
|
||||
index f23e46639380c47e2da7681ac3273f9712755be6..d703187dda67bab4804dc619a0562de4d75ff2ff 100644
|
||||
--- a/content/browser/renderer_host/render_process_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_process_host_impl.cc
|
||||
@@ -2385,7 +2385,7 @@ void RenderProcessHostImpl::CreateNotificationService(
|
||||
@@ -2389,7 +2389,7 @@ void RenderProcessHostImpl::CreateNotificationService(
|
||||
case RenderProcessHost::NotificationServiceCreatorType::kSharedWorker:
|
||||
case RenderProcessHost::NotificationServiceCreatorType::kDedicatedWorker: {
|
||||
storage_partition_impl_->GetPlatformNotificationContext()->CreateService(
|
||||
@@ -145,7 +145,7 @@ index bdf3d9ca4192818a33438dde4dd23ba40996bf00..3d7ccd3becf0ff3cc7003b9159dec776
|
||||
creator_type, std::move(receiver));
|
||||
break;
|
||||
}
|
||||
@@ -2393,7 +2393,7 @@ void RenderProcessHostImpl::CreateNotificationService(
|
||||
@@ -2397,7 +2397,7 @@ void RenderProcessHostImpl::CreateNotificationService(
|
||||
CHECK(rfh);
|
||||
|
||||
storage_partition_impl_->GetPlatformNotificationContext()->CreateService(
|
||||
|
||||
@@ -22,10 +22,10 @@ However, the patch would need to be reviewed by the security team, as it
|
||||
does touch a security-sensitive class.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
|
||||
index 3d7ccd3becf0ff3cc7003b9159dec7765d5f82c6..5cf51c7984697b1a79048338516e1d7e1cc9dc4b 100644
|
||||
index d703187dda67bab4804dc619a0562de4d75ff2ff..a5d1bafd8944afd9588d18ae8896c09f3bbce7f9 100644
|
||||
--- a/content/browser/renderer_host/render_process_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_process_host_impl.cc
|
||||
@@ -1955,6 +1955,10 @@ bool RenderProcessHostImpl::Init() {
|
||||
@@ -1957,6 +1957,10 @@ bool RenderProcessHostImpl::Init() {
|
||||
std::unique_ptr<SandboxedProcessLauncherDelegate> sandbox_delegate =
|
||||
std::make_unique<RendererSandboxedProcessLauncherDelegateWin>(
|
||||
*cmd_line, IsPdf(), IsJitDisabled());
|
||||
|
||||
@@ -15,10 +15,10 @@ Note that we also need to manually update embedder's
|
||||
`api::WebContents::IsFullscreenForTabOrPending` value.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index e4ff8f11bed9e53f3134068492ac94b4c9bb4df2..17c3b5c78c3ef08e0b901f3ace8bb07ee78e4cab 100644
|
||||
index d3a9d1b30dbbf7952203f0c93a068f493550fa79..546b5a14b9d5923e0a9e7b231041b937dbd71560 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -9260,6 +9260,17 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
@@ -9274,6 +9274,17 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ index 1f6c015c361b3146db760643e3670a110634d75b..d4d9c10d3420a5ff998aeb593ea6a255
|
||||
// An empty URL is returned if the URL is not overriden.
|
||||
virtual GURL OverrideFlashEmbedWithHTML(const GURL& url);
|
||||
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
|
||||
index 416d53d0cce178de992074b0c0371bca2e4ed900..cd0e4d7ea77ffe9fd09982ef2d9f5d57df8e2995 100644
|
||||
index 23a05a16b40b859465f7953aef8052686cbeece9..10c2a75c7106b4803d53bec02c9c347a3ca28cfe 100644
|
||||
--- a/content/renderer/renderer_blink_platform_impl.cc
|
||||
+++ b/content/renderer/renderer_blink_platform_impl.cc
|
||||
@@ -935,6 +935,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() {
|
||||
|
||||
@@ -35,7 +35,7 @@ index d4d9c10d3420a5ff998aeb593ea6a25556d1215e..d64fef6bfc37264dcdc1bbea22eb5c4e
|
||||
// from the worker thread.
|
||||
virtual void WillDestroyWorkerContextOnWorkerThread(
|
||||
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
|
||||
index cd0e4d7ea77ffe9fd09982ef2d9f5d57df8e2995..3a96f5f084061c30344552a01b3d3a7260dad65e 100644
|
||||
index 10c2a75c7106b4803d53bec02c9c347a3ca28cfe..9879ba58b68720151ab438ca85df9a30e2ad0c6f 100644
|
||||
--- a/content/renderer/renderer_blink_platform_impl.cc
|
||||
+++ b/content/renderer/renderer_blink_platform_impl.cc
|
||||
@@ -947,6 +947,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated(
|
||||
|
||||
@@ -11,7 +11,5 @@
|
||||
{ "patch_dir": "src/electron/patches/ReactiveObjC", "repo": "src/third_party/squirrel.mac/vendor/ReactiveObjC" },
|
||||
{ "patch_dir": "src/electron/patches/webrtc", "repo": "src/third_party/webrtc" },
|
||||
{ "patch_dir": "src/electron/patches/reclient-configs", "repo": "src/third_party/engflow-reclient-configs" },
|
||||
{ "patch_dir": "src/electron/patches/sqlite", "repo": "src/third_party/sqlite/src" },
|
||||
{ "patch_dir": "src/electron/patches/dawn", "repo": "src/third_party/dawn" },
|
||||
{ "patch_dir": "src/electron/patches/pdfium", "repo": "src/third_party/pdfium" }
|
||||
{ "patch_dir": "src/electron/patches/sqlite", "repo": "src/third_party/sqlite/src" }
|
||||
]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
cherry-pick-7c11e1188705.patch
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lokbondo Kung <lokokung@google.com>
|
||||
Date: Tue, 7 Apr 2026 19:22:22 -0700
|
||||
Subject: [dawn][native] Check for waiting for idle before updating serials.
|
||||
|
||||
- In the ExecutionQueue, we need to make sure to check whether a thread
|
||||
is waiting for idle prior to updating the completed serial. Otherwise,
|
||||
as the bug below points out, it's possible for the thread that's
|
||||
waiting for idle (which just waits for the completed serial to reach
|
||||
a certain value), to complete and destroy the Queue before the rest of
|
||||
the UpdateSerial call completes.
|
||||
|
||||
Bug: 497969820
|
||||
Change-Id: I7b9dba50f4ccb1aa8dfced122801e17db3ee4e0e
|
||||
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/300595
|
||||
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
|
||||
Commit-Queue: Loko Kung <lokokung@google.com>
|
||||
|
||||
diff --git a/src/dawn/native/ExecutionQueue.cpp b/src/dawn/native/ExecutionQueue.cpp
|
||||
index 8be4e707e92ce1b3d94e4bb8fec3e332e8f17fb3..2bb5a6af253f17178332da4088fefbf922463b08 100644
|
||||
--- a/src/dawn/native/ExecutionQueue.cpp
|
||||
+++ b/src/dawn/native/ExecutionQueue.cpp
|
||||
@@ -253,20 +253,21 @@ void ExecutionQueueBase::UpdateCompletedSerialTo(QueuePriority priority,
|
||||
}
|
||||
|
||||
void ExecutionQueueBase::UpdateCompletedSerialToInternal(QueuePriority priority,
|
||||
- ExecutionSerial completedSerial,
|
||||
- bool forceTasks) {
|
||||
+ ExecutionSerial newCompletedSerial,
|
||||
+ bool forceTasksForDestroy) {
|
||||
QueuePriorityArray<std::vector<Ref<SerialProcessor>>>* processors = nullptr;
|
||||
std::vector<Task> tasks;
|
||||
|
||||
- // We update the completed serial as soon as possible before waiting for callback rights so
|
||||
- // that we almost always process as many callbacks as possible.
|
||||
- ExecutionSerial serial = mCompletedSerial.Use([&](auto old) {
|
||||
- *old = std::max(*old, static_cast<uint64_t>(completedSerial));
|
||||
- return ExecutionSerial(*old);
|
||||
- });
|
||||
-
|
||||
- mState.Use<NotifyType::None>([&](auto state) {
|
||||
- if (state->mWaitingForIdle && !forceTasks) {
|
||||
+ // Note that we need to determine whether we are waiting for idle before updating the completed
|
||||
+ // serial because some backends WaitForIdleForDestructionImpl may be implemented via a call to
|
||||
+ // WaitForQueueSerial which (by default without overrides), waits on the completed serial value.
|
||||
+ // If we updated the serial value before checking the other pieces of state, a thread destroying
|
||||
+ // the Queue calling WaitForIdleForDestruction, could end up being woken up and destroying the
|
||||
+ // Queue device before the rest of this function completes. By checking the state first before
|
||||
+ // updating the serial, however, we avoid waking up the thread that's waiting for idle until we
|
||||
+ // have completed using the queue.
|
||||
+ bool waitingForIdle = mState.Use<NotifyType::None>([&](auto state) {
|
||||
+ if (state->mWaitingForIdle && !forceTasksForDestroy) {
|
||||
// If we are waiting for idle, then the callbacks will be fired there. It is currently
|
||||
// necessary to avoid calling the callbacks in this function and doing it in the
|
||||
// |WaitForIdleForDestruction| call because |WaitForIdleForDestruction| is called while
|
||||
@@ -274,8 +275,11 @@ void ExecutionQueueBase::UpdateCompletedSerialToInternal(QueuePriority priority,
|
||||
// device lock. As a result, if the main thread is waiting for idle, and another thread
|
||||
// is trying to update the completed serial and call callbacks, it could deadlock. Once
|
||||
// we update |WaitForIdleForDestruction| to release the device lock on the wait, we may
|
||||
- // be able to simplify the code here.
|
||||
- return;
|
||||
+ // be able to simplify the code here. Note that skipping this when
|
||||
+ // |forceTasksForDestroy| is currently ok because that branch is only called when we are
|
||||
+ // also holding the device lock, either via a Destroy or via an error that is being
|
||||
+ // handled.
|
||||
+ return true;
|
||||
}
|
||||
|
||||
// Wait until we can exclusively call callbacks.
|
||||
@@ -284,16 +288,22 @@ void ExecutionQueueBase::UpdateCompletedSerialToInternal(QueuePriority priority,
|
||||
// Call all callbacks that for the given priority and anything of higher priority as well.
|
||||
processors = &state->mWaitingProcessors;
|
||||
for (QueuePriority p = QueuePriority::Highest; p >= priority; p -= 1) {
|
||||
- PopWaitingTasksInto(serial, state->mWaitingTasks[p], tasks);
|
||||
+ PopWaitingTasksInto(newCompletedSerial, state->mWaitingTasks[p], tasks);
|
||||
}
|
||||
state->mCallingCallbacks = true;
|
||||
+ return false;
|
||||
+ });
|
||||
+
|
||||
+ // Update the serial now that we know whether we are waiting for idle.
|
||||
+ mCompletedSerial.Use([&](auto completedSerial) {
|
||||
+ *completedSerial = std::max(*completedSerial, static_cast<uint64_t>(newCompletedSerial));
|
||||
});
|
||||
|
||||
// Always call the processors before processing individual tasks.
|
||||
if (processors) {
|
||||
for (QueuePriority p = QueuePriority::Highest; p >= priority; p -= 1) {
|
||||
for (auto& processor : (*processors)[p]) {
|
||||
- processor->UpdateCompletedSerialTo(serial);
|
||||
+ processor->UpdateCompletedSerialTo(newCompletedSerial);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,7 +314,9 @@ void ExecutionQueueBase::UpdateCompletedSerialToInternal(QueuePriority priority,
|
||||
task();
|
||||
}
|
||||
|
||||
- mState->mCallingCallbacks = false;
|
||||
+ if (!waitingForIdle) {
|
||||
+ mState->mCallingCallbacks = false;
|
||||
+ }
|
||||
}
|
||||
|
||||
MaybeError ExecutionQueueBase::EnsureCommandsFlushed(ExecutionSerial serial) {
|
||||
diff --git a/src/dawn/native/ExecutionQueue.h b/src/dawn/native/ExecutionQueue.h
|
||||
index 9140f9eb08458fd7163dfa724e5452f800c7dc6f..c0bfd75e2d9e6701e7f61a09c3d10b1ef5c3c200 100644
|
||||
--- a/src/dawn/native/ExecutionQueue.h
|
||||
+++ b/src/dawn/native/ExecutionQueue.h
|
||||
@@ -183,7 +183,7 @@ class ExecutionQueueBase : public ApiObjectBase {
|
||||
|
||||
void UpdateCompletedSerialToInternal(QueuePriority priority,
|
||||
ExecutionSerial completedSerial,
|
||||
- bool forceTasks = false);
|
||||
+ bool forceTasksForDestroy = false);
|
||||
|
||||
// |mCompletedSerial| tracks the last completed command serial that the fence has returned.
|
||||
// |mLastSubmittedSerial| tracks the last submitted command serial.
|
||||
@@ -1 +0,0 @@
|
||||
cherry-pick-bce2e6728279.patch
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Sepez <tsepez@google.com>
|
||||
Date: Tue, 7 Apr 2026 15:50:30 -0700
|
||||
Subject: Use safe arithmetic in CFX_PSRenderer::DrawDIBits()
|
||||
|
||||
Hardening suggestion from the AI bot.
|
||||
|
||||
Bug: 500036290
|
||||
Change-Id: Ie521629d06ba944f610b941a8c9e9505fa29aea7
|
||||
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/145731
|
||||
Reviewed-by: Lei Zhang <thestig@chromium.org>
|
||||
Commit-Queue: Tom Sepez <tsepez@chromium.org>
|
||||
|
||||
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
|
||||
index b38f1a2b7c3271769e609763be2e183f2890ebb3..b8710e50ed01233b2aefbf1760e26e05964b315e 100644
|
||||
--- a/core/fxge/win32/cfx_psrenderer.cpp
|
||||
+++ b/core/fxge/win32/cfx_psrenderer.cpp
|
||||
@@ -620,8 +620,16 @@ bool CFX_PSRenderer::DrawDIBits(RetainPtr<const CFX_DIBBase> bitmap,
|
||||
encoder_iface_->pJpegEncodeFunc(bitmap, &output_buf, &output_size)) {
|
||||
filter = "/DCTDecode filter ";
|
||||
} else {
|
||||
- int src_pitch = width * bytes_per_pixel;
|
||||
- output_size = height * src_pitch;
|
||||
+ FX_SAFE_UINT32 safe_pitch = bytes_per_pixel;
|
||||
+ safe_pitch *= width;
|
||||
+ FX_SAFE_UINT32 safe_output_size = safe_pitch;
|
||||
+ safe_output_size *= height;
|
||||
+ if (!safe_output_size.IsValid()) {
|
||||
+ WriteString("\nQ\n");
|
||||
+ return false;
|
||||
+ }
|
||||
+ uint32_t src_pitch = safe_pitch.ValueOrDie();
|
||||
+ output_size = safe_output_size.ValueOrDie();
|
||||
output_buf = FX_Alloc(uint8_t, output_size);
|
||||
for (int row = 0; row < height; row++) {
|
||||
const uint8_t* src_scan = bitmap->GetScanline(row).data();
|
||||
Reference in New Issue
Block a user