diff --git a/DEPS b/DEPS index ced1a5f..f078e42 100644 --- a/DEPS +++ b/DEPS
@@ -312,15 +312,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'ef2511b0a6f21b4bb43414ac0571d755791ae22a', + 'skia_revision': '081ba94858f6ee93b518e1a4107f81cbf7a224cb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'ea012d232c66229b2d4f3f2a268aa999d668972b', + 'v8_revision': '2c63d7b6b700b0a2d257d4f4f19d2948c41e9b88', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'a971e5b42e1e8e61ccb0d8e4b3f2245aaa4f2d4d', + 'angle_revision': '19e725e49c7d23f810ebd709b79d91323af921c1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -387,7 +387,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling chromium_variations # and whatever else without interference from each other. - 'chromium_variations_revision': '1abc236bbd98af59cac64b471b3ea6ab900350db', + 'chromium_variations_revision': '32e2e5c8d0343434cf154f2b928775c2d95ad8c5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -403,7 +403,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '5ccdd75fa57a758fa15db394dd6ad6092963cfed', + 'devtools_frontend_revision': 'e13f34726b11afcb77bebca0e6a79c08ba3fd2c6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -431,7 +431,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'quiche_revision': '42dab6be059fbe2a3d98367e1d5ad19d887156d3', + 'quiche_revision': '6fed96483ed2f0fd9567211f50df8beef43ac140', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ios_webkit # and whatever else without interference from each other. @@ -827,12 +827,12 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - 'ed4b774419588a1496c267e4b9ba422d20ea8552', + '306e4ba09278acec4c40f05cc49e5eb5f42275e5', 'condition': 'checkout_android and checkout_src_internal', }, 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + 'b13d20e7483a7a2b3e600b5981693d476d43d19d', + 'url': Var('chromium_git') + '/website.git' + '@' + '7918c586771aed5e170cb5ce33fdfc38f6203063', }, 'src/ios/third_party/earl_grey2/src': { @@ -989,7 +989,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'Qdbpp4CESrciZ3ZF1ZZmOg-NQSUdK-DkNAddEJeZbbgC', + 'version': 'rTiFKohCdnT81G3SjzFlb536YE6DnBkp_3Ig-Pt7gCUC', }, ], 'condition': 'checkout_android', @@ -1205,7 +1205,7 @@ Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'b92185a4c9aba77a6cbbc8d8aa65820e8be5dae7', + 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'acc4bb2e6ba2063e1171c81951abd331aa9cf068', 'condition': 'checkout_src_internal', }, @@ -1665,7 +1665,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '609cb8ef0288f4e1667b1034dc53ad319d43ca99', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '1553701a9f0a47be12af882f0d7801df5b674122', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151', @@ -1699,7 +1699,7 @@ }, 'src/third_party/re2/src': - Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + 'f9550c3f7207f946a45bbccd1814b12b136aae72', + Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + '2d866a3d0753f4f4fce93cccc6c59c4b052d7db4', 'src/third_party/r8': { 'packages': [ @@ -1850,7 +1850,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '98673cc24786be6c10dd8908e0b0b4ed27625c6a', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'c935bb2141e2e616e1f9e1df4568d976ad1828c0', + Var('webrtc_git') + '/src.git' + '@' + '16ac10d9f75cde959f00df062f544c49941882da', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -2017,7 +2017,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/projector_app/app', - 'version': 'Y9cLYmmmtcWhsN5F6APjq_pYVro6_TSV6-7MZqVwYIQC', + 'version': 'DZBWu_gDTcO8mVYLHW38B-bCWgvLZG4RRjsxywx5roEC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/components/arc/mojom/BUILD.gn b/ash/components/arc/mojom/BUILD.gn index 30177f9..00f88f4 100644 --- a/ash/components/arc/mojom/BUILD.gn +++ b/ash/components/arc/mojom/BUILD.gn
@@ -86,7 +86,6 @@ "//services/device/public/mojom:usb", "//services/media_session/public/mojom", "//services/resource_coordinator/public/mojom", - "//ui/accessibility/mojom:ax_assistant_mojom", "//ui/gfx/geometry/mojom", "//url/mojom:url_mojom_gurl", ]
diff --git a/ash/components/arc/mojom/app.mojom b/ash/components/arc/mojom/app.mojom index 4763d7d..feb41f67 100644 --- a/ash/components/arc/mojom/app.mojom +++ b/ash/components/arc/mojom/app.mojom
@@ -10,7 +10,6 @@ import "ash/components/arc/mojom/compatibility_mode.mojom"; import "ash/components/arc/mojom/gfx.mojom"; import "ash/components/arc/mojom/scale_factor.mojom"; -import "ui/accessibility/mojom/ax_assistant_structure.mojom"; // Describes installation result. struct InstallationResult { @@ -437,9 +436,9 @@ bool active); }; -// TODO(lhchavez): Migrate all request/response messages to Mojo. // Next method ID: 44 -// Deprecated method IDs: 0, 1, 2, 3, 4, 6, 9, 12, 13, 15, 17, 18, 19, 22, 31 +// Deprecated method IDs: 0, 1, 2, 3, 4, 6, 9, 12, 13, 15, 17, 18, 19, 22, 29, +// 31 interface AppInstance { // Establishes full-duplex communication with the host. [MinVersion=26] Init@21(pending_remote<AppHost> host_remote) => (); @@ -573,11 +572,6 @@ (AppDiscoveryRequestState state@1, array<AppDiscoveryResult> results@0); - // Requests assist structure. - [MinVersion=37] RequestAssistStructure@29() => - (ax.mojom.AssistantExtra? assistant_extra, - ax.mojom.AssistantTree? assistant_tree); - // Queries whether |package_name| is installable for the current user. This // may return false if the user has already installed |package_name|, or if // it isn't available in the user's store.
diff --git a/ash/components/arc/mojom/notifications.mojom b/ash/components/arc/mojom/notifications.mojom index fe9e908..2a5772b 100644 --- a/ash/components/arc/mojom/notifications.mojom +++ b/ash/components/arc/mojom/notifications.mojom
@@ -307,7 +307,6 @@ // Deprecated method IDs: 0 // Next Method ID: 15 -// TODO(lhchavez): Migrate all request/response messages to Mojo. interface NotificationsInstance { // Establishes full-duplex communication with the host. [MinVersion=14] Init@5(pending_remote<NotificationsHost> host_remote) => ();
diff --git a/ash/components/arc/test/fake_app_instance.cc b/ash/components/arc/test/fake_app_instance.cc index f89cde8..bcb9c1eb 100644 --- a/ash/components/arc/test/fake_app_instance.cc +++ b/ash/components/arc/test/fake_app_instance.cc
@@ -464,11 +464,6 @@ ++start_fast_app_reinstall_request_count_; } -void FakeAppInstance::RequestAssistStructure( - RequestAssistStructureCallback callback) { - std::move(callback).Run(nullptr, nullptr); -} - void FakeAppInstance::IsInstallable(const std::string& package_name, IsInstallableCallback callback) { std::move(callback).Run(is_installable_);
diff --git a/ash/components/arc/test/fake_app_instance.h b/ash/components/arc/test/fake_app_instance.h index f281694..207647b 100644 --- a/ash/components/arc/test/fake_app_instance.h +++ b/ash/components/arc/test/fake_app_instance.h
@@ -166,7 +166,6 @@ void StartPaiFlow(StartPaiFlowCallback callback) override; void StartFastAppReinstallFlow( const std::vector<std::string>& package_names) override; - void RequestAssistStructure(RequestAssistStructureCallback callback) override; void IsInstallable(const std::string& package_name, IsInstallableCallback callback) override; void GetAppCategory(const std::string& package_name,
diff --git a/ash/shelf/login_shelf_button.cc b/ash/shelf/login_shelf_button.cc index c1f1aca..45a85135 100644 --- a/ash/shelf/login_shelf_button.cc +++ b/ash/shelf/login_shelf_button.cc
@@ -31,7 +31,8 @@ namespace { // The highlight radius of the button. -constexpr int kButtonHighlightRadiusDp = 16; +// The large pill buttons height is 36 and the radius should be half of that. +constexpr int kButtonHighlightRadiusDp = 18; } // namespace
diff --git a/ash/shelf/login_shelf_view_pixeltest.cc b/ash/shelf/login_shelf_view_pixeltest.cc index ad1470e7..aebdf12e 100644 --- a/ash/shelf/login_shelf_view_pixeltest.cc +++ b/ash/shelf/login_shelf_view_pixeltest.cc
@@ -77,28 +77,28 @@ PressAndReleaseKey(ui::VKEY_TAB); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_login_user_expand_button", - /*revision_number=*/8, primary_big_user_view_.get(), + /*revision_number=*/10, primary_big_user_view_.get(), primary_shelf_window)); // Trigger the tab key. Check that the login shelf shutdown button is focused. PressAndReleaseKey(ui::VKEY_TAB); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_shutdown_button", - /*revision_number=*/8, primary_big_user_view_.get(), + /*revision_number=*/10, primary_big_user_view_.get(), primary_shelf_window)); // Trigger the tab key. Check that the browser as guest button is focused. PressAndReleaseKey(ui::VKEY_TAB); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_browser_as_guest_button", - /*revision_number=*/8, primary_big_user_view_.get(), + /*revision_number=*/10, primary_big_user_view_.get(), primary_shelf_window)); // Trigger the tab key. Check that the add person button is focused. PressAndReleaseKey(ui::VKEY_TAB); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_add_person_button", - /*revision_number=*/8, primary_big_user_view_.get(), + /*revision_number=*/10, primary_big_user_view_.get(), primary_shelf_window)); } @@ -113,13 +113,13 @@ aura::Window* primary_shelf_window = GetPrimaryShelf()->GetWindow(); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_calendar_view", - /*revision_number=*/4, primary_shelf_window)); + /*revision_number=*/6, primary_shelf_window)); // Focus on the time view. PressAndReleaseKey(ui::VKEY_TAB); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_time_view.rev_0", - /*revision_number=*/4, primary_shelf_window)); + /*revision_number=*/6, primary_shelf_window)); PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); @@ -127,7 +127,7 @@ // Move the focus back to the add person button. EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "refocus_on_login_shelf", - /*revision_number=*/4, primary_shelf_window)); + /*revision_number=*/6, primary_shelf_window)); } class LoginShelfWithPolicyWallpaperPixelTestWithRTL @@ -156,7 +156,7 @@ FocusOnShutdownButton(); EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( "focus_on_shutdown_button", - /*revision_number=*/6, primary_big_user_view_.get(), + /*revision_number=*/8, primary_big_user_view_.get(), GetPrimaryShelf()->GetWindow())); }
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts b/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts index 7d57ad34..d36c89b 100644 --- a/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts +++ b/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts
@@ -115,7 +115,7 @@ ) { this.preview = new Preview(async () => { await this.reconfigure(); - }); + }, () => this.useSquareResolution()); this.scheduler = new OperationScheduler( this,
diff --git a/ash/webui/camera_app_ui/resources/js/device/preview.ts b/ash/webui/camera_app_ui/resources/js/device/preview.ts index a2323d52..7c07063 100644 --- a/ash/webui/camera_app_ui/resources/js/device/preview.ts +++ b/ash/webui/camera_app_ui/resources/js/device/preview.ts
@@ -51,7 +51,11 @@ import * as util from '../util.js'; import {WaitableEvent} from '../waitable_event.js'; -import {MediaStreamPTZController, PTZController} from './ptz_controller.js'; +import { + DigitalZoomPTZController, + MediaStreamPTZController, + PTZController, +} from './ptz_controller.js'; import { StreamConstraints, toMediaStreamConstraints, @@ -123,6 +127,9 @@ private readonly autoQRFlag = loadTimeData.getChromeFlag(Flag.AUTO_QR); + private readonly digitalZoomFlag = + loadTimeData.getChromeFlag(Flag.DIGITAL_ZOOM); + /** * PTZController for the current stream constraint. Null if PTZ is not * supported. @@ -132,7 +139,9 @@ /** * @param onNewStreamNeeded Callback to request new stream. */ - constructor(private readonly onNewStreamNeeded: () => Promise<void>) { + constructor( + private readonly onNewStreamNeeded: () => Promise<void>, + private readonly isSquareResolution: () => boolean) { expert.addObserver( expert.ExpertOption.SHOW_METADATA, queuedAsyncCallback('keepLatest', () => this.updateShowMetadata())); @@ -205,6 +214,18 @@ private async updatePTZ() { const deviceOperator = DeviceOperator.getInstance(); const {pan, tilt, zoom} = this.getVideoTrack().getCapabilities(); + const {deviceId} = getVideoTrackSettings(this.getVideoTrack()); + const isDigitalZoomSupported = this.digitalZoomFlag && + (await deviceOperator?.isDigitalZoomSupported(deviceId) ?? false); + + if (isDigitalZoomSupported) { + this.isSupportPTZInternal = true; + const isSquare = this.isSquareResolution(); + const aspectRatio = isSquare ? 1 : this.getResolution().aspectRatio; + this.ptzController = + await DigitalZoomPTZController.create(deviceId, aspectRatio); + return; + } this.isSupportPTZInternal = (() => { if (pan === undefined && tilt === undefined && zoom === undefined) { @@ -217,6 +238,8 @@ if (this.facing === Facing.EXTERNAL) { return true; } else if (expert.isEnabled(expert.ExpertOption.ENABLE_PTZ_FOR_BUILTIN)) { + // TODO(b/225112054): Remove the expert option once digital zoom is + // enabled by default. return true; } @@ -228,7 +251,6 @@ return; } - const {deviceId} = getVideoTrackSettings(this.getVideoTrack()); const deviceDefaultPTZ = await this.getDeviceDefaultPTZ(deviceId); this.ptzController = new MediaStreamPTZController( this.getVideoTrack(), deviceDefaultPTZ, this.vidPid);
diff --git a/ash/webui/camera_app_ui/resources/js/device/ptz_controller.ts b/ash/webui/camera_app_ui/resources/js/device/ptz_controller.ts index 6bf48578..21f2a22 100644 --- a/ash/webui/camera_app_ui/resources/js/device/ptz_controller.ts +++ b/ash/webui/camera_app_ui/resources/js/device/ptz_controller.ts
@@ -2,9 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {assert, assertExists} from '../assert.js'; +import {DeviceOperator} from '../mojo/device_operator.js'; import * as state from '../state.js'; +import {CropRegionRect, Resolution} from '../type.js'; -type PTZAttr = 'pan'|'tilt'|'zoom'; +enum PTZAttr { + PAN = 'pan', + TILT = 'tilt', + ZOOM = 'zoom', +} + interface PTZCapabilities { pan: MediaSettingsRange; tilt: MediaSettingsRange; @@ -121,15 +129,15 @@ } async pan(value: number): Promise<void> { - await this.applyPTZ('pan', value); + await this.applyPTZ(PTZAttr.PAN, value); } async tilt(value: number): Promise<void> { - await this.applyPTZ('tilt', value); + await this.applyPTZ(PTZAttr.TILT, value); } async zoom(value: number): Promise<void> { - await this.applyPTZ('zoom', value); + await this.applyPTZ(PTZAttr.ZOOM, value); } private async applyPTZ(attr: PTZAttr, value: number): Promise<void> { @@ -139,3 +147,191 @@ await this.track.applyConstraints({advanced: [{[attr]: value}]}); } } + +const DIGITAL_ZOOM_MAX_PAN = 1; +const DIGITAL_ZOOM_MAX_TILT = 1; +const DIGITAL_ZOOM_DEFAULT_MAX_ZOOM = 6; +const DIGITAL_ZOOM_CAPABILITIES: PTZCapabilities = { + pan: {min: -DIGITAL_ZOOM_MAX_PAN, max: DIGITAL_ZOOM_MAX_PAN, step: 0.1}, + tilt: {min: -DIGITAL_ZOOM_MAX_TILT, max: DIGITAL_ZOOM_MAX_TILT, step: 0.1}, + zoom: {min: 1, max: DIGITAL_ZOOM_DEFAULT_MAX_ZOOM, step: 0.1}, +}; +const DIGITAL_ZOOM_DEFAULT_SETTINGS: PTZSettings = { + pan: 0, + tilt: 0, + zoom: 1, +}; + +/** + * Calculate the crop region when fully zoomed out for the given aspect ratio. + * The crop region is calculated based on camera metadata + * ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE. If the target aspect ratio doesn't + * match the active array's aspect ratio, the crop region is either cropped + * vertically or horizontally, and centered within the active array. + */ +function getFullCropRegionForAspectRatio( + activeArray: Resolution, targetAspectRatio: number): CropRegionRect { + const {width: originalWidth, height: originalHeight} = activeArray; + if (activeArray.aspectRatio > targetAspectRatio) { + // Crop vertically if the original aspect ratio is wider than the target. + const croppedWidth = Math.round(originalHeight * targetAspectRatio); + return { + x: Math.round((originalWidth - croppedWidth) / 2), + y: 0, + width: croppedWidth, + height: originalHeight, + }; + } + // Otherwise, crop horizontally. + const croppedHeight = Math.round(originalWidth / targetAspectRatio); + return { + x: 0, + y: Math.round((originalHeight - croppedHeight) / 2), + width: originalWidth, + height: croppedHeight, + }; +} + +/** + * Calculate a crop region from given PTZ settings. The crop region result is + * normalized given full width and full height equal to 1. + */ +function calculateNormalizedCropRegion({pan, tilt, zoom}: PTZSettings): + CropRegionRect { + assert(pan !== undefined); + assert(tilt !== undefined); + assert(zoom !== undefined && zoom > 0, `Zoom value ${zoom} is invalid.`); + + const width = 1 / zoom; + const height = 1 / zoom; + + // Top-left coordinate of the crop region before the pan and tilt values are + // applied. + const startX = (1 - width) / 2; + const startY = (1 - height) / 2; + + // Move x, y with pan and tilt values. Pan and tilt values are in the range + // [-1, 1], with pan = -1 being leftmost, and tilt = -1 being bottommost. + const x = startX + ((pan / DIGITAL_ZOOM_MAX_PAN) * startX); + const y = startY - ((tilt / DIGITAL_ZOOM_MAX_TILT) * startY); + + // Verify that the calculated crop region is valid. + const lowerBound = -1e-3; + const upperBound = 1 + 1e-3; + assert(x > lowerBound && x < upperBound && y > lowerBound && y < upperBound); + assert((x + width) < upperBound && (y + height) < upperBound); + + return {x, y, width, height}; +} + +/** + * Calculate a crop region from PTZ settings with respect to |fullCropRegion|. + */ +function calculateCropRegion( + ptzSettings: PTZSettings, fullCropRegion: CropRegionRect): CropRegionRect { + const normCropRegion = calculateNormalizedCropRegion(ptzSettings); + const {width: fullWidth, height: fullHeight} = fullCropRegion; + + return { + x: Math.round(fullCropRegion.x + (normCropRegion.x * fullWidth)), + y: Math.round(fullCropRegion.y + (normCropRegion.y * fullHeight)), + width: Math.round(normCropRegion.width * fullWidth), + height: Math.round(normCropRegion.height * fullHeight), + }; +} + +/** + * Asserts that pan, tilt, or zoom value is within the range defined in + * |DIGITAL_ZOOM_CAPABILITIES|. + */ +function assertPTZRange(attr: PTZAttr, value: number) { + const {max: maxValue, min: minValue} = DIGITAL_ZOOM_CAPABILITIES[attr]; + const tolerance = 1e-3; + assert( + value >= minValue - tolerance && value <= maxValue + tolerance, + `${attr} value ${value} is not within the allowed range.`); +} + +export class DigitalZoomPTZController implements PTZController { + private ptzSettings: PTZSettings = DIGITAL_ZOOM_DEFAULT_SETTINGS; + + private constructor( + private readonly deviceId: string, + private readonly fullCropRegion: CropRegionRect) {} + + canPan(): boolean { + return true; + } + + canTilt(): boolean { + return true; + } + + canZoom(): boolean { + return true; + } + + getCapabilities(): PTZCapabilities { + return DIGITAL_ZOOM_CAPABILITIES; + } + + getSettings(): PTZSettings { + return this.ptzSettings; + } + + isPanTiltRestricted(): boolean { + // When fully zoomed out, calculated crop region equals to |fullCropRegion|, + // this means pan and tilt are disabled. + return true; + } + + async resetPTZ(): Promise<void> { + const deviceOperator = assertExists(DeviceOperator.getInstance()); + await deviceOperator.resetCropRegion(this.deviceId); + this.ptzSettings = DIGITAL_ZOOM_DEFAULT_SETTINGS; + } + + async pan(value: number): Promise<void> { + assertPTZRange(PTZAttr.PAN, value); + const newSettings = {...this.ptzSettings, pan: value}; + await this.applyPTZ(newSettings); + } + + async tilt(value: number): Promise<void> { + assertPTZRange(PTZAttr.TILT, value); + const newSettings = {...this.ptzSettings, tilt: value}; + await this.applyPTZ(newSettings); + } + + async zoom(value: number): Promise<void> { + assertPTZRange(PTZAttr.ZOOM, value); + const newSettings = {...this.ptzSettings, zoom: value}; + await this.applyPTZ(newSettings); + } + + private async applyPTZ(newSettings: PTZSettings): Promise<void> { + if (this.isFullFrame(newSettings)) { + return this.resetPTZ(); + } + const deviceOperator = assertExists(DeviceOperator.getInstance()); + const cropRegion = calculateCropRegion(newSettings, this.fullCropRegion); + await deviceOperator.setCropRegion(this.deviceId, cropRegion); + this.ptzSettings = newSettings; + } + + private isFullFrame({zoom}: PTZSettings): boolean { + assert(zoom !== undefined); + const minZoom = assertExists(DIGITAL_ZOOM_CAPABILITIES.zoom.min); + const zoomStep = assertExists(DIGITAL_ZOOM_CAPABILITIES.zoom.step); + return Math.abs(zoom - minZoom) < zoomStep; + } + + static async create(deviceId: string, aspectRatio: number): + Promise<DigitalZoomPTZController> { + const deviceOperator = assertExists(DeviceOperator.getInstance()); + const activeArray = await deviceOperator.getActiveArraySize(deviceId); + const fullCropRegion = + getFullCropRegionForAspectRatio(activeArray, aspectRatio); + return new DigitalZoomPTZController(deviceId, fullCropRegion); + } +}
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts b/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts index 4130d98..7f11f23 100644 --- a/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts +++ b/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts
@@ -813,6 +813,26 @@ } /** + * Returns whether digital zoom is supported in the camera. + */ + async isDigitalZoomSupported(deviceId: string): Promise<boolean> { + // Checks if the device can do zoom through the stream manipulator. + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const digitalZoomTag = 0x80070000 as CameraMetadataTag; + const digitalZoomData = + await this.getStaticMetadata(deviceId, digitalZoomTag); + + // Some devices can do zoom given the crop region in their HALs. This + // ability can be checked with AVAILABLE_MAX_DIGITAL_ZOOM value being + // greater than 1. + const maxZoomRatio = await this.getStaticMetadata( + deviceId, CameraMetadataTag.ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); + const hasInternalZoom = maxZoomRatio.length > 0 && maxZoomRatio[0] > 1; + + return digitalZoomData.length > 0 || hasInternalZoom; + } + + /** * Initializes the singleton instance. * * This should be called before all invocation of static getInstance() and
diff --git a/ash/webui/common/resources/quick_unlock/fingerprint_progress.ts b/ash/webui/common/resources/quick_unlock/fingerprint_progress.ts index 4754ffc0..d203a7c 100644 --- a/ash/webui/common/resources/quick_unlock/fingerprint_progress.ts +++ b/ash/webui/common/resources/quick_unlock/fingerprint_progress.ts
@@ -168,6 +168,7 @@ this.progressCircleBackgroundColor = getComputedStyle(document.body) .getPropertyValue('--cros-sys-primary_container'); + this.$.scanningAnimation.refreshAnimationColors(); } override connectedCallback() { @@ -204,6 +205,7 @@ this.updateAnimationAsset_(); this.resizeAndCenterIcon_(scanningAnimation); scanningAnimation.hidden = false; + this.$.scanningAnimation.refreshAnimationColors(); } /**
diff --git a/ash/wm/desks/OWNERS b/ash/wm/desks/OWNERS index dbaf9f1b8..b534716 100644 --- a/ash/wm/desks/OWNERS +++ b/ash/wm/desks/OWNERS
@@ -1,2 +1,11 @@ afakhry@chromium.org dandersson@chromium.org + +# For desks UIs, desk bars, tests, and utilities. +per-file *button*=yongshun@chromium.org +per-file *menu*=yongshun@chromium.org +per-file *textfield*=yongshun@chromium.org +per-file *view*=yongshun@chromium.org +per-file *desk_bar*=yongshun@chromium.org +per-file *test*=yongshun@chromium.org +per-file *util*=yongshun@chromium.org
diff --git a/ash/wm/desks/templates/OWNERS b/ash/wm/desks/templates/OWNERS index e4f3971..2ce2928 100644 --- a/ash/wm/desks/templates/OWNERS +++ b/ash/wm/desks/templates/OWNERS
@@ -1,3 +1,4 @@ dandersson@chromium.org richui@chromium.org sammiequon@chromium.org +yongshun@chromium.org
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc index a104bdda..a244869 100644 --- a/ash/wm/toplevel_window_event_handler.cc +++ b/ash/wm/toplevel_window_event_handler.cc
@@ -735,6 +735,10 @@ is_moving_floated_window_ = true; } + // Mark the currently dragged or resized window as excluded for occlusion + // purposes. + scoped_exclude_.emplace(window); + return true; } @@ -908,8 +912,11 @@ } bool ToplevelWindowEventHandler::CompleteDrag(DragResult result) { - if (!window_resizer_) + scoped_exclude_.reset(); + + if (!window_resizer_) { return false; + } std::unique_ptr<ScopedWindowResizer> resizer(std::move(window_resizer_)); switch (result) {
diff --git a/ash/wm/toplevel_window_event_handler.h b/ash/wm/toplevel_window_event_handler.h index 426c5f8f..42077a3 100644 --- a/ash/wm/toplevel_window_event_handler.h +++ b/ash/wm/toplevel_window_event_handler.h
@@ -14,6 +14,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "ui/aura/window_observer.h" +#include "ui/aura/window_occlusion_tracker.h" #include "ui/display/display_observer.h" #include "ui/events/event_handler.h" #include "ui/events/gestures/gesture_types.h" @@ -225,6 +226,13 @@ std::unique_ptr<ScopedWindowResizer> window_resizer_; + // We exclude dragged or resized windows from occluding things below them to + // prevent windows from being marked as occluded temporarily while another + // window is being, for example, dragged over them. This is particularly + // necessary for lacros where occlusion may cause the content to be evicted + // and replaced with a snapshot. + std::optional<aura::WindowOcclusionTracker::ScopedExclude> scoped_exclude_; + display::ScopedDisplayObserver display_observer_{this}; EndClosure end_closure_;
diff --git a/base/BUILD.gn b/base/BUILD.gn index 7317f2b..4fadefb0 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -290,6 +290,8 @@ "containers/map_util.h", "containers/small_map.h", "containers/stack.h", + "containers/to_value_list.h", + "containers/to_vector.h", "containers/unique_ptr_adapters.h", "containers/util.h", "containers/vector_buffer.h", @@ -3183,6 +3185,8 @@ "containers/map_util_unittest.cc", "containers/small_map_unittest.cc", "containers/span_unittest.cc", + "containers/to_value_list_unittest.cc", + "containers/to_vector_unittest.cc", "containers/unique_ptr_adapters_unittest.cc", "containers/vector_buffer_unittest.cc", "cpu_unittest.cc", @@ -3455,7 +3459,6 @@ "test/test_pending_task_unittest.cc", "test/test_proto_loader_unittest.cc", "test/test_waitable_event_unittest.cc", - "test/to_vector_unittest.cc", "third_party/dynamic_annotations/dynamic_annotations_compiletest.cc", "thread_annotations_unittest.cc", "threading/hang_watcher_unittest.cc", @@ -3533,7 +3536,7 @@ ] } - if (use_safe_libcxx) { + if (use_safe_libcxx || use_safe_libstdcxx) { sources += [ "libcpp_hardening_test.cc" ] } @@ -4220,6 +4223,8 @@ "containers/enum_set_nocompile.nc", "containers/heap_array_nocompile.nc", "containers/span_nocompile.nc", + "containers/to_value_list_nocompile.nc", + "containers/to_vector_nocompile.nc", "debug/crash_logging_nocompile.nc", "functional/bind_nocompile.nc", "functional/callback_nocompile.nc",
diff --git a/base/containers/to_value_list.h b/base/containers/to_value_list.h new file mode 100644 index 0000000..0f2f23f --- /dev/null +++ b/base/containers/to_value_list.h
@@ -0,0 +1,45 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_TO_VALUE_LIST_H_ +#define BASE_CONTAINERS_TO_VALUE_LIST_H_ + +#include <concepts> +#include <functional> +#include <iterator> +#include <type_traits> +#include <utility> + +#include "base/ranges/algorithm.h" +#include "base/ranges/ranges.h" +#include "base/values.h" + +namespace base { + +namespace internal { +template <typename T> +concept AppendableToValueList = + requires(T&& value) { Value::List().Append(std::forward<T>(value)); }; +} // namespace internal + +// Maps a container to a Value::List with respect to the provided projection. +// +// Complexity: Exactly `size(range)` applications of `proj`. +template <typename Range, typename Proj = std::identity> + requires requires { typename internal::range_category_t<Range>; } && + std::indirectly_unary_invocable<Proj, ranges::iterator_t<Range>> && + internal::AppendableToValueList< + std::indirect_result_t<Proj, ranges::iterator_t<Range>>> +Value::List ToValueList(Range&& range, Proj proj = {}) { + auto container = Value::List::with_capacity(std::size(range)); + ranges::for_each( + std::forward<Range>(range), + [&]<typename T>(T&& value) { container.Append(std::forward<T>(value)); }, + std::move(proj)); + return container; +} + +} // namespace base + +#endif // BASE_CONTAINERS_TO_VALUE_LIST_H_
diff --git a/base/containers/to_value_list_nocompile.nc b/base/containers/to_value_list_nocompile.nc new file mode 100644 index 0000000..2f27a92 --- /dev/null +++ b/base/containers/to_value_list_nocompile.nc
@@ -0,0 +1,49 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a "No Compile Test" suite. +// http://dev.chromium.org/developers/testing/no-compile-tests + +#include "base/containers/to_value_list.h" + +#include <tuple> +#include <utility> +#include <vector> + +namespace base { + +// ToValueList() doesn't implicitly deduce initializer lists. +void InitializerList() { + std::ignore = ToValueList({"aaa", "bbb", "ccc"}); // expected-error@*:* {{no matching function for call to 'ToValueList'}} +} + +// Lambdas operating on rvalue ranges of move-only elements expect lvalue +// references to the element type. +void MoveOnlyProjections() { + struct MoveOnly { + MoveOnly() = default; + + MoveOnly(const MoveOnly&) = delete; + MoveOnly& operator=(const MoveOnly&) = delete; + + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + }; + + std::vector<MoveOnly> vec; + std::ignore = ToValueList(std::move(vec), [](MoveOnly arg) { + return arg; + }); // expected-error@*:* {{no matching function for call to 'ToValueList'}} + std::ignore = ToValueList(std::move(vec), [](MoveOnly&& arg) { + return std::move(arg); + }); // expected-error@*:* {{no matching function for call to 'ToValueList'}} +} + +// Return type of the projection must be compatible with Value::List::Append(). +void AppendableToList() { + std::vector<int> vec; + std::ignore = ToValueList(vec, [](int) -> int* { return nullptr; }); // expected-error@*:* {{no matching function for call to 'ToValueList'}} +} + +} // namespace base
diff --git a/base/containers/to_value_list_unittest.cc b/base/containers/to_value_list_unittest.cc new file mode 100644 index 0000000..585d520 --- /dev/null +++ b/base/containers/to_value_list_unittest.cc
@@ -0,0 +1,65 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/containers/to_value_list.h" + +#include <set> + +#include "base/containers/flat_set.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +// `Value` isn't copyable, so it's not possible to match against Value(x) +// directly in `testing::ElementsAre`. This is why Value(x) is replaced with +// IsInt(x). +auto IsInt(int value) { + return testing::Property(&Value::GetInt, testing::Eq(value)); +} + +template <class C> +void IdentityTest() { + C c = {1, 2, 3, 4, 5}; + // See comment in `IsInt()` above. + EXPECT_THAT(ToValueList(c), testing::ElementsAre(IsInt(1), IsInt(2), IsInt(3), + IsInt(4), IsInt(5))); +} + +template <class C> +void ProjectionTest() { + C c = {1, 2, 3, 4, 5}; + // See comment in `IsInt()` above. + EXPECT_THAT( + ToValueList(c, [](int x) { return x + 1; }), + testing::ElementsAre(IsInt(2), IsInt(3), IsInt(4), IsInt(5), IsInt(6))); +} + +TEST(ToListTest, Identity) { + IdentityTest<std::vector<int>>(); + IdentityTest<std::set<int>>(); + IdentityTest<int[]>(); + IdentityTest<flat_set<int>>(); +} + +TEST(ToListTest, Projection) { + ProjectionTest<std::vector<int>>(); + ProjectionTest<std::set<int>>(); + ProjectionTest<int[]>(); + ProjectionTest<flat_set<int>>(); +} + +// Validates that consuming projections work as intended (every single `Value` +// inside `Value::List` is a move-only type). +TEST(ToListTest, MoveOnly) { + Value::List list; + list.resize(10); + Value::List mapped_list = ToValueList( + std::move(list), [](Value& value) { return std::move(value); }); + EXPECT_EQ(mapped_list.size(), 10U); +} + +} // namespace +} // namespace base
diff --git a/base/containers/to_vector.h b/base/containers/to_vector.h new file mode 100644 index 0000000..6213ec5 --- /dev/null +++ b/base/containers/to_vector.h
@@ -0,0 +1,43 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_TO_VECTOR_H_ +#define BASE_CONTAINERS_TO_VECTOR_H_ + +#include <functional> +#include <iterator> +#include <type_traits> +#include <utility> +#include <vector> + +#include "base/ranges/algorithm.h" +#include "base/ranges/ranges.h" + +namespace base { + +// Maps a container to a std::vector<> with respect to the provided projection. +// The deduced vector element type is equal to the projection's return type with +// cv-qualifiers removed. +// +// In C++20 this is roughly equal to: +// auto vec = range | std::views:transform(proj) | +// std::ranges::to<std::vector>; +// +// Complexity: Exactly `size(range)` applications of `proj`. +template <typename Range, typename Proj = std::identity> + requires requires { typename internal::range_category_t<Range>; } && + std::indirectly_unary_invocable<Proj, ranges::iterator_t<Range>> +auto ToVector(Range&& range, Proj proj = {}) { + using ProjectedType = + std::projected<ranges::iterator_t<Range>, Proj>::value_type; + std::vector<ProjectedType> container; + container.reserve(std::size(range)); + ranges::transform(std::forward<Range>(range), std::back_inserter(container), + std::move(proj)); + return container; +} + +} // namespace base + +#endif // BASE_CONTAINERS_TO_VECTOR_H_
diff --git a/base/containers/to_vector_nocompile.nc b/base/containers/to_vector_nocompile.nc new file mode 100644 index 0000000..e55f791 --- /dev/null +++ b/base/containers/to_vector_nocompile.nc
@@ -0,0 +1,43 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a "No Compile Test" suite. +// http://dev.chromium.org/developers/testing/no-compile-tests + +#include "base/containers/to_vector.h" + +#include <tuple> +#include <utility> +#include <vector> + +namespace base { + +// ToVector() doesn't implicitly deduce initializer lists. +void InitializerList() { + std::ignore = ToVector({"aaa", "bbb", "ccc"}); // expected-error@*:* {{no matching function for call to 'ToVector'}} +} + +// Lambdas operating on rvalue ranges of move-only elements expect lvalue +// references to the element type. +void MoveOnlyProjections() { + struct MoveOnly { + MoveOnly() = default; + + MoveOnly(const MoveOnly&) = delete; + MoveOnly& operator=(const MoveOnly&) = delete; + + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + }; + + std::vector<MoveOnly> vec; + std::ignore = ToVector(std::move(vec), [](MoveOnly arg) { + return arg; + }); // expected-error@*:* {{no matching function for call to 'ToVector'}} + std::ignore = ToVector(std::move(vec), [](MoveOnly&& arg) { + return std::move(arg); + }); // expected-error@*:* {{no matching function for call to 'ToVector'}} +} + +} // namespace base
diff --git a/base/test/to_vector_unittest.cc b/base/containers/to_vector_unittest.cc similarity index 96% rename from base/test/to_vector_unittest.cc rename to base/containers/to_vector_unittest.cc index 36117c4..9657d339 100644 --- a/base/test/to_vector_unittest.cc +++ b/base/containers/to_vector_unittest.cc
@@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/test/to_vector.h" +#include "base/containers/to_vector.h" #include <set> #include "base/containers/flat_set.h" +#include "base/ranges/ranges.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/base/libcpp_hardening_test.cc b/base/libcpp_hardening_test.cc index 4224447..b1f4d99 100644 --- a/base/libcpp_hardening_test.cc +++ b/base/libcpp_hardening_test.cc
@@ -12,7 +12,11 @@ // TODO(thakis): Remove _LIBCPP_ENABLE_ASSERTIONS here once // pnacl-saigo's libc++ is new enough. -#if !_LIBCPP_ENABLE_ASSERTIONS && \ +#if defined(__GLIBCXX__) +#if !defined(_GLIBCXX_ASSERTIONS) +#error "libstdc++ assertions should be enabled" +#endif +#elif !_LIBCPP_ENABLE_ASSERTIONS && \ _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_EXTENSIVE #error "_LIBCPP_HARDENING_MODE not defined" #endif
diff --git a/base/test/to_vector.h b/base/test/to_vector.h index a2aeeff..83dfd021 100644 --- a/base/test/to_vector.h +++ b/base/test/to_vector.h
@@ -6,33 +6,26 @@ #define BASE_TEST_TO_VECTOR_H_ #include <functional> -#include <iterator> -#include <type_traits> -#include <utility> -#include <vector> +#include "base/containers/to_vector.h" #include "base/ranges/algorithm.h" -#include "base/template_util.h" namespace base::test { -// A handy helper for mapping any container into an std::vector<> with respect -// to the provided projection. The deduced vector element type is equal to the -// projection's return type with cv-qualifiers removed. +// Maps a container to a std::vector<> with respect to the provided projection. +// The deduced vector element type is equal to the projection's return type with +// cv-qualifiers removed. // // In C++20 this is roughly equal to: -// auto vec = range | views:transform(proj) | ranges::to<std::vector>; +// auto vec = range | std::views:transform(proj) | std::ranges::to<std::vector>; // // Complexity: Exactly `size(range)` applications of `proj`. +// +// TODO(crbug.com/326392658): Replace existing callsites with base::ToVector<>. template <typename Range, typename Proj = std::identity> + requires requires { typename base::internal::range_category_t<Range>; } auto ToVector(Range&& range, Proj proj = {}) { - using ProjectedType = - std::invoke_result_t<Proj, decltype(*std::begin(range))>; - std::vector<std::remove_cvref_t<ProjectedType>> container; - container.reserve(std::size(range)); - base::ranges::transform(std::forward<Range>(range), - std::back_inserter(container), std::move(proj)); - return container; + return base::ToVector(std::forward<Range>(range), std::move(proj)); } } // namespace base::test
diff --git a/base/win/security_util.cc b/base/win/security_util.cc index 596da7bc..29634b8 100644 --- a/base/win/security_util.cc +++ b/base/win/security_util.cc
@@ -10,6 +10,7 @@ #include <optional> #include "base/check.h" +#include "base/containers/to_vector.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/threading/scoped_blocking_call.h" @@ -87,12 +88,7 @@ } std::vector<Sid> CloneSidVector(const std::vector<Sid>& sids) { - std::vector<Sid> clone; - clone.reserve(sids.size()); - for (const Sid& sid : sids) { - clone.push_back(sid.Clone()); - } - return clone; + return base::ToVector(sids, &Sid::Clone); } void AppendSidVector(std::vector<Sid>& base_sids,
diff --git a/build/config/c++/c++.gni b/build/config/c++/c++.gni index 3c0b549..bd8e711 100644 --- a/build/config/c++/c++.gni +++ b/build/config/c++/c++.gni
@@ -59,6 +59,10 @@ # enable libc++ hardening there as well. use_safe_libcxx = (use_custom_libcxx && enable_safe_libcxx) || is_nacl_saigo +# libstdc++ has its own hardening assertions that we want to enable by default +# in Chromium builds. +use_safe_libstdcxx = is_linux && !use_custom_libcxx && enable_safe_libstdcxx + # libc++abi needs to be exported from executables to be picked up by shared # libraries on certain instrumented builds. export_libcxxabi_from_executables =
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 84e6c03e..62088a0 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -1069,6 +1069,12 @@ } else { defines += [ "_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE" ] } + + # Enable libstdc++ hardening lightweight assertions. Those have a low + # performance penalty but are considered a bare minimum for security. + if (use_safe_libstdcxx) { + defines += [ "_GLIBCXX_ASSERTIONS=1" ] + } } # The BUILDCONFIG file sets this config on targets by default, which means when
diff --git a/build_overrides/build.gni b/build_overrides/build.gni index 4ea4da3..6c36c6c 100644 --- a/build_overrides/build.gni +++ b/build_overrides/build.gni
@@ -30,6 +30,12 @@ # `use_custom_libcxx = true`. enable_safe_libcxx = true +# Enable assertions on safety checks, also in libstdc++ +# +# In case the C++ standard library implementation used is libstdc++, then +# enable its own hardening checks. +enable_safe_libstdcxx = true + # Features used by //base/trace_event and //services/tracing. declare_args() { # Tracing support requires //third_party/perfetto, which is not available in
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java index 4650cd20..955769c 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java
@@ -42,6 +42,7 @@ import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.base.test.util.RequiresRestart; @@ -157,6 +158,7 @@ @Test @MediumTest @RequiresRestart + @DisabledTest(message = "https://crbug.com/327457591") public void testEnterAndExitTabSwitcher() throws TimeoutException { Layout layout = sActivityTestRule.getActivity().getLayoutManager().getOverviewLayout(); assertNull("StartSurface layout should not be initialized", layout);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java index bf9561f..8c7225c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
@@ -44,6 +44,8 @@ import org.chromium.components.signin.AccountManagerFacadeProvider; import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.GAIAServiceType; +import org.chromium.components.signin.SigninFeatureMap; +import org.chromium.components.signin.SigninFeatures; import org.chromium.components.signin.Tribool; import org.chromium.components.signin.base.AccountInfo; import org.chromium.components.signin.base.CoreAccountInfo; @@ -555,8 +557,11 @@ } private boolean isSupervisedUser() { + // SEED_ACCOUNTS_REVAMP is needed for using capabilities, otherwise + // findExtendedAccountInfoByEmailAddress is not guaranteed to have the needed account if (ChromeFeatureList.isEnabled( - ChromeFeatureList.MIGRATE_ACCOUNT_MANAGEMENT_SETTINGS_TO_CAPABILITIES)) { + ChromeFeatureList.MIGRATE_ACCOUNT_MANAGEMENT_SETTINGS_TO_CAPABILITIES) + && SigninFeatureMap.isEnabled(SigninFeatures.SEED_ACCOUNTS_REVAMP)) { assert mSignedInCoreAccountInfo != null; AccountInfo accountinfo = IdentityServicesProvider.get()
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn index 9ab6494..002134f 100644 --- a/chrome/browser/ash/crosapi/BUILD.gn +++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -183,6 +183,7 @@ "lacros_data_backward_migration_mode_policy_observer.cc", "lacros_data_backward_migration_mode_policy_observer.h", "lacros_selection_loader.h", + "lacros_selection_loader_factory.h", "lacros_shelf_item_tracker.cc", "lacros_shelf_item_tracker.h", "local_printer_ash.cc",
diff --git a/chrome/browser/ash/crosapi/browser_loader.cc b/chrome/browser/ash/crosapi/browser_loader.cc index d5c0eab..68c21ea 100644 --- a/chrome/browser/ash/crosapi/browser_loader.cc +++ b/chrome/browser/ash/crosapi/browser_loader.cc
@@ -22,6 +22,7 @@ #include "base/values.h" #include "chrome/browser/ash/crosapi/browser_util.h" #include "chrome/browser/ash/crosapi/lacros_selection_loader.h" +#include "chrome/browser/ash/crosapi/lacros_selection_loader_factory.h" #include "chrome/browser/ash/crosapi/rootfs_lacros_loader.h" #include "chrome/browser/ash/crosapi/stateful_lacros_loader.h" #include "chrome/browser/browser_process.h" @@ -33,18 +34,45 @@ namespace { // There are 2 lacros selections, rootfs lacros and stateful lacros. constexpr size_t kLacrosSelectionTypes = 2; + +class LacrosSelectionLoaderFactoryImpl : public LacrosSelectionLoaderFactory { + public: + explicit LacrosSelectionLoaderFactoryImpl( + scoped_refptr<component_updater::CrOSComponentManager> manager) + : component_manager_(manager) {} + + LacrosSelectionLoaderFactoryImpl(const LacrosSelectionLoaderFactoryImpl&) = + delete; + LacrosSelectionLoaderFactoryImpl& operator=( + const LacrosSelectionLoaderFactoryImpl&) = delete; + + ~LacrosSelectionLoaderFactoryImpl() override = default; + + std::unique_ptr<LacrosSelectionLoader> CreateRootfsLacrosLoader() override { + return std::make_unique<RootfsLacrosLoader>(); + } + + std::unique_ptr<LacrosSelectionLoader> CreateStatefulLacrosLoader() override { + return std::make_unique<StatefulLacrosLoader>(component_manager_); + } + + private: + scoped_refptr<component_updater::CrOSComponentManager> component_manager_; +}; + +bool IsUnloading(LacrosSelectionLoader* loader) { + return loader && loader->IsUnloading(); +} + } // namespace BrowserLoader::BrowserLoader( scoped_refptr<component_updater::CrOSComponentManager> manager) - : BrowserLoader(std::make_unique<RootfsLacrosLoader>(), - std::make_unique<StatefulLacrosLoader>(manager)) {} + : factory_(std::make_unique<LacrosSelectionLoaderFactoryImpl>(manager)) {} BrowserLoader::BrowserLoader( - std::unique_ptr<LacrosSelectionLoader> rootfs_lacros_loader, - std::unique_ptr<LacrosSelectionLoader> stateful_lacros_loader) - : rootfs_lacros_loader_(std::move(rootfs_lacros_loader)), - stateful_lacros_loader_(std::move(stateful_lacros_loader)) {} + std::unique_ptr<LacrosSelectionLoaderFactory> factory) + : factory_(std::move(factory)) {} BrowserLoader::~BrowserLoader() = default; @@ -78,6 +106,8 @@ void BrowserLoader::SelectRootfsLacros(LoadCompletionCallback callback, LacrosSelectionSource source) { + CHECK(rootfs_lacros_loader_); + LOG(WARNING) << "rootfs lacros is selected by " << source; rootfs_lacros_loader_->Load( @@ -88,6 +118,8 @@ void BrowserLoader::SelectStatefulLacros(LoadCompletionCallback callback, LacrosSelectionSource source) { + CHECK(stateful_lacros_loader_); + LOG(WARNING) << "stateful lacros is selected by " << source; stateful_lacros_loader_->Load( @@ -98,13 +130,38 @@ // Unmount the rootfs lacros-chrome when using stateful lacros-chrome. // This will keep stateful lacros-chrome only mounted and not hold the rootfs // lacros-chrome mount until an `Unload`. - rootfs_lacros_loader_->Unload(); + if (rootfs_lacros_loader_) { + rootfs_lacros_loader_->Unload( + base::BindOnce(&BrowserLoader::OnUnloadCompleted, + weak_factory_.GetWeakPtr(), LacrosSelection::kRootfs)); + } } void BrowserLoader::Load(LoadCompletionCallback callback) { - // Reset lacros selection loaders before reloading. - rootfs_lacros_loader_->Reset(); - stateful_lacros_loader_->Reset(); + // Load should NOT be called after Unload is requested to BrowserLoader. + CHECK(!is_unload_requested_); + + // If either of rootfs or stateful lacros loader is still unloading, wait + // until the unload completion. + if (IsUnloading(rootfs_lacros_loader_.get()) || + IsUnloading(stateful_lacros_loader_.get())) { + LOG(WARNING) << "Wait load until unload completes"; + callback_on_unload_completion_ = + base::BindOnce(&BrowserLoader::LoadNow, weak_factory_.GetWeakPtr(), + std::move(callback)); + return; + } + + LoadNow(std::move(callback)); +} + +void BrowserLoader::LoadNow(LoadCompletionCallback callback) { + // Reset lacros selection loaders since it may be already initialized one if + // this is reloading. + // TODO(elkurin): We should call Unload before reloading if these loaders + // exist, then we can remove `reset` here. + rootfs_lacros_loader_.reset(); + stateful_lacros_loader_.reset(); lacros_start_load_time_ = base::TimeTicks::Now(); // TODO(crbug.com/1078607): Remove non-error logging from this class. @@ -130,9 +187,11 @@ // too. switch (lacros_selection.value()) { case browser_util::LacrosSelection::kRootfs: + rootfs_lacros_loader_ = factory_->CreateRootfsLacrosLoader(); SelectRootfsLacros(std::move(callback), LacrosSelectionSource::kForced); return; case browser_util::LacrosSelection::kStateful: + stateful_lacros_loader_ = factory_->CreateStatefulLacrosLoader(); SelectStatefulLacros(std::move(callback), LacrosSelectionSource::kForced); return; @@ -145,6 +204,9 @@ } } + rootfs_lacros_loader_ = factory_->CreateRootfsLacrosLoader(); + stateful_lacros_loader_ = factory_->CreateStatefulLacrosLoader(); + // Proceed to load/mount the stateful lacros-chrome binary. // In the case that the stateful lacros-chrome binary wasn't installed, this // might take some time. @@ -174,6 +236,13 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK_EQ(versions.size(), kLacrosSelectionTypes); + if (is_unload_requested_) { + LOG(WARNING) << "Unload is requested during collecting Lacros version."; + std::move(callback).Run(base::FilePath(), LacrosSelection::kStateful, + base::Version()); + return; + } + // Compare the rootfs vs stateful lacros-chrome binary versions. // If the rootfs lacros-chrome is greater than lacros-chrome version, // prioritize using the rootfs lacros-chrome. If the stateful lacros-chrome is @@ -236,10 +305,52 @@ } void BrowserLoader::Unload() { + is_unload_requested_ = true; + // Can be called even if Lacros isn't enabled, to clean up the old install. - stateful_lacros_loader_->Unload(); - // Unmount the rootfs lacros-chrome if it was mounted. - rootfs_lacros_loader_->Unload(); + // Unmount the rootfs/stateful lacros-chrome if it was mounted. + if (rootfs_lacros_loader_) { + rootfs_lacros_loader_->Unload( + base::BindOnce(&BrowserLoader::OnUnloadCompleted, + weak_factory_.GetWeakPtr(), LacrosSelection::kRootfs)); + } + + if (stateful_lacros_loader_) { + stateful_lacros_loader_->Unload( + base::BindOnce(&BrowserLoader::OnUnloadCompleted, + weak_factory_.GetWeakPtr(), LacrosSelection::kStateful)); + } +} + +void BrowserLoader::OnUnloadCompleted(LacrosSelection selection) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + switch (selection) { + case LacrosSelection::kRootfs: + CHECK(rootfs_lacros_loader_->IsUnloaded()); + rootfs_lacros_loader_.reset(); + break; + case LacrosSelection::kStateful: + CHECK(stateful_lacros_loader_->IsUnloaded()); + stateful_lacros_loader_.reset(); + break; + case LacrosSelection::kDeployedLocally: + NOTREACHED(); + break; + } + + // If either of rootfs or stateful lacros loader is still in the process of + // unload, wait running completion callback. + if (IsUnloading(rootfs_lacros_loader_.get()) || + IsUnloading(stateful_lacros_loader_.get())) { + return; + } + + // If both of the rootfs and stateful lacros load completed unloading, run the + // stored callback if exists. + if (callback_on_unload_completion_) { + std::move(callback_on_unload_completion_).Run(); + } } base::FilePath DetermineLacrosBinaryPath(const base::FilePath& path) { @@ -261,6 +372,13 @@ const base::FilePath& path) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (is_unload_requested_) { + LOG(WARNING) << "Unload is requested during loading."; + std::move(callback).Run(base::FilePath(), LacrosSelection::kStateful, + base::Version()); + return; + } + // Bail out on empty `path` which implies there was an error on loading // lacros. if (path.empty()) { @@ -288,6 +406,13 @@ const base::FilePath& lacros_binary) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (is_unload_requested_) { + LOG(WARNING) << "Unload is requested during determining lacros path."; + std::move(callback).Run(base::FilePath(), LacrosSelection::kStateful, + base::Version()); + return; + } + if (lacros_binary.empty()) { LOG(ERROR) << "Failed to find binary at " << path; std::move(callback).Run(base::FilePath(), selection, base::Version());
diff --git a/chrome/browser/ash/crosapi/browser_loader.h b/chrome/browser/ash/crosapi/browser_loader.h index c063f86c..cd6ca11 100644 --- a/chrome/browser/ash/crosapi/browser_loader.h +++ b/chrome/browser/ash/crosapi/browser_loader.h
@@ -26,19 +26,18 @@ namespace crosapi { class LacrosSelectionLoader; +class LacrosSelectionLoaderFactory; using browser_util::LacrosSelection; // Manages download of the lacros-chrome binary. // This class is a part of ash-chrome. class BrowserLoader { public: - // Constructor for production. explicit BrowserLoader( scoped_refptr<component_updater::CrOSComponentManager> manager); + // Constructor for testing. - explicit BrowserLoader( - std::unique_ptr<LacrosSelectionLoader> rootfs_lacros_loader, - std::unique_ptr<LacrosSelectionLoader> stateful_lacros_loader); + explicit BrowserLoader(std::unique_ptr<LacrosSelectionLoaderFactory> factory); BrowserLoader(const BrowserLoader&) = delete; BrowserLoader& operator=(const BrowserLoader&) = delete; @@ -81,15 +80,36 @@ }; // Starts to load lacros-chrome binary or the rootfs lacros-chrome binary. - // |callback| is called on completion with the path to the lacros-chrome on + // `callback` is called on completion with the path to the lacros-chrome on // success, or an empty filepath on failure, and the loaded lacros selection // which is either 'rootfs' or 'stateful'. + // + // If Load is called during Load, the previous load requests are discarded + // and immediately begin loading. LoadCompletionCallback for the previous + // request will NOT be called in this scenario. + // TODO(elkurin): We should run Unload for previous LacroSelectionLoader. + // + // Load should NOT be called once Unlaod is requested. Note that per- + // LacrosSelectionLoader unload that is requested inside BrowserManager class + // may run before or while calling Load. + // If Load is called during per-loader unload, Load request will be processed + // after all ongoing per-loader unload requests complete that are at most 2. + // Currently, this happens only when selecting stateful lacros-chrome and + // unloading rootfs. using LoadCompletionCallback = base::OnceCallback< void(const base::FilePath&, LacrosSelection, base::Version)>; virtual void Load(LoadCompletionCallback callback); // Starts to unload lacros-chrome binary. // Note that this triggers to remove the user directory for lacros-chrome. + // Once Unload is called, BrowserLoader should NOT accept Load requests. + // + // If Unload is called during Load, stops loading procedure on main thread and + // requests LacrosSelectionLoaders to unload. Each loader will start unloading + // after the current task completed. + // + // If Unload is called during Unload, no need to unload again so the + // coming unload request will be ignored. virtual void Unload(); private: @@ -118,6 +138,10 @@ OnLoadLacrosBinarySpecifiedBySwitch); FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest, OnLoadLacrosDirectorySpecifiedBySwitch); + FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest, LoadWhileUnloading); + + // Starts loading now/ + void LoadNow(LoadCompletionCallback callback); // `source` indicates why rootfs/stateful is selected. `source` is only used // for logging. @@ -150,13 +174,25 @@ base::Version version, const base::FilePath& lacros_binary); - // Loader for rootfs lacros and stateful lacros. + // Called on unload completed. + void OnUnloadCompleted(LacrosSelection selection); + + // Loader for rootfs lacros and stateful lacros. Loader objects are + // constructed on start loading and reset on unload completion or on reload. std::unique_ptr<LacrosSelectionLoader> rootfs_lacros_loader_; std::unique_ptr<LacrosSelectionLoader> stateful_lacros_loader_; + std::unique_ptr<LacrosSelectionLoaderFactory> factory_; + // Time when the lacros component was loaded. base::TimeTicks lacros_start_load_time_; + // Called when Unload is completed for both rootfs and stateful lacros. + base::OnceClosure callback_on_unload_completion_; + + // Set to true if BrowserLoader::Unload is requested. + bool is_unload_requested_ = false; + // Used for DCHECKs to ensure method calls executed in the correct thread. SEQUENCE_CHECKER(sequence_checker_);
diff --git a/chrome/browser/ash/crosapi/browser_loader_unittest.cc b/chrome/browser/ash/crosapi/browser_loader_unittest.cc index b2c00a3..e2a8b459 100644 --- a/chrome/browser/ash/crosapi/browser_loader_unittest.cc +++ b/chrome/browser/ash/crosapi/browser_loader_unittest.cc
@@ -6,17 +6,23 @@ #include "ash/constants/ash_switches.h" #include "base/auto_reset.h" +#include "base/check_op.h" #include "base/command_line.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/functional/callback.h" +#include "base/memory/scoped_refptr.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/single_thread_task_runner.h" #include "base/test/bind.h" #include "base/test/scoped_command_line.h" #include "base/test/test_future.h" #include "base/version.h" #include "chrome/browser/ash/crosapi/browser_util.h" #include "chrome/browser/ash/crosapi/lacros_selection_loader.h" +#include "chrome/browser/ash/crosapi/lacros_selection_loader_factory.h" +#include "chrome/browser/component_updater/cros_component_manager.h" #include "chromeos/ash/components/standalone_browser/browser_support.h" #include "components/policy/core/common/policy_map.h" #include "components/policy/policy_constants.h" @@ -28,6 +34,156 @@ namespace crosapi { namespace { +// Call the registered callback when Unload is started. +class UnloadObserver { + public: + UnloadObserver() = default; + UnloadObserver(const UnloadObserver&) = delete; + UnloadObserver& operator=(const UnloadObserver&) = delete; + ~UnloadObserver() = default; + + void OnUnloadStarted() { + if (callback_) { + std::move(callback_).Run(); + } + } + + void SetCallback(base::OnceClosure cb) { callback_ = std::move(cb); } + + private: + base::OnceClosure callback_; +}; + +// This fake class is used to test BrowserLoader who is responsible for deciding +// which lacros selection to use. +// This class does not load nor get actual version. Such features are tested in +// RootfsLacrosLoaderTest for rootfs and StatefulLacrosLoaderTest for stateful. +class FakeLacrosSelectionLoader : public LacrosSelectionLoader { + public: + FakeLacrosSelectionLoader( + const base::Version& version, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : version_(version), task_runner_(task_runner) { + // Create dummy chrome binary path. + CHECK(temp_dir_.CreateUniqueTempDir()); + chrome_path_ = temp_dir_.GetPath().Append(kLacrosChromeBinary); + base::WriteFile(chrome_path_, "I am chrome binary."); + } + + FakeLacrosSelectionLoader(const FakeLacrosSelectionLoader&) = delete; + FakeLacrosSelectionLoader& operator=(const FakeLacrosSelectionLoader&) = + delete; + + ~FakeLacrosSelectionLoader() override = default; + + void Load(LoadCompletionCallback callback, bool forced) override { + // Load should NOT be called when it's unloaded or busy. + CHECK(!is_unloading_ && !is_unloaded_); + + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&FakeLacrosSelectionLoader::OnLoadCompleted, + base::Unretained(this), std::move(callback))); + } + + void Unload(base::OnceClosure callback) override { + is_unloading_ = true; + unload_observer_.OnUnloadStarted(); + + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&FakeLacrosSelectionLoader::OnUnloadCompleted, + base::Unretained(this), std::move(callback))); + } + + bool IsUnloading() const override { return is_unloading_; } + + bool IsUnloaded() const override { return is_unloaded_; } + + void GetVersion( + base::OnceCallback<void(const base::Version&)> callback) override { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&FakeLacrosSelectionLoader::OnGetVersionCompleted, + base::Unretained(this), std::move(callback))); + } + + void SetCallbackOnUnload(base::OnceClosure cb) { + unload_observer_.SetCallback(std::move(cb)); + } + + private: + void OnLoadCompleted(LoadCompletionCallback callback) { + if (!callback) { + return; + } + + // If version is invalid, returns empty path. Otherwise fill in with some + // path. Whether path is empty or not is used as a condition to check + // whether error has occurred during loading. + const base::FilePath path = + version_.IsValid() ? temp_dir_.GetPath() : base::FilePath(); + std::move(callback).Run(version_, path); + } + + void OnUnloadCompleted(base::OnceClosure callback) { + is_unloading_ = false; + is_unloaded_ = true; + if (callback) { + std::move(callback).Run(); + } + } + + void OnGetVersionCompleted( + base::OnceCallback<void(const base::Version&)> callback) { + std::move(callback).Run(version_); + } + + const base::Version version_; + base::ScopedTempDir temp_dir_; + base::FilePath chrome_path_; + // `task_runner_` to run Load/Unload/GetVersion task as asynchronous + // operations. + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + UnloadObserver unload_observer_; + + bool is_unloaded_ = false; + bool is_unloading_ = false; +}; + +class FakeLacrosSelectionLoaderFactory : public LacrosSelectionLoaderFactory { + public: + FakeLacrosSelectionLoaderFactory( + const base::Version& rootfs_version, + const base::Version& stateful_version, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : rootfs_version_(rootfs_version), + stateful_version_(stateful_version), + task_runner_(task_runner) {} + + FakeLacrosSelectionLoaderFactory(const FakeLacrosSelectionLoaderFactory&) = + delete; + FakeLacrosSelectionLoaderFactory& operator=( + const FakeLacrosSelectionLoaderFactory&) = delete; + + ~FakeLacrosSelectionLoaderFactory() override = default; + + std::unique_ptr<LacrosSelectionLoader> CreateRootfsLacrosLoader() override { + return std::make_unique<FakeLacrosSelectionLoader>(rootfs_version_, + task_runner_); + } + + std::unique_ptr<LacrosSelectionLoader> CreateStatefulLacrosLoader() override { + return std::make_unique<FakeLacrosSelectionLoader>(stateful_version_, + task_runner_); + } + + private: + // These versions will be set on initializing lacros selection loaders. + const base::Version rootfs_version_ = base::Version(); + const base::Version stateful_version_ = base::Version(); + + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; +}; + // This implementation of RAII for LacrosSelection is to make it easy reset // the state between runs. class ScopedLacrosSelectionCache { @@ -57,150 +213,108 @@ } // namespace -// This fake class is used to test BrowserLoader who is responsible for deciding -// which lacros selection to use. -// This class does not load nor get actual version. Such features are tested in -// RootfsLacrosLoaderTest for rootfs and StatefulLacrosLoaderTest for stateful. -class FakeLacrosSelectionLoader : public LacrosSelectionLoader { - public: - FakeLacrosSelectionLoader() { - // Create dummy chrome binary path. - CHECK(temp_dir_.CreateUniqueTempDir()); - chrome_path_ = temp_dir_.GetPath().Append(kLacrosChromeBinary); - base::WriteFile(chrome_path_, "I am chrome binary."); - } - - FakeLacrosSelectionLoader(const FakeLacrosSelectionLoader&) = delete; - FakeLacrosSelectionLoader& operator=(const FakeLacrosSelectionLoader&) = - delete; - - ~FakeLacrosSelectionLoader() override = default; - - void Load(LoadCompletionCallback callback, bool forced) override { - if (!callback) { - return; - } - - // If version is invalid, returns empty path. Otherwise fill in with some - // path. Whether path is empty or not is used as a condition to check - // whether error has occurred during loading. - const base::FilePath path = - version_.IsValid() ? temp_dir_.GetPath() : base::FilePath(); - std::move(callback).Run(version_, path); - } - void Unload() override {} - void Reset() override {} - void GetVersion( - base::OnceCallback<void(const base::Version&)> callback) override { - std::move(callback).Run(version_); - } - - void SetVersionForTesting(const base::Version& version) override { - version_ = version; - } - - private: - base::Version version_ = base::Version(); - base::ScopedTempDir temp_dir_; - base::FilePath chrome_path_; -}; - class BrowserLoaderTest : public testing::Test { public: BrowserLoaderTest() { - browser_loader_ = std::make_unique<BrowserLoader>( - /*rootfs_lacros_loader=*/std::make_unique<FakeLacrosSelectionLoader>(), - /*stateful_lacros_loader=*/std::make_unique< - FakeLacrosSelectionLoader>()); EXPECT_TRUE(BrowserLoader::WillLoadStatefulComponentBuilds()); } - // Public because this is test code. - content::BrowserTaskEnvironment task_environment_; - protected: - std::unique_ptr<BrowserLoader> browser_loader_; + BrowserLoader CreateBrowserLoaderWithFakeSelectionLoaders( + const base::Version& rootfs_lacros_version, + const base::Version& stateful_lacros_version) { + return BrowserLoader(std::make_unique<FakeLacrosSelectionLoaderFactory>( + rootfs_lacros_version, stateful_lacros_version, + task_environment_.GetMainThreadTaskRunner())); + } + + void WaitForTaskComplete() { task_environment_.RunUntilIdle(); } + + private: + content::BrowserTaskEnvironment task_environment_; }; TEST_F(BrowserLoaderTest, OnLoadVersionSelectionNeitherIsAvailable) { // If both stateful and rootfs lacros-chrome version is invalid, the chrome // path should be empty. + const base::Version rootfs_lacros_version = base::Version(); + const base::Version stateful_lacros_version = base::Version(); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); + base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); EXPECT_TRUE(future.Get<0>().empty()); } TEST_F(BrowserLoaderTest, OnLoadVersionSelectionStatefulIsUnavailable) { - const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); // Pass invalid `base::Version` to stateful lacros-chrome and set valid // version to rootfs lacros-chrome. + const base::Version rootfs_lacros_version = base::Version("2.0.0"); + const base::Version stateful_lacros_version = base::Version(); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); + base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); EXPECT_EQ(LacrosSelection::kRootfs, future.Get<1>()); EXPECT_EQ(rootfs_lacros_version, future.Get<2>()); } TEST_F(BrowserLoaderTest, OnLoadVersionSelectionRootfsIsUnavailable) { - const base::Version stateful_lacros_version = base::Version("1.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); - // Pass invalid `base::Version` as a rootfs lacros-chrome version. + const base::Version rootfs_lacros_version = base::Version(); + const base::Version stateful_lacros_version = base::Version("1.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); + base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); EXPECT_EQ(LacrosSelection::kStateful, future.Get<1>()); EXPECT_EQ(stateful_lacros_version, future.Get<2>()); } TEST_F(BrowserLoaderTest, OnLoadVersionSelectionRootfsIsNewer) { // Use rootfs when a stateful lacros-chrome version is older. - const base::Version stateful_lacros_version = base::Version("1.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("1.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); EXPECT_EQ(LacrosSelection::kRootfs, future.Get<1>()); EXPECT_EQ(rootfs_lacros_version, future.Get<2>()); } TEST_F(BrowserLoaderTest, OnLoadVersionSelectionRootfsIsOlder) { // Use stateful when a rootfs lacros-chrome version is older. - const base::Version stateful_lacros_version = base::Version("3.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("3.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); EXPECT_EQ(LacrosSelection::kStateful, future.Get<1>()); EXPECT_EQ(stateful_lacros_version, future.Get<2>()); } TEST_F(BrowserLoaderTest, OnLoadVersionSelectionSameVersions) { // Use stateful when rootfs and stateful lacros-chrome versions are the same. - const base::Version stateful_lacros_version = base::Version("2.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("2.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); EXPECT_EQ(LacrosSelection::kStateful, future.Get<1>()); EXPECT_EQ(stateful_lacros_version, future.Get<2>()); } @@ -215,20 +329,22 @@ // Set stateful lacros version newer than rootfs to test that the selection // policy is prioritized higher. - const base::Version stateful_lacros_version = base::Version("3.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("3.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); const LacrosSelection selection = future.Get<1>(); EXPECT_EQ(selection, LacrosSelection::kRootfs); EXPECT_FALSE(BrowserLoader::WillLoadStatefulComponentBuilds()); + + // Check stateful lacros loader is not initialized since the selection is + // forced by policy. + EXPECT_FALSE(browser_loader.stateful_lacros_loader_); } TEST_F(BrowserLoaderTest, @@ -242,20 +358,22 @@ // Set stateful lacros version newer than rootfs to test that the user choice // is prioritized higher. - const base::Version stateful_lacros_version = base::Version("3.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("3.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); const LacrosSelection selection = future.Get<1>(); EXPECT_EQ(selection, LacrosSelection::kRootfs); EXPECT_FALSE(BrowserLoader::WillLoadStatefulComponentBuilds()); + + // Check stateful lacros loader is not initialized since the selection is + // forced by policy. + EXPECT_FALSE(browser_loader.stateful_lacros_loader_); } TEST_F(BrowserLoaderTest, @@ -267,22 +385,24 @@ browser_util::kLacrosSelectionSwitch, browser_util::kLacrosSelectionStateful); - // Set rootfs lacros version newer than stateful to test that the user choice + // Set rootfs lacros version newer than rootfs to test that the user choice // is prioritized higher. - const base::Version stateful_lacros_version = base::Version("1.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("1.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback()); + browser_loader.Load(future.GetCallback()); const LacrosSelection selection = future.Get<1>(); EXPECT_EQ(selection, LacrosSelection::kStateful); EXPECT_TRUE(BrowserLoader::WillLoadStatefulComponentBuilds()); + + // Check rootfs lacros loader is not initialized since the selection is forced + // by policy. + EXPECT_FALSE(browser_loader.rootfs_lacros_loader_); } TEST_F(BrowserLoaderTest, OnLoadLacrosBinarySpecifiedBySwitch) { @@ -301,21 +421,24 @@ // Set stateful/rootfs lacros-chrome version to check that specified // lacros-chrome is prioritized higher. - const base::Version stateful_lacros_version = base::Version("3.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("3.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<base::FilePath, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback<const base::FilePath&, - LacrosSelection, base::Version>()); + browser_loader.Load(future.GetCallback<const base::FilePath&, LacrosSelection, + base::Version>()); const base::FilePath path = future.Get<0>(); const LacrosSelection selection = future.Get<1>(); EXPECT_EQ(path, lacros_chrome_path); EXPECT_EQ(selection, LacrosSelection::kDeployedLocally); + + // Check both rootfs and stateful lacros loader are not initialized since the + // selection is forced by switch. + EXPECT_FALSE(browser_loader.rootfs_lacros_loader_); + EXPECT_FALSE(browser_loader.stateful_lacros_loader_); } TEST_F(BrowserLoaderTest, OnLoadLacrosDirectorySpecifiedBySwitch) { @@ -330,21 +453,64 @@ // Set stateful/rootfs lacros-chrome version to check that specified // lacros-chrome is prioritized higher. - const base::Version stateful_lacros_version = base::Version("3.0.0"); - browser_loader_->stateful_lacros_loader_->SetVersionForTesting( - stateful_lacros_version); const base::Version rootfs_lacros_version = base::Version("2.0.0"); - browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( - rootfs_lacros_version); + const base::Version stateful_lacros_version = base::Version("3.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); base::test::TestFuture<base::FilePath, LacrosSelection, base::Version> future; - browser_loader_->Load(future.GetCallback<const base::FilePath&, - LacrosSelection, base::Version>()); + browser_loader.Load(future.GetCallback<const base::FilePath&, LacrosSelection, + base::Version>()); const base::FilePath path = future.Get<0>(); const LacrosSelection selection = future.Get<1>(); EXPECT_EQ(path, lacros_chrome_dir.Append("chrome")); EXPECT_EQ(selection, LacrosSelection::kDeployedLocally); + + // Check both rootfs and stateful lacros loader are not initialized since the + // selection is forced by switch. + EXPECT_FALSE(browser_loader.rootfs_lacros_loader_); + EXPECT_FALSE(browser_loader.stateful_lacros_loader_); +} + +TEST_F(BrowserLoaderTest, LoadWhileUnloading) { + // If stateful is newer, rootfs lacros will be unloaded. + const base::Version rootfs_lacros_version = base::Version("2.0.0"); + const base::Version stateful_lacros_version = base::Version("3.0.0"); + auto browser_loader = CreateBrowserLoaderWithFakeSelectionLoaders( + rootfs_lacros_version, stateful_lacros_version); + + // Load once. This will start asynchronous unload for rootfs lacros loader. + base::test::TestFuture<base::FilePath, LacrosSelection, base::Version> future; + browser_loader.Load(future.GetCallback<const base::FilePath&, LacrosSelection, + base::Version>()); + + // Wait until rootfs lacros loader starts Unload. GetVersion runs + // asynchronously before it start unloading. + base::RunLoop run_loop1; + FakeLacrosSelectionLoader* rootfs_lacros_loader = + static_cast<FakeLacrosSelectionLoader*>( + browser_loader.rootfs_lacros_loader_.get()); + rootfs_lacros_loader->SetCallbackOnUnload(run_loop1.QuitClosure()); + run_loop1.Run(); + + // On requesting Load while Unloading, the load request should be stored to + // `callback_on_unload_completion_` and wait for unload to complete to resume + // load request. + base::RunLoop run_loop2; + std::optional<LacrosSelection> selection_result; + browser_loader.Load(base::BindOnce( + [](base::OnceClosure quit_closure, + std::optional<LacrosSelection>* selection_result, + const base::FilePath&, LacrosSelection selection, base::Version) { + *selection_result = selection; + std::move(quit_closure).Run(); + }, + run_loop2.QuitClosure(), &selection_result)); + EXPECT_FALSE(selection_result.has_value()); + + run_loop2.Run(); + EXPECT_EQ(LacrosSelection::kStateful, selection_result); } } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/lacros_selection_loader.h b/chrome/browser/ash/crosapi/lacros_selection_loader.h index 0a29028..d6b0b5b 100644 --- a/chrome/browser/ash/crosapi/lacros_selection_loader.h +++ b/chrome/browser/ash/crosapi/lacros_selection_loader.h
@@ -28,26 +28,25 @@ using LoadCompletionCallback = base::OnceCallback<void(base::Version, const base::FilePath&)>; - // Loads chrome binary. + // Loads chrome binary. This must be called only when LacrosSelectionLoader is + // idle and has not yet unloaded. // `forced` specifies whether the lacros selection is forced. virtual void Load(LoadCompletionCallback callback, bool forced) = 0; - // Unloads chrome binary. - virtual void Unload() = 0; + // Unloads chrome binary. Unload can be called anytime, and this request will + // stop other tasks. Once Unload is called, LacrosSelectionLoader is no longer + // valid. + // `callback` will be called on unload completion. + virtual void Unload(base::OnceClosure callback) = 0; - // Resets the state. This is called before reloading lacros. - // TODO(elkurin): Instead of resetting the state, throw away and recreate - // loader instance. - virtual void Reset() = 0; + // Returns the current unloading/unloaded state. + virtual bool IsUnloading() const = 0; + virtual bool IsUnloaded() const = 0; // Calculates version and send it back via `callback`. // It may take time since it requires to load/mount lacros binary. virtual void GetVersion( base::OnceCallback<void(const base::Version&)> callback) = 0; - - // Sets version. - // Only implemented for testing class (FakeLacrosSelectionLoader). - virtual void SetVersionForTesting(const base::Version& version) {} }; } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/lacros_selection_loader_factory.h b/chrome/browser/ash/crosapi/lacros_selection_loader_factory.h new file mode 100644 index 0000000..06010259 --- /dev/null +++ b/chrome/browser/ash/crosapi/lacros_selection_loader_factory.h
@@ -0,0 +1,26 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_CROSAPI_LACROS_SELECTION_LOADER_FACTORY_H_ +#define CHROME_BROWSER_ASH_CROSAPI_LACROS_SELECTION_LOADER_FACTORY_H_ + +#include <memory> + +namespace crosapi { +class LacrosSelectionLoader; + +// Abstract interface to create LacrosSelectionLoader. +class LacrosSelectionLoaderFactory { + public: + virtual ~LacrosSelectionLoaderFactory() = default; + + // Interface to create LacrosSelectionLoader for rootfs/stateful. + virtual std::unique_ptr<LacrosSelectionLoader> CreateRootfsLacrosLoader() = 0; + virtual std::unique_ptr<LacrosSelectionLoader> + CreateStatefulLacrosLoader() = 0; +}; + +} // namespace crosapi + +#endif // CHROME_BROWSER_ASH_CROSAPI_LACROS_SELECTION_LOADER_FACTORY_H_
diff --git a/chrome/browser/ash/crosapi/rootfs_lacros_loader.cc b/chrome/browser/ash/crosapi/rootfs_lacros_loader.cc index 18cae9c0..4d09962 100644 --- a/chrome/browser/ash/crosapi/rootfs_lacros_loader.cc +++ b/chrome/browser/ash/crosapi/rootfs_lacros_loader.cc
@@ -51,35 +51,70 @@ RootfsLacrosLoader::~RootfsLacrosLoader() = default; void RootfsLacrosLoader::Load(LoadCompletionCallback callback, bool forced) { + CHECK(state_ == State::kNotLoaded || + state_ == State::kVersionReadyButNotLoaded) + << state_; LOG(WARNING) << "Loading rootfs lacros."; - // Make sure to calculate `version_` before start loading. - // It may not be calculated yet in case when lacros selection is defined by + if (state_ == State::kVersionReadyButNotLoaded) { + OnVersionReadyToLoad(std::move(callback), version_.value()); + return; + } + + // Calculate `version_` before start loading. + // It's not calculated yet in case when lacros selection is defined by // selection policy or stateful lacros is not installed. - GetVersion(base::BindOnce(&RootfsLacrosLoader::OnVersionReadyToLoad, - weak_factory_.GetWeakPtr(), std::move(callback))); + state_ = State::kReadingVersion; + GetVersionInternal(base::BindOnce(&RootfsLacrosLoader::OnVersionReadyToLoad, + weak_factory_.GetWeakPtr(), + std::move(callback))); } -void RootfsLacrosLoader::Unload() { - upstart_client_->StartJob(kLacrosUnmounterUpstartJob, {}, - base::BindOnce([](bool) {})); -} +void RootfsLacrosLoader::Unload(base::OnceClosure callback) { + switch (state_) { + case State::kNotLoaded: + case State::kVersionReadyButNotLoaded: + case State::kUnloaded: + // Nothing to unload if it's not loaded or already unloaded. + std::move(callback).Run(); + break; + case State::kReadingVersion: + case State::kLoading: + case State::kUnloading: + // If loader is busy, wait Unload until the current task has finished. + pending_unload_ = + base::BindOnce(&RootfsLacrosLoader::Unload, + weak_factory_.GetWeakPtr(), std::move(callback)); + break; + case State::kLoaded: + state_ = State::kUnloading; -void RootfsLacrosLoader::Reset() { - // TODO(crbug.com/1432069): Reset call while loading breaks the behavior. Need - // to handle such edge cases. - version_ = std::nullopt; + upstart_client_->StartJob( + kLacrosUnmounterUpstartJob, {}, + base::BindOnce(&RootfsLacrosLoader::OnUnloadCompleted, + weak_factory_.GetWeakPtr(), std::move(callback))); + } } void RootfsLacrosLoader::GetVersion( base::OnceCallback<void(const base::Version&)> callback) { - // If version is already calculated, immediately return the cached value. - // Calculate if not. - // Note that version value is reset on reloading. - if (version_.has_value()) { - std::move(callback).Run(version_.value()); - return; - } + CHECK_EQ(state_, State::kNotLoaded) << state_; + state_ = State::kReadingVersion; + GetVersionInternal(std::move(callback)); +} + +bool RootfsLacrosLoader::IsUnloading() const { + return state_ == State::kUnloading; +} + +bool RootfsLacrosLoader::IsUnloaded() const { + return state_ == State::kUnloaded; +} + +void RootfsLacrosLoader::GetVersionInternal( + base::OnceCallback<void(const base::Version&)> callback) { + CHECK_EQ(state_, State::kReadingVersion) << state_; + CHECK(!version_); base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock()}, @@ -93,19 +128,39 @@ base::OnceCallback<void(const base::Version&)> callback, base::Version version) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kReadingVersion) << state_; version_ = version; + state_ = State::kVersionReadyButNotLoaded; + + if (pending_unload_) { + LOG(WARNING) << "Unload is requested during getting version of rootfs."; + std::move(callback).Run(base::Version()); + std::move(pending_unload_).Run(); + return; + } + std::move(callback).Run(version_.value()); } void RootfsLacrosLoader::OnVersionReadyToLoad(LoadCompletionCallback callback, const base::Version& version) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kVersionReadyButNotLoaded) << state_; + + if (pending_unload_) { + LOG(WARNING) << "Unload is requested during loading rootfs."; + std::move(callback).Run(base::Version(), base::FilePath()); + std::move(pending_unload_).Run(); + return; + } // `version_` must be already filled by `version`. - DCHECK(version_.has_value() && - ((!version_.value().IsValid() && !version.IsValid()) || - (version_.value() == version))); + CHECK(version_.has_value() && + ((!version_.value().IsValid() && !version.IsValid()) || + (version_.value() == version))); + + state_ = State::kLoading; base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock()}, @@ -119,6 +174,15 @@ void RootfsLacrosLoader::OnMountCheckToLoad(LoadCompletionCallback callback, bool already_mounted) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kLoading) << state_; + + if (pending_unload_) { + LOG(WARNING) << "Unload is requested during loading rootfs."; + state_ = State::kVersionReadyButNotLoaded; + std::move(callback).Run(base::Version(), base::FilePath()); + std::move(pending_unload_).Run(); + return; + } if (already_mounted) { OnUpstartLacrosMounter(std::move(callback), true); @@ -139,17 +203,20 @@ void RootfsLacrosLoader::OnUpstartLacrosMounter(LoadCompletionCallback callback, bool success) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kLoading) << state_; + state_ = State::kLoaded; LOG_IF(WARNING, !success) << "Upstart failed to mount rootfs lacros."; - // `version_` must be calculated before coming here. - // If `version_` is not filled, it implies Reset() is called, so handling this - // case as an error. - if (!version_.has_value()) { + if (pending_unload_) { + LOG(WARNING) << "Unload is requested during loading rootfs."; std::move(callback).Run(base::Version(), base::FilePath()); + std::move(pending_unload_).Run(); return; } + // `version_` must be calculated before coming here. + CHECK(version_.has_value()); std::move(callback).Run( version_.value(), // If mounting wasn't successful, return a empty mount point to indicate @@ -158,4 +225,36 @@ success ? base::FilePath(kRootfsLacrosMountPoint) : base::FilePath()); } +void RootfsLacrosLoader::OnUnloadCompleted(base::OnceClosure callback, + bool success) { + // Proceed anyway regardless of unload success. + if (!success) { + LOG(ERROR) << "Failed to unload rootfs lacros"; + } + + CHECK_EQ(state_, State::kUnloading) << state_; + state_ = State::kUnloaded; + std::move(callback).Run(); +} + +std::ostream& operator<<(std::ostream& ostream, + RootfsLacrosLoader::State state) { + switch (state) { + case RootfsLacrosLoader::State::kNotLoaded: + return ostream << "NotLoaded"; + case RootfsLacrosLoader::State::kReadingVersion: + return ostream << "ReadingVersion"; + case RootfsLacrosLoader::State::kVersionReadyButNotLoaded: + return ostream << "VersionReadyButNotLoaded"; + case RootfsLacrosLoader::State::kLoading: + return ostream << "Loading"; + case RootfsLacrosLoader::State::kLoaded: + return ostream << "Loaded"; + case RootfsLacrosLoader::State::kUnloading: + return ostream << "Unloading"; + case RootfsLacrosLoader::State::kUnloaded: + return ostream << "Unloaded"; + } +} + } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/rootfs_lacros_loader.h b/chrome/browser/ash/crosapi/rootfs_lacros_loader.h index ee028dc..b492af5 100644 --- a/chrome/browser/ash/crosapi/rootfs_lacros_loader.h +++ b/chrome/browser/ash/crosapi/rootfs_lacros_loader.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_ASH_CROSAPI_ROOTFS_LACROS_LOADER_H_ #include <optional> +#include <ostream> #include "base/files/file_path.h" #include "base/functional/callback.h" @@ -32,14 +33,45 @@ RootfsLacrosLoader& operator=(const RootfsLacrosLoader&) = delete; ~RootfsLacrosLoader() override; + // The state representing the loading state. + enum class State { + // Loader is not running any task. + kNotLoaded, + + // Loader is in the process of reading the version from manifest file. + kReadingVersion, + + // Loader gets version of lacros-chrome but not loaded it yet. + kVersionReadyButNotLoaded, + + // Loader is in the process of loading lacros-chrome. + kLoading, + + // Loader has loaded lacros-chrome and `version_` and `path_` is ready. + kLoaded, + + // Loader is in the process of unloading lacros-chrome. + kUnloading, + + // Loader has unloaded the lacros-chrome. State must NOT change once it + // becomes kUnloaded. + kUnloaded, + }; + + State GetState() const { return state_; } + // LacrosSelectionLoader: void Load(LoadCompletionCallback callback, bool forced) override; - void Unload() override; - void Reset() override; + void Unload(base::OnceClosure callback) override; void GetVersion( base::OnceCallback<void(const base::Version&)> callback) override; + bool IsUnloading() const override; + bool IsUnloaded() const override; private: + void GetVersionInternal( + base::OnceCallback<void(const base::Version&)> callback); + // Called after GetVersion. void OnGetVersion(base::OnceCallback<void(const base::Version&)> callback, base::Version version); @@ -56,6 +88,9 @@ // Callback from upstart mounting lacros-chrome. void OnUpstartLacrosMounter(LoadCompletionCallback callback, bool success); + // Called on unload completed. + void OnUnloadCompleted(base::OnceClosure callback, bool success); + // The bundled rootfs lacros-chrome binary version. This is set after the // first async call that checks the installed rootfs lacros version number. // If `version_` is null, it implies the version is not yet calculated. @@ -72,12 +107,18 @@ // testing. base::FilePath metadata_path_; + base::OnceClosure pending_unload_; + + State state_ = State::kNotLoaded; + // Used for DCHECKs to ensure method calls executed in the correct thread. SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<RootfsLacrosLoader> weak_factory_{this}; }; +std::ostream& operator<<(std::ostream&, RootfsLacrosLoader::State); + } // namespace crosapi #endif // CHROME_BROWSER_ASH_CROSAPI_ROOTFS_LACROS_LOADER_H_
diff --git a/chrome/browser/ash/crosapi/rootfs_lacros_loader_unittest.cc b/chrome/browser/ash/crosapi/rootfs_lacros_loader_unittest.cc index a163e5a..8fcdbde 100644 --- a/chrome/browser/ash/crosapi/rootfs_lacros_loader_unittest.cc +++ b/chrome/browser/ash/crosapi/rootfs_lacros_loader_unittest.cc
@@ -49,7 +49,7 @@ std::unique_ptr<RootfsLacrosLoader> rootfs_lacros_loader_; }; -TEST_F(RootfsLacrosLoaderTest, LoadRootfsLacros) { +TEST_F(RootfsLacrosLoaderTest, LoadRootfsLacrosSelectedByCompatibilityCheck) { bool callback_called = false; fake_upstart_client_.set_start_job_cb(base::BindRepeating( [](bool* callback_called, const std::string& job, @@ -60,12 +60,57 @@ }, &callback_called)); + EXPECT_EQ(RootfsLacrosLoader::State::kNotLoaded, + rootfs_lacros_loader_->GetState()); + + // If rootfs is selected by compatibility check, it first calls GetVersion to + // read the version, and then Load is requested. Inside GetVersion, Load won't + // complete. + base::test::TestFuture<const base::Version&> future1; + rootfs_lacros_loader_->GetVersion( + future1.GetCallback<const base::Version&>()); + EXPECT_EQ(base::Version(version_str), future1.Get<0>()); + EXPECT_EQ(RootfsLacrosLoader::State::kVersionReadyButNotLoaded, + rootfs_lacros_loader_->GetState()); + EXPECT_FALSE(callback_called); + + // Load is called after version is calculated. + base::test::TestFuture<base::Version, const base::FilePath&> future2; + rootfs_lacros_loader_->Load( + future2.GetCallback<base::Version, const base::FilePath&>(), + /*forced=*/false); + EXPECT_EQ(base::Version(version_str), future2.Get<0>()); + EXPECT_TRUE(callback_called); + + EXPECT_EQ(RootfsLacrosLoader::State::kLoaded, + rootfs_lacros_loader_->GetState()); +} + +TEST_F(RootfsLacrosLoaderTest, LoadRootfsLacrosSelectedByPolicy) { + bool callback_called = false; + fake_upstart_client_.set_start_job_cb(base::BindRepeating( + [](bool* callback_called, const std::string& job, + const std::vector<std::string>& upstart_env) { + EXPECT_EQ(job, kLacrosMounterUpstartJob); + *callback_called = true; + return ash::FakeUpstartClient::StartJobResult(true /* success */); + }, + &callback_called)); + + EXPECT_EQ(RootfsLacrosLoader::State::kNotLoaded, + rootfs_lacros_loader_->GetState()); + + // If rootfs is selected by policy, it does not call GetVersion. Instead, it + // calls Load directly and compute read the version inside Load together. base::test::TestFuture<base::Version, const base::FilePath&> future; rootfs_lacros_loader_->Load( future.GetCallback<base::Version, const base::FilePath&>(), /*forced=*/false); EXPECT_EQ(base::Version(version_str), future.Get<0>()); EXPECT_TRUE(callback_called); + + EXPECT_EQ(RootfsLacrosLoader::State::kLoaded, + rootfs_lacros_loader_->GetState()); } } // namespace
diff --git a/chrome/browser/ash/crosapi/stateful_lacros_loader.cc b/chrome/browser/ash/crosapi/stateful_lacros_loader.cc index bda31947..cb497b85 100644 --- a/chrome/browser/ash/crosapi/stateful_lacros_loader.cc +++ b/chrome/browser/ash/crosapi/stateful_lacros_loader.cc
@@ -146,9 +146,11 @@ DCHECK(component_manager_); } +// TODO(elkurin): Maybe we should call Unload or pending_unload at least? StatefulLacrosLoader::~StatefulLacrosLoader() = default; void StatefulLacrosLoader::Load(LoadCompletionCallback callback, bool forced) { + CHECK(state_ == State::kNotLoaded || state_ == State::kLoaded) << state_; LOG(WARNING) << "Loading stateful lacros."; // If stateful lacros-chrome is already loaded once, run `callback` @@ -156,39 +158,49 @@ // This code path is used in most cases as they are already calculated on // getting version except for the case where BrowserLoader is forced to select // stateful lacros-chrome by lacros selection policy. - if (IsReady()) { + if (state_ == State::kLoaded) { std::move(callback).Run(version_.value(), path_.value()); return; } + state_ = State::kLoading; LoadInternal(std::move(callback), forced); } -void StatefulLacrosLoader::Unload() { - base::ThreadPool::PostTaskAndReplyWithResult( - FROM_HERE, {base::MayBlock()}, - base::BindOnce(&CheckInstalledAndMaybeRemoveUserDirectory, - component_manager_, lacros_component_name_), - base::BindOnce(&StatefulLacrosLoader::OnCheckInstalledToUnload, - weak_factory_.GetWeakPtr())); -} +void StatefulLacrosLoader::Unload(base::OnceClosure callback) { + switch (state_) { + case State::kNotLoaded: + case State::kUnloaded: + // Nothing to unload if it's not loaded or already unloaded. + std::move(callback).Run(); + state_ = State::kUnloaded; + break; + case State::kLoading: + case State::kUnloading: + // If loader is busy, wait Unload until the current task has finished. + pending_unload_ = + base::BindOnce(&StatefulLacrosLoader::Unload, + weak_factory_.GetWeakPtr(), std::move(callback)); + break; + case State::kLoaded: + // Start unloading if lacros-chrome is loaded. + state_ = State::kUnloading; -void StatefulLacrosLoader::Reset() { - // TODO(crbug.com/1432069): Reset call while loading breaks the behavior. Need - // to handle such edge cases. - version_ = std::nullopt; - path_ = std::nullopt; + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&CheckInstalledAndMaybeRemoveUserDirectory, + component_manager_, lacros_component_name_), + base::BindOnce(&StatefulLacrosLoader::OnCheckInstalledToUnload, + weak_factory_.GetWeakPtr(), std::move(callback))); + break; + } } void StatefulLacrosLoader::GetVersion( base::OnceCallback<void(const base::Version&)> callback) { - // If version is already calculated, immediately return the cached value. - // Calculate if not. - // Note that version value is reset on reloading. - if (IsReady()) { - std::move(callback).Run(version_.value()); - return; - } + CHECK_EQ(state_, State::kNotLoaded) << state_; + + state_ = State::kLoading; // TODO(crbug.com/1455070): There's KI that the current implementation // occasionally wrongly identifies there exists. Fix the logic. @@ -202,8 +214,18 @@ weak_factory_.GetWeakPtr(), std::move(callback))); } +bool StatefulLacrosLoader::IsUnloading() const { + return state_ == State::kUnloading; +} + +bool StatefulLacrosLoader::IsUnloaded() const { + return state_ == State::kUnloaded; +} + void StatefulLacrosLoader::LoadInternal(LoadCompletionCallback callback, bool forced) { + CHECK_EQ(state_, State::kLoading) << state_; + // If a compatible installation exists, use that and download any updates in // the background. If not, report just there is no available stateful lacros // unless stateful lacros is forced by policy or about:flag entry. @@ -223,15 +245,22 @@ std::move(callback))); } -bool StatefulLacrosLoader::IsReady() { - return version_.has_value() && path_.has_value(); -} - void StatefulLacrosLoader::OnLoad( LoadCompletionCallback callback, component_updater::CrOSComponentManager::Error error, const base::FilePath& path) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kLoading) << state_; + state_ = State::kLoaded; + + if (pending_unload_) { + LOG(WARNING) << "Unload is requested during loading stateful."; + if (callback) { + std::move(callback).Run(base::Version(), base::FilePath()); + } + std::move(pending_unload_).Run(); + return; + } bool is_stateful_lacros_available = error == component_updater::CrOSComponentManager::Error::NONE && @@ -262,12 +291,21 @@ base::OnceCallback<void(const base::Version&)> callback, bool is_installed) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kLoading) << state_; + + if (pending_unload_) { + LOG(WARNING) << "Unload is requested during getting version of stateful."; + state_ = State::kNotLoaded; + if (callback) { + std::move(callback).Run(base::Version()); + } + std::move(pending_unload_).Run(); + return; + } if (!is_installed) { // Run `callback` immediately with empty version and start loading stateful // lacros-chrome in the background. - // TODO(crbug.com/1432069): Reconsider `version_` and `path_` values - // semantics. LoadInternal({}, /*forced=*/false); std::move(callback).Run(base::Version()); return; @@ -282,10 +320,14 @@ false); } -void StatefulLacrosLoader::OnCheckInstalledToUnload(bool was_installed) { +void StatefulLacrosLoader::OnCheckInstalledToUnload(base::OnceClosure callback, + bool was_installed) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kUnloading) << state_; if (!was_installed) { + state_ = State::kUnloaded; + std::move(callback).Run(); return; } @@ -294,15 +336,37 @@ // assumes that system salt is available. This isn't always true when chrome // restarts to apply non-owner flags. It's hard to make MetadataTable async. // Ensure salt is available before unloading. https://crbug.com/1122674 - ash::SystemSaltGetter::Get()->GetSystemSalt(base::BindOnce( - &StatefulLacrosLoader::UnloadAfterCleanUp, weak_factory_.GetWeakPtr())); + ash::SystemSaltGetter::Get()->GetSystemSalt( + base::BindOnce(&StatefulLacrosLoader::UnloadAfterCleanUp, + weak_factory_.GetWeakPtr(), std::move(callback))); } -void StatefulLacrosLoader::UnloadAfterCleanUp(const std::string& ignored_salt) { +void StatefulLacrosLoader::UnloadAfterCleanUp(base::OnceClosure callback, + const std::string& ignored_salt) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK_EQ(state_, State::kUnloading) << state_; CHECK(ash::SystemSaltGetter::Get()->GetRawSalt()); component_manager_->Unload(lacros_component_name_); + + state_ = State::kUnloaded; + std::move(callback).Run(); +} + +std::ostream& operator<<(std::ostream& ostream, + StatefulLacrosLoader::State state) { + switch (state) { + case StatefulLacrosLoader::State::kNotLoaded: + return ostream << "NotLoaded"; + case StatefulLacrosLoader::State::kLoading: + return ostream << "Loading"; + case StatefulLacrosLoader::State::kLoaded: + return ostream << "Loaded"; + case StatefulLacrosLoader::State::kUnloading: + return ostream << "Unloading"; + case StatefulLacrosLoader::State::kUnloaded: + return ostream << "Unloaded"; + } } } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/stateful_lacros_loader.h b/chrome/browser/ash/crosapi/stateful_lacros_loader.h index d23316f..d9737ae 100644 --- a/chrome/browser/ash/crosapi/stateful_lacros_loader.h +++ b/chrome/browser/ash/crosapi/stateful_lacros_loader.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_ASH_CROSAPI_STATEFUL_LACROS_LOADER_H_ #include <optional> +#include <ostream> #include <string> #include "base/files/file_path.h" @@ -38,20 +39,38 @@ StatefulLacrosLoader& operator=(const StatefulLacrosLoader&) = delete; ~StatefulLacrosLoader() override; + // The state representing the loading state. + enum class State { + // Loader is not running any task. + kNotLoaded, + + // Loader is in the process of loading lacros-chrome. + kLoading, + + // Loader has loaded lacros-chrome and `version_` and `path_` is ready. + kLoaded, + + // Loader is in the process of unloading lacros-chrome. + kUnloading, + + // Loader has unloaded the lacros-chrome. State must NOT change once it + // becomes kUnloaded. + kUnloaded, + }; + + State GetState() const { return state_; } + // LacrosSelectionLoader: void Load(LoadCompletionCallback callback, bool forced) override; - void Unload() override; - void Reset() override; + void Unload(base::OnceClosure callback) override; void GetVersion( base::OnceCallback<void(const base::Version&)> callback) override; + bool IsUnloading() const override; + bool IsUnloaded() const override; private: void LoadInternal(LoadCompletionCallback callback, bool forced); - // Returns true if the stateful lacros-chrome is already loaded and both - // `version_` and `path_` are ready. - bool IsReady(); - // Called after Load. void OnLoad(LoadCompletionCallback callback, component_updater::CrOSComponentManager::Error error, @@ -70,10 +89,11 @@ // Called in Unload sequence. // Unloading hops threads. This is called after we check whether Lacros was // installed and maybe clean up the user directory. - void OnCheckInstalledToUnload(bool was_installed); + void OnCheckInstalledToUnload(base::OnceClosure callback, bool was_installed); // Unloads the component. Called after system salt is available. - void UnloadAfterCleanUp(const std::string& ignored_salt); + void UnloadAfterCleanUp(base::OnceClosure callback, + const std::string& ignored_salt); // If `version_` is null, it implies the version is not yet calculated. // For cases where it failed to read the version, invalid `base::Version()` is @@ -90,12 +110,18 @@ const std::string lacros_component_name_; + base::OnceClosure pending_unload_; + + State state_ = State::kNotLoaded; + // Used for DCHECKs to ensure method calls executed in the correct thread. SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<StatefulLacrosLoader> weak_factory_{this}; }; +std::ostream& operator<<(std::ostream&, StatefulLacrosLoader::State); + } // namespace crosapi #endif // CHROME_BROWSER_ASH_CROSAPI_STATEFUL_LACROS_LOADER_H_
diff --git a/chrome/browser/ash/crosapi/stateful_lacros_loader_unittest.cc b/chrome/browser/ash/crosapi/stateful_lacros_loader_unittest.cc index f523331..a870e25 100644 --- a/chrome/browser/ash/crosapi/stateful_lacros_loader_unittest.cc +++ b/chrome/browser/ash/crosapi/stateful_lacros_loader_unittest.cc
@@ -10,15 +10,20 @@ #include "base/auto_reset.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" +#include "base/test/scoped_path_override.h" #include "base/test/test_future.h" #include "base/version.h" #include "chrome/browser/ash/crosapi/browser_loader.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/component_updater/cros_component_installer_chromeos.h" #include "chrome/browser/component_updater/fake_cros_component_manager.h" #include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h" #include "chromeos/ash/components/standalone_browser/browser_support.h" +#include "components/component_updater/component_updater_paths.h" #include "components/component_updater/mock_component_updater_service.h" #include "components/update_client/update_client.h" #include "content/public/test/browser_task_environment.h" @@ -49,6 +54,15 @@ g_browser_process->platform_part()); browser_part_->InitializeCrosComponentManager(component_manager_); + // Set up component path. + base::FilePath root_dir; + CHECK(base::PathService::Get(component_updater::DIR_COMPONENT_USER, + &root_dir)); + base::FilePath component_root = + root_dir.Append(component_updater::kComponentsRootPath) + .Append(kLacrosComponentName); + base::CreateDirectory(component_root.Append("sub-dir")); + stateful_lacros_loader_ = std::make_unique<StatefulLacrosLoader>( component_manager_, &mock_component_update_service_, kLacrosComponentName); @@ -56,6 +70,8 @@ } ~StatefulLacrosLoaderTest() override { + stateful_lacros_loader_.reset(); + browser_part_->ShutdownCrosComponentManager(); } @@ -64,26 +80,42 @@ protected: content::BrowserTaskEnvironment task_environment_; + base::ScopedPathOverride scoped_component_user_dir_{ + component_updater::DIR_COMPONENT_USER}; component_updater::MockComponentUpdateService mock_component_update_service_; scoped_refptr<component_updater::FakeCrOSComponentManager> component_manager_; std::unique_ptr<BrowserProcessPlatformPartTestApi> browser_part_; std::unique_ptr<StatefulLacrosLoader> stateful_lacros_loader_; }; -TEST_F(StatefulLacrosLoaderTest, LoadStatefulLacros) { +TEST_F(StatefulLacrosLoaderTest, + LoadStatefulLacrosSelectedByCompatibilityCheck) { std::u16string lacros_component_name = base::UTF8ToUTF16(std::string_view(kLacrosComponentName)); EXPECT_CALL(mock_component_update_service_, GetComponents()) .WillOnce(Return(std::vector<component_updater::ComponentInfo>{ {kLacrosComponentId, "", lacros_component_name, version, ""}})); - // Set stateful lacros-chrome version. Wait until the version calculation is - // completed before verifying the version. - base::test::TestFuture<base::Version, const base::FilePath&> future; - stateful_lacros_loader_->Load( - future.GetCallback<base::Version, const base::FilePath&>(), - /*forced=*/false); + EXPECT_EQ(StatefulLacrosLoader::State::kNotLoaded, + stateful_lacros_loader_->GetState()); + + // If stateful is selected by compatibility check, it first calls GetVersion + // and complete loading, and then Load is called. + base::test::TestFuture<const base::Version&> future; + stateful_lacros_loader_->GetVersion( + future.GetCallback<const base::Version&>()); EXPECT_EQ(version, future.Get<0>()); + EXPECT_EQ(StatefulLacrosLoader::State::kLoaded, + stateful_lacros_loader_->GetState()); + + // Load is already completed in GetVersion, so this call is synchronous. + base::Version result_version; + stateful_lacros_loader_->Load( + base::BindOnce([](base::Version* result_version, base::Version version, + const base::FilePath&) { *result_version = version; }, + &result_version), + /*forced=*/false); + EXPECT_EQ(version, result_version); } TEST_F(StatefulLacrosLoaderTest, LoadStatefulLacrosSelectedByPolicy) { @@ -93,10 +125,12 @@ .WillOnce(Return(std::vector<component_updater::ComponentInfo>{ {kLacrosComponentId, "", lacros_component_name, version, ""}})); - // Set stateful lacros-chrome version. Wait until the version calculation is - // completed before verifying the version. + EXPECT_EQ(StatefulLacrosLoader::State::kNotLoaded, + stateful_lacros_loader_->GetState()); + + // If stateful is forced by policy, it does not call GetVersion. Instead, it + // calls Load directly and compute the version inside Load together. base::test::TestFuture<base::Version, const base::FilePath&> future; - // Load stateful lacros-chrome as enforced by a policy. stateful_lacros_loader_->Load( future.GetCallback<base::Version, const base::FilePath&>(), /*forced=*/true);
diff --git a/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc b/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc index ef5bad5..60d16ec 100644 --- a/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc +++ b/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc
@@ -88,13 +88,8 @@ // VmApplicationsServiceProvider::SelectFile(). Take back ownership. auto data = base::WrapUnique(static_cast<SelectFileData*>(params)); - ui::EndpointType target = ui::EndpointType::kDefault; - if (data->signal.vm_name() == crostini::kCrostiniDefaultVmName) { - target = ui::EndpointType::kCrostini; - } - ShareWithVMAndTranslateToFileUrls( - target, ui::SelectedFileInfoListToFilePathList(files), + data->signal.vm_name(), ui::SelectedFileInfoListToFilePathList(files), base::BindOnce( [](std::unique_ptr<SelectFileData> data, std::vector<std::string> file_urls) { @@ -287,14 +282,8 @@ base::FilePath())); } // Translate to path in host and DLP component type if possible. - ui::EndpointType source = ui::EndpointType::kUnknownVm; - if (request.vm_name() == crostini::kCrostiniDefaultVmName) { - source = ui::EndpointType::kCrostini; - owner.dialog_caller = - policy::DlpFileDestination(data_controls::Component::kCrostini); - } std::vector<base::FilePath> paths = - TranslateVMPathsToHost(source, file_infos); + TranslateVMPathsToHost(request.vm_name(), file_infos); default_path = !paths.empty() ? std::move(paths[0]) : std::move(file_infos[0].path); }
diff --git a/chrome/browser/ash/exo/chrome_security_delegate.cc b/chrome/browser/ash/exo/chrome_security_delegate.cc index 374b393..0627f08 100644 --- a/chrome/browser/ash/exo/chrome_security_delegate.cc +++ b/chrome/browser/ash/exo/chrome_security_delegate.cc
@@ -41,6 +41,7 @@ constexpr char kUriListSeparator[] = "\r\n"; constexpr char kVmFileScheme[] = "vmfile"; +constexpr char kDefaultVmMount[] = "/mnt/shared"; void SendArcUrls(exo::SecurityDelegate::SendDataCallback callback, const std::vector<GURL>& urls) { @@ -95,23 +96,23 @@ return false; } +// Return VM mount path for specified `vm_name`. +base::FilePath GetVmMount(const std::string& vm_name) { + if (vm_name == crostini::kCrostiniDefaultVmName) { + return crostini::ContainerChromeOSBaseDirectory(); + } + if (vm_name == plugin_vm::kPluginVmName) { + return plugin_vm::ChromeOSBaseDirectory(); + } + return base::FilePath(std::string(kDefaultVmMount)); +} + // Translate |vm_paths| from |source| VM to host paths. -std::vector<FileInfo> TranslateVMToHost(ui::EndpointType source, +std::vector<FileInfo> TranslateVMToHost(const std::string vm_name, std::vector<ui::FileInfo> vm_paths) { std::vector<FileInfo> file_infos; Profile* primary_profile = ProfileManager::GetPrimaryUserProfile(); - bool is_crostini = source == ui::EndpointType::kCrostini; - bool is_plugin_vm = source == ui::EndpointType::kPluginVm; - - base::FilePath vm_mount; - std::string vm_name; - if (is_crostini) { - vm_mount = crostini::ContainerChromeOSBaseDirectory(); - vm_name = crostini::kCrostiniDefaultVmName; - } else if (is_plugin_vm) { - vm_mount = plugin_vm::ChromeOSBaseDirectory(); - vm_name = plugin_vm::kPluginVmName; - } + bool is_crostini = vm_name == crostini::kCrostiniDefaultVmName; for (ui::FileInfo& info : vm_paths) { base::FilePath path = std::move(info.path); @@ -121,9 +122,9 @@ // /mnt/chromeos for crostini; in //ChromeOS for Plugin VM), otherwise // prefix with 'vmfile:<vm_name>:' to avoid VMs spoofing host paths. // E.g. crostini /etc/mime.types => vmfile:termina:/etc/mime.types. - if (is_crostini || is_plugin_vm) { + if (!vm_name.empty() && vm_name != arc::kArcVmName) { if (file_manager::util::ConvertPathInsideVMToFileSystemURL( - primary_profile, path, vm_mount, + primary_profile, path, GetVmMount(vm_name), /*map_crostini_home=*/is_crostini, &url)) { path = url.path(); // Only allow write to clipboard for paths that are shared. @@ -163,27 +164,11 @@ // Share |files| with |target| VM and invoke |callback| with translated file: // URLs. void ShareAndTranslateHostToVM( - ui::EndpointType target, - std::vector<FileInfo> file_infos, + const std::string& vm_name, + const std::vector<FileInfo>& file_infos, base::OnceCallback<void(std::vector<std::string>)> callback) { Profile* primary_profile = ProfileManager::GetPrimaryUserProfile(); - bool is_arc = target == ui::EndpointType::kArc; - bool is_crostini = target == ui::EndpointType::kCrostini; - bool is_plugin_vm = target == ui::EndpointType::kPluginVm; - - base::FilePath vm_mount; - std::string vm_name; - if (is_arc) { - // For ARC, |share_required| below will always be false and |vm_name| will - // not be used. Setting it to arc::kArcVmName has no effect. - vm_name = arc::kArcVmName; - } else if (is_crostini) { - vm_mount = crostini::ContainerChromeOSBaseDirectory(); - vm_name = crostini::kCrostiniDefaultVmName; - } else if (is_plugin_vm) { - vm_mount = plugin_vm::ChromeOSBaseDirectory(); - vm_name = plugin_vm::kPluginVmName; - } + bool is_crostini = vm_name == crostini::kCrostiniDefaultVmName; const std::string vm_prefix = base::StrCat({kVmFileScheme, ":", vm_name, ":"}); @@ -194,7 +179,7 @@ for (auto& info : file_infos) { std::string file_url; bool share_required = false; - if (is_arc) { + if (vm_name == arc::kArcVmName) { GURL arc_url; if (!file_manager::util::ConvertPathToArcUrl(info.path, &arc_url, &share_required)) { @@ -202,7 +187,7 @@ continue; } file_url = arc_url.spec(); - } else if (is_crostini || is_plugin_vm) { + } else if (!vm_name.empty()) { base::FilePath path; // Check if it is a path inside the VM: 'vmfile:<vm_name>:'. if (base::StartsWith(info.path.value(), vm_prefix, @@ -210,7 +195,7 @@ file_url = ui::FilePathToFileURL( base::FilePath(info.path.value().substr(vm_prefix.size()))); } else if (file_manager::util::ConvertFileSystemURLToPathInsideVM( - primary_profile, info.url, vm_mount, + primary_profile, info.url, GetVmMount(vm_name), /*map_crostini_home=*/is_crostini, &path)) { // Convert to path inside the VM. file_url = ui::FilePathToFileURL(path); @@ -230,7 +215,7 @@ } if (!paths_to_share.empty()) { - if (!is_plugin_vm) { + if (vm_name != plugin_vm::kPluginVmName) { auto vm_info = guest_os::GuestOsSessionTracker::GetForProfile(primary_profile) ->GetVmInfo(vm_name); @@ -252,7 +237,7 @@ } // Still send the data, even if sharing failed. - std::move(callback).Run(file_urls); + std::move(callback).Run(std::move(file_urls)); }, std::move(callback), std::move(file_urls))); return; @@ -273,9 +258,9 @@ // static std::vector<base::FilePath> TranslateVMPathsToHost( - ui::EndpointType source, + const std::string& vm_name, const std::vector<ui::FileInfo>& vm_paths) { - std::vector<FileInfo> translated = TranslateVMToHost(source, vm_paths); + std::vector<FileInfo> translated = TranslateVMToHost(vm_name, vm_paths); std::vector<base::FilePath> result; for (auto& info : translated) { result.push_back(std::move(info.path)); @@ -285,10 +270,10 @@ // static void ShareWithVMAndTranslateToFileUrls( - ui::EndpointType target, + const std::string& vm_name, const std::vector<base::FilePath>& files, base::OnceCallback<void(std::vector<std::string>)> callback) { - ShareAndTranslateHostToVM(target, CrackPaths(files), std::move(callback)); + ShareAndTranslateHostToVM(vm_name, CrackPaths(files), std::move(callback)); } ChromeSecurityDelegate::ChromeSecurityDelegate() = default; @@ -328,7 +313,8 @@ const std::vector<uint8_t>& data) const { std::vector<ui::FileInfo> result; std::vector<FileInfo> file_infos = TranslateVMToHost( - source, ui::URIListToFileInfos(std::string(data.begin(), data.end()))); + GetVmName(source), + ui::URIListToFileInfos(std::string(data.begin(), data.end()))); for (auto& info : file_infos) { result.push_back(ui::FileInfo(std::move(info.path), base::FilePath())); } @@ -345,7 +331,7 @@ } ShareAndTranslateHostToVM( - target, CrackPaths(std::move(paths)), + GetVmName(target), CrackPaths(std::move(paths)), base::BindOnce(&SendAfterShare, target, std::move(callback))); } @@ -379,11 +365,16 @@ } ShareAndTranslateHostToVM( - target, std::move(file_infos), + GetVmName(target), std::move(file_infos), base::BindOnce(&SendAfterShare, target, std::move(callback))); } -std::string ChromeSecurityDelegate::GetVmName() const { +std::string ChromeSecurityDelegate::GetVmName(ui::EndpointType target) const { + if (target == ui::EndpointType::kArc) { + return arc::kArcVmName; + } else if (target == ui::EndpointType::kPluginVm) { + return plugin_vm::kPluginVmName; + } return std::string(); }
diff --git a/chrome/browser/ash/exo/chrome_security_delegate.h b/chrome/browser/ash/exo/chrome_security_delegate.h index da2d6e57..f373b8e 100644 --- a/chrome/browser/ash/exo/chrome_security_delegate.h +++ b/chrome/browser/ash/exo/chrome_security_delegate.h
@@ -13,14 +13,14 @@ // Translate paths from |source| VM to valid paths in the host. Invalid paths // are ignored. std::vector<base::FilePath> TranslateVMPathsToHost( - ui::EndpointType source, + const std::string& vm_name, const std::vector<ui::FileInfo>& vm_paths); // Share |files| with |target| VM, and translate |files| to be "file://" URLs // which can be used inside the vm. |callback| is invoked with translated // "file://" URLs. void ShareWithVMAndTranslateToFileUrls( - ui::EndpointType target, + const std::string& vm_name, const std::vector<base::FilePath>& files, base::OnceCallback<void(std::vector<std::string>)> callback); @@ -45,7 +45,7 @@ const base::Pickle& pickle, SendDataCallback callback) override; - virtual std::string GetVmName() const; + virtual std::string GetVmName(ui::EndpointType target) const; }; } // namespace ash
diff --git a/chrome/browser/ash/exo/chrome_security_delegate_unittest.cc b/chrome/browser/ash/exo/chrome_security_delegate_unittest.cc index 16a81fe7..ad97c426 100644 --- a/chrome/browser/ash/exo/chrome_security_delegate_unittest.cc +++ b/chrome/browser/ash/exo/chrome_security_delegate_unittest.cc
@@ -11,10 +11,14 @@ #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_refptr.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind.h" +#include "chrome/browser/ash/bruschetta/bruschetta_util.h" #include "chrome/browser/ash/crostini/crostini_manager.h" +#include "chrome/browser/ash/crostini/crostini_security_delegate.h" #include "chrome/browser/ash/crostini/crostini_test_helper.h" #include "chrome/browser/ash/crostini/crostini_util.h" #include "chrome/browser/ash/file_manager/path_util.h" +#include "chrome/browser/ash/guest_os/guest_os_security_delegate.h" #include "chrome/browser/ash/guest_os/guest_os_share_path.h" #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h" #include "chrome/test/base/testing_profile.h" @@ -162,6 +166,8 @@ shared_path); guest_os_share_path->RegisterSharedPath(plugin_vm::kPluginVmName, shared_path); + guest_os_share_path->RegisterSharedPath(bruschetta::kBruschettaVmName, + shared_path); // Multiple lines should be parsed. // Arc should not translate paths. @@ -175,30 +181,31 @@ EXPECT_EQ("", files[1].display_name.value()); // Crostini shared paths should be mapped. - files = security_delegate.GetFilenames( + guest_os::GuestOsSecurityDelegate crostini_security_delegate("termina"); + files = crostini_security_delegate.GetFilenames( ui::EndpointType::kCrostini, Data("file:///mnt/chromeos/MyFiles/shared/file")); EXPECT_EQ(1u, files.size()); EXPECT_EQ(shared_path.Append("file"), files[0].path); // Crostini homedir should be mapped. - files = security_delegate.GetFilenames(ui::EndpointType::kCrostini, - Data("file:///home/testuser/file")); + files = crostini_security_delegate.GetFilenames( + ui::EndpointType::kCrostini, Data("file:///home/testuser/file")); EXPECT_EQ(1u, files.size()); EXPECT_EQ(crostini_dir_.Append("file"), files[0].path); // Crostini internal paths should be mapped. - files = security_delegate.GetFilenames(ui::EndpointType::kCrostini, - Data("file:///etc/hosts")); + files = crostini_security_delegate.GetFilenames(ui::EndpointType::kCrostini, + Data("file:///etc/hosts")); EXPECT_EQ(1u, files.size()); EXPECT_EQ("vmfile:termina:/etc/hosts", files[0].path.value()); // Unshared paths should fail. - files = security_delegate.GetFilenames( + files = crostini_security_delegate.GetFilenames( ui::EndpointType::kCrostini, Data("file:///mnt/chromeos/MyFiles/unshared/file")); EXPECT_EQ(0u, files.size()); - files = security_delegate.GetFilenames( + files = crostini_security_delegate.GetFilenames( ui::EndpointType::kCrostini, Data("file:///mnt/chromeos/MyFiles/shared/file1\r\n" "file:///mnt/chromeos/MyFiles/unshared/file2")); @@ -206,29 +213,29 @@ EXPECT_EQ(shared_path.Append("file1"), files[0].path); // file:/path should fail. - files = security_delegate.GetFilenames( + files = crostini_security_delegate.GetFilenames( ui::EndpointType::kCrostini, Data("file:/mnt/chromeos/MyFiles/file")); EXPECT_EQ(0u, files.size()); // file:path should fail. - files = security_delegate.GetFilenames( + files = crostini_security_delegate.GetFilenames( ui::EndpointType::kCrostini, Data("file:mnt/chromeos/MyFiles/file")); EXPECT_EQ(0u, files.size()); // file:// should fail. - files = security_delegate.GetFilenames(ui::EndpointType::kCrostini, - Data("file://")); + files = crostini_security_delegate.GetFilenames(ui::EndpointType::kCrostini, + Data("file://")); EXPECT_EQ(0u, files.size()); // file:/// maps to internal root. - files = security_delegate.GetFilenames(ui::EndpointType::kCrostini, - Data("file:///")); + files = crostini_security_delegate.GetFilenames(ui::EndpointType::kCrostini, + Data("file:///")); EXPECT_EQ(1u, files.size()); EXPECT_EQ("vmfile:termina:/", files[0].path.value()); // /path should fail. - files = security_delegate.GetFilenames(ui::EndpointType::kCrostini, - Data("/mnt/chromeos/MyFiles/file")); + files = crostini_security_delegate.GetFilenames( + ui::EndpointType::kCrostini, Data("/mnt/chromeos/MyFiles/file")); EXPECT_EQ(0u, files.size()); // Plugin VM shared paths should be mapped. @@ -254,6 +261,38 @@ "file://ChromeOS/MyFiles/unshared/file2")); EXPECT_EQ(1u, files.size()); EXPECT_EQ(shared_path.Append("file1"), files[0].path); + + // Bruschetta shared paths should be mapped. + guest_os::GuestOsSecurityDelegate bru_security_delegate("bru"); + files = bru_security_delegate.GetFilenames( + ui::EndpointType::kCrostini, + Data("file:///mnt/shared/MyFiles/shared/file")); + EXPECT_EQ(1u, files.size()); + EXPECT_EQ(shared_path.Append("file"), files[0].path); + + // Bruschetta homedir is mapped as an internal path. + files = bru_security_delegate.GetFilenames( + ui::EndpointType::kCrostini, Data("file:///home/testuser/file")); + EXPECT_EQ(1u, files.size()); + EXPECT_EQ("vmfile:bru:/home/testuser/file", files[0].path.value()); + + // Bruschetta internal paths should be mapped. + files = bru_security_delegate.GetFilenames(ui::EndpointType::kCrostini, + Data("file:///etc/hosts")); + EXPECT_EQ(1u, files.size()); + EXPECT_EQ("vmfile:bru:/etc/hosts", files[0].path.value()); + + // Unshared paths should fail. + files = bru_security_delegate.GetFilenames( + ui::EndpointType::kCrostini, + Data("file:///mnt/shared/MyFiles/unshared/file")); + EXPECT_EQ(0u, files.size()); + files = bru_security_delegate.GetFilenames( + ui::EndpointType::kCrostini, + Data("file:///mnt/shared/MyFiles/shared/file1\r\n" + "file:///mnt/shared/MyFiles/unshared/file2")); + EXPECT_EQ(1u, files.size()); + EXPECT_EQ(shared_path.Append("file1"), files[0].path); } TEST_F(ChromeSecurityDelegateTest, SendFileInfoConvertPaths) { @@ -288,14 +327,16 @@ data); // Crostini should convert path to inside VM, and share the path. - security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, - base::BindOnce(&Capture, &data)); + guest_os::GuestOsSecurityDelegate crostini_security_delegate("termina"); + crostini_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, + base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ("file:///mnt/chromeos/MyFiles/file1", data); // Crostini should join lines with CRLF. - security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1, file2}, - base::BindOnce(&Capture, &data)); + crostini_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, + {file1, file2}, + base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ( "file:///mnt/chromeos/MyFiles/file1" @@ -309,17 +350,24 @@ task_environment_.RunUntilIdle(); EXPECT_EQ("file://ChromeOS/MyFiles/file1", data); + // Bruschetta should convert path to inside VM, and share the path. + guest_os::GuestOsSecurityDelegate bru_security_delegate("bru"); + bru_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, + base::BindOnce(&Capture, &data)); + task_environment_.RunUntilIdle(); + EXPECT_EQ("file:///mnt/shared/MyFiles/file1", data); + // Crostini should handle vmfile:termina:/etc/hosts. file1.path = base::FilePath("vmfile:termina:/etc/hosts"); - security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, - base::BindOnce(&Capture, &data)); + crostini_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, + base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ("file:///etc/hosts", data); // Crostini should ignore vmfile:PvmDefault:C:/WINDOWS/notepad.exe. file1.path = base::FilePath("vmfile:PvmDefault:C:/WINDOWS/notepad.exe"); - security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, - base::BindOnce(&Capture, &data)); + crostini_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, + base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ("", data); @@ -336,10 +384,24 @@ base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ("", data); + + // Bruschetta should handle vmfile:bru:/etc/hosts. + file1.path = base::FilePath("vmfile:bru:/etc/hosts"); + bru_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, + base::BindOnce(&Capture, &data)); + task_environment_.RunUntilIdle(); + EXPECT_EQ("file:///etc/hosts", data); + + // Bruschetta should ignore vmfile:termina:/etc/hosts. + file1.path = base::FilePath("vmfile:termina:/etc/hosts"); + bru_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file1}, + base::BindOnce(&Capture, &data)); + task_environment_.RunUntilIdle(); + EXPECT_EQ("", data); } TEST_F(ChromeSecurityDelegateTest, SendFileInfoSharePathsCrostini) { - ChromeSecurityDelegate security_delegate; + guest_os::GuestOsSecurityDelegate crostini_security_delegate("termina"); // A path which is already shared should not be shared again. base::FilePath shared_path = myfiles_dir_.Append("shared"); @@ -350,16 +412,16 @@ ui::FileInfo file(shared_path, base::FilePath()); EXPECT_FALSE(FakeSeneschalClient::Get()->share_path_called()); std::string data; - security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file}, - base::BindOnce(&Capture, &data)); + crostini_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file}, + base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ("file:///mnt/chromeos/MyFiles/shared", data); EXPECT_FALSE(FakeSeneschalClient::Get()->share_path_called()); // A path which is not already shared should be shared. file = ui::FileInfo(myfiles_dir_.Append("file"), base::FilePath()); - security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file}, - base::BindOnce(&Capture, &data)); + crostini_security_delegate.SendFileInfo(ui::EndpointType::kCrostini, {file}, + base::BindOnce(&Capture, &data)); task_environment_.RunUntilIdle(); EXPECT_EQ("file:///mnt/chromeos/MyFiles/file", data); EXPECT_TRUE(FakeSeneschalClient::Get()->share_path_called());
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.cc b/chrome/browser/ash/extensions/file_manager/event_router.cc index 5c1127b..4d70ef90 100644 --- a/chrome/browser/ash/extensions/file_manager/event_router.cc +++ b/chrome/browser/ash/extensions/file_manager/event_router.cc
@@ -58,6 +58,7 @@ #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/api/file_manager_private.h" #include "chrome/common/extensions/extension_constants.h" @@ -1570,4 +1571,11 @@ fmp::OnDeviceConnectionStatusChanged::Create(result)); } +void EventRouter::OnLocalUserFilesPolicyChanged() { + if (!base::FeatureList::IsEnabled(features::kSkyVault)) { + return; + } + OnFileManagerPrefsChanged(); +} + } // namespace file_manager
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.h b/chrome/browser/ash/extensions/file_manager/event_router.h index eb42f320..ffc14ef 100644 --- a/chrome/browser/ash/extensions/file_manager/event_router.h +++ b/chrome/browser/ash/extensions/file_manager/event_router.h
@@ -30,6 +30,7 @@ #include "chrome/browser/ash/guest_os/guest_os_share_path.h" #include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h" #include "chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry.h" +#include "chrome/browser/ash/policy/local_user_files/observer.h" #include "chrome/common/extensions/api/file_manager_private.h" #include "chromeos/ash/components/settings/timezone_settings.h" #include "chromeos/dbus/dlp/dlp_client.h" @@ -77,7 +78,8 @@ guest_os::GuestOsMountProviderRegistry::Observer, chromeos::DlpClient::Observer, apps::AppRegistryCache::Observer, - network::NetworkConnectionTracker::NetworkConnectionObserver { + network::NetworkConnectionTracker::NetworkConnectionObserver, + policy::local_user_files::Observer { public: using DispatchDirectoryChangeEventImplCallback = base::RepeatingCallback<void(const base::FilePath& virtual_path, @@ -218,6 +220,9 @@ // network::NetworkConnectionTracker::NetworkConnectionObserver: void OnConnectionChanged(const network::mojom::ConnectionType type) override; + // policy::local_user_files::Observer: + void OnLocalUserFilesPolicyChanged() override; + // Use this method for unit tests to bypass checking if there are any SWA // windows. void ForceBroadcastingForTesting(bool enabled) {
diff --git a/chrome/browser/ash/extensions/file_manager/event_router_unittest.cc b/chrome/browser/ash/extensions/file_manager/event_router_unittest.cc index b51dda5..4d1376f0 100644 --- a/chrome/browser/ash/extensions/file_manager/event_router_unittest.cc +++ b/chrome/browser/ash/extensions/file_manager/event_router_unittest.cc
@@ -19,6 +19,10 @@ #include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/ash/file_manager/volume_manager_factory.h" #include "chrome/browser/ash/fileapi/file_system_backend.h" +#include "chrome/common/chrome_features.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/scoped_testing_local_state.h" +#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/disks/fake_disk_mount_manager.h" #include "content/public/test/browser_task_environment.h" @@ -115,7 +119,8 @@ class FileManagerEventRouterTest : public testing::Test { public: - FileManagerEventRouterTest() = default; + FileManagerEventRouterTest() + : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {} FileManagerEventRouterTest(const FileManagerEventRouterTest&) = delete; FileManagerEventRouterTest& operator=(const FileManagerEventRouterTest&) = delete; @@ -162,8 +167,9 @@ return io_task::EntryStatus(std::move(url), base::File::FILE_OK); } + ScopedTestingLocalState scoped_testing_local_state_; content::BrowserTaskEnvironment task_environment_; - display::test::TestScreen test_screen_{/*create_dispay=*/true, + display::test::TestScreen test_screen_{/*create_display=*/true, /*register_screen=*/true}; base::ScopedTempDir temp_dir_; std::unique_ptr<TestingProfile> profile_; @@ -173,6 +179,10 @@ ash::disks::FakeDiskMountManager disk_mount_manager_; }; +MATCHER(ExpectNoArgs, "") { + return arg.size() == 0; +} + // A matcher that matches an `extensions::Event::event_args` and attempts to // extract the "outputs" key. It then looks at the output at `index` and matches // the `field` against the `expected_value`. @@ -390,5 +400,40 @@ run_loop.Run(); } +class FileManagerEventRouterLocalFilesTest : public FileManagerEventRouterTest { + public: + FileManagerEventRouterLocalFilesTest() { + scoped_feature_list_.InitAndEnableFeature(features::kSkyVault); + } + ~FileManagerEventRouterLocalFilesTest() override = default; + + void SetLocalUserFilesPolicy(bool allowed) { + scoped_testing_local_state_.Get()->SetBoolean(prefs::kLocalUserFilesAllowed, + allowed); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +TEST_F(FileManagerEventRouterLocalFilesTest, OnLocalUserFilesPolicyChanged) { + // Set up event routers. + extensions::TestEventRouter* test_event_router = + extensions::CreateAndUseTestEventRouter(profile_.get()); + TestEventRouterObserver observer(test_event_router); + auto event_router = std::make_unique<EventRouter>(profile_.get()); + event_router->ForceBroadcastingForTesting(true); + + // Expect the preferences changed event. + base::Value::List event_args = + extensions::api::file_manager_private::OnPreferencesChanged::Create(); + base::RunLoop run_loop; + EXPECT_CALL(observer, OnBroadcastEvent(Field(&extensions::Event::event_args, + AllOf(ExpectNoArgs())))) + .WillOnce(RunClosure(run_loop.QuitClosure())); + SetLocalUserFilesPolicy(/*allowed=*/false); + run_loop.Run(); +} + } // namespace } // namespace file_manager
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc index 8d2b898..2270719 100644 --- a/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc +++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc
@@ -28,6 +28,8 @@ #include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/ash/file_manager/volume_manager.h" #include "chrome/browser/ash/file_manager/volume_manager_factory.h" +#include "chrome/test/base/scoped_testing_local_state.h" +#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/disks/fake_disk_mount_manager.h" #include "content/public/test/browser_task_environment.h" @@ -91,6 +93,10 @@ } class CopyOrMoveIOTaskTestBase : public testing::Test { + public: + CopyOrMoveIOTaskTestBase() + : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {} + protected: void SetUp() override { // Define a VolumeManager to associate with the testing profile. @@ -110,6 +116,7 @@ base::FilePath::FromUTF8Unsafe(path)); } + ScopedTestingLocalState scoped_testing_local_state_; content::BrowserTaskEnvironment task_environment_; ash::disks::FakeDiskMountManager disk_mount_manager_; TestingProfile profile_;
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc index e41d823..a51d1441 100644 --- a/chrome/browser/ash/file_manager/volume_manager.cc +++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -10,6 +10,7 @@ #include "ash/constants/ash_features.h" #include "base/auto_reset.h" #include "base/base64url.h" +#include "base/feature_list.h" #include "base/files/file_util.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -31,8 +32,10 @@ #include "chrome/browser/ash/file_manager/snapshot_manager.h" #include "chrome/browser/ash/file_manager/volume_manager_factory.h" #include "chrome/browser/ash/file_manager/volume_manager_observer.h" +#include "chrome/browser/ash/policy/local_user_files/policy_utils.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h" +#include "chrome/common/chrome_features.h" #include "chromeos/components/disks/disks_prefs.h" #include "components/prefs/pref_service.h" #include "components/storage_monitor/storage_monitor.h" @@ -957,6 +960,17 @@ *volume_id = volume_id->substr(prefix).insert(0, "provided:"); } +void VolumeManager::OnLocalUserFilesPolicyChanged() { + if (!base::FeatureList::IsEnabled(features::kSkyVault)) { + return; + } + if (policy::local_user_files::LocalUserFilesAllowed()) { + MountLocalFolders(); + } else { + UnmountLocalFolders(); + } +} + void VolumeManager::OnProvidedFileSystemUnmount( const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info, base::File::Error error) { @@ -1597,4 +1611,16 @@ } } +void VolumeManager::MountLocalFolders() { + // TODO(b/322779059): Mount arc. + // TODO(b/322779967): Mount crostini. + // TODO(b/325006828): Mount My Files. +} + +void VolumeManager::UnmountLocalFolders() { + // TODO(b/322779059): Unmount arc. + // TODO(b/322779967): Unmount crostini. + // TODO(b/325006828): Unmount My Files. +} + } // namespace file_manager
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h index b0af976..429b71b 100644 --- a/chrome/browser/ash/file_manager/volume_manager.h +++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -26,6 +26,7 @@ #include "chrome/browser/ash/file_system_provider/observer.h" #include "chrome/browser/ash/file_system_provider/service.h" #include "chrome/browser/ash/guest_os/public/types.h" +#include "chrome/browser/ash/policy/local_user_files/observer.h" #include "components/prefs/pref_change_registrar.h" #include "components/storage_monitor/removable_storage_observer.h" #include "services/device/public/mojom/mtp_manager.mojom.h" @@ -63,7 +64,8 @@ ash::file_system_provider::Observer, storage_monitor::RemovableStorageObserver, ui::ClipboardObserver, - DocumentsProviderRootManager::Observer { + DocumentsProviderRootManager::Observer, + policy::local_user_files::Observer { public: // An alternate to device::mojom::MtpManager::GetStorageInfo. // Used for injecting fake MTP manager for testing in VolumeManagerTest. @@ -263,6 +265,9 @@ void ConvertFuseBoxFSPVolumeIdToFSPIfNeeded(std::string* volume_id) const; + // policy::local_user_files::Observer: + void OnLocalUserFilesPolicyChanged() override; + SnapshotManager* snapshot_manager() { return snapshot_manager_.get(); } io_task::IOTaskController* io_task_controller() { @@ -337,6 +342,11 @@ RemoveSftpGuestOsVolumeCallback callback, ash::MountError error); + // Mounts local folders (My Files, Play and Linux files). + void MountLocalFolders(); + // Unmounts local folders (My Files, Play and Linux files). + void UnmountLocalFolders(); + static int counter_; const int id_ = ++counter_; // Only used in log traces
diff --git a/chrome/browser/ash/guest_os/guest_os_security_delegate.cc b/chrome/browser/ash/guest_os/guest_os_security_delegate.cc index bf9b6b1..f1ec6b76 100644 --- a/chrome/browser/ash/guest_os/guest_os_security_delegate.cc +++ b/chrome/browser/ash/guest_os/guest_os_security_delegate.cc
@@ -34,7 +34,7 @@ base::BindOnce(std::move(callback), cap_ptr)); } -std::string GuestOsSecurityDelegate::GetVmName() const { +std::string GuestOsSecurityDelegate::GetVmName(ui::EndpointType target) const { return vm_name_; }
diff --git a/chrome/browser/ash/guest_os/guest_os_security_delegate.h b/chrome/browser/ash/guest_os/guest_os_security_delegate.h index f1f77415..2a6222e5 100644 --- a/chrome/browser/ash/guest_os/guest_os_security_delegate.h +++ b/chrome/browser/ash/guest_os/guest_os_security_delegate.h
@@ -36,7 +36,8 @@ std::unique_ptr<exo::WaylandServerHandle>)> callback); - std::string GetVmName() const override; + // ash::ChromeSecurityDelegate: + std::string GetVmName(ui::EndpointType target) const override; private: std::string vm_name_;
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc index 242e155..c1d023e 100644 --- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc +++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc
@@ -789,6 +789,7 @@ TEST_F(TargetDeviceBootstrapControllerTest, NoUserVerificationRequiredWhenResumeAfterUpdate) { + GetSessionContext()->FillOrResetSession(); ResumeAfterUpdate(); bootstrap_controller_->StartAdvertisingAndMaybeGetQRCode(); EXPECT_EQ(fake_observer_->last_status.step,
diff --git a/chrome/browser/ash/policy/local_user_files/observer.cc b/chrome/browser/ash/policy/local_user_files/observer.cc index aec4c99..5c840be8 100644 --- a/chrome/browser/ash/policy/local_user_files/observer.cc +++ b/chrome/browser/ash/policy/local_user_files/observer.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/policy/local_user_files/observer.h" +#include "base/check_is_test.h" #include "chrome/browser/browser_process.h" #include "chrome/common/pref_names.h" @@ -11,6 +12,11 @@ Observer::Observer() : pref_change_registrar_(std::make_unique<PrefChangeRegistrar>()) { + if (!g_browser_process->local_state()) { + // Can be NULL in tests. + CHECK_IS_TEST(); + return; + } pref_change_registrar_->Init(g_browser_process->local_state()); pref_change_registrar_->Add( prefs::kLocalUserFilesAllowed,
diff --git a/chrome/browser/autocomplete/in_memory_url_index_factory.cc b/chrome/browser/autocomplete/in_memory_url_index_factory.cc index b6979852..4e4646ac 100644 --- a/chrome/browser/autocomplete/in_memory_url_index_factory.cc +++ b/chrome/browser/autocomplete/in_memory_url_index_factory.cc
@@ -9,6 +9,7 @@ #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" +#include "components/bookmarks/browser/bookmark_model.h" #include "components/keyed_service/core/service_access_type.h" #include "components/omnibox/browser/in_memory_url_index.h" #include "content/public/common/url_constants.h"
diff --git a/chrome/browser/compose/compose_session.cc b/chrome/browser/compose/compose_session.cc index 4a67139..6eee182 100644 --- a/chrome/browser/compose/compose_session.cc +++ b/chrome/browser/compose/compose_session.cc
@@ -35,6 +35,7 @@ #include "components/compose/core/browser/compose_features.h" #include "components/compose/core/browser/compose_manager_impl.h" #include "components/compose/core/browser/compose_metrics.h" +#include "components/compose/core/browser/compose_utils.h" #include "components/compose/core/browser/config.h" #include "components/optimization_guide/core/model_execution/optimization_guide_model_execution_error.h" #include "components/optimization_guide/core/model_quality/feature_type_map.h" @@ -61,20 +62,8 @@ return false; } - base::StringTokenizer tokenizer( - prompt, " ", base::StringTokenizer::WhitespacePolicy::kSkipOver); - unsigned int word_count = 0; - while (tokenizer.GetNext()) { - ++word_count; - if (word_count > config.input_max_words) { - return false; - } - } - - if (word_count < config.input_min_words) { - return false; - } - return true; + return compose::IsWordCountWithinBounds(prompt, config.input_min_words, + config.input_max_words); } const char kComposeBugReportURL[] = "https://goto.google.com/ccbrfd";
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc index 69aa2af..a26c98c 100644 --- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -13,6 +13,7 @@ #include <vector> #include "base/containers/contains.h" +#include "base/containers/to_value_list.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/functional/bind.h" @@ -146,15 +147,6 @@ constexpr char kDefaultRulesetID[] = "id"; -template <class T> -base::Value::List VectorToList(const std::vector<T>& values) { - base::Value::List lv; - for (const auto& value : values) { - lv.Append(value); - } - return lv; -} - // Returns true if |window.scriptExecuted| is true for the given frame. bool WasFrameWithScriptLoaded(content::RenderFrameHost* render_frame_host) { if (!render_frame_host) { @@ -462,8 +454,8 @@ }); )"; - base::Value::List ids_to_disable = VectorToList(rule_ids_to_disable); - base::Value::List ids_to_enable = VectorToList(rule_ids_to_enable); + base::Value::List ids_to_disable = base::ToValueList(rule_ids_to_disable); + base::Value::List ids_to_enable = base::ToValueList(rule_ids_to_enable); const std::string script = content::JsReplace( kScript, ruleset_id, base::Value(std::move(ids_to_disable)), @@ -505,7 +497,7 @@ : ['expected:', expected, '; actual:', actual].join('')); }); )"; - base::Value::List expected = VectorToList(expected_disabled_rule_ids); + base::Value::List expected = base::ToValueList(expected_disabled_rule_ids); std::string result = ExecuteScriptInBackgroundPageAndReturnString( extension_id, content::JsReplace(kScript, ruleset_id_string, std::move(expected))); @@ -743,15 +735,6 @@ }); )"; - // Serialize |rules_to_add|. - base::Value::List rules_to_add_builder; - for (const auto& rule : rules_to_add) - rules_to_add_builder.Append(rule.ToValue()); - - // Serialize |rule_ids|. - base::Value::List rule_ids_to_remove_value = - VectorToList(rule_ids_to_remove); - const char* function_name = nullptr; switch (scope) { case RulesetScope::kDynamic: @@ -764,8 +747,8 @@ const std::string script = content::JsReplace(base::StringPrintf(kScript, function_name), - base::Value(std::move(rules_to_add_builder)), - base::Value(std::move(rule_ids_to_remove_value))); + base::ToValueList(rules_to_add, &TestRule::ToValue), + base::ToValueList(rule_ids_to_remove)); ASSERT_EQ("success", ExecuteScriptInBackgroundPageAndReturnString( extension_id, script)); } @@ -898,12 +881,9 @@ }); )"; - base::Value::List ids_to_remove = VectorToList(ruleset_ids_to_remove); - base::Value::List ids_to_add = VectorToList(ruleset_ids_to_add); - const std::string script = - content::JsReplace(kScript, base::Value(std::move(ids_to_remove)), - base::Value(std::move(ids_to_add))); + content::JsReplace(kScript, base::ToValueList(ruleset_ids_to_remove), + base::ToValueList(ruleset_ids_to_add)); return ExecuteScriptInBackgroundPageAndReturnString(extension_id, script); }
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc index e58671a2..68d2c02 100644 --- a/chrome/browser/extensions/install_signer.cc +++ b/chrome/browser/extensions/install_signer.cc
@@ -12,6 +12,7 @@ #include "base/base64.h" #include "base/command_line.h" +#include "base/containers/to_value_list.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/json/values_util.h" @@ -119,15 +120,6 @@ return true; } -// Helper for serialization of ExtensionIdSets to/from a base::Value::List. -[[nodiscard]] base::Value::List ExtensionIdSetToList( - const ExtensionIdSet& ids) { - base::Value::List id_list; - base::ranges::for_each(ids, - [&id_list](const auto& id) { id_list.Append(id); }); - return id_list; -} - [[nodiscard]] std::optional<ExtensionIdSet> ExtensionIdSetFromList( const base::Value::List& list) { ExtensionIdSet ids; @@ -150,8 +142,8 @@ base::Value::Dict InstallSignature::ToDict() const { base::Value::Dict dict; dict.Set(kSignatureFormatVersionKey, kSignatureFormatVersion); - dict.Set(kIdsKey, ExtensionIdSetToList(ids)); - dict.Set(kInvalidIdsKey, ExtensionIdSetToList(invalid_ids)); + dict.Set(kIdsKey, base::ToValueList(ids)); + dict.Set(kInvalidIdsKey, base::ToValueList(invalid_ids)); dict.Set(kExpireDateKey, expire_date); dict.Set(kSaltKey, base::Base64Encode(salt)); dict.Set(kSignatureKey, base::Base64Encode(signature));
diff --git a/chrome/browser/extensions/menu_manager.cc b/chrome/browser/extensions/menu_manager.cc index c3242c6..584dad2 100644 --- a/chrome/browser/extensions/menu_manager.cc +++ b/chrome/browser/extensions/menu_manager.cc
@@ -10,6 +10,7 @@ #include "base/check_op.h" #include "base/containers/contains.h" +#include "base/containers/to_value_list.h" #include "base/functional/bind.h" #include "base/json/json_writer.h" #include "base/notreached.h" @@ -97,13 +98,6 @@ return items; } -base::Value::List MenuItemsToValue(const MenuItem::List& items) { - base::Value::List list; - for (const auto* item : items) - list.Append(item->ToValue()); - return list; -} - bool GetStringList(const base::Value::Dict& dict, const std::string& key, std::vector<std::string>* out) { @@ -874,8 +868,9 @@ observer.WillWriteToStorage(extension_key.extension_id); if (store_) { - store_->SetExtensionValue(extension_key.extension_id, kContextMenusKey, - base::Value(MenuItemsToValue(all_items))); + store_->SetExtensionValue( + extension_key.extension_id, kContextMenusKey, + base::Value(base::ToValueList(all_items, &MenuItem::ToValue))); } }
diff --git a/chrome/browser/extensions/process_map_browsertest.cc b/chrome/browser/extensions/process_map_browsertest.cc index 800591b3..b25598b 100644 --- a/chrome/browser/extensions/process_map_browsertest.cc +++ b/chrome/browser/extensions/process_map_browsertest.cc
@@ -17,6 +17,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" +#include "content/public/test/test_navigation_observer.h" #include "extensions/browser/app_window/app_window.h" #include "extensions/browser/app_window/app_window_registry.h" #include "extensions/browser/guest_view/web_view/web_view_guest.h" @@ -221,6 +222,9 @@ extension_dir->WriteFile( FILE_PATH_LITERAL("parent.html"), base::StringPrintf(kPageContent, e1_page_url.spec().c_str())); + // Create a page that is not listed as a web_accessible_resource. + extension_dir->WriteFile(FILE_PATH_LITERAL("private_page.html"), + R"(<html>E2 Private</html>)"); extension2 = LoadExtension(extension_dir->UnpackedPath()); extension_dirs_.push_back(std::move(extension_dir)); } @@ -739,6 +743,28 @@ // doesn't mean the extension it contains is "sandboxed". EXPECT_FALSE(ExtensionFrameIsSandboxed(main_frame)); EXPECT_FALSE(ExtensionFrameIsSandboxed(sandboxed_child_frame)); + + // Attempt to have `extension1` (in `sandboxed_child_frame`) load a + // non-web-accessible resource from `extension2`. This should fail. The fact + // that the child is sandboxed doesn't matter. + GURL e2_private_page_url = extension2->GetResourceURL("private_page.html"); + const char kJsScript[] = + R"( + frm = document.createElement('iframe'); + frm.src = $1; + document.body.appendChild(frm); + )"; + content::TestNavigationObserver observer(GetActiveTab(), 1); + EXPECT_TRUE(ExecJs(sandboxed_child_frame, + content::JsReplace(kJsScript, e2_private_page_url))); + observer.Wait(); + + EXPECT_FALSE(observer.last_navigation_succeeded()); + EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, observer.last_net_error_code()); + content::RenderFrameHost* grand_child_frame = + content::ChildFrameAt(sandboxed_child_frame, 0); + EXPECT_NE(nullptr, grand_child_frame); + EXPECT_EQ(e2_private_page_url, grand_child_frame->GetLastCommittedURL()); } // Tests that sandboxed extension frames are considered privileged
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index e8936a0..37cedddd2 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1810,6 +1810,11 @@ "expiry_milestone": -1 }, { + "name": "disable-fullscreen-scrolling", + "owners": [ "ajuma@google.com", "alionadangla@google.com", "bling-flags@google.com" ], + "expiry_milestone": 130 + }, + { "name": "disable-idle-sockets-close-on-memory-pressure", "owners": [ "pmarko@chromium.org" ], "expiry_milestone": 112 @@ -3440,12 +3445,12 @@ { "name": "enable-save-to-drive", "owners": [ "qpubert@google.com", "djean@google.com", "olivierrobin@google.com", "bling-flags@google.com" ], - "expiry_milestone": 123 + "expiry_milestone": 125 }, { "name": "enable-save-to-photos", "owners": [ "qpubert@google.com", "djean@google.com", "bling-flags@google.com" ], - "expiry_milestone": 123 + "expiry_milestone": 125 }, { "name": "enable-seamless-refresh-rate-switching", @@ -6618,14 +6623,6 @@ "expiry_milestone": 123 }, { - "name": "privacy-guide-android", - "owners": [ - "rubindl@chromium.org", - "msramek@chromium.org", - "chrome-privacy-controls@google.com"], - "expiry_milestone": 120 - }, - { "name": "privacy-guide-android-3", "owners": [ "rubindl@chromium.org",
diff --git a/chrome/browser/media/cdm_pref_service_helper.cc b/chrome/browser/media/cdm_pref_service_helper.cc index 1c187a8..c94ea011 100644 --- a/chrome/browser/media/cdm_pref_service_helper.cc +++ b/chrome/browser/media/cdm_pref_service_helper.cc
@@ -7,6 +7,7 @@ #include <optional> #include "base/base64.h" +#include "base/containers/to_value_list.h" #include "base/json/values_util.h" #include "base/logging.h" #include "base/time/time.h" @@ -35,15 +36,6 @@ return time >= start && (end.is_null() || time <= end); } -// Converts a std::vector<base::Time> to a base::Value::List -base::Value::List TimesToList(const std::vector<base::Time>& times) { - base::Value::List times_list; - for (auto time : times) { - times_list.Append(base::TimeToValue(time)); - } - return times_list; -} - // Converts a base::Value::List of Time to std::vector<base::Time> std::vector<base::Time> ListToTimes(const base::Value::List& time_list) { std::vector<base::Time> times; @@ -90,7 +82,8 @@ base::TimeToValue(pref_data.client_token_creation_time())); } dict.Set(prefs::kHardwareSecureDecryptionDisabledTimes, - TimesToList(pref_data.hw_secure_decryption_disable_times())); + base::ToValueList(pref_data.hw_secure_decryption_disable_times(), + &base::TimeToValue)); return dict; }
diff --git a/chrome/browser/password_manager/DEPS b/chrome/browser/password_manager/DEPS index a5c67a3..f7cf4d6 100644 --- a/chrome/browser/password_manager/DEPS +++ b/chrome/browser/password_manager/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+dbus", + "+components/device_reauth", "+components/webauthn/android", ]
diff --git a/chrome/browser/password_manager/android/OWNERS b/chrome/browser/password_manager/android/OWNERS index a609b60..61b70fe7 100644 --- a/chrome/browser/password_manager/android/OWNERS +++ b/chrome/browser/password_manager/android/OWNERS
@@ -1,2 +1,10 @@ fhorschig@chromium.org ioanap@chromium.org + +per-file password_manager_android_util*=vsemeniuk@google.com +per-file password_store_android_account_backend*=vsemeniuk@google.com +# For this one use a '.', there are other files matching the prefix. +per-file password_store_android_backend.*=vsemeniuk@google.com +per-file password_store_android_local_backend*=vsemeniuk@google.com +per-file password_store_backend_migration_decorator*=vsemeniuk@google.com +per-file password_store_proxy_backend*=vsemeniuk@google.com
diff --git a/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator.cc b/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator.cc index ab11bae..85f7157 100644 --- a/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator.cc +++ b/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator.cc
@@ -517,6 +517,8 @@ case MigrationType::kNonSyncableToBuiltInBackend: break; } + prefs_->SetBoolean(prefs::kShouldShowPostPasswordMigrationSheetAtStartup, + true); } migration_in_progress_type_ = MigrationType::kNone;
diff --git a/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc b/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc index 5c36e4d..dc9afcd 100644 --- a/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc +++ b/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc
@@ -89,6 +89,8 @@ prefs::kPasswordsUseUPMLocalAndSeparateStores, static_cast<int>( password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff)); + prefs_.registry()->RegisterBooleanPref( + prefs::kShouldShowPostPasswordMigrationSheetAtStartup, false); CreateMigrator(&built_in_backend_, &android_backend_, &prefs_); } @@ -184,6 +186,8 @@ EXPECT_EQ( base::Time::Now().InSecondsFSinceUnixEpoch(), prefs()->GetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt)); + EXPECT_TRUE(prefs()->GetBoolean( + prefs::kShouldShowPostPasswordMigrationSheetAtStartup)); } TEST_F(BuiltInBackendToAndroidBackendMigratorTest, @@ -700,6 +704,8 @@ prefs::kPasswordsUseUPMLocalAndSeparateStores, static_cast<int>( password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff)); + prefs()->registry()->RegisterBooleanPref( + prefs::kShouldShowPostPasswordMigrationSheetAtStartup, false); if (GetParam().migration_ran_before) { // Setup the pref to indicate that the initial migration has happened
diff --git a/chrome/browser/password_manager/android/password_manager_android_util.cc b/chrome/browser/password_manager/android/password_manager_android_util.cc index 0669b32..fbbfba6 100644 --- a/chrome/browser/password_manager/android/password_manager_android_util.cc +++ b/chrome/browser/password_manager/android/password_manager_android_util.cc
@@ -264,8 +264,8 @@ } } -// Must only be called if the state pref is kOn, to set it to kOff if any of -// these happened: +// Must only be called if the state pref is kOn or kOffAndMigrationPending, to +// set it to kOff if any of these happened: // - The user downgraded GmsCore and can no longer use the local UPM properly. // - The min GmsCore version for the A/B experiment was bumped server-side. // - The A/B experiment was stopped due to bugs. @@ -273,7 +273,7 @@ void MaybeDeactivateSplitStoresAndLocalUpm( PrefService* pref_service, const base::FilePath& login_db_directory) { - CHECK_EQ(GetSplitStoresAndLocalUpmPrefValue(pref_service), kOn); + CHECK_NE(GetSplitStoresAndLocalUpmPrefValue(pref_service), kOff); // Only deactivate based on the *NoMigration* flag. // - If problems arise when rolling out NoMigration (first launch), disable @@ -332,7 +332,8 @@ login_db_directory.Append(password_manager::kLoginDataForProfileFileName); base::FilePath account_db_path = login_db_directory.Append(password_manager::kLoginDataForAccountFileName); - if (IsPasswordSyncEnabled(pref_service) && + if (GetSplitStoresAndLocalUpmPrefValue(pref_service) == kOn && + IsPasswordSyncEnabled(pref_service) && !base::ReplaceFile(account_db_path, profile_db_path, /*error=*/nullptr)) { return; } @@ -359,12 +360,9 @@ void SetUsesSplitStoresAndUPMForLocal( PrefService* pref_service, const base::FilePath& login_db_directory) { - if (GetSplitStoresAndLocalUpmPrefValue(pref_service) == kOn) { + if (GetSplitStoresAndLocalUpmPrefValue(pref_service) != kOff) { MaybeDeactivateSplitStoresAndLocalUpm(pref_service, login_db_directory); } else { - // Reset the migration pending state if needed. - pref_service->SetInteger(kPasswordsUseUPMLocalAndSeparateStores, - static_cast<int>(kOff)); MaybeActivateSplitStoresAndLocalUpm(pref_service, login_db_directory); }
diff --git a/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc b/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc index 69dba2a..0d3593026 100644 --- a/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc +++ b/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
@@ -572,6 +572,75 @@ } TEST_F(PasswordManagerAndroidUtilTest, + SetUsesSplitStoresAndUPMForLocal_KeepMigrationPendingIfSyncEnabled) { + // Set up a user who was signed out with saved passwords (thus got into + // kOffAndMigrationPending), failed to migrate (thus stayed in + // kOffAndMigrationPending) and later enabled sync. + // kLoginDataForAccountFileName exists because the account store was created + // when the migration got scheduled, even if it was never used. + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList enable_local_upm; + enable_local_upm.InitWithFeatures( + {password_manager::features:: + kUnifiedPasswordManagerLocalPasswordsAndroidNoMigration, + password_manager::features:: + kUnifiedPasswordManagerLocalPasswordsAndroidWithMigration}, + {}); + pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores, + static_cast<int>(kOffAndMigrationPending)); + pref_service()->SetBoolean( + password_manager::prefs::kEmptyProfileStoreLoginDatabase, false); + SetPasswordSyncEnabledPref(true); + base::WriteFile(login_db_directory().Append( + password_manager::kLoginDataForAccountFileName), + ""); + ASSERT_TRUE(base::PathExists(login_db_directory().Append( + password_manager::kLoginDataForProfileFileName))); + ASSERT_TRUE(base::PathExists(login_db_directory().Append( + password_manager::kLoginDataForAccountFileName))); + + SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory()); + + // The browser should keep trying to migrate existing passwords to the *local* + // Android backend. The login database files should be untouched. + EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores), + static_cast<int>(kOffAndMigrationPending)); + EXPECT_TRUE(base::PathExists(login_db_directory().Append( + password_manager::kLoginDataForProfileFileName))); + EXPECT_TRUE(base::PathExists(login_db_directory().Append( + password_manager::kLoginDataForAccountFileName))); + histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated", + false, 1); + + // Advanced case: roll back too. + base::test::ScopedFeatureList disable_local_upm; + disable_local_upm.InitWithFeatures( + {}, {password_manager::features:: + kUnifiedPasswordManagerLocalPasswordsAndroidNoMigration, + password_manager::features:: + kUnifiedPasswordManagerLocalPasswordsAndroidWithMigration}); + + SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory()); + + // kOn syncing users that get rolled back will "undo" the login DB file move, + // i.e. they replace the "profile" loginDB with the "account" one. This isn't + // always perfect, see comment MaybeDeactivateSplitStoresAndLocalUpm(). The + // "account" DB might even be empty and overwrite a non-empty "profile" one. + // However: for kOffAndMigrationPending users, the "account" DB is *surely* + // empty (password sync is suppressed). So replacing the file can only be + // worse. Instead, the DB files should just be untouched. The account one is + // empty anyway, so no data is leftover. + EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores), + static_cast<int>(kOff)); + EXPECT_TRUE(base::PathExists(login_db_directory().Append( + password_manager::kLoginDataForProfileFileName))); + EXPECT_TRUE(base::PathExists(login_db_directory().Append( + password_manager::kLoginDataForAccountFileName))); + histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated", + false, 2); +} + +TEST_F(PasswordManagerAndroidUtilTest, SetUsesSplitStoresAndUPMForLocal_SyncingHealthy) { auto histogram_tester = std::make_unique<base::HistogramTester>(); base::test::ScopedFeatureList disable_local_upm;
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc index f41a0e0..a05091c 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -466,6 +466,27 @@ } #endif +bool ChromePasswordManagerClient::CanUseBiometricAuthForFilling( + device_reauth::DeviceAuthenticator* authenticator) { +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) + if (!GetLocalStatePrefs() || !GetPrefs() || !authenticator) { + return false; + } + return GetPasswordFeatureManager() + ->IsBiometricAuthenticationBeforeFillingEnabled(); +#elif BUILDFLAG(IS_ANDROID) + if (base::android::BuildInfo::GetInstance()->is_automotive()) { + CHECK(authenticator); + return true; + } + return authenticator && authenticator->CanAuthenticateWithBiometrics() && + base::FeatureList::IsEnabled( + password_manager::features::kBiometricTouchToFill); +#else + return false; +#endif +} + std::unique_ptr<device_reauth::DeviceAuthenticator> ChromePasswordManagerClient::GetDeviceAuthenticator() { #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h index 3706b0c..b07ee27 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.h +++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -149,6 +149,8 @@ bool is_webauthn_form) override; #endif + bool CanUseBiometricAuthForFilling( + device_reauth::DeviceAuthenticator* authenticator) override; // Returns a pointer to the DeviceAuthenticator which is created on demand. // This is currently only implemented for Android, Mac and Windows. On all // other platforms this will always be null.
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc index c7504e0c..fdb9d574 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -31,6 +31,8 @@ #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/autofill/chrome_autofill_client.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "chrome/test/base/scoped_testing_local_state.h" +#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "components/autofill/content/browser/content_autofill_client.h" #include "components/autofill/content/browser/content_autofill_driver.h" @@ -45,6 +47,7 @@ #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/signatures.h" #include "components/autofill/core/common/unique_ids.h" +#include "components/device_reauth/mock_device_authenticator.h" #include "components/password_manager/content/browser/content_password_manager_driver.h" #include "components/password_manager/content/browser/password_manager_log_router_factory.h" #include "components/password_manager/core/browser/credential_cache.h" @@ -55,10 +58,12 @@ #include "components/password_manager/core/browser/password_manager_test_utils.h" #include "components/password_manager/core/browser/password_store/mock_password_store_interface.h" #include "components/password_manager/core/browser/password_store/password_store_consumer.h" +#include "components/password_manager/core/common/password_manager_pref_names.h" #include "components/safe_browsing/buildflags.h" #include "components/safe_browsing/core/common/features.h" #include "components/sessions/content/content_record_password_state.h" #include "components/sync/test/test_sync_service.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" @@ -331,7 +336,8 @@ public: ChromePasswordManagerClientTest() : ChromeRenderViewHostTestHarness( - base::test::TaskEnvironment::TimeSource::MOCK_TIME) { + base::test::TaskEnvironment::TimeSource::MOCK_TIME), + local_state_(TestingBrowserProcess::GetGlobal()) { scoped_feature_list_.InitAndEnableFeature(safe_browsing::kDelayedWarnings); } ~ChromePasswordManagerClientTest() override = default; @@ -377,6 +383,7 @@ bool WasLoggingActivationMessageSent(bool* activation_flag); FakePasswordAutofillAgent fake_agent_; + ScopedTestingLocalState local_state_; bool metrics_enabled_ = false; @@ -747,6 +754,117 @@ GURL("https://passwords.google.com/path?query=1"))); } +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) +// Test that authentication is not possible if the `authenticator` is `nullptr`. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthNoAuthenticator) { + EXPECT_FALSE( + GetClient()->CanUseBiometricAuthForFilling(/*authenticator=*/nullptr)); +} + +// Test that authentication is not possible if the device doesn't have +// necessary hardware for biometric authentication. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthNoBiometrics) { + device_reauth::MockDeviceAuthenticator authenticator; + // Both prefs are registered by the `PasswordManager`. + local_state_.Get()->SetBoolean( + password_manager::prefs::kHadBiometricsAvailable, false); + profile()->GetTestingPrefService()->SetBoolean( + password_manager::prefs::kBiometricAuthenticationBeforeFilling, true); + EXPECT_FALSE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} + +// Test that authentication is not possible if the user didn't configure the +// corresponding setting. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthSettingDisabled) { + device_reauth::MockDeviceAuthenticator authenticator; + // Both prefs are registered by the `PasswordManager`. + local_state_.Get()->SetBoolean( + password_manager::prefs::kHadBiometricsAvailable, true); + profile()->GetTestingPrefService()->SetBoolean( + password_manager::prefs::kBiometricAuthenticationBeforeFilling, false); + EXPECT_FALSE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} + +// Test that authentication is possible if both the biometric authentication +// hardware is available and the user configured the corresponding setting. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthSettingEnabled) { + device_reauth::MockDeviceAuthenticator authenticator; + // Both prefs are registered by the `PasswordManager`. + local_state_.Get()->SetBoolean( + password_manager::prefs::kHadBiometricsAvailable, true); + profile()->GetTestingPrefService()->SetBoolean( + password_manager::prefs::kBiometricAuthenticationBeforeFilling, true); + EXPECT_TRUE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} + +#elif BUILDFLAG(IS_ANDROID) +// Test that authentication is not possible if the `authenticator` is `nullptr`. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthAndroid) { + if (base::android::BuildInfo::GetInstance()->is_automotive()) { + // Authentication is always available for automotive and the `authenticator` + // is always available. + device_reauth::MockDeviceAuthenticator authenticator; + EXPECT_TRUE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); + } else { + EXPECT_FALSE( + GetClient()->CanUseBiometricAuthForFilling(/*authenticator=*/nullptr)); + } +} + +// Test that authentication is not possible if the `kBiometricTouchToFill` +// feature is not enabled. +TEST_F(ChromePasswordManagerClientTest, + CanUseBiometricAuthAndroidFeatureIsDisabled) { + // Authentication is always available for automotive. + if (base::android::BuildInfo::GetInstance()->is_automotive()) { + GTEST_SKIP(); + } + device_reauth::MockDeviceAuthenticator authenticator; + ON_CALL(authenticator, CanAuthenticateWithBiometrics) + .WillByDefault(Return(true)); + EXPECT_FALSE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} + +// Test that authentication is not possible if the +// `CanAuthenticateWithBiometrics` returns `false` when `kBiometricTouchToFill` +// is enabled. +TEST_F(ChromePasswordManagerClientTest, + CanUseBiometricAuthAndroidAuthDisabled) { + // Authentication is always available for automotive. + if (base::android::BuildInfo::GetInstance()->is_automotive()) { + GTEST_SKIP(); + } + base::test::ScopedFeatureList enabled_features( + password_manager::features::kBiometricTouchToFill); + device_reauth::MockDeviceAuthenticator authenticator; + ON_CALL(authenticator, CanAuthenticateWithBiometrics) + .WillByDefault(Return(false)); + EXPECT_FALSE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} + +// Test that authentication is possible if the `CanAuthenticateWithBiometrics` +// returns `true` when `kBiometricTouchToFill` is enabled. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthAndroidAuthEnabled) { + // Authentication is always available for automotive. + if (base::android::BuildInfo::GetInstance()->is_automotive()) { + GTEST_SKIP(); + } + base::test::ScopedFeatureList enabled_features( + password_manager::features::kBiometricTouchToFill); + device_reauth::MockDeviceAuthenticator authenticator; + ON_CALL(authenticator, CanAuthenticateWithBiometrics) + .WillByDefault(Return(true)); + EXPECT_TRUE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} + +#else +// Test that authentication is not possible on other platforms. +TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuth) { + device_reauth::MockDeviceAuthenticator authenticator; + EXPECT_FALSE(GetClient()->CanUseBiometricAuthForFilling(&authenticator)); +} +#endif // BUILDFLAG(IS_ANDROID) + namespace { struct SchemeTestCase {
diff --git a/chrome/browser/preloading/preview/COMMON_METADATA b/chrome/browser/preloading/preview/COMMON_METADATA new file mode 100644 index 0000000..d9e17ee --- /dev/null +++ b/chrome/browser/preloading/preview/COMMON_METADATA
@@ -0,0 +1 @@ +team_email: "loading-dev@chromium.org"
diff --git a/chrome/browser/preloading/preview/DIR_METADATA b/chrome/browser/preloading/preview/DIR_METADATA new file mode 100644 index 0000000..ae58a56 --- /dev/null +++ b/chrome/browser/preloading/preview/DIR_METADATA
@@ -0,0 +1 @@ +mixins: "//chrome/browser/preloading/preview/COMMON_METADATA"
diff --git a/chrome/browser/preloading/preview/preview_tab.cc b/chrome/browser/preloading/preview/preview_tab.cc index 7c498c72..9e73154 100644 --- a/chrome/browser/preloading/preview/preview_tab.cc +++ b/chrome/browser/preloading/preview/preview_tab.cc
@@ -68,8 +68,10 @@ return; } - if (!is_event_for_preview_window && - event->type() == ui::ET_MOUSE_RELEASED) { + // This doesn't triggered for long press trigger. + // + // TODO(b:320386573): Cancel preview when focus lost. + if (!is_event_for_preview_window && event->type() == ui::ET_MOUSE_PRESSED) { event->SetHandled(); preview_manager_->Cancel(content::PreviewCancelReason::Build( content::PreviewFinalStatus::kCancelledByWindowClose));
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 2682273..aae767a 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1429,7 +1429,7 @@ int enum_id = FindUMAEnumValueForCommand(id, UmaEnumIdLookupType::GeneralEnumId); if (enum_id == -1) { - NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id; + NOTREACHED() << "Update GetIdcToUmaMap. Unhandled IDC: " << id; return; } @@ -1542,7 +1542,7 @@ } else { // Just warning here. It's harder to maintain list of all possibly // visible items than executable items. - DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id; + DLOG(ERROR) << "Update GetIdcToUmaMap. Unhandled IDC: " << id; } }
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn index acf417d..e4ca582 100644 --- a/chrome/browser/resources/chromeos/BUILD.gn +++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -34,8 +34,7 @@ "kerberos:resources", "launcher_internals:resources", "lock_screen_reauth:resources", - "login:conditional_resources", - "login:unconditional_resources", + "login:resources", "mako:resources", "manage_mirrorsync:resources", "multidevice_internals:resources",
diff --git a/chrome/browser/resources/chromeos/accessibility/common/cursors/recovery_strategy_test.js b/chrome/browser/resources/chromeos/accessibility/common/cursors/recovery_strategy_test.js index 66adb0e..5a29869c 100644 --- a/chrome/browser/resources/chromeos/accessibility/common/cursors/recovery_strategy_test.js +++ b/chrome/browser/resources/chromeos/accessibility/common/cursors/recovery_strategy_test.js
@@ -24,6 +24,8 @@ }; +// TODO(https://issuetracker.google.com/issues/263127143) Recovery can likely be +// simplified now that most ids are stable. AX_TEST_F( 'AccessibilityExtensionRecoveryStrategyTest', 'ReparentedRecovery', async function() { @@ -54,30 +56,21 @@ assertFalse( bAncestryRecovery.requiresRecovery(), 'bAncestryRecovery.requiresRecovery'); - assertTrue( + assertFalse( pAncestryRecovery.requiresRecovery(), 'pAncestryRecovery.requiresRecovery()'); - assertTrue( + assertFalse( sAncestryRecovery.requiresRecovery(), 'sAncestryRecovery.requiresRecovery()'); assertFalse( bTreePathRecovery.requiresRecovery(), 'bTreePathRecovery.requiresRecovery()'); - assertTrue( + assertFalse( pTreePathRecovery.requiresRecovery(), 'pTreePathRecovery.requiresRecovery()'); - assertTrue( + assertFalse( sTreePathRecovery.requiresRecovery(), 'sTreePathRecovery.requiresRecovery()'); - - assertEquals(RoleType.BUTTON, bAncestryRecovery.node.role); - assertEquals(root, pAncestryRecovery.node); - assertEquals(root, sAncestryRecovery.node); - - assertEquals(b, bTreePathRecovery.node); - assertEquals(b, pTreePathRecovery.node); - assertEquals(b, sTreePathRecovery.node); - assertFalse( bAncestryRecovery.requiresRecovery(), 'bAncestryRecovery.requiresRecovery()');
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn index 51f7a12..5ee4e74 100644 --- a/chrome/browser/resources/chromeos/login/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -2,115 +2,14 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//chrome/common/features.gni") -import("//tools/grit/grit_rule.gni") -import("//tools/grit/preprocess_if_expr.gni") -import("//tools/typescript/ts_library.gni") -import("//ui/webui/resources/tools/generate_grd.gni") -import("./login.gni") +import("//ui/webui/resources/tools/build_webui.gni") assert(is_chromeos, "OOBE UI is only available on ChromeOS builds") -existing_unconditional_structure_files_manifest = - "existing_unconditional_structure_files_manifest.json" +build_webui("build") { + grd_prefix = "oobe" -# Name is aligned with the constant used in -# tools/typescript/validate_tsconfig.py to allow intermediate JS files. -oobe_preprocessed_folder = "preprocessed" - -############################## -#### CONDITONAL RESOURCES #### -############################## - -# Set of OOBE resources that need to be served conditionally during runtime -# depending on a feature/flag/condition/etc. Based on a manually created GRD -# for convenience. -# ----------------------------------------------------------------------------- -grit("conditional_resources") { - defines = chrome_grit_defines - - # Needed since some of the files are generated during build time. - enable_input_discovery_for_gn_analyze = false - - source = "$target_gen_dir/oobe_conditional_resources.grd" - outputs = [ - "grit/oobe_conditional_resources.h", - "grit/oobe_conditional_resources_map.h", - "grit/oobe_conditional_resources_map.cc", - "oobe_conditional_resources.pak", - ] - - deps = [ ":build_oobe_conditional_grd" ] - output_dir = "$root_gen_dir/chrome" -} - -generate_grd("build_oobe_conditional_grd") { - grd_prefix = "oobe_conditional" - out_grd = "$target_gen_dir/${grd_prefix}_resources.grd" - deps = [ - ":build_ts", - ":preprocess_conditional", - ] - input_files_base_dir = rebase_path("$target_gen_dir/tsc", root_build_dir) - input_files = conditional_js_files -} - -# Files that are served conditionally -preprocess_if_expr("preprocess_conditional") { - deps = [ - "components/oobe_vars:css_wrapper_files", - "debug:web_components", - "test_api:copy_ts", - ] - in_folder = target_gen_dir - out_folder = "$target_gen_dir/$oobe_preprocessed_folder" - in_files = conditional_files -} - -################################ -#### UNCONDITONAL RESOURCES #### -################################ - -# OOBE's default set of resources based on an automatically generated GRD file. -# These resources are made available to the WebUI through the generated C++ resource -# map. Since we use the AddResourcePaths method to add the full map instead of adding -# each resource path individually in oobe_ui.cc, this section is not suitable for -# serving files in a conditional manner. Thus the naming - unconditional_resources. -# ----------------------------------------------------------------------------- -grit("unconditional_resources") { - defines = chrome_grit_defines - - # This is necessary since the GRD is generated during build time. - enable_input_discovery_for_gn_analyze = false - - source = "$target_gen_dir/oobe_unconditional_resources.grd" - deps = [ ":build_oobe_grd" ] - outputs = [ - "grit/oobe_unconditional_resources.h", - "grit/oobe_unconditional_resources_map.h", - "grit/oobe_unconditional_resources_map.cc", - "oobe_unconditional_resources.pak", - ] - output_dir = "$root_gen_dir/chrome" -} - -# Generates OOBE's default GRD file that contains only resources that are -# served unconditionally. -# ----------------------------------------------------------------------------- -generate_grd("build_oobe_grd") { - grd_prefix = "oobe_unconditional" - out_grd = "$target_gen_dir/${grd_prefix}_resources.grd" - deps = [ - ":build_ts", - ":preprocess_unconditional_existing_structure", - "../supervision:build_oobe_grdp", - ] - manifest_files = [ - "$target_gen_dir/build_ts_manifest.json", - "$target_gen_dir/$existing_unconditional_structure_files_manifest", - ] - input_files_base_dir = rebase_path(".", "//") - input_files = [ + static_files = [ # Lottie animation resources "animations/all_set.json", "animations/checking_for_update.json", @@ -143,61 +42,186 @@ "images/2x/thumbnail-theme-dark-2x.png", "images/1x/thumbnail-theme-auto-1x.png", "images/2x/thumbnail-theme-auto-2x.png", - ] - grdp_files = [ "$root_gen_dir/chrome/browser/resources/chromeos/supervision/supervision_oobe_resources.grdp" ] -} - -# Preprocess existing and autogenerated files by copying them to an -# intermediate location and generating a manifest file to be used when -# generating OOBE's GRD file. -# ----------------------------------------------------------------------------- - -# Preprocess existing (not autogenerated) files. -preprocess_if_expr("preprocess_unconditional_existing") { - out_folder = "$target_gen_dir/$oobe_preprocessed_folder" - in_files = unconditional_existing_files -} - -# These files shouldn't be wrapped and compiled thus they remain in a -# separate target. -preprocess_if_expr("preprocess_unconditional_existing_structure") { - out_folder = "$target_gen_dir/$oobe_preprocessed_folder" - out_manifest = - "$target_gen_dir/$existing_unconditional_structure_files_manifest" - in_files = [ + # Structure files "oobe.css", "oobe.html", "oobe_popup_overlay.css", "oobe_screen.css", ] -} -preprocess_if_expr("preprocess_unconditional_autogenerated") { - defines = chrome_grit_defines - deps = [ ":web_components" ] - in_folder = target_gen_dir - out_folder = "$target_gen_dir/$oobe_preprocessed_folder" - in_files = unconditional_autogenerated_files -} + web_component_files = [ + # Oobe screens + "screens/oobe/auto_enrollment_check.ts", + "screens/oobe/consumer_update.ts", + "screens/oobe/demo_preferences.ts", + "screens/oobe/demo_setup.ts", + "screens/oobe/enable_debugging.ts", + "screens/oobe/enterprise_enrollment.ts", + "screens/oobe/hid_detection.ts", + "screens/oobe/oobe_network.ts", + "screens/oobe/packaged_license.ts", + "screens/oobe/update.ts", + "screens/oobe/welcome.ts", + "screens/oobe/welcome_dialog.ts", -group("web_components") { - public_deps = [ - "components:web_components", - "screens/common:web_components", - "screens/login:web_components", - "screens/oobe:web_components", - "screens/osauth:web_components", + # Common screens + "screens/common/adb_sideloading.ts", + "screens/common/add_child.ts", + "screens/common/app_downloading.ts", + "screens/common/app_launch_splash.ts", + "screens/common/assistant_optin.ts", + "screens/common/autolaunch.ts", + "screens/common/choobe.ts", + "screens/common/consolidated_consent.ts", + "screens/common/device_disabled.ts", + "screens/common/display_size.ts", + "screens/common/drive_pinning.ts", + "screens/common/enable_kiosk.ts", + "screens/common/error_message.ts", + "screens/common/family_link_notice.ts", + "screens/common/gaia_info.ts", + "screens/common/gaia_signin.ts", + "screens/common/gesture_navigation.ts", + "screens/common/guest_tos.ts", + "screens/common/hw_data_collection.ts", + "screens/common/install_attributes_error.ts", + "screens/common/local_state_error.ts", + "screens/common/managed_terms_of_service.ts", + "screens/common/marketing_opt_in.ts", + "screens/common/multidevice_setup.ts", + "screens/common/online_authentication_screen.ts", + "screens/common/oobe_reset.ts", + "screens/common/os_install.ts", + "screens/common/os_trial.ts", + "screens/common/parental_handoff.ts", + "screens/common/quick_start.ts", + "screens/common/remote_activity_notification.ts", + + # Template used by the `tools/oobe/generate_screen_template.py` script. + "screens/common/placeholder.ts", + "screens/common/recommend_apps.ts", + "screens/common/saml_confirm_password.ts", + "screens/common/signin_fatal_error.ts", + "screens/common/smart_privacy_protection.ts", + "screens/common/sync_consent.ts", + "screens/common/theme_selection.ts", + "screens/common/touchpad_scroll.ts", + "screens/common/tpm_error.ts", + "screens/common/user_allowlist_check_screen.ts", + "screens/common/user_creation.ts", + "screens/common/wrong_hwid.ts", + + # Login screens + "screens/login/arc_vm_data_migration.ts", + "screens/login/checking_downloading_update.ts", + "screens/login/encryption_migration.ts", + "screens/login/lacros_data_backward_migration.ts", + "screens/login/lacros_data_migration.ts", + "screens/login/management_transition.ts", + "screens/login/offline_login.ts", + "screens/login/update_required_card.ts", + + # Osauth screens + "screens/osauth/apply_online_password.ts", + "screens/osauth/cryptohome_recovery.ts", + "screens/osauth/cryptohome_recovery_setup.ts", + "screens/osauth/factor_setup_success.ts", + "screens/osauth/fingerprint_setup.ts", + "screens/osauth/gaia_password_changed.ts", + "screens/osauth/local_password_setup.ts", + "screens/osauth/local_data_loss_warning.ts", + "screens/osauth/enter_old_password.ts", + "screens/osauth/osauth_error.ts", + "screens/osauth/password_selection.ts", + "screens/osauth/pin_setup.ts", + + # Components + "components/dialogs/oobe_adaptive_dialog.ts", + "components/dialogs/oobe_content_dialog.ts", + "components/dialogs/oobe_loading_dialog.ts", + "components/dialogs/oobe_modal_dialog.ts", + "components/buttons/oobe_back_button.ts", + "components/buttons/oobe_icon_button.ts", + "components/buttons/oobe_next_button.ts", + "components/buttons/oobe_text_button.ts", + "components/api_keys_notice.ts", + "components/gaia_button.ts", + "components/gaia_dialog.ts", + "components/hd_iron_icon.ts", + "components/network_select_login.ts", + "components/notification_card.ts", + "components/oobe_a11y_option.ts", + "components/oobe_apps_list.ts", + "components/oobe_carousel.ts", + "components/oobe_cr_lottie.ts", + "components/oobe_display_size_selector.ts", + "components/oobe_i18n_dropdown.ts", + "components/oobe_screens_list.ts", + "components/oobe_slide.ts", + "components/progress_list_item.ts", + "components/security_token_pin.ts", + "components/throbber_notice.ts", + "components/quick_start_pin.ts", + "components/quick_start_entry_point.ts", + + # Conditional + "debug/quick_start_debugger.ts", ] -} -ts_library("build_ts") { - tsconfig_base = "//tools/typescript/tsconfig_base_polymer.json" - root_dir = "$target_gen_dir/$oobe_preprocessed_folder" - out_dir = "$target_gen_dir/tsc" - in_files = unconditional_autogenerated_files + unconditional_existing_files + - conditional_files - definitions = [ + non_web_component_files = [ + "components/behaviors/oobe_dialog_host_behavior.js", + "components/behaviors/oobe_focus_behavior.js", + "components/behaviors/oobe_i18n_behavior.js", + "components/behaviors/oobe_scrollable_behavior.js", + "components/behaviors/login_screen_behavior.js", + "components/behaviors/multi_step_behavior.js", + "components/buttons/oobe_base_button.ts", + "components/display_manager_types.ts", + "components/keyboard_utils.ts", + "components/keyboard_utils_oobe.ts", + "components/long_touch_detector.ts", + "components/oobe_select.ts", + "components/oobe_types.ts", + "components/qr_code_canvas.ts", + "components/web_view_helper.ts", + "components/web_view_loader.ts", + "cr_ui.ts", + "debug/debug.js", + "debug/no_debug.js", + "display_manager.ts", + "i18n_setup.ts", + "install_oobe_error_store.ts", + "lazy_load_screens.ts", + "login_ui_tools.ts", + "multi_tap_detector.ts", + "oobe.ts", + "oobe_trace.ts", + "oobe_trace_start.ts", + "priority_screens_common_flow.ts", + "priority_screens_oobe_flow.ts", + "screens.ts", + "test_api/no_test_api.ts", + "test_api/test_api.ts", + ] + + icons_html_files = [ + "components/oobe_icons.html", + "components/oobe_illo_icons.html", + "components/oobe_network_icons.html", + ] + + css_files = [ + "components/common_styles/cr_card_radio_group_styles.css", + "components/common_styles/oobe_common_styles.css", + "components/common_styles/oobe_dialog_host_styles.css", + "components/common_styles/oobe_flex_layout_styles.css", + "components/oobe_vars/oobe_shared_vars.css", + "components/oobe_vars/oobe_custom_vars.css", + "components/oobe_vars/oobe_custom_vars_remora.css", + ] + + ts_definitions = [ "//chrome/browser/resources/chromeos/accessibility/definitions/tts.d.ts", "//chrome/browser/resources/gaia_auth_host/saml_password_attributes.d.ts", "//chrome/browser/resources/gaia_auth_host/authenticator.d.ts", @@ -211,7 +235,8 @@ "//tools/typescript/definitions/web_request.d.ts", "//tools/typescript/definitions/quick_unlock_private.d.ts", ] - deps = [ + + ts_deps = [ "//ash/webui/common/resources:build_ts", "//ash/webui/common/resources/cr_elements:build_ts", "//third_party/cros-components:cros_components_ts", @@ -220,20 +245,12 @@ "//ui/webui/resources/js:build_ts", "//ui/webui/resources/mojo:build_ts", ] - extra_deps = [ - ":preprocess_conditional", - ":preprocess_unconditional_autogenerated", - ":preprocess_unconditional_existing", - ":web_components", - "components:html_wrapper_files", - "components/oobe_vars:css_wrapper_files", - ] - path_mappings = [ "//oobe/gaia_auth_host/*|" + - rebase_path("//chrome/browser/resources/gaia_auth_host/*", - target_gen_dir) ] + ts_path_mappings = + [ "//oobe/gaia_auth_host/*|" + + rebase_path("//chrome/browser/resources/gaia_auth_host/*", + target_gen_dir) ] - # These files can be added conditionally in the runtime, so they shouldn't - # be added to the same grd file as the rest of the sources. - manifest_excludes = conditional_js_files + extra_grdp_deps = [ "../supervision:build_oobe_grdp" ] + extra_grdp_files = [ "$root_gen_dir/chrome/browser/resources/chromeos/supervision/supervision_oobe_resources.grdp" ] }
diff --git a/chrome/browser/resources/chromeos/login/components/BUILD.gn b/chrome/browser/resources/chromeos/login/components/BUILD.gn deleted file mode 100644 index 6c94642f9..0000000 --- a/chrome/browser/resources/chromeos/login/components/BUILD.gn +++ /dev/null
@@ -1,27 +0,0 @@ -# Copyright 2020 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../login.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":html_wrapper_files", - "./behaviors:copy_js", - "./buttons:web_components", - "./common_styles:css_wrapper_files", - "./dialogs:web_components", - "./oobe_vars:css_wrapper_files", - ] -} - -copy("copy_ts") { - sources = rebase_path(components_ts_files, "./components", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -} - -html_to_wrapper("html_wrapper_files") { - in_files = rebase_path(components_html_files, "./components", ".") -}
diff --git a/chrome/browser/resources/chromeos/login/components/behaviors/BUILD.gn b/chrome/browser/resources/chromeos/login/components/behaviors/BUILD.gn deleted file mode 100644 index 30e7a924..0000000 --- a/chrome/browser/resources/chromeos/login/components/behaviors/BUILD.gn +++ /dev/null
@@ -1,15 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -copy("copy_js") { - sources = [ - "login_screen_behavior.js", - "multi_step_behavior.js", - "oobe_dialog_host_behavior.js", - "oobe_focus_behavior.js", - "oobe_i18n_behavior.js", - "oobe_scrollable_behavior.js", - ] - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -}
diff --git a/chrome/browser/resources/chromeos/login/components/buttons/BUILD.gn b/chrome/browser/resources/chromeos/login/components/buttons/BUILD.gn deleted file mode 100644 index 4f3ed06f..0000000 --- a/chrome/browser/resources/chromeos/login/components/buttons/BUILD.gn +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../../login.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = rebase_path(buttons_html_files, "./components/buttons", ".") -} - -copy("copy_ts") { - sources = rebase_path(buttons_ts_files, "./components/buttons", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -}
diff --git a/chrome/browser/resources/chromeos/login/components/common_styles/BUILD.gn b/chrome/browser/resources/chromeos/login/components/common_styles/BUILD.gn deleted file mode 100644 index fc8b5f7..0000000 --- a/chrome/browser/resources/chromeos/login/components/common_styles/BUILD.gn +++ /dev/null
@@ -1,15 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/css_to_wrapper.gni") - -css_to_wrapper("css_wrapper_files") { - in_files = [ - "cr_card_radio_group_styles.css", - "oobe_common_styles.css", - "oobe_dialog_host_styles.css", - "oobe_flex_layout_styles.css", - ] - use_js = true -}
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/BUILD.gn b/chrome/browser/resources/chromeos/login/components/dialogs/BUILD.gn deleted file mode 100644 index 086fda1..0000000 --- a/chrome/browser/resources/chromeos/login/components/dialogs/BUILD.gn +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../../login.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = rebase_path(dialogs_html_files, "./components/dialogs", ".") -} - -copy("copy_ts") { - sources = rebase_path(dialogs_ts_files, "./components/dialogs", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -}
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_vars/BUILD.gn b/chrome/browser/resources/chromeos/login/components/oobe_vars/BUILD.gn deleted file mode 100644 index 09efb96..0000000 --- a/chrome/browser/resources/chromeos/login/components/oobe_vars/BUILD.gn +++ /dev/null
@@ -1,13 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/css_to_wrapper.gni") - -css_to_wrapper("css_wrapper_files") { - in_files = [ - "oobe_custom_vars.css", - "oobe_custom_vars_remora.css", - "oobe_shared_vars.css", - ] -}
diff --git a/chrome/browser/resources/chromeos/login/debug/BUILD.gn b/chrome/browser/resources/chromeos/login/debug/BUILD.gn deleted file mode 100644 index 7b2d7d7..0000000 --- a/chrome/browser/resources/chromeos/login/debug/BUILD.gn +++ /dev/null
@@ -1,28 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -# Copy existing files to output directory. -copy("copy_ts") { - sources = [ - "debug.js", - "no_debug.js", - "quick_start_debugger.ts", - ] - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = [ - "quick_start_debugger.html", - ] -}
diff --git a/chrome/browser/resources/chromeos/login/login.gni b/chrome/browser/resources/chromeos/login/login.gni deleted file mode 100644 index 2a946e5..0000000 --- a/chrome/browser/resources/chromeos/login/login.gni +++ /dev/null
@@ -1,282 +0,0 @@ -# Copyright 2023 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -conditional_files = [ - "debug/no_debug.js", - "test_api/no_test_api.ts", - "components/oobe_vars/oobe_custom_vars.css.ts", - "components/oobe_vars/oobe_custom_vars_remora.css.ts", - "debug/debug.js", - "debug/quick_start_debugger.ts", - "debug/quick_start_debugger.html.ts", - "test_api/test_api.ts", -] - -conditional_js_files = [] -foreach(f, conditional_files) { - # TODO(b/322301624): Refactor once all conditional files are migrated. - extension = get_path_info(f, "extension") - if (extension == "ts") { - conditional_js_files += [ string_replace(f, ".ts", ".js") ] - } else { - conditional_js_files += [ f ] - } -} - -unconditional_existing_files = [ - "cr_ui.ts", - "display_manager.ts", - "i18n_setup.ts", - "install_oobe_error_store.ts", - "lazy_load_screens.ts", - "login_ui_tools.ts", - "multi_tap_detector.ts", - "oobe.ts", - "oobe_trace.ts", - "oobe_trace_start.ts", - "priority_screens_common_flow.ts", - "priority_screens_oobe_flow.ts", - "screens.ts", -] - -unconditional_autogenerated_files = [ - "components/behaviors/oobe_dialog_host_behavior.js", - "components/behaviors/oobe_focus_behavior.js", - "components/behaviors/oobe_i18n_behavior.js", - "components/behaviors/oobe_scrollable_behavior.js", - "components/behaviors/login_screen_behavior.js", - "components/behaviors/multi_step_behavior.js", - "components/common_styles/cr_card_radio_group_styles.css.js", - "components/common_styles/oobe_common_styles.css.js", - "components/common_styles/oobe_dialog_host_styles.css.js", - "components/common_styles/oobe_flex_layout_styles.css.js", - "components/oobe_vars/oobe_shared_vars.css.ts", -] - -oobe_screens_ts_files = [ - "screens/oobe/auto_enrollment_check.ts", - "screens/oobe/consumer_update.ts", - "screens/oobe/demo_preferences.ts", - "screens/oobe/demo_setup.ts", - "screens/oobe/enable_debugging.ts", - "screens/oobe/enterprise_enrollment.ts", - "screens/oobe/hid_detection.ts", - "screens/oobe/oobe_network.ts", - "screens/oobe/packaged_license.ts", - "screens/oobe/update.ts", - "screens/oobe/welcome.ts", - "screens/oobe/welcome_dialog.ts", -] - -oobe_screens_html_files = [ - "components/oobe_icons.html", - "components/oobe_illo_icons.html", - "components/oobe_network_icons.html", -] - -foreach(f, oobe_screens_ts_files) { - oobe_screens_html_files += [ string_replace(f, ".ts", ".html") ] -} - -oobe_screens_html_wrapped_files = [] -foreach(f, oobe_screens_html_files) { - oobe_screens_html_wrapped_files += [ string_replace(f, ".html", ".html.ts") ] -} - -common_screens_ts_files = [ - "screens/common/adb_sideloading.ts", - "screens/common/add_child.ts", - "screens/common/app_downloading.ts", - "screens/common/app_launch_splash.ts", - "screens/common/assistant_optin.ts", - "screens/common/autolaunch.ts", - "screens/common/choobe.ts", - "screens/common/consolidated_consent.ts", - "screens/common/device_disabled.ts", - "screens/common/display_size.ts", - "screens/common/drive_pinning.ts", - "screens/common/enable_kiosk.ts", - "screens/common/error_message.ts", - "screens/common/family_link_notice.ts", - "screens/common/gaia_info.ts", - "screens/common/gaia_signin.ts", - "screens/common/gesture_navigation.ts", - "screens/common/guest_tos.ts", - "screens/common/hw_data_collection.ts", - "screens/common/install_attributes_error.ts", - "screens/common/local_state_error.ts", - "screens/common/managed_terms_of_service.ts", - "screens/common/marketing_opt_in.ts", - "screens/common/multidevice_setup.ts", - "screens/common/online_authentication_screen.ts", - "screens/common/oobe_reset.ts", - "screens/common/os_install.ts", - "screens/common/os_trial.ts", - "screens/common/parental_handoff.ts", - "screens/common/quick_start.ts", - "screens/common/remote_activity_notification.ts", - - # Template used by the `tools/oobe/generate_screen_template.py` script. - "screens/common/placeholder.ts", - "screens/common/recommend_apps.ts", - "screens/common/saml_confirm_password.ts", - "screens/common/signin_fatal_error.ts", - "screens/common/smart_privacy_protection.ts", - "screens/common/sync_consent.ts", - "screens/common/theme_selection.ts", - "screens/common/touchpad_scroll.ts", - "screens/common/tpm_error.ts", - "screens/common/user_allowlist_check_screen.ts", - "screens/common/user_creation.ts", - "screens/common/wrong_hwid.ts", -] - -common_screens_html_files = [] -foreach(f, common_screens_ts_files) { - common_screens_html_files += [ string_replace(f, ".ts", ".html") ] -} - -common_screens_html_wrapped_files = [] -foreach(f, common_screens_html_files) { - common_screens_html_wrapped_files += - [ string_replace(f, ".html", ".html.ts") ] -} - -login_screens_ts_files = [ - "screens/login/arc_vm_data_migration.ts", - "screens/login/checking_downloading_update.ts", - "screens/login/encryption_migration.ts", - "screens/login/lacros_data_backward_migration.ts", - "screens/login/lacros_data_migration.ts", - "screens/login/management_transition.ts", - "screens/login/offline_login.ts", - "screens/login/update_required_card.ts", -] - -login_screens_html_files = [] -foreach(f, login_screens_ts_files) { - login_screens_html_files += [ string_replace(f, ".ts", ".html") ] -} - -login_screens_html_wrapped_files = [] -foreach(f, login_screens_html_files) { - login_screens_html_wrapped_files += [ string_replace(f, ".html", ".html.ts") ] -} - -osauth_screens_ts_files = [ - "screens/osauth/apply_online_password.ts", - "screens/osauth/cryptohome_recovery.ts", - "screens/osauth/cryptohome_recovery_setup.ts", - "screens/osauth/factor_setup_success.ts", - "screens/osauth/fingerprint_setup.ts", - "screens/osauth/gaia_password_changed.ts", - "screens/osauth/local_password_setup.ts", - "screens/osauth/local_data_loss_warning.ts", - "screens/osauth/enter_old_password.ts", - "screens/osauth/osauth_error.ts", - "screens/osauth/password_selection.ts", - "screens/osauth/pin_setup.ts", -] - -osauth_screens_html_files = [] -foreach(f, osauth_screens_ts_files) { - osauth_screens_html_files += [ string_replace(f, ".ts", ".html") ] -} - -osauth_screens_html_wrapped_files = [] -foreach(f, osauth_screens_html_files) { - osauth_screens_html_wrapped_files += - [ string_replace(f, ".html", ".html.ts") ] -} - -dialogs_ts_files = [ - "components/dialogs/oobe_adaptive_dialog.ts", - "components/dialogs/oobe_content_dialog.ts", - "components/dialogs/oobe_loading_dialog.ts", - "components/dialogs/oobe_modal_dialog.ts", -] - -dialogs_html_files = [] -foreach(f, dialogs_ts_files) { - dialogs_html_files += [ string_replace(f, ".ts", ".html") ] -} - -dialogs_html_wrapped_files = [] -foreach(f, dialogs_html_files) { - dialogs_html_wrapped_files += [ string_replace(f, ".html", ".html.ts") ] -} - -buttons_ts_files = [ - "components/buttons/oobe_back_button.ts", - "components/buttons/oobe_icon_button.ts", - "components/buttons/oobe_next_button.ts", - "components/buttons/oobe_text_button.ts", -] - -buttons_html_files = [] -foreach(f, buttons_ts_files) { - buttons_html_files += [ string_replace(f, ".ts", ".html") ] -} - -buttons_html_wrapped_files = [] -foreach(f, buttons_html_files) { - buttons_html_wrapped_files += [ string_replace(f, ".html", ".html.ts") ] -} - -buttons_ts_files += [ "components/buttons/oobe_base_button.ts" ] - -components_ts_files = [ - "components/api_keys_notice.ts", - "components/gaia_button.ts", - "components/gaia_dialog.ts", - "components/hd_iron_icon.ts", - "components/network_select_login.ts", - "components/notification_card.ts", - "components/oobe_a11y_option.ts", - "components/oobe_apps_list.ts", - "components/oobe_carousel.ts", - "components/oobe_cr_lottie.ts", - "components/oobe_display_size_selector.ts", - "components/oobe_i18n_dropdown.ts", - "components/oobe_screens_list.ts", - "components/oobe_slide.ts", - "components/progress_list_item.ts", - "components/security_token_pin.ts", - "components/throbber_notice.ts", - "components/quick_start_pin.ts", - "components/quick_start_entry_point.ts", -] - -components_html_files = [] -foreach(f, components_ts_files) { - components_html_files += [ string_replace(f, ".ts", ".html") ] -} - -components_html_wrapped_files = [] -foreach(f, components_html_files) { - components_html_wrapped_files += [ string_replace(f, ".html", ".html.ts") ] -} - -# The following components do not have corresponding html files, we are adding -# them after the list of html files has been populated -components_ts_files += [ - "components/display_manager_types.ts", - "components/keyboard_utils.ts", - "components/keyboard_utils_oobe.ts", - "components/long_touch_detector.ts", - "components/oobe_select.ts", - "components/oobe_types.ts", - "components/qr_code_canvas.ts", - "components/web_view_helper.ts", - "components/web_view_loader.ts", -] - -unconditional_autogenerated_files += - oobe_screens_ts_files + oobe_screens_html_wrapped_files + - common_screens_ts_files + common_screens_html_wrapped_files + - login_screens_ts_files + login_screens_html_wrapped_files + - osauth_screens_ts_files + osauth_screens_html_wrapped_files + - dialogs_ts_files + dialogs_html_wrapped_files + buttons_ts_files + - buttons_html_wrapped_files + components_ts_files + - components_html_wrapped_files
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn deleted file mode 100644 index dbef53a..0000000 --- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn +++ /dev/null
@@ -1,24 +0,0 @@ -# Copyright 2018 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../../login.gni") - -assert(is_chromeos, "OOBE UI is only available on ChromeOS builds") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -copy("copy_ts") { - sources = rebase_path(common_screens_ts_files, "./screens/common", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = rebase_path(common_screens_html_files, "./screens/common", ".") -}
diff --git a/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn deleted file mode 100644 index 551c8cf8..0000000 --- a/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../../login.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -copy("copy_ts") { - sources = rebase_path(login_screens_ts_files, "./screens/login", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = rebase_path(login_screens_html_files, "./screens/login", ".") -}
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn deleted file mode 100644 index b414df8..0000000 --- a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../../login.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -copy("copy_ts") { - sources = rebase_path(oobe_screens_ts_files, "./screens/oobe", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = rebase_path(oobe_screens_html_files, "./screens/oobe", ".") -}
diff --git a/chrome/browser/resources/chromeos/login/screens/osauth/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/osauth/BUILD.gn deleted file mode 100644 index 538551f8..0000000 --- a/chrome/browser/resources/chromeos/login/screens/osauth/BUILD.gn +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright 2023 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//tools/polymer/html_to_wrapper.gni") -import("../../login.gni") - -group("web_components") { - public_deps = [ - ":copy_ts", - ":generate_web_component_html_wrapper_files", - ] -} - -copy("copy_ts") { - sources = rebase_path(osauth_screens_ts_files, "./screens/osauth", ".") - outputs = [ "$target_gen_dir/{{source_file_part}}" ] -} - -html_to_wrapper("generate_web_component_html_wrapper_files") { - in_files = rebase_path(osauth_screens_html_files, "./screens/osauth", ".") -}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index a8d5a97..eb2d8f0 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1501,6 +1501,8 @@ "tabs/organization/trigger_policies.h", "tabs/pinned_tab_codec.cc", "tabs/pinned_tab_codec.h", + "tabs/pinned_tab_collection.cc", + "tabs/pinned_tab_collection.h", "tabs/pinned_tab_service.cc", "tabs/pinned_tab_service.h", "tabs/pinned_tab_service_factory.cc", @@ -1521,6 +1523,8 @@ "tabs/supports_handles.h", "tabs/tab_change_type.h", "tabs/tab_collection.h", + "tabs/tab_collection_storage.cc", + "tabs/tab_collection_storage.h", "tabs/tab_group.cc", "tabs/tab_group.h", "tabs/tab_group_controller.h",
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc index 2c8500f..9bba40f64 100644 --- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc +++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -50,6 +50,7 @@ #include "chrome/browser/ui/location_bar/location_bar.h" #include "chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.h" #include "chrome/browser/ui/omnibox/omnibox_tab_helper.h" +#include "components/bookmarks/browser/bookmark_model.h" #include "components/favicon/content/content_favicon_driver.h" #include "components/favicon/core/favicon_service.h" #include "components/omnibox/browser/autocomplete_controller_emitter.h" @@ -157,7 +158,7 @@ return profile_->GetPrefs(); } -bookmarks::BookmarkModel* ChromeOmniboxClient::GetBookmarkModel() { +bookmarks::CoreBookmarkModel* ChromeOmniboxClient::GetBookmarkModel() { return BookmarkModelFactory::GetForBrowserContext(profile_); }
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.h b/chrome/browser/ui/omnibox/chrome_omnibox_client.h index db1e94d..991712e 100644 --- a/chrome/browser/ui/omnibox/chrome_omnibox_client.h +++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
@@ -44,7 +44,7 @@ bool IsDefaultSearchProviderEnabled() const override; SessionID GetSessionID() const override; PrefService* GetPrefs() override; - bookmarks::BookmarkModel* GetBookmarkModel() override; + bookmarks::CoreBookmarkModel* GetBookmarkModel() override; AutocompleteControllerEmitter* GetAutocompleteControllerEmitter() override; TemplateURLService* GetTemplateURLService() override; const AutocompleteSchemeClassifier& GetSchemeClassifier() const override;
diff --git a/chrome/browser/ui/tabs/pinned_tab_collection.cc b/chrome/browser/ui/tabs/pinned_tab_collection.cc new file mode 100644 index 0000000..05c3cf0a --- /dev/null +++ b/chrome/browser/ui/tabs/pinned_tab_collection.cc
@@ -0,0 +1,80 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <optional> + +#include "chrome/browser/ui/tabs/pinned_tab_collection.h" +#include "chrome/browser/ui/tabs/tab_collection_storage.h" +#include "chrome/browser/ui/tabs/tab_model.h" + +namespace tabs { + +PinnedTabCollection::PinnedTabCollection() { + impl_ = std::make_unique<TabCollectionStorage>(*this); +} + +PinnedTabCollection::~PinnedTabCollection() = default; + +void PinnedTabCollection::AddTab(std::unique_ptr<TabModel> tab_model, + size_t index) { + NOTIMPLEMENTED(); +} + +void PinnedTabCollection::AppendTab(std::unique_ptr<TabModel> tab_model) { + NOTIMPLEMENTED(); +} + +void PinnedTabCollection::MoveTab(TabModel* tab_model, size_t index) { + NOTIMPLEMENTED(); +} + +void PinnedTabCollection::CloseTab(TabModel* tab_model) { + NOTIMPLEMENTED(); +} + +bool PinnedTabCollection::ContainsTabRecursive(TabModel* tab_model) const { + NOTIMPLEMENTED(); + return false; +} + +bool PinnedTabCollection::ContainsCollection(TabCollection* collection) const { + NOTIMPLEMENTED(); + return false; +} + +std::optional<size_t> PinnedTabCollection::GetIndexOfTabRecursive( + TabModel* tab_model) const { + NOTIMPLEMENTED(); + return std::nullopt; +} + +std::optional<size_t> PinnedTabCollection::GetIndexOfCollection( + TabCollection* collection) const { + NOTIMPLEMENTED(); + return std::nullopt; +} + +std::unique_ptr<TabModel> PinnedTabCollection::MaybeRemoveTab( + TabModel* tab_model) { + NOTIMPLEMENTED(); + return nullptr; +} + +size_t PinnedTabCollection::ChildCount() const { + NOTIMPLEMENTED(); + return 0; +} + +size_t PinnedTabCollection::TabCountRecursive() const { + NOTIMPLEMENTED(); + return 0; +} + +std::unique_ptr<TabCollection> PinnedTabCollection::MaybeRemoveCollection( + TabCollection* collection) { + return nullptr; +} + +} // namespace tabs
diff --git a/chrome/browser/ui/tabs/pinned_tab_collection.h b/chrome/browser/ui/tabs/pinned_tab_collection.h new file mode 100644 index 0000000..52c0e14a --- /dev/null +++ b/chrome/browser/ui/tabs/pinned_tab_collection.h
@@ -0,0 +1,72 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_TABS_PINNED_TAB_COLLECTION_H_ +#define CHROME_BROWSER_UI_TABS_PINNED_TAB_COLLECTION_H_ + +#include <memory> +#include <optional> + +#include "chrome/browser/ui/tabs/tab_collection.h" + +namespace tabs { + +class TabModel; +class TabCollectionStorage; + +class PinnedTabCollection : public TabCollection { + public: + PinnedTabCollection(); + ~PinnedTabCollection() override; + PinnedTabCollection(const PinnedTabCollection&) = delete; + PinnedTabCollection& operator=(const PinnedTabCollection&) = delete; + + // Adds a `tab_model` to the `impl_` at a particular index. + void AddTab(std::unique_ptr<TabModel> tab_model, size_t index); + // Appends a `tab_model` to the end of a `impl_`. + void AppendTab(std::unique_ptr<TabModel> tab_model); + // Moves a `tab_model` to the `dst_index` within `impl_`. + void MoveTab(TabModel* tab_model, size_t dst_index); + // Removes and cleans the `tab_model`. + void CloseTab(TabModel* tab_model); + + // TabCollection: + // This is non-recursive for pinned tab collection as it does not contain + // another collection. + bool ContainsTabRecursive(TabModel* tab_model) const override; + // This is false as pinned tab collection does not contain another collection. + bool ContainsCollection(TabCollection* collection) const override; + // This is non-recursive for pinned tab collection as it does not contain + // another collection. + std::optional<size_t> GetIndexOfTabRecursive( + TabModel* tab_model) const override; + // This is nullopt as pinned tab collection does not contain another + // collection. + std::optional<size_t> GetIndexOfCollection( + TabCollection* collection) const override; + std::unique_ptr<TabModel> MaybeRemoveTab(TabModel* tab_model) override; + // This is the same as number of tabs `impl_` contains as pinned tab + // collection does not contain another collection. + size_t ChildCount() const override; + // This is non-recursive for pinned tab collection as it does not contain + // another collection. + size_t TabCountRecursive() const override; + + // TabCollection interface methods that are currently not supported by the + // collection. + std::unique_ptr<TabCollection> MaybeRemoveCollection( + TabCollection* collection) override; + + TabCollectionStorage* GetTabCollectionStorageForTesting() { + return impl_.get(); + } + + private: + // Underlying implementation for the storage of children. + std::unique_ptr<TabCollectionStorage> impl_; +}; + +} // namespace tabs + +#endif // CHROME_BROWSER_UI_TABS_PINNED_TAB_COLLECTION_H_
diff --git a/chrome/browser/ui/tabs/tab_collection.h b/chrome/browser/ui/tabs/tab_collection.h index a1ab895..9daf55d 100644 --- a/chrome/browser/ui/tabs/tab_collection.h +++ b/chrome/browser/ui/tabs/tab_collection.h
@@ -8,6 +8,7 @@ #include <cstddef> #include <memory> #include <optional> + #include "base/memory/raw_ptr.h" #include "base/types/pass_key.h" @@ -17,7 +18,7 @@ class TabCollection { public: - TabCollection(); + TabCollection() = default; virtual ~TabCollection() = default; TabCollection(const TabCollection&) = delete; TabCollection& operator=(const TabCollection&) = delete;
diff --git a/chrome/browser/ui/tabs/tab_collection_storage.cc b/chrome/browser/ui/tabs/tab_collection_storage.cc new file mode 100644 index 0000000..d2a9129 --- /dev/null +++ b/chrome/browser/ui/tabs/tab_collection_storage.cc
@@ -0,0 +1,98 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "base/notimplemented.h" +#include "chrome/browser/ui/tabs/tab_collection.h" +#include "chrome/browser/ui/tabs/tab_collection_storage.h" +#include "chrome/browser/ui/tabs/tab_model.h" + +namespace tabs { + +TabCollectionStorage::TabCollectionStorage(TabCollection& owner) + : owning_collection_(owner) {} + +TabCollectionStorage::~TabCollectionStorage() = default; + +bool TabCollectionStorage::ContainsTab(TabModel* tab_model) const { + return GetIndexOfTab(tab_model).has_value(); +} + +TabModel* TabCollectionStorage::AddTab(std::unique_ptr<TabModel> tab_model, + size_t index) { + CHECK(index <= GetChildrenCount()); + CHECK(tab_model); + + TabModel* tab_model_ptr = tab_model.get(); + children_.insert(children_.begin() + index, std::move(tab_model)); + return tab_model_ptr; +} + +void TabCollectionStorage::MoveTab(TabModel* tab_model, size_t dst_index) { + CHECK(tab_model); + std::unique_ptr<TabModel> tab_model_to_move = RemoveTab(tab_model); + CHECK(tab_model_to_move); + AddTab(std::move(tab_model_to_move), dst_index); +} + +std::unique_ptr<TabModel> TabCollectionStorage::RemoveTab(TabModel* tab_model) { + CHECK(tab_model); + for (size_t i = 0; i < children_.size(); ++i) { + if (std::holds_alternative<std::unique_ptr<TabModel>>(children_[i])) { + auto& stored_tab_model = + std::get<std::unique_ptr<TabModel>>(children_[i]); + if (stored_tab_model.get() == tab_model) { + auto removed_tab_model = std::move(stored_tab_model); + children_.erase(children_.begin() + i); + return removed_tab_model; + } + } + } + NOTREACHED_NORETURN(); +} + +void TabCollectionStorage::CloseTab(TabModel* tab) { + std::unique_ptr<TabModel> removed_tab_model = RemoveTab(tab); + removed_tab_model.reset(); +} + +TabCollection* TabCollectionStorage::AddCollection( + std::unique_ptr<TabCollection> collection, + size_t index) { + NOTIMPLEMENTED(); + return nullptr; +} + +void TabCollectionStorage::MoveCollection(TabCollection* collection, + size_t dst_index) { + NOTIMPLEMENTED(); +} + +std::unique_ptr<TabCollection> TabCollectionStorage::RemoveCollection( + TabCollection* collection) { + NOTIMPLEMENTED(); + return nullptr; +} + +void TabCollectionStorage::CloseCollection(TabCollection* collection) { + // This should free all the children as well. + NOTIMPLEMENTED(); +} + +std::optional<size_t> TabCollectionStorage::GetIndexOfTab( + TabModel* tab_model) const { + const auto it = std::find_if( + children_.begin(), children_.end(), [tab_model](const auto& child) { + return std::holds_alternative<std::unique_ptr<TabModel>>(child) && + std::get<std::unique_ptr<TabModel>>(child).get() == tab_model; + }); + return it == children_.end() ? std::nullopt + : std::optional<size_t>(it - children_.begin()); +} + +size_t TabCollectionStorage::GetChildrenCount() const { + return children_.size(); +} +} // namespace tabs
diff --git a/chrome/browser/ui/tabs/tab_collection_storage.h b/chrome/browser/ui/tabs/tab_collection_storage.h new file mode 100644 index 0000000..f7f5bac --- /dev/null +++ b/chrome/browser/ui/tabs/tab_collection_storage.h
@@ -0,0 +1,99 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_TABS_TAB_COLLECTION_STORAGE_H_ +#define CHROME_BROWSER_UI_TABS_TAB_COLLECTION_STORAGE_H_ + +#include <memory> +#include <optional> +#include <variant> +#include <vector> + +#include "base/memory/raw_ref.h" + +namespace tabs { + +class TabModel; +class TabCollection; + +using ChildrenVector = std::vector< + std::variant<std::unique_ptr<TabCollection>, std::unique_ptr<TabModel>>>; + +// Provides reusable functionality useful to most TabCollections for storing +// and manipulating a vector of child tabs and collections. +// Note that a TabCollectionStorage *is not* a TabCollection, and it +// does not have: +// - a parent TabCollection: a TabCollectionStorage doesn't live in the +// collection tree +// - MaybeRemoveTab/MaybeRemoveCollection - the storage layer doesn't get to say +// no +class TabCollectionStorage { + public: + explicit TabCollectionStorage(TabCollection& owner); + virtual ~TabCollectionStorage(); + TabCollectionStorage(const TabCollectionStorage&) = delete; + TabCollectionStorage& operator=(const TabCollectionStorage&) = delete; + + // Inserts a Tab into the TabCollectionStorage. The `index` represents the + // position in the direct children vector (non-recursive). + TabModel* AddTab(std::unique_ptr<TabModel> tab_model, size_t index); + + // Moves a tab already within this TabCollectionStorage to `dst_index`. Shifts + // other tabs and collections in the collection as needed. Will check if index + // is OOB. + void MoveTab(TabModel* tab_model, size_t dst_index); + + // Removes `tab_model` from storage and returns it to the caller. + [[nodiscard]] std::unique_ptr<TabModel> RemoveTab(TabModel* tab_model); + + // Removes a Tab in the TabCollectionStorage and frees the memory. + void CloseTab(TabModel* tab_model); + + // Inserts a TabCollection into the TabCollectionStorage. The `index` + // represents the position in the direct children vector (non-recursive). + TabCollection* AddCollection(std::unique_ptr<TabCollection> collection, + size_t index); + + // Moves a collection already within this TabCollectionStorage to a new + // `index` which is the destination before its move. Shifts other tabs + // and collections in the collection as needed. Will check if index + // is OOB. + void MoveCollection(TabCollection* collection, size_t dst_index); + + // Removes a TabCollection from storage and returns it to the caller. If no + // collection is found, returns nullptr. + [[nodiscard]] std::unique_ptr<TabCollection> RemoveCollection( + TabCollection* collection); + + // Closes a stored TabCollection, and all tabs and collections it recursively + // contains. This frees the memory as well. + void CloseCollection(TabCollection* collection); + + // Returns true if the `tab_model` is owned by the `children_`. + bool ContainsTab(TabModel* tab_model) const; + + // Returns the index of the `tab_model` in `children_`. It returns a nullopt + // if the `tab_model` is not present in the `children_`. + std::optional<size_t> GetIndexOfTab(TabModel* tab_model) const; + + // Returns the total number of elements stored in `children_`. This is + // equivalent to the sum of TabModel and TabCollection present in `children_`. + size_t GetChildrenCount() const; + + // Returns read only version of `children_` for clients to query + // information about the individual elements. + const ChildrenVector& GetChildren() const { return children_; } + + private: + // This is where the actual storage is present. `children_` is a vector of + // either a `TabModel`or a `TabCollection` and has ownership of the elements. + ChildrenVector children_; + + // The collection that owns this TabCollectionStorage. + const raw_ref<TabCollection> owning_collection_; +}; + +} // namespace tabs + +#endif // CHROME_BROWSER_UI_TABS_TAB_COLLECTION_STORAGE_H_
diff --git a/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc b/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc new file mode 100644 index 0000000..be744e7d --- /dev/null +++ b/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc
@@ -0,0 +1,224 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "chrome/browser/ui/tabs/pinned_tab_collection.h" +#include "chrome/browser/ui/tabs/tab_collection.h" +#include "chrome/browser/ui/tabs/tab_collection_storage.h" +#include "chrome/browser/ui/tabs/tab_model.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" +#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" +#include "chrome/browser/ui/ui_features.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +class TabCollectionStorageTest : public ::testing::Test { + public: + TabCollectionStorageTest() { + scoped_feature_list_.InitWithFeatures( + {features::kTabStripCollectionStorage}, {}); + pinned_collection_ = std::make_unique<tabs::PinnedTabCollection>(); + testing_profile_ = std::make_unique<TestingProfile>(); + tab_strip_model_delegate_ = std::make_unique<TestTabStripModelDelegate>(); + tab_strip_model_ = std::make_unique<TabStripModel>( + tab_strip_model_delegate_.get(), testing_profile_.get()); + } + TabCollectionStorageTest(const TabCollectionStorageTest&) = delete; + TabCollectionStorageTest& operator=(const TabCollectionStorageTest&) = delete; + ~TabCollectionStorageTest() override { pinned_collection_.reset(); } + + tabs::TabCollectionStorage* GetTabCollectionStorage() { + return pinned_collection_->GetTabCollectionStorageForTesting(); + } + + TabStripModel* GetTabStripModel() { return tab_strip_model_.get(); } + + void AddTabs(int num) { + for (int i = 0; i < num; i++) { + std::unique_ptr<tabs::TabModel> tab_model = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + tabs::TabModel* tab_model_ptr = tab_model.get(); + + tabs::TabModel* inserted_tab_model_ptr = + GetTabCollectionStorage()->AddTab( + std::move(tab_model), + GetTabCollectionStorage()->GetChildrenCount()); + EXPECT_EQ(tab_model_ptr, inserted_tab_model_ptr); + EXPECT_EQ(GetTabCollectionStorage()->GetIndexOfTab(tab_model_ptr), + GetTabCollectionStorage()->GetChildrenCount() - 1); + } + } + + void SetChildID(tabs::TabModel* tab_model, int id) { + tab_handle_to_id_map_[tab_model->GetHandle()] = id; + } + + void ResetChildrenIDs(int start) { + int i = 0; + const auto& children = GetTabCollectionStorage()->GetChildren(); + for (const auto& child : children) { + if (std::holds_alternative<std::unique_ptr<tabs::TabModel>>(child)) { + SetChildID(std::get<std::unique_ptr<tabs::TabModel>>(child).get(), + start + i); + i += 1; + } + } + } + + std::vector<int> IDString() { + std::vector<int> res; + const auto& children = GetTabCollectionStorage()->GetChildren(); + for (const auto& child : children) { + if (std::holds_alternative<std::unique_ptr<tabs::TabModel>>(child)) { + tabs::TabModel* tab_model = + std::get<std::unique_ptr<tabs::TabModel>>(child).get(); + res.push_back(tab_handle_to_id_map_[tab_model->GetHandle()]); + } + } + return res; + } + + private: + content::BrowserTaskEnvironment task_environment_; + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<tabs::PinnedTabCollection> pinned_collection_; + std::unique_ptr<TabStripModel> tab_strip_model_; + std::unique_ptr<Profile> testing_profile_; + std::unique_ptr<TestTabStripModelDelegate> tab_strip_model_delegate_; + std::map<tabs::TabHandle, int> tab_handle_to_id_map_; +}; + +TEST_F(TabCollectionStorageTest, AddTabOperation) { + auto tab_model_one = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + auto tab_model_two = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + + tabs::TabModel* tab_model_one_ptr = tab_model_one.get(); + tabs::TabModel* tab_model_two_ptr = tab_model_two.get(); + + tabs::TabCollectionStorage* collection_storage = GetTabCollectionStorage(); + collection_storage->AddTab(std::move(tab_model_one), 0); + + EXPECT_TRUE(collection_storage->ContainsTab(tab_model_one_ptr)); + EXPECT_FALSE(collection_storage->ContainsTab(tab_model_two.get())); + + // Add four more tabs. + AddTabs(4); + ResetChildrenIDs(0); + + EXPECT_EQ(collection_storage->GetChildrenCount(), 5ul); + + // Annotate `tab_model_two_ptr` with an id of 5. + SetChildID(tab_model_two_ptr, 5); + collection_storage->AddTab(std::move(tab_model_two), 3ul); + EXPECT_EQ(collection_storage->GetIndexOfTab(tab_model_two_ptr), 3ul); + EXPECT_EQ(IDString(), (std::vector<int>{0, 1, 2, 5, 3, 4})); +} + +TEST_F(TabCollectionStorageTest, RemoveTabOperation) { + auto tab_model_one = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + tabs::TabModel* tab_model_one_ptr = tab_model_one.get(); + + tabs::TabCollectionStorage* collection_storage = GetTabCollectionStorage(); + + // Add four tabs + AddTabs(4); + + // Add `tab_model_one` to index 3. + collection_storage->AddTab(std::move(tab_model_one), 3ul); + EXPECT_EQ(collection_storage->GetChildrenCount(), 5ul); + ResetChildrenIDs(0); + + auto removed_tab_model = collection_storage->RemoveTab(tab_model_one_ptr); + + EXPECT_EQ(collection_storage->GetChildrenCount(), 4ul); + EXPECT_EQ(removed_tab_model.get(), tab_model_one_ptr); + // `tab_model_one_ptr` was removed from index 3. + EXPECT_EQ(IDString(), (std::vector<int>{0, 1, 2, 4})); +} + +TEST_F(TabCollectionStorageTest, CloseTabOperation) { + auto tab_model_one = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + tabs::TabModel* tab_model_one_ptr = tab_model_one.get(); + + tabs::TabCollectionStorage* collection_storage = GetTabCollectionStorage(); + + // Add four tabs + AddTabs(4); + + // Add `tab_model_one` to index 3. + collection_storage->AddTab(std::move(tab_model_one), 3ul); + EXPECT_EQ(collection_storage->GetChildrenCount(), 5ul); + ResetChildrenIDs(0); + + collection_storage->CloseTab(tab_model_one_ptr); + + EXPECT_EQ(collection_storage->GetChildrenCount(), 4ul); + EXPECT_EQ(IDString(), (std::vector<int>{0, 1, 2, 4})); +} + +TEST_F(TabCollectionStorageTest, MoveTabOperation) { + auto tab_model_one = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + tabs::TabModel* tab_model_one_ptr = tab_model_one.get(); + + tabs::TabCollectionStorage* collection_storage = GetTabCollectionStorage(); + + // Add four tabs + AddTabs(4); + + // Add `tab_model_one` to index 3. + collection_storage->AddTab(std::move(tab_model_one), 3ul); + EXPECT_EQ(collection_storage->GetIndexOfTab(tab_model_one_ptr), 3ul); + EXPECT_EQ(collection_storage->GetChildrenCount(), 5ul); + ResetChildrenIDs(0); + + collection_storage->MoveTab(tab_model_one_ptr, 1ul); + + EXPECT_EQ(collection_storage->GetChildrenCount(), 5ul); + EXPECT_EQ(collection_storage->GetIndexOfTab(tab_model_one_ptr), 1ul); + EXPECT_EQ(IDString(), (std::vector<int>{0, 3, 1, 2, 4})); + + collection_storage->MoveTab(tab_model_one_ptr, 4ul); + EXPECT_EQ(collection_storage->GetChildrenCount(), 5ul); + EXPECT_EQ(collection_storage->GetIndexOfTab(tab_model_one_ptr), 4ul); + EXPECT_EQ(IDString(), (std::vector<int>{0, 1, 2, 4, 3})); +} + +TEST_F(TabCollectionStorageTest, InvalidArgumentsTabOperations) { + auto tab_model_one = + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()); + tabs::TabCollectionStorage* collection_storage = GetTabCollectionStorage(); + std::unique_ptr<tabs::TabModel> empty_ptr; + + EXPECT_DEATH( + collection_storage->AddTab( + std::make_unique<tabs::TabModel>(nullptr, GetTabStripModel()), 10ul), + ""); + EXPECT_DEATH(collection_storage->AddTab(std::move(empty_ptr), 1ul), ""); + + EXPECT_DEATH( + { + std::unique_ptr<tabs::TabModel> tab_model = + collection_storage->RemoveTab(tab_model_one.get()); + }, + ""); + EXPECT_DEATH( + { + std::unique_ptr<tabs::TabModel> tab_model = + collection_storage->RemoveTab(nullptr); + }, + ""); + + EXPECT_DEATH(collection_storage->MoveTab(tab_model_one.get(), 0ul), ""); + collection_storage->AddTab(std::move(tab_model_one), 0ul); + EXPECT_DEATH(collection_storage->MoveTab(tab_model_one.get(), 10ul), ""); + EXPECT_DEATH(collection_storage->MoveTab(nullptr, 10ul), ""); +}
diff --git a/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc index c25a8154..b96e5dd 100644 --- a/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc +++ b/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
@@ -28,7 +28,6 @@ #include "chrome/grit/assistant_optin_resources.h" #include "chrome/grit/assistant_optin_resources_map.h" #include "chrome/grit/browser_resources.h" -#include "chrome/grit/oobe_conditional_resources.h" #include "chromeos/ash/components/assistant/buildflags.h" #include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h" #include "chromeos/ash/services/assistant/public/cpp/features.h"
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc index b50400a0..e67b0310 100644 --- a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc +++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
@@ -25,7 +25,6 @@ #include "chrome/grit/generated_resources.h" #include "chrome/grit/lock_screen_reauth_resources.h" #include "chrome/grit/lock_screen_reauth_resources_map.h" -#include "chrome/grit/oobe_unconditional_resources_map.h" #include "components/prefs/pref_service.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc index fe7bde2..a12dd3f3 100644 --- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc +++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -143,9 +143,8 @@ #include "chrome/grit/gaia_auth_host_resources.h" #include "chrome/grit/gaia_auth_host_resources_map.h" #include "chrome/grit/generated_resources.h" -#include "chrome/grit/oobe_conditional_resources.h" -#include "chrome/grit/oobe_unconditional_resources.h" -#include "chrome/grit/oobe_unconditional_resources_map.h" +#include "chrome/grit/oobe_resources.h" +#include "chrome/grit/oobe_resources_map.h" #include "chromeos/ash/components/assistant/buildflags.h" #include "chromeos/ash/services/auth_factor_config/in_process_instances.h" #include "chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom.h" @@ -267,25 +266,23 @@ base::SysInfo::CrashIfChromeOSNonTestImage(); } - source->AddResourcePath(kDebuggerMJSPath, - dev_overlay_enabled - ? IDR_OOBE_CONDITIONAL_DEBUG_DEBUG_JS - : IDR_OOBE_CONDITIONAL_DEBUG_NO_DEBUG_JS); + source->AddResourcePath(kDebuggerMJSPath, dev_overlay_enabled + ? IDR_OOBE_DEBUG_DEBUG_JS + : IDR_OOBE_DEBUG_NO_DEBUG_JS); - source->AddResourcePath( - kQuickStartDebuggerPath, - quick_start_debugger_enabled - ? IDR_OOBE_CONDITIONAL_DEBUG_QUICK_START_DEBUGGER_JS - : IDR_OOBE_CONDITIONAL_DEBUG_NO_DEBUG_JS); + source->AddResourcePath(kQuickStartDebuggerPath, + quick_start_debugger_enabled + ? IDR_OOBE_DEBUG_QUICK_START_DEBUGGER_JS + : IDR_OOBE_DEBUG_NO_DEBUG_JS); } void AddTestAPIResources(content::WebUIDataSource* source) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const bool enabled = command_line->HasSwitch(switches::kEnableOobeTestAPI); - source->AddResourcePath( - kTestAPIJsMPath, enabled ? IDR_OOBE_CONDITIONAL_TEST_API_TEST_API_JS - : IDR_OOBE_CONDITIONAL_TEST_API_NO_TEST_API_JS); + source->AddResourcePath(kTestAPIJsMPath, + enabled ? IDR_OOBE_TEST_API_TEST_API_JS + : IDR_OOBE_TEST_API_NO_TEST_API_JS); } // Creates a WebUIDataSource for chrome://oobe @@ -302,7 +299,7 @@ OobeUI::AddOobeComponents(source); - source->SetDefaultResource(IDR_OOBE_UNCONDITIONAL_OOBE_HTML); + source->SetDefaultResource(IDR_OOBE_OOBE_HTML); // Add boolean variables that are used to add screens // dynamically depending on the flow type. @@ -742,11 +739,23 @@ } // static - void OobeUI::AddOobeComponents(content::WebUIDataSource* source) { // Add all resources from OOBE's autogenerated GRD. - source->AddResourcePaths(base::make_span(kOobeUnconditionalResources, - kOobeUnconditionalResourcesSize)); + const base::flat_set<std::string_view> kConditionalResources = { + "debug/debug.js", + "debug/no_debug.js", + "debug/quick_start_debugger.js", + "debug/quick_start_debugger.html.js", + "components/oobe_vars/oobe_custom_vars.css.js", + "components/oobe_vars/oobe_custom_vars_remora.css.js", + "test_api/no_test_api.js", + "test_api/test_api.js", + }; + for (const auto& path : base::make_span(kOobeResources, kOobeResourcesSize)) { + if (!kConditionalResources.contains(path.path)) { + source->AddResourcePath(path.path, path.id); + } + } // Add Gaia Authenticator resources source->AddResourcePaths( base::make_span(kGaiaAuthHostResources, kGaiaAuthHostResourcesSize)); @@ -754,11 +763,11 @@ if (policy::EnrollmentRequisitionManager::IsRemoraRequisition()) { source->AddResourcePath( kOobeCustomVarsCssJs, - IDR_OOBE_CONDITIONAL_COMPONENTS_OOBE_VARS_OOBE_CUSTOM_VARS_REMORA_CSS_JS); + IDR_OOBE_COMPONENTS_OOBE_VARS_OOBE_CUSTOM_VARS_REMORA_CSS_JS); } else { source->AddResourcePath( kOobeCustomVarsCssJs, - IDR_OOBE_CONDITIONAL_COMPONENTS_OOBE_VARS_OOBE_CUSTOM_VARS_CSS_JS); + IDR_OOBE_COMPONENTS_OOBE_VARS_OOBE_CUSTOM_VARS_CSS_JS); } source->OverrideContentSecurityPolicy(
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.h b/chrome/browser/ui/webui/ash/login/oobe_ui.h index bb31228..2c316af0 100644 --- a/chrome/browser/ui/webui/ash/login/oobe_ui.h +++ b/chrome/browser/ui/webui/ash/login/oobe_ui.h
@@ -11,6 +11,7 @@ #include <vector> #include "ash/webui/common/chrome_os_webui_config.h" +#include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/observer_list.h"
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc index b040490..fca7aab8 100644 --- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc +++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -10,6 +10,7 @@ #include <utility> #include "base/base64.h" +#include "base/containers/to_value_list.h" #include "base/containers/unique_ptr_adapters.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" @@ -73,11 +74,8 @@ // This function converts std::vector<net::IPEndPoint> to base::Value::List. base::Value::List IPEndpointsToBaseList( const std::vector<net::IPEndPoint>& resolved_addresses) { - base::Value::List resolved_addresses_list; - for (const net::IPEndPoint& resolved_address : resolved_addresses) { - resolved_addresses_list.Append(resolved_address.ToStringWithoutPort()); - } - return resolved_addresses_list; + return base::ToValueList(resolved_addresses, + &net::IPEndPoint::ToStringWithoutPort); } // This function converts std::optional<net::HostResolverEndpointResults> to
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc index 6e51eeeb..594e4f2 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
@@ -12,6 +12,7 @@ #include "base/check.h" #include "base/check_op.h" +#include "base/containers/to_value_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/functional/callback_helpers.h" @@ -73,11 +74,9 @@ base::Value::List ConvertPrintersToValues( const std::vector<crosapi::mojom::LocalDestinationInfoPtr>& printers) { - base::Value::List list; - for (const crosapi::mojom::LocalDestinationInfoPtr& p : printers) { - list.Append(LocalPrinterHandlerChromeos::PrinterToValue(*p)); - } - return list; + return base::ToValueList(printers, [](const auto& printer) { + return LocalPrinterHandlerChromeos::PrinterToValue(*printer); + }); } } // namespace
diff --git a/chrome/browser/ui/webui/realbox/realbox_handler.cc b/chrome/browser/ui/webui/realbox/realbox_handler.cc index 175ab567..79ffd4a 100644 --- a/chrome/browser/ui/webui/realbox/realbox_handler.cc +++ b/chrome/browser/ui/webui/realbox/realbox_handler.cc
@@ -309,7 +309,7 @@ std::vector<omnibox::mojom::AutocompleteMatchPtr> CreateAutocompleteMatches( const AutocompleteResult& result, - bookmarks::BookmarkModel* bookmark_model, + bookmarks::CoreBookmarkModel* bookmark_model, const omnibox::GroupConfigMap& suggestion_groups_map) { std::vector<omnibox::mojom::AutocompleteMatchPtr> matches; int line = 0; @@ -424,7 +424,7 @@ omnibox::mojom::AutocompleteResultPtr CreateAutocompleteResult( const std::u16string& input, const AutocompleteResult& result, - bookmarks::BookmarkModel* bookmark_model, + bookmarks::CoreBookmarkModel* bookmark_model, PrefService* prefs) { return omnibox::mojom::AutocompleteResult::New( input, @@ -472,7 +472,7 @@ bool IsPasteAndGoEnabled() const override; SessionID GetSessionID() const override; PrefService* GetPrefs() override; - bookmarks::BookmarkModel* GetBookmarkModel() override; + bookmarks::CoreBookmarkModel* GetBookmarkModel() override; AutocompleteControllerEmitter* GetAutocompleteControllerEmitter() override; TemplateURLService* GetTemplateURLService() override; const AutocompleteSchemeClassifier& GetSchemeClassifier() const override; @@ -538,7 +538,7 @@ return profile_->GetPrefs(); } -bookmarks::BookmarkModel* RealboxOmniboxClient::GetBookmarkModel() { +bookmarks::CoreBookmarkModel* RealboxOmniboxClient::GetBookmarkModel() { return BookmarkModelFactory::GetForBrowserContext(profile_); }
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn index 62eff6e..33cfe10 100644 --- a/chrome/browser/web_applications/BUILD.gn +++ b/chrome/browser/web_applications/BUILD.gn
@@ -516,8 +516,6 @@ if (is_chromeos) { sources += [ - "preinstalled_web_apps/app_mall.cc", - "preinstalled_web_apps/app_mall.h", "preinstalled_web_apps/calculator.cc", "preinstalled_web_apps/calculator.h", "preinstalled_web_apps/google_calendar.cc",
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc index 48f0ea6f..4a0bb03 100644 --- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/containers/to_vector.h" #include "base/feature_list.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -23,7 +24,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" #include "base/test/test_timeouts.h" -#include "base/test/to_vector.h" #include "base/test/values_test_util.h" #include "base/time/time.h" #include "base/version.h" @@ -69,8 +69,8 @@ namespace web_app { namespace { +using base::ToVector; using base::test::DictionaryHasValue; -using base::test::ToVector; using base::test::ValueIs; using ::testing::_; using ::testing::AllOf;
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc index 7fce3e8..a30eee74 100644 --- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc +++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
@@ -10,6 +10,7 @@ #include <string> #include "base/barrier_callback.h" +#include "base/containers/to_value_list.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/functional/bind.h" @@ -44,15 +45,6 @@ namespace { -template <typename Range, typename Proj = std::identity> -base::Value::List ToList(const Range& items, Proj proj = {}) { - base::Value::List list; - for (const auto& item : items) { - list.Append(std::invoke(proj, item)); - } - return list; -} - base::File::Error CreateDirectoryWithStatus(const base::FilePath& path) { base::File::Error err = base::File::FILE_OK; base::CreateDirectoryAndGetError(path, &err); @@ -575,20 +567,23 @@ } debug_info.Set("apps_in_policy", - ToList(apps_in_policy, [](const auto& options) { + base::ToValueList(apps_in_policy, [](const auto& options) { return options.web_bundle_id().id(); })); - debug_info.Set("installed_apps", - ToList(installed_apps, &web_package::SignedWebBundleId::id)); debug_info.Set( - "to_be_installed", ToList(to_be_installed, [](const auto& options) { + "installed_apps", + base::ToValueList(installed_apps, &web_package::SignedWebBundleId::id)); + debug_info.Set( + "to_be_installed", + base::ToValueList(to_be_installed, [](const auto& options) { return base::Value::Dict() .Set("id", options.web_bundle_id().id()) .Set("update_manifest_url", options.update_manifest_url().possibly_invalid_spec()); })); - debug_info.Set("to_be_removed", - ToList(to_be_removed, &web_package::SignedWebBundleId::id)); + debug_info.Set( + "to_be_removed", + base::ToValueList(to_be_removed, &web_package::SignedWebBundleId::id)); current_process_log_.Merge(debug_info.Clone()); auto weak_ptr = weak_ptr_factory_.GetWeakPtr(); @@ -626,7 +621,8 @@ } } current_process_log_.Set( - "uninstall_results", ToList(uninstall_results, [](const auto& result) { + "uninstall_results", + base::ToValueList(uninstall_results, [](const auto& result) { const auto& [web_bundle_id, uninstall_result] = result; return base::Value::Dict() .Set("id", web_bundle_id.id()) @@ -666,7 +662,8 @@ } } current_process_log_.Set( - "install_results", ToList(install_results, [](const auto& result) { + "install_results", + base::ToValueList(install_results, [](const auto& result) { const auto& [web_bundle_id, install_result] = result; return base::Value::Dict() .Set("id", web_bundle_id.id()) @@ -711,7 +708,7 @@ } base::Value IsolatedWebAppPolicyManager::ProcessLogs::ToDebugValue() const { - return base::Value(ToList(logs_, &base::Value::Dict::Clone)); + return base::Value(base::ToValueList(logs_, &base::Value::Dict::Clone)); } } // namespace web_app
diff --git a/chrome/browser/web_applications/preinstalled_app_install_features.cc b/chrome/browser/web_applications/preinstalled_app_install_features.cc index 0ab7d630f..eacf58fc 100644 --- a/chrome/browser/web_applications/preinstalled_app_install_features.cc +++ b/chrome/browser/web_applications/preinstalled_app_install_features.cc
@@ -27,11 +27,7 @@ // After a feature flag has been shipped and should be cleaned up, move it into // kShippedPreinstalledAppInstallFeatures to ensure any external installation // configs that reference it continue to see it as enabled. -constexpr const base::Feature* kPreinstalledAppInstallFeatures[] = { -#if BUILDFLAG(IS_CHROMEOS) - &chromeos::features::kCrosMall, -#endif -}; +constexpr const base::Feature* kPreinstalledAppInstallFeatures[] = {}; constexpr const base::StringPiece kShippedPreinstalledAppInstallFeatures[] = { // Enables installing the PWA version of the chrome os calculator instead of
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/app_mall.cc b/chrome/browser/web_applications/preinstalled_web_apps/app_mall.cc deleted file mode 100644 index d0f41d0..0000000 --- a/chrome/browser/web_applications/preinstalled_web_apps/app_mall.cc +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2024 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/web_applications/preinstalled_web_apps/app_mall.h" - -#include <memory> - -#include "base/functional/bind.h" -#include "chrome/browser/web_applications/external_install_options.h" -#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" -#include "chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_app_definition_utils.h" -#include "chrome/browser/web_applications/web_app_install_info.h" -#include "chrome/grit/preinstalled_web_apps_resources.h" -#include "chromeos/constants/chromeos_features.h" - -namespace web_app { - -ExternalInstallOptions GetConfigForAppMall() { - ExternalInstallOptions options( - /*install_url=*/GURL("https://discover.apps.chrome/"), - /*user_display_mode=*/mojom::UserDisplayMode::kStandalone, - /*install_source=*/ExternalInstallSource::kExternalDefault); - - options.user_type_allowlist = {"unmanaged"}; - options.gate_on_feature = chromeos::features::kCrosMall.name; - - options.load_and_await_service_worker_registration = false; - options.only_use_app_info_factory = false; - - // This App Info Factory is temporary, to help with prototyping. - // TODO(b/327080071): Remove. - options.app_info_factory = base::BindRepeating([]() { - auto info = std::make_unique<WebAppInstallInfo>(); - info->title = u"Get Apps and Games"; - info->start_url = GURL("https://discover.apps.chrome/"); - info->manifest_id = GURL("https://discover.apps.chrome/"); - info->display_mode = DisplayMode::kStandalone; - info->icon_bitmaps.any = LoadBundledIcons( - {IDR_PREINSTALLED_WEB_APPS_GOOGLE_SHEETS_ICON_192_PNG}); - return info; - }); - - return options; -} - -} // namespace web_app
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/app_mall.h b/chrome/browser/web_applications/preinstalled_web_apps/app_mall.h deleted file mode 100644 index 57bfb03..0000000 --- a/chrome/browser/web_applications/preinstalled_web_apps/app_mall.h +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright 2024 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_APP_MALL_H_ -#define CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_APP_MALL_H_ - -#include "chrome/browser/web_applications/external_install_options.h" - -namespace web_app { - -ExternalInstallOptions GetConfigForAppMall(); - -} // namespace web_app - -#endif // CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_APP_MALL_H_
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc index c7fa851..a52d0445 100644 --- a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc +++ b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
@@ -27,7 +27,6 @@ #if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/web_applications/preinstalled_web_apps/app_mall.h" #include "chrome/browser/web_applications/preinstalled_web_apps/calculator.h" #include "chrome/browser/web_applications/preinstalled_web_apps/google_calendar.h" #include "chrome/browser/web_applications/preinstalled_web_apps/google_chat.h" @@ -79,7 +78,6 @@ GetConfigForGoogleSlides(is_standalone_tabbed), GetConfigForYouTube(), #if BUILDFLAG(IS_CHROMEOS) - GetConfigForAppMall(), GetConfigForCalculator(), GetConfigForGoogleCalendar(), GetConfigForGoogleChat(),
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index a7f2d83..6176109 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1709164492-6288f66c85f9dc6799707634bc3a1860e9d514b2-19f774aa7739fac564fc3816b1c5d1f5596db9ab.profdata +chrome-android32-main-1709186356-93559781e9690b9e55432fbfd8166bb7987a35c9-248b5659e1d1fbb9b258b554b81f1fd00f330fb2.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index cce47b6..121d2285 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1709143178-4f64a456c85743b5cc5e5f4b03be4963bead106d-9a354d5fa97fbe84ba3e505ef4efbb4252c24d52.profdata +chrome-linux-main-1709186356-02794aa8bc1d2b1b6af82e52230a764c74522837-248b5659e1d1fbb9b258b554b81f1fd00f330fb2.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index bc4c572..367c8c30 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1709178867-df03373861caa8b82efa8359359cd07254332c76-6046afcc87040c3b54b2c48838f0b797c71f510c.profdata +chrome-mac-arm-main-1709207912-fd591abbe16e6948fed9e2ec00ad114d32d6c267-2d819bfbac4fb4878136cb51604920b8d923177b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 9eae478..5b7fe93 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1709164492-206dd0714e97f2cc11a2279e72dcfe214277a020-19f774aa7739fac564fc3816b1c5d1f5596db9ab.profdata +chrome-mac-main-1709186356-ba3ade54f201b6cf0664673d73b9426259a14b77-248b5659e1d1fbb9b258b554b81f1fd00f330fb2.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 79a11d01..fbebfdfa 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1709164492-de17ea76f0d4d78c8045ececd53014373fd50ecc-19f774aa7739fac564fc3816b1c5d1f5596db9ab.profdata +chrome-win-arm64-main-1709207912-5ecdf22b3ee625cf221e95f8f02c1197d1b3c9a9-2d819bfbac4fb4878136cb51604920b8d923177b.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 8c97a93..529f2c6 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1709164492-253bd6167de3270ba7ff11b7de3142addf91a091-19f774aa7739fac564fc3816b1c5d1f5596db9ab.profdata +chrome-win32-main-1709196988-bc081fad4c34f433d5f6c03c346d0ba135c9245d-d37490bdd3ab3ae5ecdee3e75decb68862646baa.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index f4e8fba..6cc34c56 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1709164492-273b526152379e597770e53a70c963e24842c057-19f774aa7739fac564fc3816b1c5d1f5596db9ab.profdata +chrome-win64-main-1709196988-ee1211a6840a65dcaf67c68cabcc2e232d0da890-d37490bdd3ab3ae5ecdee3e75decb68862646baa.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni index d90256a3..76fbf92 100644 --- a/chrome/chrome_paks.gni +++ b/chrome/chrome_paks.gni
@@ -283,8 +283,7 @@ "$root_gen_dir/chrome/network_ui_resources.pak", "$root_gen_dir/chrome/notification_tester_resources.pak", "$root_gen_dir/chrome/office_fallback_resources.pak", - "$root_gen_dir/chrome/oobe_conditional_resources.pak", - "$root_gen_dir/chrome/oobe_unconditional_resources.pak", + "$root_gen_dir/chrome/oobe_resources.pak", "$root_gen_dir/chrome/orca_resources.pak", "$root_gen_dir/chrome/os_settings_resources.pak", "$root_gen_dir/chrome/parent_access_resources.pak",
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn index b5e29a0..dde8502 100644 --- a/chrome/renderer/BUILD.gn +++ b/chrome/renderer/BUILD.gn
@@ -101,6 +101,8 @@ "url_loader_throttle_provider_impl.h", "v8_unwinder.cc", "v8_unwinder.h", + "web_link_preview_triggerer_impl.cc", + "web_link_preview_triggerer_impl.h", "websocket_handshake_throttle_provider_impl.cc", "websocket_handshake_throttle_provider_impl.h", "worker_content_settings_client.cc",
diff --git a/chrome/renderer/OWNERS b/chrome/renderer/OWNERS index 5a76272..4a01ff05 100644 --- a/chrome/renderer/OWNERS +++ b/chrome/renderer/OWNERS
@@ -20,3 +20,6 @@ per-file chrome_content_renderer_client.cc=* per-file chrome_content_renderer_client.h=* per-file chrome_content_renderer_client_unittest.cc=* + +# Link Preview. +per-file web_link_preview_triggerer_impl*=file://chrome/browser/preloading/preview/OWNERS
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc index 1e1b91ed..228414e 100644 --- a/chrome/renderer/autofill/form_autofill_browsertest.cc +++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -1372,9 +1372,6 @@ ExecuteJavaScriptForTests("document.getElementById('firstname').focus();"); ApplyFormAction(input_element.GetDocument(), form.fields, mojom::ActionPersistence::kPreview); - // The selection should be set after the fifth character. - EXPECT_EQ(5u, input_element.SelectionStart()); - EXPECT_EQ(5u, input_element.SelectionEnd()); // Fill the form. ApplyFormAction(input_element.GetDocument(), form.fields, @@ -1481,10 +1478,6 @@ expected.is_user_edited = false; expected.max_length = 0; EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields2[5]); - - // Verify that the cursor position has been updated. - EXPECT_EQ(5u, input_element.SelectionStart()); - EXPECT_EQ(5u, input_element.SelectionEnd()); } // Similar to TestFillFormAndModifyValues(). @@ -1538,9 +1531,6 @@ ExecuteJavaScriptForTests("document.getElementById('firstname').focus();"); ApplyFormAction(input_element.GetDocument(), form.fields, mojom::ActionPersistence::kPreview); - // The selection should be set after the fifth character. - EXPECT_EQ(5u, input_element.SelectionStart()); - EXPECT_EQ(5u, input_element.SelectionEnd()); // Fill the form. ApplyFormAction(input_element.GetDocument(), form.fields, @@ -1599,10 +1589,6 @@ } expected.is_autofilled = false; EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields2[2]); - - // Verify that the cursor position has been updated. - EXPECT_EQ(5u, input_element.SelectionStart()); - EXPECT_EQ(5u, input_element.SelectionEnd()); } // Similar to TestFillFormAndModifyValues().
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 3c359dc..086c1d1 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -65,6 +65,7 @@ #include "chrome/renderer/trusted_vault_encryption_keys_extension.h" #include "chrome/renderer/url_loader_throttle_provider_impl.h" #include "chrome/renderer/v8_unwinder.h" +#include "chrome/renderer/web_link_preview_triggerer_impl.h" #include "chrome/renderer/websocket_handshake_throttle_provider_impl.h" #include "chrome/renderer/worker_content_settings_client.h" #include "chrome/services/speech/buildflags/buildflags.h" @@ -1850,3 +1851,8 @@ network::mojom::ContentSecurityPolicySource::kHTTP}); #endif } + +std::unique_ptr<blink::WebLinkPreviewTriggerer> +ChromeContentRendererClient::CreateLinkPreviewTriggerer() { + return ::CreateWebLinkPreviewTriggerer(); +}
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h index 68d8afb..3b7c2fe4 100644 --- a/chrome/renderer/chrome_content_renderer_client.h +++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -210,6 +210,8 @@ void AppendContentSecurityPolicy( const blink::WebURL& url, blink::WebVector<blink::WebContentSecurityPolicyHeader>* csp) override; + std::unique_ptr<blink::WebLinkPreviewTriggerer> CreateLinkPreviewTriggerer() + override; #if BUILDFLAG(ENABLE_PLUGINS) static blink::WebPlugin* CreatePlugin(
diff --git a/chrome/renderer/web_link_preview_triggerer_impl.cc b/chrome/renderer/web_link_preview_triggerer_impl.cc new file mode 100644 index 0000000..78d5a9a --- /dev/null +++ b/chrome/renderer/web_link_preview_triggerer_impl.cc
@@ -0,0 +1,178 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/web_link_preview_triggerer_impl.h" + +#include "base/functional/bind.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/input/web_input_event.h" +#include "third_party/blink/public/platform/web_url.h" +#include "third_party/blink/public/web/web_document.h" +#include "third_party/blink/public/web/web_element.h" + +namespace { + +constexpr base::TimeDelta kHoverThreshold = base::Milliseconds(800); +constexpr base::TimeDelta kLongPressThreshold = base::Milliseconds(800); + +blink::WebURL GetURL(blink::WebElement& anchor_element) { + if (anchor_element.IsNull()) { + return blink::WebURL(); + } + + blink::WebString href = anchor_element.GetAttribute("href"); + if (href.IsNull()) { + return blink::WebURL(); + } + + // TODO(b:325558426): Mimic HTMLAnchorElement::Href and + // StripLeadingAndTrailingHTMLSpaces. + blink::WebURL url = anchor_element.GetDocument().CompleteURL(href); + + if (!url.IsValid()) { + return blink::WebURL(); + } + + return url; +} + +} // namespace + +std::unique_ptr<blink::WebLinkPreviewTriggerer> +CreateWebLinkPreviewTriggerer() { + if (!base::FeatureList::IsEnabled(blink::features::kLinkPreview)) { + return nullptr; + } + + switch (blink::features::kLinkPreviewTriggerType.Get()) { + // Alt+click is handled by navigation policy. + case blink::features::LinkPreviewTriggerType::kAltClick: + return nullptr; + case blink::features::LinkPreviewTriggerType::kAltHover: + return std::make_unique<WebLinkPreviewTriggererAltHover>(); + case blink::features::LinkPreviewTriggerType::kLongPress: + return std::make_unique<WebLinkPreviewTriggererLongPress>(); + } +} + +WebLinkPreviewTriggererAltHover::WebLinkPreviewTriggererAltHover() + : timer_(std::make_unique<base::OneShotTimer>()) {} + +WebLinkPreviewTriggererAltHover::~WebLinkPreviewTriggererAltHover() = default; + +WebLinkPreviewTriggererAltHover::WebLinkPreviewTriggererAltHover( + WebLinkPreviewTriggererAltHover&& other) = default; + +WebLinkPreviewTriggererAltHover& WebLinkPreviewTriggererAltHover::operator=( + WebLinkPreviewTriggererAltHover&& other) = default; + +void WebLinkPreviewTriggererAltHover::MaybeChangedKeyEventModifier( + int modifiers) { + bool is_alt_on = (modifiers & blink::WebInputEvent::kAltKey) != 0; + UpdateState(is_alt_on, anchor_element_); +} + +void WebLinkPreviewTriggererAltHover::DidChangeHoverElement( + blink::WebElement element) { + blink::WebElement most_inner_anchor_element; + for (blink::WebNode curr = element; !curr.IsNull() && curr.IsElementNode(); + curr = curr.ParentNode()) { + blink::WebElement curr_as_element = curr.To<blink::WebElement>(); + if (curr_as_element.HasHTMLTagName("a")) { + most_inner_anchor_element = curr_as_element; + break; + } + } + + UpdateState(is_alt_on_, most_inner_anchor_element); +} + +void WebLinkPreviewTriggererAltHover::UpdateState( + bool is_alt_on, + blink::WebElement anchor_element) { + if (is_alt_on_ == is_alt_on && anchor_element_ == anchor_element) { + return; + } + + is_alt_on_ = is_alt_on; + anchor_element_ = anchor_element; + + if (is_alt_on_ && !GetURL(anchor_element).IsNull()) { + timer_->Start( + FROM_HERE, kHoverThreshold, + base::BindOnce(&WebLinkPreviewTriggererAltHover::InitiatePreview, + // base::Unretained() is safe since `this` owns `timer_`. + base::Unretained(this))); + } else { + timer_->Stop(); + } +} + +void WebLinkPreviewTriggererAltHover::InitiatePreview() { + blink::WebDocument document = anchor_element_.GetDocument(); + if (document.IsNull()) { + return; + } + + blink::WebURL url = GetURL(anchor_element_); + if (url.IsNull()) { + return; + } + + document.InitiatePreview(url); +} + +WebLinkPreviewTriggererLongPress::WebLinkPreviewTriggererLongPress() + : timer_(std::make_unique<base::OneShotTimer>()) {} + +WebLinkPreviewTriggererLongPress::~WebLinkPreviewTriggererLongPress() = default; + +WebLinkPreviewTriggererLongPress::WebLinkPreviewTriggererLongPress( + WebLinkPreviewTriggererLongPress&& other) = default; + +WebLinkPreviewTriggererLongPress& WebLinkPreviewTriggererLongPress::operator=( + WebLinkPreviewTriggererLongPress&& other) = default; + +void WebLinkPreviewTriggererLongPress::DidAnchorElementReceiveMouseEvent( + blink::WebElement anchor_element, + blink::WebMouseEvent mouse_event) { + if (mouse_event.GetType() == blink::WebInputEvent::Type::kMouseDown && + mouse_event.button == blink::WebMouseEvent::Button::kLeft && + mouse_event.ClickCount() == 1) { + anchor_element_ = anchor_element; + timer_->Start( + FROM_HERE, kLongPressThreshold, + base::BindOnce(&WebLinkPreviewTriggererLongPress::InitiatePreview, + // base::Unretained() is safe since `this` owns `timer_`. + base::Unretained(this))); + return; + } + + if (mouse_event.GetType() == blink::WebInputEvent::Type::kMouseUp && + mouse_event.button == blink::WebMouseEvent::Button::kLeft) { + anchor_element_ = blink::WebElement(); + timer_->Stop(); + return; + } + + if (mouse_event.GetType() == blink::WebInputEvent::Type::kMouseLeave) { + anchor_element_ = blink::WebElement(); + timer_->Stop(); + return; + } +} + +void WebLinkPreviewTriggererLongPress::InitiatePreview() { + blink::WebDocument document = anchor_element_.GetDocument(); + if (document.IsNull()) { + return; + } + + blink::WebURL url = GetURL(anchor_element_); + if (url.IsNull()) { + return; + } + + document.InitiatePreview(url); +}
diff --git a/chrome/renderer/web_link_preview_triggerer_impl.h b/chrome/renderer/web_link_preview_triggerer_impl.h new file mode 100644 index 0000000..f98fda8 --- /dev/null +++ b/chrome/renderer/web_link_preview_triggerer_impl.h
@@ -0,0 +1,82 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_WEB_LINK_PREVIEW_TRIGGERER_IMPL_H_ +#define CHROME_RENDERER_WEB_LINK_PREVIEW_TRIGGERER_IMPL_H_ + +#include "base/timer/timer.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" + +// Creates appropriate WebLinkPreviewTriggerer depending on feature flag and +// params. +std::unique_ptr<blink::WebLinkPreviewTriggerer> CreateWebLinkPreviewTriggerer(); + +// Observes events in frame and triggers Link Preview: Alt+hover trigger. +// +// This class tracks the state "is Alt key pressed" and mouse hovered anchor +// element. Alt key tracking is done by observing modifiers for each keyboard +// events and mouse leave event. See the comment of `last_key_event_modifiers_` +// for more details and limitations. +class WebLinkPreviewTriggererAltHover final + : public blink::WebLinkPreviewTriggerer { + public: + WebLinkPreviewTriggererAltHover(); + + ~WebLinkPreviewTriggererAltHover() override; + + // Movable but not copyable. + WebLinkPreviewTriggererAltHover(WebLinkPreviewTriggererAltHover&& other); + WebLinkPreviewTriggererAltHover& operator=( + WebLinkPreviewTriggererAltHover&& other); + WebLinkPreviewTriggererAltHover(const WebLinkPreviewTriggererAltHover&) = + delete; + WebLinkPreviewTriggererAltHover& operator=( + const WebLinkPreviewTriggererAltHover&) = delete; + + // Implements blink::WebLinkPreviewTriggerer. + void MaybeChangedKeyEventModifier(int modifiers) override; + void DidChangeHoverElement(blink::WebElement element) override; + + private: + void UpdateState(bool is_alt_on, blink::WebElement anchor_element); + + void InitiatePreview(); + + std::unique_ptr<base::OneShotTimer> timer_; + + bool is_alt_on_ = false; + blink::WebElement anchor_element_; +}; + +// Observes events in frame and triggers Link Preview: Long press trigger. +class WebLinkPreviewTriggererLongPress final + : public blink::WebLinkPreviewTriggerer { + public: + WebLinkPreviewTriggererLongPress(); + + ~WebLinkPreviewTriggererLongPress() override; + + // Movable but not copyable. + WebLinkPreviewTriggererLongPress(WebLinkPreviewTriggererLongPress&& other); + WebLinkPreviewTriggererLongPress& operator=( + WebLinkPreviewTriggererLongPress&& other); + WebLinkPreviewTriggererLongPress(const WebLinkPreviewTriggererLongPress&) = + delete; + WebLinkPreviewTriggererLongPress& operator=( + const WebLinkPreviewTriggererLongPress&) = delete; + + // Implements blink::WebLinkPreviewTriggerer. + void DidAnchorElementReceiveMouseEvent( + blink::WebElement anchor_element, + blink::WebMouseEvent mouse_event) override; + + private: + void InitiatePreview(); + + std::unique_ptr<base::OneShotTimer> timer_; + + blink::WebElement anchor_element_; +}; + +#endif // CHROME_RENDERER_WEB_LINK_PREVIEW_TRIGGERER_IMPL_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 9de4120..d5ea605 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -7890,6 +7890,7 @@ "../browser/ui/tabs/pinned_tab_service_unittest.cc", "../browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service_unittest.cc", "../browser/ui/tabs/supports_handles_unittest.cc", + "../browser/ui/tabs/tab_collection_storage_unittest.cc", "../browser/ui/tabs/tab_menu_model_unittest.cc", "../browser/ui/tabs/tab_strip_model_stats_recorder_unittest.cc", "../browser/ui/tabs/tab_strip_model_unittest.cc",
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/document_selection.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/document_selection.js index 067573a..765df550 100644 --- a/chrome/test/data/extensions/api_test/automation/tests/tabs/document_selection.js +++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/document_selection.js
@@ -33,19 +33,26 @@ }, function selectInTextField() { - listenOnce(rootNode, EventType.DOCUMENT_SELECTION_CHANGED, function(evt1) { - listenOnce(textField, EventType.TEXT_SELECTION_CHANGED, function(evt2) { - assertTrue(evt1.target === rootNode); - assertTrue(evt2.target == textField); - assertEq(textField, rootNode.anchorObject); - assertEq(0, rootNode.anchorOffset); - assertEq(textField, rootNode.focusObject); - assertEq(0, rootNode.focusOffset); - chrome.automation.setDocumentSelection({anchorObject: textField, - anchorOffset: 1, - focusObject: textField, - focusOffset: 3}); - listenOnce(rootNode, EventType.DOCUMENT_SELECTION_CHANGED, + var textField = rootNode.find({role: RoleType.TEXT_FIELD}); + textField.focus(); + assertTrue(!!textField); + // Focusing the textfield will cause a text selection changed event in it. + listenOnce(textField, EventType.TEXT_SELECTION_CHANGED, function(evt2) { + assertTrue(evt2.target == textField); + assertEq(textField, rootNode.anchorObject); + assertEq(0, rootNode.anchorOffset); + assertEq(textField, rootNode.focusObject); + assertEq(0, rootNode.focusOffset); + + // Setting selection within the textfield causes a document selection and + // a textfield selection event. + chrome.automation.setDocumentSelection({anchorObject: textField, + anchorOffset: 1, + focusObject: textField, + focusOffset: 3}); + listenOnce(rootNode, EventType.DOCUMENT_SELECTION_CHANGED, + function(evt) { + listenOnce(textField, EventType.TEXT_SELECTION_CHANGED, function(evt) { assertEq(textField, rootNode.anchorObject); assertEq(1, rootNode.anchorOffset); @@ -55,10 +62,6 @@ }); }); }); - - var textField = rootNode.find({role: RoleType.TEXT_FIELD}); - assertTrue(!!textField); - textField.focus(); }, ];
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index 4297b6f..d29f23f 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -73,10 +73,6 @@ "CrosComponents", base::FEATURE_DISABLED_BY_DEFAULT); -// Enables an app to discover and install other apps. This flag will be enabled -// with Finch. -BASE_FEATURE(kCrosMall, "CrosMall", base::FEATURE_DISABLED_BY_DEFAULT); - // Enables the behaviour difference between web apps and browser created // shortcut backed by the web app system on Chrome OS. BASE_FEATURE(kCrosShortstand,
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h index 9909949..94b9cad 100644 --- a/chromeos/constants/chromeos_features.h +++ b/chromeos/constants/chromeos_features.h
@@ -39,7 +39,6 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kCrosAppsBackgroundEventHandling); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kCrosComponents); -COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kCrosMall); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kCrosShortstand); COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/chromeos/version/version_loader.cc b/chromeos/version/version_loader.cc index 28cfd17b..4b4839f 100644 --- a/chromeos/version/version_loader.cc +++ b/chromeos/version/version_loader.cc
@@ -7,6 +7,7 @@ #include <stddef.h> #include <string> +#include <string_view> #include <utility> #include <vector> @@ -108,7 +109,7 @@ // fixed. So we just match kFirmwarePrefix at the start of the line and find // the first character that is not "|" or space - base::StringPiece firmware_prefix(kFirmwarePrefix); + std::string_view firmware_prefix(kFirmwarePrefix); for (const std::string& line : base::SplitString( contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { if (base::StartsWith(line, firmware_prefix,
diff --git a/clank b/clank index ed4b774..306e4ba 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit ed4b774419588a1496c267e4b9ba422d20ea8552 +Subproject commit 306e4ba09278acec4c40f05cc49e5eb5f42275e5
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc index 00679048..4c2e5e8 100644 --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -50,7 +50,9 @@ #include "third_party/blink/public/web/web_form_element.h" #include "third_party/blink/public/web/web_form_related_change_type.h" #include "third_party/blink/public/web/web_input_element.h" +#include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_node.h" +#include "third_party/blink/public/web/web_range.h" #include "ui/base/l10n/l10n_util.h" #include "ui/events/keycodes/keyboard_codes.h" @@ -64,7 +66,9 @@ using blink::WebFrame; using blink::WebInputElement; using blink::WebKeyboardEvent; +using blink::WebLocalFrame; using blink::WebNode; +using blink::WebRange; using blink::WebString; namespace autofill { @@ -771,12 +775,9 @@ previewed_elements_.emplace_back(last_queried_element_, form_control.GetAutofillState()); form_control.SetSuggestedValue(WebString::FromUTF16(value)); - form_util::PreviewSuggestion(form_control.SuggestedValue().Utf16(), - form_control.Value().Utf16(), - form_control); break; case mojom::FieldActionType::kSelectAll: - DCHECK(value.empty()); + NOTIMPLEMENTED() << "Previewing select all is not implemented"; break; } break; @@ -794,6 +795,7 @@ } case mojom::FieldActionType::kSelectAll: DCHECK(value.empty()); + form_control.SelectText(/*select_all=*/true); break; } break; @@ -810,15 +812,20 @@ << "Previewing replacement of selection is not implemented"; break; case mojom::ActionPersistence::kFill: - if (action_type == mojom::FieldActionType::kSelectAll) { - DCHECK(value.empty()); - break; + switch (action_type) { + case mojom::FieldActionType::kSelectAll: + DCHECK(value.empty()); + content_editable.SelectText(/*select_all=*/true); + break; + case mojom::FieldActionType::kReplaceAll: + [[fallthrough]]; + case mojom::FieldActionType::kReplaceSelection: + content_editable.PasteText( + WebString::FromUTF16(value), + /*replace_all=*/ + (action_type == mojom::FieldActionType::kReplaceAll)); + break; } - content_editable.PasteText( - WebString::FromUTF16(value), - /*replace_all=*/ - (action_type == mojom::FieldActionType::kReplaceAll)); - break; } } }
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc index 1ada583..43ad032 100644 --- a/components/autofill/content/renderer/form_autofill_util.cc +++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1091,7 +1091,6 @@ // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. // Also sets the "autofilled" attribute, causing the background to be blue. void PreviewFormField(const FormFieldData::FillData& data, - bool is_initiating_node, WebFormControlElement& field, FieldDataManager& field_data_manager) { CHECK(!IsCheckableElement(field)); @@ -1112,13 +1111,6 @@ field.SetSuggestedValue(WebString::FromUTF16(data.value)); field.SetAutofillState(new_autofill_state); } - - if (is_initiating_node && - (IsTextInput(input_element) || IsTextAreaElement(field))) { - // Select the part of the text that the user didn't type. - PreviewSuggestion(field.SuggestedValue().Utf16(), field.Value().Utf16(), - field); - } } // A less-than comparator for FormFieldData's pointer by their FieldRendererId. @@ -2239,11 +2231,6 @@ SetPreventHighlightingOfAutofilledFields(document, true); } - auto fill_or_preview = - action_persistence == mojom::ActionPersistence::kPreview - ? &PreviewFormField - : &FillFormField; - // This container stores the FormFieldData::FillData* of `form.fields` that // will be filled into their corresponding blink elements. std::vector<std::pair<FieldRef, WebAutofillState>> filled_fields; @@ -2308,8 +2295,13 @@ features::kAutofillHighlightOnlyChangedValuesInPreviewMode)) { filled_fields.emplace_back(focused_field.element, focused_field.element.GetAutofillState()); - fill_or_preview(*focused_field.data, /*is_initiating_element=*/true, + if (action_persistence == mojom::ActionPersistence::kFill) { + FillFormField(*focused_field.data, /*is_initiating_node=*/true, focused_field.element, field_data_manager); + } else { + PreviewFormField(*focused_field.data, focused_field.element, + field_data_manager); + } } } @@ -2330,7 +2322,12 @@ // events. for (Field& field : unfocused_fields) { filled_fields.emplace_back(field.element, field.element.GetAutofillState()); - fill_or_preview(*field.data, false, field.element, field_data_manager); + if (action_persistence == mojom::ActionPersistence::kFill) { + FillFormField(*field.data, /*is_initiating_node=*/false, field.element, + field_data_manager); + } else { + PreviewFormField(*field.data, field.element, field_data_manager); + } } // Step 4: A focus event is emitted for the initiating element after @@ -2423,14 +2420,6 @@ return true; } -void PreviewSuggestion(const std::u16string& suggestion, - const std::u16string& user_input, - WebFormControlElement& input_element) { - input_element.SetSelectionRange( - base::checked_cast<unsigned>(user_input.length()), - base::checked_cast<unsigned>(suggestion.length())); -} - std::u16string FindChildText(const WebNode& node) { return FindChildTextWithIgnoreList(node, std::set<WebNode>()); }
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h index 6f76188..b5dfb9e 100644 --- a/components/autofill/content/renderer/form_autofill_util.h +++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -385,15 +385,6 @@ // are of the type <script>, <meta>, or <title>. bool IsWebElementEmpty(const blink::WebElement& element); -// Previews |suggestion| in |input_element| and highlights the suffix of -// |suggestion| not included in the |input_element| text. |input_element| must -// not be null. |user_input| should be the text typed by the user into -// |input_element|. Note that |user_input| cannot be easily derived from -// |input_element| by calling value(), because of http://crbug.com/507714. -void PreviewSuggestion(const std::u16string& suggestion, - const std::u16string& user_input, - blink::WebFormControlElement& input_element); - // Returns the aggregated values of the descendants of |element| that are // non-empty text nodes. This is a faster alternative to |innerText()| for // performance critical operations. It does a full depth-first search so can be
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 631cfd2..2caff2e 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -1143,6 +1143,7 @@ "metrics/field_filling_stats_and_score_metrics_unittest.cc", "metrics/form_events/address_form_event_logger_unittest.cc", "metrics/form_events/form_event_logger_base_unittest.cc", + "metrics/manual_fallback_metrics_unittest.cc", "metrics/payments/card_metadata_metrics_unittest.cc", "metrics/payments/cvc_storage_metrics_unittest.cc", "metrics/payments/iban_metrics_unittest.cc",
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc index 104ea13..e827701 100644 --- a/components/autofill/core/browser/browser_autofill_manager.cc +++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -148,6 +148,35 @@ // our analysis. constexpr size_t kMinFormSizeToTriggerUserPerceptionSurvey = 4; +// Checks if the user triggered address Autofill through the +// Chrome context menu on a field not classified as address. +// `popup_item_id` defines the suggestion type shown. +// `autofill_field` is the `AutofillField` from where the user triggered +// suggestions. +bool IsAddressAutofillManuallyTriggeredOnNonAddressField( + PopupItemId popup_item_id, + const AutofillField* autofill_field) { + return GetFillingProductFromPopupItemId(popup_item_id) == + FillingProduct::kAddress && + (!autofill_field || + !IsAddressType(autofill_field->Type().GetStorableType())); +} + +// Checks if the user triggered payments Autofill through the +// Chrome context menu on a field not classified as credit card. +// `popup_item_id` defines the suggestion type shown. +// `autofill_field` is the `AutofillField` from where the user triggered +// suggestions. +bool IsCreditCardAutofillManuallyTriggeredOnNonCreditCardField( + PopupItemId popup_item_id, + const AutofillField* autofill_field) { + return GetFillingProductFromPopupItemId(popup_item_id) == + FillingProduct::kCreditCard && + (!autofill_field || + GroupTypeOfFieldType(autofill_field->Type().GetStorableType()) != + FieldTypeGroup::kCreditCard); +} + // Converts `filling_stats` to a key-value representation, where the key // is the "stats category" and the value is the number of fields that match // such category. This is used to show users a survey that will measure the @@ -1281,6 +1310,18 @@ form.global_id(), base::make_span(&const_field, 1u), base::make_span(&const_autofill_field, 1u)); } + + const bool is_address_manual_fallback_on_non_address_field = + IsAddressAutofillManuallyTriggeredOnNonAddressField( + popup_item_id, const_autofill_field); + const bool is_payments_manual_fallback_on_non_payments_field = + IsCreditCardAutofillManuallyTriggeredOnNonCreditCardField( + popup_item_id, const_autofill_field); + if (is_address_manual_fallback_on_non_address_field || + is_payments_manual_fallback_on_non_payments_field) { + manual_fallback_logger_->OnDidFillSuggestion( + GetFillingProductFromPopupItemId(popup_item_id)); + } } } @@ -1445,8 +1486,36 @@ FormStructure* form_structure = nullptr; AutofillField* autofill_field = nullptr; - // TODO(crbug.com/1493361): Adapt for the unclassified forms. - if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field)) { + const bool has_cached_form_and_field = + GetCachedFormAndField(form, field, &form_structure, &autofill_field); + + // Check if Autofill was triggered via manual fallback on a field that was + // either unclassified or classified differently as the target + // `FillingProduct`. + // Note that in this type of flow we purposely do not log key metrics so we do + // not mess with the current denominator (classified forms). + const bool is_address_manual_fallback_on_non_address_field = + base::ranges::any_of( + shown_suggestions_types, [autofill_field](PopupItemId popup_item_id) { + return IsAddressAutofillManuallyTriggeredOnNonAddressField( + popup_item_id, autofill_field); + }); + const bool is_payments_manual_fallback_on_non_payments_field = + base::ranges::any_of( + shown_suggestions_types, [autofill_field](PopupItemId popup_item_id) { + return IsCreditCardAutofillManuallyTriggeredOnNonCreditCardField( + popup_item_id, autofill_field); + }); + if (is_address_manual_fallback_on_non_address_field) { + manual_fallback_logger_->OnDidShowSuggestions(FillingProduct::kAddress); + return; + } + if (is_payments_manual_fallback_on_non_payments_field) { + manual_fallback_logger_->OnDidShowSuggestions(FillingProduct::kCreditCard); + return; + } + + if (!has_cached_form_and_field) { return; }
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc index e229859..ae8528878 100644 --- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc +++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -750,10 +750,12 @@ trigger_source); } - void DidShowAutofillSuggestions(const FormData& form, - size_t field_index = 0) { + void DidShowAutofillSuggestions( + const FormData& form, + size_t field_index = 0, + PopupItemId popup_item_id = PopupItemId::kAddressEntry) { browser_autofill_manager_->DidShowSuggestions( - std::vector<PopupItemId>({PopupItemId::kAddressEntry}), form, + std::vector<PopupItemId>({popup_item_id}), form, form.fields[field_index]); } @@ -6182,7 +6184,8 @@ FormsSeen({form}); base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", AutofillMetrics::SUGGESTIONS_SHOWN, 1); histogram_tester.ExpectBucketCount("Autofill.UserHappiness.CreditCard",
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc index e1076f8..6ded0fe7 100644 --- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc +++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -1798,7 +1798,8 @@ // Simulate showing a credit card suggestion polled from "Name on card" field. { base::UserActionTester user_action_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_ShowedCreditCardSuggestions")); } @@ -1807,7 +1808,8 @@ // field. { base::UserActionTester user_action_tester; - DidShowAutofillSuggestions(form, /*field_index=*/1); + DidShowAutofillSuggestions(form, /*field_index=*/1, + PopupItemId::kCreditCardEntry); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_ShowedCreditCardSuggestions")); } @@ -1832,7 +1834,8 @@ // field along with a "Clear form" footer suggestion. { base::UserActionTester user_action_tester; - DidShowAutofillSuggestions(form, /*field_index=*/1); + DidShowAutofillSuggestions(form, /*field_index=*/1, + PopupItemId::kCreditCardEntry); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_ShowedCreditCardSuggestions")); } @@ -1873,7 +1876,8 @@ // field, this time to submit the form. { base::UserActionTester user_action_tester; - DidShowAutofillSuggestions(form, /*field_index=*/1); + DidShowAutofillSuggestions(form, /*field_index=*/1, + PopupItemId::kCreditCardEntry); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_ShowedCreditCardSuggestions")); } @@ -2347,7 +2351,8 @@ { // Simulating new popup being shown. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), @@ -2365,8 +2370,10 @@ { // Simulating two popups in the same page load. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), @@ -2427,7 +2434,8 @@ // Simulate new popup being shown. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), BucketsInclude( @@ -2453,8 +2461,10 @@ // Simulating two popups in the same page load. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), BucketsInclude( @@ -2517,8 +2527,10 @@ // logged, but suggestions shown with virtual card should not. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), BucketsInclude( @@ -3424,7 +3436,8 @@ // Simulating submission with suggestion shown, but not selected. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); SubmitForm(form); histogram_tester.ExpectBucketCount( @@ -3455,7 +3468,8 @@ // Simulating submission with suggestion shown, but not selected. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); SubmitForm(form); histogram_tester.ExpectBucketCount( @@ -3489,7 +3503,8 @@ // Simulating submission with suggestion shown, but not selected. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); SubmitForm(form); histogram_tester.ExpectBucketCount( @@ -3523,7 +3538,8 @@ // Simulating submission with suggestion shown, but not selected. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); SubmitForm(form); histogram_tester.ExpectBucketCount( @@ -3557,7 +3573,8 @@ // Simulating submission with suggestion shown, but not selected. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); SubmitForm(form); histogram_tester.ExpectBucketCount( @@ -3591,7 +3608,8 @@ // Simulating submission with suggestion shown and selected. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); autofill_manager().AuthenticateThenFillCreditCardForm( form, form.fields.back(), @@ -3689,7 +3707,8 @@ { // Simulating submission with suggestion shown. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); SubmitForm(form); EXPECT_THAT( @@ -3731,7 +3750,8 @@ // autofill manager is reset before UploadFormDataAsyncCallback is // triggered. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); SubmitForm(form); // Trigger UploadFormDataAsyncCallback. @@ -4128,7 +4148,8 @@ { // Simulating submission with suggestion shown. base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); SubmitForm(form); EXPECT_THAT( @@ -4368,7 +4389,8 @@ // popup being shown and filling a local card suggestion. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); autofill_manager().AuthenticateThenFillCreditCardForm( form, form.fields.front(), *personal_data().GetCreditCardByGUID(kTestLocalCardId), @@ -4414,7 +4436,8 @@ // logged to offer sub-histogram. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); // Select the masked server card with the linked offer. autofill_manager().AuthenticateThenFillCreditCardForm( form, form.fields.back(), @@ -4460,7 +4483,8 @@ // logged to offer sub-histogram. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); // Select another card, and still log to offer // sub-histogram because user has another masked server card with offer. autofill_manager().AuthenticateThenFillCreditCardForm( @@ -4513,7 +4537,8 @@ // new popup being shown and filling a local card suggestion. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); // Select the card with linked offer, though metrics should not record it // since the offer is expired. autofill_manager().AuthenticateThenFillCreditCardForm( @@ -4579,7 +4604,8 @@ // for crbug/1198751. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); // Select the masked server card with the linked offer. autofill_manager().AuthenticateThenFillCreditCardForm( form, form.fields.back(), @@ -4591,7 +4617,8 @@ // Simulate user showing suggestions but then submitting form with // previously filled card info. autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples( @@ -4630,7 +4657,8 @@ // related form events are correctly logged to offer sub-histogram. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); // Select the masked server card with the linked offer, but fail the CVC // check. autofill_manager().AuthenticateThenFillCreditCardForm( @@ -4681,7 +4709,8 @@ // Show suggestions and select the card with offer. autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); autofill_manager().AuthenticateThenFillCreditCardForm( form, form.fields.back(), *personal_data().GetCreditCardByGUID(kMaskedServerCardIds[2]), @@ -4691,7 +4720,8 @@ // Show suggestions again, and select a local card instead. autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); - DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); + DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, + PopupItemId::kCreditCardEntry); autofill_manager().AuthenticateThenFillCreditCardForm( form, form.fields.back(), *personal_data().GetCreditCardByGUID(kTestLocalCardId), @@ -5948,8 +5978,10 @@ { SCOPED_TRACE("Separate pop-ups"); base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.UserHappiness"), BucketsInclude(Bucket(AutofillMetrics::SUGGESTIONS_SHOWN, 2), @@ -5968,8 +6000,10 @@ { SCOPED_TRACE("Multiple keystrokes"); base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form); - DidShowAutofillSuggestions(form); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); + DidShowAutofillSuggestions(form, /*field_index=*/0, + PopupItemId::kCreditCardEntry); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.UserHappiness"), BucketsInclude(Bucket(AutofillMetrics::SUGGESTIONS_SHOWN, 2), @@ -5984,7 +6018,8 @@ { SCOPED_TRACE("Different field"); base::HistogramTester histogram_tester; - DidShowAutofillSuggestions(form, /*field_index=*/1); + DidShowAutofillSuggestions(form, /*field_index=*/1, + PopupItemId::kCreditCardEntry); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::SUGGESTIONS_SHOWN, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard",
diff --git a/components/autofill/core/browser/metrics/manual_fallback_metrics.cc b/components/autofill/core/browser/metrics/manual_fallback_metrics.cc index 455a5a7..968e1868 100644 --- a/components/autofill/core/browser/metrics/manual_fallback_metrics.cc +++ b/components/autofill/core/browser/metrics/manual_fallback_metrics.cc
@@ -19,6 +19,36 @@ "Address"); EmitExplicitlyTriggeredMetric(not_classified_as_target_filling_credit_card, "CreditCard"); + EmitFillAfterSuggestionMetric(address_suggestions_state_, "Address"); + EmitFillAfterSuggestionMetric(credit_card_suggestions_state_, "CreditCard"); +} + +void ManualFallbackEventLogger::OnDidShowSuggestions( + FillingProduct target_filling_product) { + CHECK(target_filling_product == FillingProduct::kAddress || + target_filling_product == FillingProduct::kCreditCard); + SuggestionState& suggestion_state = + target_filling_product == FillingProduct::kAddress + ? address_suggestions_state_ + : credit_card_suggestions_state_; + + if (suggestion_state == SuggestionState::kNotShown) { + suggestion_state = SuggestionState::kShown; + } +} + +void ManualFallbackEventLogger::OnDidFillSuggestion( + FillingProduct target_filling_product) { + CHECK(target_filling_product == FillingProduct::kAddress || + target_filling_product == FillingProduct::kCreditCard); + SuggestionState& suggestion_state = + target_filling_product == FillingProduct::kAddress + ? address_suggestions_state_ + : credit_card_suggestions_state_; + + if (suggestion_state == SuggestionState::kShown) { + suggestion_state = SuggestionState::kFilled; + } } void ManualFallbackEventLogger::ContextMenuEntryShown( @@ -68,4 +98,17 @@ base::UmaHistogramBoolean(metric_name("Total"), was_accepted); } +void ManualFallbackEventLogger::EmitFillAfterSuggestionMetric( + SuggestionState suggestion_state, + std::string_view bucket) { + if (suggestion_state == SuggestionState::kNotShown) { + return; + } + base::UmaHistogramBoolean( + base::StrCat({"Autofill.Funnel.NotClassifiedAsTargetFilling." + "FillAfterSuggestion.", + bucket}), + suggestion_state == SuggestionState::kFilled); +} + } // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/manual_fallback_metrics.h b/components/autofill/core/browser/metrics/manual_fallback_metrics.h index 017e68b..ccfffce 100644 --- a/components/autofill/core/browser/metrics/manual_fallback_metrics.h +++ b/components/autofill/core/browser/metrics/manual_fallback_metrics.h
@@ -22,6 +22,14 @@ // Emits metrics before destruction. ~ManualFallbackEventLogger(); + // Called when a suggestion is shown on an unclassified field or a field that + // does not match the `target_filling_product`. + void OnDidShowSuggestions(FillingProduct target_filling_product); + + // Called when a suggestion is triggered from an unclassified field or a field + // that does not match the `target_filling_product` + void OnDidFillSuggestion(FillingProduct target_filling_product); + // Called when context menu was opened on a qualifying field. // `address_fallback_present` indicates where the address fallback was // added. Similarly, `credit_cards_fallback_present` indicates whether a @@ -30,24 +38,37 @@ bool credit_cards_fallback_present); // Called when a fallback option was accepted (not just hovered). - // `target_filling_product` specifies which of the available options was + // `target_filling_product` specifies of the available options was // chosen. void ContextMenuEntryAccepted(FillingProduct target_filling_product); private: enum class ContextMenuEntryState { kNotShown = 0, kShown = 1, kAccepted = 2 }; + enum class SuggestionState { kNotShown = 0, kShown = 1, kFilled = 2 }; // If according to the `state` the context menu was used, emits into the // `bucket` (address or credit_card) whether an entry was accepted or not void EmitExplicitlyTriggeredMetric(ContextMenuEntryState state, std::string_view bucket); + // If suggestions for an unclassified field or a field that has a different + // classification from the target `FillingProduct` were shown, emits whether + // they were filled based on their respective `suggestion_state_`. + // `bucket` defines the `FillingProduct`, i.e. address or credit_cards. + void EmitFillAfterSuggestionMetric(SuggestionState suggestion_state, + std::string_view bucket); + // For addresses and credit cards filling, tracks if the manual fallback // context menu entry was shown or accepted. ContextMenuEntryState not_classified_as_target_filling_address = ContextMenuEntryState::kNotShown; ContextMenuEntryState not_classified_as_target_filling_credit_card = ContextMenuEntryState::kNotShown; + + // Tracks if address suggestions were shown/filled. + SuggestionState address_suggestions_state_ = SuggestionState::kNotShown; + // Tracks if credit card suggestions were shown/filled. + SuggestionState credit_card_suggestions_state_ = SuggestionState::kNotShown; }; } // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/manual_fallback_metrics_unittest.cc b/components/autofill/core/browser/metrics/manual_fallback_metrics_unittest.cc new file mode 100644 index 0000000..ddc8c01 --- /dev/null +++ b/components/autofill/core/browser/metrics/manual_fallback_metrics_unittest.cc
@@ -0,0 +1,163 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/metrics/manual_fallback_metrics.h" + +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h" +#include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/form_data.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill::autofill_metrics { + +namespace { + +// Test parameter data for asserting that the expected metrics are emitted +// depending on the fallback option selected. +struct ManualFallbackTestParams { + const AutofillSuggestionTriggerSource manual_fallback_option; + const std::string test_name; +}; + +class ManualFallbackEventLoggerTest + : public AutofillMetricsBaseTest, + public testing::TestWithParam<ManualFallbackTestParams> { + protected: + void SetUp() override { SetUpHelper(); } + void TearDown() override { TearDownHelper(); } + + // Show suggestions on the first field of the `form`. + void ShowSuggestions( + const FormData& form, + AutofillSuggestionTriggerSource fallback_trigger_source) { + autofill_manager().OnAskForValuesToFillTest( + form, form.fields[0], /*bounding_box=*/{}, fallback_trigger_source); + DidShowAutofillSuggestions( + form, /*field_index=*/0, + fallback_trigger_source == + AutofillSuggestionTriggerSource::kManualFallbackAddress + ? PopupItemId::kAddressEntry + : PopupItemId::kCreditCardEntry); + } + + // Fills the first field in the form by calling `FillOrPreviewField()`. Uses a + // hardcoded value to be filled but makes the `popup_item_id` passed to the + // filling function depend on whether the `manual_fallback_option` param + // attribute is `AutofillSuggestionTriggerSource::kManualFallbackAddress` or + // `AutofillSuggestionTriggerSource::kManualFallbackPayments`. Using + // `PopupItemId::kAddressFieldByFieldFilling` for the former and + // `PopupItemId::kCreditCardFieldByFieldFilling` for the latter. + void FillFirstFormField(const FormData& form) { + const ManualFallbackTestParams& params = GetParam(); + autofill_manager().FillOrPreviewField( + mojom::ActionPersistence::kFill, mojom::FieldActionType::kReplaceAll, + form, form.fields[0], u"value to fill", + params.manual_fallback_option == + AutofillSuggestionTriggerSource::kManualFallbackAddress + ? PopupItemId::kAddressFieldByFieldFilling + : PopupItemId::kCreditCardFieldByFieldFilling); + } + + std::string ExpectedBucketNameForManualFallbackOption() const { + return GetParam().manual_fallback_option == + AutofillSuggestionTriggerSource::kManualFallbackAddress + ? "Address" + : "CreditCard"; + } +}; + +// Tests that when suggestions on an unclassified field are shown and filled, +// the FillAfterSuggestion metric is emitted correctly. Note that only +// field-by-field filling is available in this flow. +TEST_P(ManualFallbackEventLoggerTest, FillAfterSuggestion_Filled) { + FormData form = test::GetFormData( + {.fields = {{.label = u"unclassified", .name = u"unclassified"}}}); + SeeForm(form); + const ManualFallbackTestParams& params = GetParam(); + + ShowSuggestions(form, + /*fallback_trigger_source=*/params.manual_fallback_option); + // Fill the suggestion. + FillFirstFormField(form); + + base::HistogramTester histogram_tester; + ResetDriverToCommitMetrics(); + histogram_tester.ExpectUniqueSample( + "Autofill.Funnel.NotClassifiedAsTargetFilling." + "FillAfterSuggestion." + + ExpectedBucketNameForManualFallbackOption(), + true, 1); +} + +// Tests that when suggestions on an unclassified field are shown, +// but not selected, FillAfterSuggestion metric is +// emitted correctly. Note that only field-by-field filling is available in this +// flow. +TEST_P(ManualFallbackEventLoggerTest, FillAfterSuggestion_NotFilled) { + FormData form = test::GetFormData( + {.fields = {{.label = u"unclassified", .name = u"unclassified"}}}); + SeeForm(form); + const ManualFallbackTestParams& params = GetParam(); + + ShowSuggestions(form, + /*fallback_trigger_source=*/params.manual_fallback_option); + + base::HistogramTester histogram_tester; + // No suggestion was selected. + ResetDriverToCommitMetrics(); + histogram_tester.ExpectUniqueSample( + "Autofill.Funnel.NotClassifiedAsTargetFilling." + "FillAfterSuggestion." + + ExpectedBucketNameForManualFallbackOption(), + false, 1); +} + +// Tests that regular field-by-field filling suggestion acceptance (i.e, on a +// classified field), does not emit the metric. +TEST_P(ManualFallbackEventLoggerTest, + FillAfterSuggestion_RegularFieldByFieldFillingSuggestion) { + const ManualFallbackTestParams& params = GetParam(); + bool const is_address_filling = + params.manual_fallback_option == + AutofillSuggestionTriggerSource::kManualFallbackAddress; + FormData form = is_address_filling + ? test::CreateTestAddressFormData() + : test::CreateTestCreditCardFormData( + /*is_https=*/true, /*use_month_type=*/false); + SeeForm(form); + + ShowSuggestions(form, + /*fallback_trigger_source=*/params.manual_fallback_option); + // Fill the suggestion. + FillFirstFormField(form); + + base::HistogramTester histogram_tester; + // No suggestion was selected. + ResetDriverToCommitMetrics(); + histogram_tester.ExpectTotalCount( + "Autofill.Funnel.NotClassifiedAsTargetFilling." + "FillAfterSuggestion." + + ExpectedBucketNameForManualFallbackOption(), + 0); +} + +INSTANTIATE_TEST_SUITE_P( + All, + ManualFallbackEventLoggerTest, + ::testing::ValuesIn(std::vector<ManualFallbackTestParams>( + {{.manual_fallback_option = + AutofillSuggestionTriggerSource::kManualFallbackAddress, + .test_name = "_AddressManualFallback"}, + {.manual_fallback_option = + AutofillSuggestionTriggerSource::kManualFallbackPayments, + .test_name = "_PaymentsManualFallback"}})), + [](const ::testing::TestParamInfo<ManualFallbackEventLoggerTest::ParamType>& + info) { return info.param.test_name; }); + +} // namespace + +} // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/ui/suggestion.h b/components/autofill/core/browser/ui/suggestion.h index 546c037..15b449e 100644 --- a/components/autofill/core/browser/ui/suggestion.h +++ b/components/autofill/core/browser/ui/suggestion.h
@@ -141,6 +141,8 @@ #if DCHECK_IS_ON() bool Invariant() const { switch (popup_item_id) { + case PopupItemId::kFillPassword: + return absl::holds_alternative<ValueToFill>(payload); case PopupItemId::kSeePromoCodeDetails: return absl::holds_alternative<GURL>(payload); case PopupItemId::kIbanEntry:
diff --git a/components/autofill/core/browser/ui/suggestion_test_helpers.cc b/components/autofill/core/browser/ui/suggestion_test_helpers.cc index f06d39a3..3e5cff9c 100644 --- a/components/autofill/core/browser/ui/suggestion_test_helpers.cc +++ b/components/autofill/core/browser/ui/suggestion_test_helpers.cc
@@ -28,4 +28,13 @@ return AllOf(EqualsSuggestion(id, main_text), Field(&Suggestion::icon, icon)); } +::testing::Matcher<Suggestion> EqualsSuggestion( + PopupItemId id, + const std::u16string& main_text, + Suggestion::Icon icon, + const Suggestion::Payload& payload) { + return AllOf(EqualsSuggestion(id, main_text, icon), + Field(&Suggestion::payload, payload)); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/ui/suggestion_test_helpers.h b/components/autofill/core/browser/ui/suggestion_test_helpers.h index 9c8cf4b..e86034d 100644 --- a/components/autofill/core/browser/ui/suggestion_test_helpers.h +++ b/components/autofill/core/browser/ui/suggestion_test_helpers.h
@@ -21,6 +21,12 @@ const std::u16string& main_text, Suggestion::Icon icon); +::testing::Matcher<Suggestion> EqualsSuggestion( + PopupItemId id, + const std::u16string& main_text, + Suggestion::Icon icon, + const Suggestion::Payload& payload); + template <class... Matchers> inline auto SuggestionVectorIdsAre(const Matchers&... matchers) { return ::testing::ElementsAre(::testing::Field(
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp index f832c36..5784a8e7 100644 --- a/components/autofill_strings.grdp +++ b/components/autofill_strings.grdp
@@ -23,12 +23,6 @@ </message> </if> - <if expr="is_ios"> - <message name="IDS_AUTOFILL_REMOVE_LOCAL_COPY_BUTTON" desc="The label of the button that removes the local copy of a Wallet Credit Card." formatter_data="android_java"> - Remove copy - </message> - </if> - <message name="IDS_AUTOFILL_WARNING_INSECURE_CONNECTION" desc="Warning text to show when credit card autofill is disabled because the website is not using a secure connection."> Automatic credit card filling is disabled because this form does not use a secure connection. </message>
diff --git a/components/compose/core/browser/BUILD.gn b/components/compose/core/browser/BUILD.gn index f52efc5..ae1ba01 100644 --- a/components/compose/core/browser/BUILD.gn +++ b/components/compose/core/browser/BUILD.gn
@@ -22,6 +22,8 @@ "compose_manager_impl.h", "compose_metrics.cc", "compose_metrics.h", + "compose_utils.cc", + "compose_utils.h", "config.cc", "config.h", ]
diff --git a/components/compose/core/browser/compose_manager_impl.cc b/components/compose/core/browser/compose_manager_impl.cc index d152b0ce..23a0a89c 100644 --- a/components/compose/core/browser/compose_manager_impl.cc +++ b/components/compose/core/browser/compose_manager_impl.cc
@@ -10,12 +10,15 @@ #include "base/feature_list.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/browser_autofill_manager.h" #include "components/compose/core/browser/compose_client.h" #include "components/compose/core/browser/compose_features.h" #include "components/compose/core/browser/compose_metrics.h" +#include "components/compose/core/browser/compose_utils.h" +#include "components/compose/core/browser/config.h" namespace { @@ -93,10 +96,31 @@ client_->getPageUkmTracker()->ShowDialogAbortedDueToMissingFormFieldData(); return; } + autofill::AutofillManager& manager = driver->GetAutofillManager(); - auto compose_callback = - base::BindOnce(&FillTextWithAutofill, manager.GetWeakPtr(), - form_data.value(), *form_field_data); + ComposeCallback compose_callback; + + if (base::FeatureList::IsEnabled(compose::features::kComposeTextSelection) && + IsWordCountWithinBounds(base::UTF16ToUTF8(form_field_data->selected_text), + 0, compose::GetComposeConfig().input_min_words)) { + // Select all words. + static_cast<autofill::BrowserAutofillManager*>(&manager) + ->FillOrPreviewField(autofill::mojom::ActionPersistence::kFill, + autofill::mojom::FieldActionType::kSelectAll, + form_data.value(), *form_field_data, u"", + autofill::PopupItemId::kCompose); + + // Update form_field_data to use the newly selected text. + autofill::FormFieldData updated_form_field_data = + autofill::FormFieldData(*form_field_data); + + updated_form_field_data.selected_text = form_field_data->value; + + form_field_data = &updated_form_field_data; + } + + compose_callback = base::BindOnce(&FillTextWithAutofill, manager.GetWeakPtr(), + form_data.value(), *form_field_data); OpenComposeWithFormFieldData(ui_entry_point, *form_field_data, manager.client().GetPopupScreenLocation(),
diff --git a/components/compose/core/browser/compose_utils.cc b/components/compose/core/browser/compose_utils.cc new file mode 100644 index 0000000..7f7c9ea --- /dev/null +++ b/components/compose/core/browser/compose_utils.cc
@@ -0,0 +1,35 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/compose/core/browser/compose_utils.h" + +#include "base/strings/string_tokenizer.h" + +namespace compose { + +namespace { +const std::string kWhitespace = " ,.\r\n\t\f\v"; +} + +bool IsWordCountWithinBounds(const std::string& prompt, + unsigned int minimum, + unsigned int maximum) { + base::StringTokenizer tokenizer( + prompt, kWhitespace, base::StringTokenizer::WhitespacePolicy::kSkipOver); + + unsigned int word_count = 0; + while (tokenizer.GetNext()) { + ++word_count; + if (word_count > maximum) { + return false; + } + } + + if (word_count < minimum) { + return false; + } + return true; +} + +} // namespace compose
diff --git a/components/compose/core/browser/compose_utils.h b/components/compose/core/browser/compose_utils.h new file mode 100644 index 0000000..8163a52 --- /dev/null +++ b/components/compose/core/browser/compose_utils.h
@@ -0,0 +1,20 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_COMPOSE_CORE_BROWSER_COMPOSE_UTILS_H_ +#define COMPONENTS_COMPOSE_CORE_BROWSER_COMPOSE_UTILS_H_ + +#include <string> + +namespace compose { + +// Returns true if the number of words in `prompt` is between minimum and +// maximum. False otherwise. +bool IsWordCountWithinBounds(const std::string& prompt, + unsigned int minimum, + unsigned int maximum); + +} // namespace compose + +#endif // COMPONENTS_COMPOSE_CORE_BROWSER_COMPOSE_UTILS_H_
diff --git a/components/omnibox/browser/in_memory_url_index.cc b/components/omnibox/browser/in_memory_url_index.cc index a24582d..2819515 100644 --- a/components/omnibox/browser/in_memory_url_index.cc +++ b/components/omnibox/browser/in_memory_url_index.cc
@@ -75,7 +75,7 @@ // InMemoryURLIndex ------------------------------------------------------------ -InMemoryURLIndex::InMemoryURLIndex(bookmarks::BookmarkModel* bookmark_model, +InMemoryURLIndex::InMemoryURLIndex(bookmarks::CoreBookmarkModel* bookmark_model, history::HistoryService* history_service, TemplateURLService* template_url_service, const base::FilePath& history_dir,
diff --git a/components/omnibox/browser/in_memory_url_index.h b/components/omnibox/browser/in_memory_url_index.h index 25cb364..f9d8ae0 100644 --- a/components/omnibox/browser/in_memory_url_index.h +++ b/components/omnibox/browser/in_memory_url_index.h
@@ -38,7 +38,7 @@ } namespace bookmarks { -class BookmarkModel; +class CoreBookmarkModel; } namespace history { @@ -74,7 +74,7 @@ public base::trace_event::MemoryDumpProvider { public: // `history_service` may be null during unit testing. - InMemoryURLIndex(bookmarks::BookmarkModel* bookmark_model, + InMemoryURLIndex(bookmarks::CoreBookmarkModel* bookmark_model, history::HistoryService* history_service, TemplateURLService* template_url_service, const base::FilePath& history_dir, @@ -191,7 +191,7 @@ const SchemeSet& scheme_allowlist() { return scheme_allowlist_; } // The BookmarkModel; may be null when testing. - raw_ptr<bookmarks::BookmarkModel> bookmark_model_; + raw_ptr<bookmarks::CoreBookmarkModel> bookmark_model_; // The HistoryService; may be null when testing. raw_ptr<history::HistoryService> history_service_;
diff --git a/components/omnibox/browser/omnibox_client.cc b/components/omnibox/browser/omnibox_client.cc index 4c9a5ae..6678d48 100644 --- a/components/omnibox/browser/omnibox_client.cc +++ b/components/omnibox/browser/omnibox_client.cc
@@ -37,7 +37,7 @@ return true; } -bookmarks::BookmarkModel* OmniboxClient::GetBookmarkModel() { +bookmarks::CoreBookmarkModel* OmniboxClient::GetBookmarkModel() { return nullptr; }
diff --git a/components/omnibox/browser/omnibox_client.h b/components/omnibox/browser/omnibox_client.h index 59589e2b..139a3d11 100644 --- a/components/omnibox/browser/omnibox_client.h +++ b/components/omnibox/browser/omnibox_client.h
@@ -31,7 +31,7 @@ struct OmniboxLog; namespace bookmarks { -class BookmarkModel; +class CoreBookmarkModel; } namespace gfx { @@ -95,7 +95,7 @@ omnibox::mojom::NavigationPredictor navigation_predictor) {} virtual PrefService* GetPrefs() = 0; - virtual bookmarks::BookmarkModel* GetBookmarkModel(); + virtual bookmarks::CoreBookmarkModel* GetBookmarkModel(); virtual AutocompleteControllerEmitter* GetAutocompleteControllerEmitter() = 0; virtual TemplateURLService* GetTemplateURLService(); virtual const AutocompleteSchemeClassifier& GetSchemeClassifier() const = 0;
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc index 38006c1..811c4bd 100644 --- a/components/omnibox/browser/omnibox_edit_model.cc +++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -24,7 +24,7 @@ #include "base/trace_event/typed_macros.h" #include "build/branding_buildflags.h" #include "build/build_config.h" -#include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/browser/core_bookmark_model.h" #include "components/dom_distiller/core/url_constants.h" #include "components/dom_distiller/core/url_utils.h" #include "components/navigation_metrics/navigation_metrics.h" @@ -84,7 +84,7 @@ #include "components/vector_icons/vector_icons.h" // nogncheck #endif -using bookmarks::BookmarkModel; +using bookmarks::CoreBookmarkModel; using metrics::OmniboxEventProto; using omnibox::mojom::NavigationPredictor; @@ -2572,7 +2572,8 @@ deviation_char_in_hostname); } - BookmarkModel* bookmark_model = controller_->client()->GetBookmarkModel(); + CoreBookmarkModel* bookmark_model = + controller_->client()->GetBookmarkModel(); if (bookmark_model && bookmark_model->IsBookmarked(destination_url)) { controller_->client()->OnBookmarkLaunched(); }
diff --git a/components/omnibox/browser/omnibox_view.cc b/components/omnibox/browser/omnibox_view.cc index 7974d08b..5e51c40 100644 --- a/components/omnibox/browser/omnibox_view.cc +++ b/components/omnibox/browser/omnibox_view.cc
@@ -18,7 +18,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" -#include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/browser/core_bookmark_model.h" #include "components/omnibox/browser/autocomplete_controller.h" #include "components/omnibox/browser/autocomplete_input.h" #include "components/omnibox/browser/autocomplete_match.h" @@ -247,7 +247,7 @@ // If it's never called, the vector icon we provide below should remain. // For bookmarked suggestions, display bookmark icon. - bookmarks::BookmarkModel* bookmark_model = + bookmarks::CoreBookmarkModel* bookmark_model = controller_->client()->GetBookmarkModel(); const bool is_bookmarked = bookmark_model && bookmark_model->IsBookmarked(match.destination_url);
diff --git a/components/omnibox/browser/test_omnibox_client.h b/components/omnibox/browser/test_omnibox_client.h index 07b1f06d..86d561e 100644 --- a/components/omnibox/browser/test_omnibox_client.h +++ b/components/omnibox/browser/test_omnibox_client.h
@@ -65,7 +65,7 @@ const AutocompleteMatch& alternative_nav_match, IDNA2008DeviationCharacter deviation_char_in_hostname)); MOCK_METHOD(LocationBarModel*, GetLocationBarModel, ()); - MOCK_METHOD(bookmarks::BookmarkModel*, GetBookmarkModel, ()); + MOCK_METHOD(bookmarks::CoreBookmarkModel*, GetBookmarkModel, ()); MOCK_METHOD(PrefService*, GetPrefs, ()); base::WeakPtr<OmniboxClient> AsWeakPtr() override;
diff --git a/components/omnibox/browser/url_index_private_data.cc b/components/omnibox/browser/url_index_private_data.cc index 6e25194..ef3f8015 100644 --- a/components/omnibox/browser/url_index_private_data.cc +++ b/components/omnibox/browser/url_index_private_data.cc
@@ -29,8 +29,8 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "base/trace_event/memory_usage_estimator.h" -#include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_utils.h" +#include "components/bookmarks/browser/core_bookmark_model.h" #include "components/history/core/browser/history_database.h" #include "components/history/core/browser/history_db_task.h" #include "components/history/core/browser/history_service.h" @@ -129,7 +129,7 @@ size_t cursor_position, const std::string& host_filter, size_t max_matches, - bookmarks::BookmarkModel* bookmark_model, + bookmarks::CoreBookmarkModel* bookmark_model, TemplateURLService* template_url_service, OmniboxTriggeredFeatureService* triggered_feature_service) { // This list will contain the original search string and any other string @@ -647,7 +647,7 @@ const std::u16string& lower_raw_string, const std::string& host_filter, const TemplateURLService* template_url_service, - bookmarks::BookmarkModel* bookmark_model, + bookmarks::CoreBookmarkModel* bookmark_model, ScoredHistoryMatches* scored_items, OmniboxTriggeredFeatureService* triggered_feature_service) const { if (history_ids.empty())
diff --git a/components/omnibox/browser/url_index_private_data.h b/components/omnibox/browser/url_index_private_data.h index ddb274e..de8b8ad 100644 --- a/components/omnibox/browser/url_index_private_data.h +++ b/components/omnibox/browser/url_index_private_data.h
@@ -26,7 +26,7 @@ class TemplateURLService; namespace bookmarks { -class BookmarkModel; +class CoreBookmarkModel; } namespace history { @@ -72,7 +72,7 @@ size_t cursor_position, const std::string& host_filter, size_t max_matches, - bookmarks::BookmarkModel* bookmark_model, + bookmarks::CoreBookmarkModel* bookmark_model, TemplateURLService* template_url_service, OmniboxTriggeredFeatureService* triggered_feature_service); @@ -254,7 +254,7 @@ const std::u16string& lower_raw_string, const std::string& host_filter, const TemplateURLService* template_url_service, - bookmarks::BookmarkModel* bookmark_model, + bookmarks::CoreBookmarkModel* bookmark_model, ScoredHistoryMatches* scored_items, OmniboxTriggeredFeatureService* triggered_feature_service) const;
diff --git a/components/password_manager/core/browser/features/password_features.cc b/components/password_manager/core/browser/features/password_features.cc index 86237ca..140fb2c 100644 --- a/components/password_manager/core/browser/features/password_features.cc +++ b/components/password_manager/core/browser/features/password_features.cc
@@ -161,7 +161,7 @@ BASE_FEATURE(kUsernameFirstFlowWithIntermediateValues, "UsernameFirstFlowWithIntermediateValues", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); extern const base::FeatureParam<int> kSingleUsernameTimeToLive{ &kUsernameFirstFlowWithIntermediateValues, /*name=*/"ttl", /*default_value=*/5};
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc index 2236387..e7c8bba 100644 --- a/components/password_manager/core/browser/password_autofill_manager.cc +++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -37,7 +37,6 @@ #include "components/password_manager/core/browser/password_manager_driver.h" #include "components/password_manager/core/browser/password_manager_metrics_recorder.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" -#include "components/password_manager/core/browser/password_manager_util.h" #include "components/password_manager/core/browser/password_manual_fallback_flow.h" #include "components/password_manager/core/browser/password_ui_utils.h" #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" @@ -267,8 +266,8 @@ password_client_->GetDeviceAuthenticator(); // Note: this is currently only implemented on Android, Mac and Windows. // For other platforms, the `authenticator` will be null. - if (!password_manager_util::CanUseBiometricAuth(authenticator.get(), - password_client_)) { + if (!password_client_->CanUseBiometricAuthForFilling( + authenticator.get())) { bool success = FillSuggestion( GetUsernameFromSuggestion(suggestion.main_text.value), suggestion.popup_item_id);
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc index c7a76d0..fe030f64 100644 --- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc +++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -63,13 +63,8 @@ #include "ui/gfx/image/image_unittest_util.h" #if BUILDFLAG(IS_ANDROID) -#include "base/android/build_info.h" #include "components/webauthn/android/cred_man_support.h" #include "components/webauthn/android/webauthn_cred_man_delegate.h" -#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) -#include "components/password_manager/core/common/password_manager_pref_names.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/prefs/testing_pref_service.h" #endif // The name of the username/password element in the form. @@ -111,8 +106,6 @@ class AutofillPopupDelegate; } -class PrefService; - namespace password_manager { namespace { @@ -201,12 +194,14 @@ GetWebAuthnCredentialsDelegateForDriver, (PasswordManagerDriver*), (override)); - MOCK_METHOD(PrefService*, GetPrefs, (), (const, override)); - MOCK_METHOD(PrefService*, GetLocalStatePrefs, (), (const, override)); MOCK_METHOD(std::unique_ptr<device_reauth::DeviceAuthenticator>, GetDeviceAuthenticator, (), (override)); + MOCK_METHOD(bool, + CanUseBiometricAuthForFilling, + (device_reauth::DeviceAuthenticator*), + (override)); private: MockPasswordManagerDriver driver_; @@ -340,15 +335,6 @@ webauthn_credentials_delegate_ = std::make_unique<MockWebAuthnCredentialsDelegate>(); -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - test_pref_service_ = std::make_unique<TestingPrefServiceSimple>(); - test_pref_service_->registry()->RegisterBooleanPref( - password_manager::prefs::kBiometricAuthenticationBeforeFilling, true); - ON_CALL(*client, GetPrefs()) - .WillByDefault(Return(test_pref_service_.get())); - ON_CALL(*client, GetLocalStatePrefs()) - .WillByDefault(Return(test_pref_service_.get())); -#endif ON_CALL(*client, GetWebAuthnCredentialsDelegateForDriver) .WillByDefault(Return(webauthn_credentials_delegate_.get())); @@ -376,14 +362,6 @@ EXPECT_CALL(*authenticator, AuthenticateWithMessage); } -#if BUILDFLAG(IS_ANDROID) - void ExpectAndSimulateAuthenticationSuccess( - device_reauth::MockDeviceAuthenticator* authenticator) { - EXPECT_CALL(*authenticator, AuthenticateWithMessage) - .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/true)); - } -#endif - MockPasswordSuggestionFlow& manual_fallback_flow() { return *static_cast<MockPasswordSuggestionFlow*>( password_autofill_manager_->manual_fallback_flow()); @@ -406,9 +384,6 @@ // The TestAutofillDriver uses a SequencedWorkerPool which expects the // existence of a MessageLoop. base::test::SingleThreadTaskEnvironment task_environment_; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - std::unique_ptr<TestingPrefServiceSimple> test_pref_service_; -#endif }; TEST_F(PasswordAutofillManagerTest, FillSuggestion) { @@ -453,16 +428,6 @@ // suggestions. TEST_F(PasswordAutofillManagerTest, ExternalDelegatePasswordSuggestions) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_ANDROID) - if (base::android::BuildInfo::GetInstance()->is_automotive()) { - auto authenticator = - std::make_unique<device_reauth::MockDeviceAuthenticator>(); - ExpectAndSimulateAuthenticationSuccess(authenticator.get()); - ON_CALL(client, GetDeviceAuthenticator) - .WillByDefault(Return(testing::ByMove(std::move(authenticator)))); - } -#endif - NiceMock<MockAutofillClient> autofill_client; InitializePasswordAutofillManager(&client, &autofill_client); @@ -517,16 +482,6 @@ TEST_F(PasswordAutofillManagerTest, ExternalDelegateAccountStorePasswordSuggestions) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_ANDROID) - if (base::android::BuildInfo::GetInstance()->is_automotive()) { - auto authenticator = - std::make_unique<device_reauth::MockDeviceAuthenticator>(); - ExpectAndSimulateAuthenticationSuccess(authenticator.get()); - ON_CALL(client, GetDeviceAuthenticator) - .WillByDefault(Return(testing::ByMove(std::move(authenticator)))); - } -#endif - NiceMock<MockAutofillClient> autofill_client; InitializePasswordAutofillManager(&client, &autofill_client); @@ -1174,16 +1129,6 @@ TEST_F(PasswordAutofillManagerTest, PreviewAndFillEmptyUsernameSuggestion) { // Initialize PasswordAutofillManager with credentials without username. TestPasswordManagerClient client; -#if BUILDFLAG(IS_ANDROID) - if (base::android::BuildInfo::GetInstance()->is_automotive()) { - auto authenticator = - std::make_unique<device_reauth::MockDeviceAuthenticator>(); - ExpectAndSimulateAuthenticationSuccess(authenticator.get()); - ON_CALL(client, GetDeviceAuthenticator) - .WillByDefault(Return(testing::ByMove(std::move(authenticator)))); - } -#endif - NiceMock<MockAutofillClient> autofill_client; fill_data().preferred_login.username_value.clear(); InitializePasswordAutofillManager(&client, &autofill_client); @@ -1512,12 +1457,6 @@ } TEST_F(PasswordAutofillManagerTest, FillsSuggestionIfAuthNotAvailable) { -#if BUILDFLAG(IS_ANDROID) - if (base::android::BuildInfo::GetInstance()->is_automotive()) { - GTEST_SKIP(); - } -#endif - TestPasswordManagerClient client; NiceMock<MockAutofillClient> autofill_client; @@ -1548,12 +1487,7 @@ EXPECT_CALL(*client.mock_driver(), FillSuggestion(test_username_, test_password_)); -#if BUILDFLAG(IS_ANDROID) - // The authenticator exists, but cannot be used for authentication. - EXPECT_CALL(*authenticator, CanAuthenticateWithBiometrics) - .WillOnce(Return(false)); -#endif - + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(false)); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator)))); @@ -1565,20 +1499,9 @@ SuggestionPosition{.row = 1}); } -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) TEST_F(PasswordAutofillManagerTest, FillsSuggestionIfAuthSuccessful) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); -#else - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kBiometricTouchToFill); -#endif NiceMock<MockAutofillClient> autofill_client; - InitializePasswordAutofillManager(&client, &autofill_client); // Show the popup @@ -1611,8 +1534,7 @@ std::make_unique<device_reauth::MockDeviceAuthenticator>(); // The authenticator exists and is available. - ON_CALL(*authenticator, CanAuthenticateWithBiometrics) - .WillByDefault(Return(true)); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); EXPECT_CALL(*authenticator, AuthenticateWithMessage) .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/true)); @@ -1629,15 +1551,6 @@ TEST_F(PasswordAutofillManagerTest, DoesntFillSuggestionIfAuthFailed) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); -#else - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kBiometricTouchToFill); -#endif NiceMock<MockAutofillClient> autofill_client; auto authenticator = std::make_unique<device_reauth::MockDeviceAuthenticator>(); @@ -1671,8 +1584,7 @@ HideAutofillPopup(autofill::PopupHidingReason::kAcceptSuggestion)); // The authenticator exists and is available. - ON_CALL(*authenticator, CanAuthenticateWithBiometrics) - .WillByDefault(Return(true)); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); EXPECT_CALL(*authenticator, AuthenticateWithMessage) .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/false)); @@ -1689,15 +1601,6 @@ TEST_F(PasswordAutofillManagerTest, CancelsOngoingBiometricAuthOnDestroy) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); -#else - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kBiometricTouchToFill); -#endif NiceMock<MockAutofillClient> autofill_client; auto authenticator = std::make_unique<device_reauth::MockDeviceAuthenticator>(); @@ -1726,7 +1629,8 @@ .Times(0); // The authenticator exists and is available. - ExpectAndAllowAuthentication(authenticator_ptr); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); + EXPECT_CALL(*authenticator, AuthenticateWithMessage); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator)))); @@ -1744,15 +1648,6 @@ TEST_F(PasswordAutofillManagerTest, CancelsOngoingBiometricAuthOnDeleteFillData) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); -#else - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kBiometricTouchToFill); -#endif NiceMock<MockAutofillClient> autofill_client; auto authenticator = std::make_unique<device_reauth::MockDeviceAuthenticator>(); @@ -1781,7 +1676,8 @@ .Times(0); // The authenticator exists and is available. - ExpectAndAllowAuthentication(authenticator_ptr); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); + EXPECT_CALL(*authenticator, AuthenticateWithMessage); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator)))); @@ -1800,15 +1696,6 @@ TEST_F(PasswordAutofillManagerTest, CancelsOngoingBiometricAuthOnFillDataChange) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); -#else - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kBiometricTouchToFill); -#endif NiceMock<MockAutofillClient> autofill_client; auto authenticator = std::make_unique<device_reauth::MockDeviceAuthenticator>(); @@ -1837,7 +1724,8 @@ .Times(0); // The authenticator exists and is available. - ExpectAndAllowAuthentication(authenticator_ptr); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); + EXPECT_CALL(*authenticator, AuthenticateWithMessage); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator)))); @@ -1855,15 +1743,6 @@ TEST_F(PasswordAutofillManagerTest, CancelsOngoingBiometricAuthOnNewRequest) { TestPasswordManagerClient client; -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); -#else - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kBiometricTouchToFill); -#endif NiceMock<MockAutofillClient> autofill_client; auto authenticator = std::make_unique<device_reauth::MockDeviceAuthenticator>(); @@ -1874,7 +1753,8 @@ password_autofill_manager_->OnShowPasswordSuggestions( kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT, std::u16string(), ShowWebAuthnCredentials(false), gfx::RectF()); - ExpectAndAllowAuthentication(authenticator_ptr); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); + EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator)))); @@ -1890,7 +1770,8 @@ // Triggering new authentication should cancel ongoing authentication. EXPECT_CALL(*authenticator_ptr, Cancel()); - ExpectAndAllowAuthentication(authenticator_ptr2); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); + EXPECT_CALL(*authenticator_ptr2, AuthenticateWithMessage); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator2)))); @@ -1904,16 +1785,11 @@ EXPECT_CALL(*authenticator_ptr2, Cancel()); } -#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) TEST_F(PasswordAutofillManagerTest, MetricsRecordedForBiometricAuth) { base::ScopedMockElapsedTimersForTest mock_elapsed_timers_; base::HistogramTester histograms; TestPasswordManagerClient client; - ON_CALL(*client.GetPasswordFeatureManager(), - IsBiometricAuthenticationBeforeFillingEnabled) - .WillByDefault(Return(true)); NiceMock<MockAutofillClient> autofill_client; auto authenticator = std::make_unique<device_reauth::MockDeviceAuthenticator>(); @@ -1930,6 +1806,7 @@ EXPECT_CALL(*authenticator, AuthenticateWithMessage) .WillOnce(MoveArg<1>(&auth_callback)); + EXPECT_CALL(client, CanUseBiometricAuthForFilling).WillOnce(Return(true)); EXPECT_CALL(client, GetDeviceAuthenticator) .WillOnce(Return(testing::ByMove(std::move(authenticator)))); @@ -1954,7 +1831,6 @@ "PasswordManager.PasswordFilling.AuthenticationTime", kMockElapsedTime, 1); } -#endif TEST_F(PasswordAutofillManagerTest, ShowsWebAuthnSuggestions) { #if BUILDFLAG(IS_ANDROID) @@ -2273,7 +2149,6 @@ #endif // !BUILDFLAG(IS_ANDROID) -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) TEST_F(PasswordAutofillManagerTest, NoPreviewSuggestionWithAuthBeforeFilling) { TestPasswordManagerClient client; ON_CALL(*client.GetPasswordFeatureManager(), @@ -2287,7 +2162,6 @@ password_autofill_manager_->PreviewSuggestionForTest(test_username_)); testing::Mock::VerifyAndClearExpectations(client.mock_driver()); } -#endif TEST_F(PasswordAutofillManagerTest, ManualFallback_InvokesFlow) { TestPasswordManagerClient client;
diff --git a/components/password_manager/core/browser/password_manager_client.cc b/components/password_manager/core/browser/password_manager_client.cc index 546d7d64..fb8467f 100644 --- a/components/password_manager/core/browser/password_manager_client.cc +++ b/components/password_manager/core/browser/password_manager_client.cc
@@ -42,6 +42,11 @@ } #endif +bool PasswordManagerClient::CanUseBiometricAuthForFilling( + device_reauth::DeviceAuthenticator*) { + return false; +} + std::unique_ptr<device_reauth::DeviceAuthenticator> PasswordManagerClient::GetDeviceAuthenticator() { return nullptr;
diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h index 7eacba8..5a847213 100644 --- a/components/password_manager/core/browser/password_manager_client.h +++ b/components/password_manager/core/browser/password_manager_client.h
@@ -210,6 +210,9 @@ bool is_webauthn_form); #endif + virtual bool CanUseBiometricAuthForFilling( + device_reauth::DeviceAuthenticator* authenticator); + // Returns a pointer to a DeviceAuthenticator. Might be null if // BiometricAuthentication is not available for a given platform. virtual std::unique_ptr<device_reauth::DeviceAuthenticator>
diff --git a/components/password_manager/core/browser/password_manager_util.h b/components/password_manager/core/browser/password_manager_util.h index 548efb3..7692548 100644 --- a/components/password_manager/core/browser/password_manager_util.h +++ b/components/password_manager/core/browser/password_manager_util.h
@@ -143,6 +143,7 @@ #endif // Helper which checks if biometric authentication is available. +// TODO(b/326735413): Remove this function. bool CanUseBiometricAuth(device_reauth::DeviceAuthenticator* authenticator, password_manager::PasswordManagerClient* client);
diff --git a/components/password_manager/core/browser/password_suggestion_generator.cc b/components/password_manager/core/browser/password_suggestion_generator.cc index f5fc4b0..536fc0e 100644 --- a/components/password_manager/core/browser/password_suggestion_generator.cc +++ b/components/password_manager/core/browser/password_suggestion_generator.cc
@@ -244,11 +244,14 @@ username, autofill::PopupItemId::kPasswordFieldByFieldFilling)); } -void AddFillPasswordChildSuggestion(autofill::Suggestion& suggestion) { - suggestion.children.push_back(autofill::Suggestion( +void AddFillPasswordChildSuggestion(autofill::Suggestion& suggestion, + const std::u16string& password) { + autofill::Suggestion fill_password( l10n_util::GetStringUTF16( IDS_PASSWORD_MANAGER_MANUAL_FALLBACK_FILL_PASSWORD_ENTRY), - autofill::PopupItemId::kFillPassword)); + autofill::PopupItemId::kFillPassword); + fill_password.payload = autofill::Suggestion::ValueToFill(password); + suggestion.children.push_back(fill_password); } void AddViewPasswordDetailsChildSuggestion(autofill::Suggestion& suggestion) { @@ -274,7 +277,7 @@ if (!replaced) { AddPasswordUsernameChildSuggestion(maybe_username, suggestion); } - AddFillPasswordChildSuggestion(suggestion); + AddFillPasswordChildSuggestion(suggestion, credential.password); suggestion.children.push_back( autofill::Suggestion(autofill::PopupItemId::kSeparator)); AddViewPasswordDetailsChildSuggestion(suggestion);
diff --git a/components/password_manager/core/browser/password_suggestion_generator_unittest.cc b/components/password_manager/core/browser/password_suggestion_generator_unittest.cc index 7ed3344..2dfef0a 100644 --- a/components/password_manager/core/browser/password_suggestion_generator_unittest.cc +++ b/components/password_manager/core/browser/password_suggestion_generator_unittest.cc
@@ -193,7 +193,8 @@ EqualsSuggestion( PopupItemId::kFillPassword, l10n_util::GetStringUTF16( - IDS_PASSWORD_MANAGER_MANUAL_FALLBACK_FILL_PASSWORD_ENTRY)), + IDS_PASSWORD_MANAGER_MANUAL_FALLBACK_FILL_PASSWORD_ENTRY), + Suggestion::Icon::kNoIcon, Suggestion::ValueToFill(u"password")), EqualsSuggestion(PopupItemId::kSeparator), EqualsSuggestion( PopupItemId::kViewPasswordDetails,
diff --git a/components/password_manager/core/browser/possible_username_data_unittest.cc b/components/password_manager/core/browser/possible_username_data_unittest.cc index f7f7900b..d5a7fee 100644 --- a/components/password_manager/core/browser/possible_username_data_unittest.cc +++ b/components/password_manager/core/browser/possible_username_data_unittest.cc
@@ -8,6 +8,7 @@ #include "base/test/task_environment.h" #include "base/time/time.h" #include "components/autofill/core/common/unique_ids.h" +#include "components/password_manager/core/browser/features/password_features.h" #include "testing/gtest/include/gtest/gtest.h" using base::Time; @@ -32,15 +33,17 @@ /*is_likely_otp=*/false}; }; -// Check that if more than |kPossibleUsernameExpirationTimeout| time has -// passed since the last change of |possible_username_data_|, then it is stale. +// Checks that `possible_username_data_` becomes stale over some time. TEST_F(IsPossibleUsernameValidTest, IsPossibleUsernameStale) { EXPECT_FALSE(possible_username_data_.IsStale()); // Fast forward for a little less than expiration time, but not // exactly to not flake the test. - task_environment_.FastForwardBy(kPossibleUsernameExpirationTimeout - - base::Seconds(3)); + task_environment_.FastForwardBy( + base::FeatureList::IsEnabled( + features::kUsernameFirstFlowWithIntermediateValues) + ? base::Minutes(features::kSingleUsernameTimeToLive.Get()) + : kPossibleUsernameExpirationTimeout - base::Seconds(3)); EXPECT_FALSE(possible_username_data_.IsStale()); // Fast forward more until the data becomes stale.
diff --git a/components/services/app_service/public/cpp/permission.cc b/components/services/app_service/public/cpp/permission.cc index 16b29d5f6..1b1d820 100644 --- a/components/services/app_service/public/cpp/permission.cc +++ b/components/services/app_service/public/cpp/permission.cc
@@ -6,6 +6,7 @@ #include <sstream> +#include "base/containers/to_value_list.h" #include "third_party/abseil-cpp/absl/types/variant.h" namespace apps { @@ -165,11 +166,7 @@ } base::Value::List ConvertPermissionsToList(const Permissions& permissions) { - base::Value::List list; - for (const auto& permission : permissions) { - list.Append(ConvertPermissionToDict(permission)); - } - return list; + return base::ToValueList(permissions, &ConvertPermissionToDict); } Permissions ConvertListToPermissions(const base::Value::List* list) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 6bd5d01..14a5a76 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -985,8 +985,6 @@ "file_system_access/file_system_access_access_handle_host_impl.h", "file_system_access/file_system_access_bucket_path_watcher.cc", "file_system_access/file_system_access_bucket_path_watcher.h", - "file_system_access/file_system_access_capacity_allocation_host_impl.cc", - "file_system_access/file_system_access_capacity_allocation_host_impl.h", "file_system_access/file_system_access_change_source.cc", "file_system_access/file_system_access_change_source.h", "file_system_access/file_system_access_data_transfer_token_impl.cc", @@ -999,6 +997,8 @@ "file_system_access/file_system_access_file_delegate_host_impl.h", "file_system_access/file_system_access_file_handle_impl.cc", "file_system_access/file_system_access_file_handle_impl.h", + "file_system_access/file_system_access_file_modification_host_impl.cc", + "file_system_access/file_system_access_file_modification_host_impl.h", "file_system_access/file_system_access_file_writer_impl.cc", "file_system_access/file_system_access_file_writer_impl.h", "file_system_access/file_system_access_handle_base.cc",
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc index b4289bc3..d18ee37 100644 --- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc +++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -2929,18 +2929,19 @@ EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Before frame\nText in iframe\nAfter frame"); + // Traversing by word should include trailing whitespace. EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word, /*count*/ 2, /*expected_text*/ L"Text ", /*expected_count*/ 2); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word, /*count*/ -1, - /*expected_text*/ L"frame", + /*expected_text*/ L"frame\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 2, - /*expected_text*/ L"frame\nT", + /*expected_text*/ L"frame\nTe", /*expected_count*/ 2); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character, /*count*/ 7,
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index 5b5fdd27b..2ac028e 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc
@@ -858,7 +858,10 @@ const ui::AXUniqueId& BrowserAccessibility::GetUniqueId() const { // This is not the same as GetData().id which comes from Blink, because // those ids are only unique within the Blink process. We need one that is - // unique for the browser process. + // unique per OS window. + // For example, Windows ATs use this to retrieve IA2 event targets for events + // that are fired on an OS-level window with an id. They also use it to + // save positions via IAccessible2::get_uniqueID(). return unique_id_; }
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index 3c3e9d9..09da79a 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h
@@ -532,6 +532,9 @@ static bool HasInvalidAttribute(const ui::TextAttributeList& attributes); // A unique ID, since node IDs are frame-local. + // TODO(accessibility) We should be able to get rid of this, because node IDs + // are actually local to the renderer process, and each renderer process has + // its own OS-level window, which is all the uniqueness we need. ui::AXUniqueId unique_id_; };
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index 8fbcc79..51791d9 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -161,7 +161,7 @@ // static ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() { ui::AXNodeData empty_document; - empty_document.id = 1; + empty_document.id = ui::kInitialEmptyDocumentRootNodeID; empty_document.role = ax::mojom::Role::kRootWebArea; ui::AXTreeUpdate update; update.root_id = empty_document.id;
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc index 03062124..9bc927a 100644 --- a/content/browser/accessibility/browser_accessibility_manager_android.cc +++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -66,7 +66,7 @@ // static ui::AXTreeUpdate BrowserAccessibilityManagerAndroid::GetEmptyDocument() { ui::AXNodeData empty_document; - empty_document.id = 1; + empty_document.id = ui::kInitialEmptyDocumentRootNodeID; empty_document.role = ax::mojom::Role::kRootWebArea; empty_document.SetRestriction(ax::mojom::Restriction::kReadOnly); ui::AXTreeUpdate update;
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc index 64c3f938..0f976bb 100644 --- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc +++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -58,7 +58,7 @@ // static ui::AXTreeUpdate BrowserAccessibilityManagerAuraLinux::GetEmptyDocument() { ui::AXNodeData empty_document; - empty_document.id = 1; + empty_document.id = ui::kInitialEmptyDocumentRootNodeID; empty_document.role = ax::mojom::Role::kRootWebArea; ui::AXTreeUpdate update; update.root_id = empty_document.id; @@ -301,9 +301,25 @@ FireReadonlyChangedEvent(wrapper); break; case ui::AXEventGenerator::Event::RANGE_VALUE_CHANGED: - DCHECK(wrapper->GetData().IsRangeValueSupported()); - FireEvent(wrapper, ax::mojom::Event::kValueChanged); + // Before firing the platform event, check to see that the object's + // properties still support range values, because some of the properties + // may have been updated after the event was generated. + if (wrapper->GetData().IsRangeValueSupported()) { + FireEvent(wrapper, ax::mojom::Event::kValueChanged); + } break; + case ui::AXEventGenerator::Event::ALERT: + case ui::AXEventGenerator::Event::ROLE_CHANGED: { + // In ATK, there is no role change event, and instead, role changes are + // mapped to a subtree being removed and re-added. Since the AXNodeID + // (and therefore the AXNode) in such cases may remain the same, this must + // be done manually. + ui::AXPlatformNodeAuraLinux* platform_node = + ToBrowserAccessibilityAuraLinux(wrapper)->GetNode(); + platform_node->OnSubtreeWillBeDeleted(); + platform_node->OnSubtreeCreated(); + break; + } case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED: FireEvent(wrapper, ax::mojom::Event::kSelectedChildrenChanged); break; @@ -332,7 +348,6 @@ // Currently unused events on this platform. case ui::AXEventGenerator::Event::NONE: case ui::AXEventGenerator::Event::ACCESS_KEY_CHANGED: - case ui::AXEventGenerator::Event::ALERT: case ui::AXEventGenerator::Event::ATOMIC_CHANGED: case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED: case ui::AXEventGenerator::Event::AUTOFILL_AVAILABILITY_CHANGED: @@ -375,7 +390,6 @@ case ui::AXEventGenerator::Event::RANGE_VALUE_STEP_CHANGED: case ui::AXEventGenerator::Event::RELATED_NODE_CHANGED: case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED: - case ui::AXEventGenerator::Event::ROLE_CHANGED: case ui::AXEventGenerator::Event::ROW_COUNT_CHANGED: case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED: case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm index ce3623b..96979b6 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -63,7 +63,7 @@ // static ui::AXTreeUpdate BrowserAccessibilityManagerMac::GetEmptyDocument() { ui::AXNodeData empty_document; - empty_document.id = 1; + empty_document.id = ui::kInitialEmptyDocumentRootNodeID; empty_document.role = ax::mojom::Role::kRootWebArea; ui::AXTreeUpdate update; update.root_id = empty_document.id;
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc index 60ab68326..9368849c 100644 --- a/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -442,6 +442,7 @@ HandleAriaPropertiesChangedEvent(*wrapper); break; case ui::AXEventGenerator::Event::ROLE_CHANGED: + FireWinAccessibilityEvent(IA2_EVENT_ROLE_CHANGED, wrapper); FireUiaPropertyChangedEvent(UIA_AriaRolePropertyId, wrapper); break; case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED:
diff --git a/content/browser/file_system_access/file_system_access_access_handle_host_impl.cc b/content/browser/file_system_access/file_system_access_access_handle_host_impl.cc index 4bd602b..2a7342a 100644 --- a/content/browser/file_system_access/file_system_access_access_handle_host_impl.cc +++ b/content/browser/file_system_access/file_system_access_access_handle_host_impl.cc
@@ -6,8 +6,8 @@ #include "base/feature_list.h" #include "base/functional/callback_helpers.h" -#include "content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h" #include "content/browser/file_system_access/file_system_access_file_delegate_host_impl.h" +#include "content/browser/file_system_access/file_system_access_file_modification_host_impl.h" #include "storage/browser/file_system/file_system_context.h" #include "third_party/blink/public/common/features_generated.h" @@ -22,8 +22,8 @@ receiver, mojo::PendingReceiver<blink::mojom::FileSystemAccessFileDelegateHost> file_delegate_receiver, - mojo::PendingReceiver<blink::mojom::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_receiver, + mojo::PendingReceiver<blink::mojom::FileSystemAccessFileModificationHost> + file_modification_host_receiver, int64_t file_size, base::ScopedClosureRunner on_close_callback) : manager_(manager), @@ -48,13 +48,13 @@ std::move(file_delegate_receiver)) : nullptr; - // Only create a capacity allocation host in non-incognito mode. - capacity_allocation_host_ = + // Only create a file modification host in non-incognito mode. + file_modification_host_ = !manager_->context()->is_incognito() - ? std::make_unique<FileSystemAccessCapacityAllocationHostImpl>( + ? std::make_unique<FileSystemAccessFileModificationHostImpl>( manager_, url_, base::PassKey<FileSystemAccessAccessHandleHostImpl>(), - std::move(capacity_allocation_host_receiver), file_size) + std::move(file_modification_host_receiver), file_size) : nullptr; receiver_.set_disconnect_handler( @@ -72,8 +72,8 @@ return; } - // Run `callback` when this instance is destroyed, after capacity allocation - // has been released. + // Run `callback` when this instance is destroyed, after file modification + // host has been released. close_callback_ = base::ScopedClosureRunner(std::move(callback)); // Removes `this`.
diff --git a/content/browser/file_system_access/file_system_access_access_handle_host_impl.h b/content/browser/file_system_access/file_system_access_access_handle_host_impl.h index 3d7c5ef9..ae589651 100644 --- a/content/browser/file_system_access/file_system_access_access_handle_host_impl.h +++ b/content/browser/file_system_access/file_system_access_access_handle_host_impl.h
@@ -10,8 +10,8 @@ #include "base/functional/callback_helpers.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" -#include "content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h" #include "content/browser/file_system_access/file_system_access_file_delegate_host_impl.h" +#include "content/browser/file_system_access/file_system_access_file_modification_host_impl.h" #include "content/browser/file_system_access/file_system_access_manager_impl.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -39,9 +39,8 @@ receiver, mojo::PendingReceiver<blink::mojom::FileSystemAccessFileDelegateHost> file_delegate_receiver, - mojo::PendingReceiver< - blink::mojom::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_receiver, + mojo::PendingReceiver<blink::mojom::FileSystemAccessFileModificationHost> + file_modification_host_receiver, int64_t file_size, base::ScopedClosureRunner on_close_callback); FileSystemAccessAccessHandleHostImpl( @@ -56,9 +55,9 @@ // Returns the the total capacity allocated for the file whose capacity is // managed through this host. int64_t granted_capacity() const { - DCHECK(capacity_allocation_host_) - << "Capacity allocation requires a CapacityAllocationHost"; - return capacity_allocation_host_->granted_capacity(); + DCHECK(file_modification_host_) + << "Capacity allocation requires a FileModificationHost"; + return file_modification_host_->granted_capacity(); } storage::FileSystemURL url() const { return url_; } @@ -81,7 +80,7 @@ // Non-incognito file I/O operations on Access Handles are performed in the // renderer process. Before increasing a file's size, the renderer must // request additional capacity from the - // FileSystemAccessCapacityAllocationHostImpl. The host grants capacity if the + // FileSystemAccessFileModificationHostImpl. The host grants capacity if the // quota management system allows it. From the browser's perspective, all // granted capacity is fully used by the file. // @@ -89,8 +88,8 @@ // between the perceived file size, as reported by `granted_capacity()`, and // the actual file size on disk. This step is // performed by the FileSystemAccessManagerImpl owning this host. - std::unique_ptr<FileSystemAccessCapacityAllocationHostImpl> - capacity_allocation_host_; + std::unique_ptr<FileSystemAccessFileModificationHostImpl> + file_modification_host_; const storage::FileSystemURL url_;
diff --git a/content/browser/file_system_access/file_system_access_file_handle_impl.cc b/content/browser/file_system_access/file_system_access_file_handle_impl.cc index e445f031..47e3a0d 100644 --- a/content/browser/file_system_access/file_system_access_file_handle_impl.cc +++ b/content/browser/file_system_access/file_system_access_file_handle_impl.cc
@@ -37,10 +37,10 @@ #include "storage/common/file_system/file_system_types.h" #include "third_party/blink/public/mojom/blob/blob.mojom.h" #include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_cloud_identifier.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_transfer_token.mojom.h" #if BUILDFLAG(IS_WIN) @@ -400,12 +400,12 @@ } DCHECK_GE(length_or_error.value(), 0); - mojo::PendingRemote<blink::mojom::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_remote; + mojo::PendingRemote<blink::mojom::FileSystemAccessFileModificationHost> + file_modification_host_remote; mojo::PendingRemote<blink::mojom::FileSystemAccessAccessHandleHost> access_handle_host_remote = manager()->CreateAccessHandleHost( url(), mojo::NullReceiver(), - capacity_allocation_host_remote.InitWithNewPipeAndPassReceiver(), + file_modification_host_remote.InitWithNewPipeAndPassReceiver(), length_or_error.value(), std::move(lock), std::move(on_close_callback)); @@ -414,7 +414,7 @@ blink::mojom::FileSystemAccessAccessHandleFile::NewRegularFile( blink::mojom::FileSystemAccessRegularFile::New( std::move(file), length_or_error.value(), - std::move(capacity_allocation_host_remote))), + std::move(file_modification_host_remote))), std::move(access_handle_host_remote)); }
diff --git a/content/browser/file_system_access/file_system_access_file_handle_impl.h b/content/browser/file_system_access/file_system_access_file_handle_impl.h index 701c8461..226384e 100644 --- a/content/browser/file_system_access/file_system_access_file_handle_impl.h +++ b/content/browser/file_system_access/file_system_access_file_handle_impl.h
@@ -17,8 +17,8 @@ #include "content/common/content_export.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "storage/browser/file_system/file_system_url.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom.h" namespace content {
diff --git a/content/browser/file_system_access/file_system_access_file_handle_impl_unittest.cc b/content/browser/file_system_access/file_system_access_file_handle_impl_unittest.cc index 7aba457..b0e3ed7 100644 --- a/content/browser/file_system_access/file_system_access_file_handle_impl_unittest.cc +++ b/content/browser/file_system_access/file_system_access_file_handle_impl_unittest.cc
@@ -528,7 +528,7 @@ std::move(file->get_regular_file()); EXPECT_TRUE(regular_file->os_file.IsValid()); EXPECT_EQ(regular_file->file_size, 0); - EXPECT_TRUE(regular_file->capacity_allocation_host.is_valid()); + EXPECT_TRUE(regular_file->file_modification_host.is_valid()); EXPECT_TRUE(access_handle_remote.is_valid()); }
diff --git a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.cc b/content/browser/file_system_access/file_system_access_file_modification_host_impl.cc similarity index 73% rename from content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.cc rename to content/browser/file_system_access/file_system_access_file_modification_host_impl.cc index 2afbe3e..27c8208 100644 --- a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.cc +++ b/content/browser/file_system_access/file_system_access_file_modification_host_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h" +#include "content/browser/file_system_access/file_system_access_file_modification_host_impl.h" #include "base/memory/weak_ptr.h" #include "base/task/sequenced_task_runner.h" @@ -16,13 +16,13 @@ namespace content { -FileSystemAccessCapacityAllocationHostImpl:: - FileSystemAccessCapacityAllocationHostImpl( +FileSystemAccessFileModificationHostImpl:: + FileSystemAccessFileModificationHostImpl( FileSystemAccessManagerImpl* manager, const storage::FileSystemURL& url, base::PassKey<FileSystemAccessAccessHandleHostImpl> pass_key, mojo::PendingReceiver< - blink::mojom::FileSystemAccessCapacityAllocationHost> receiver, + blink::mojom::FileSystemAccessFileModificationHost> receiver, int64_t file_size) : manager_(manager), url_(url), @@ -31,22 +31,22 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(manager_); // base::Unretained is safe here because this - // FileSystemAccessCapacityAllocationHostImpl owns `receiver_`. So, the - // unretained FileSystemAccessCapacityAllocationHostImpl is guaranteed to + // FileSystemAccessFileModificationHostImpl owns `receiver_`. So, the + // unretained FileSystemAccessFileModificationHostImpl is guaranteed to // outlive `receiver_` and the closure that it uses. receiver_.set_disconnect_handler(base::BindOnce( - &FileSystemAccessCapacityAllocationHostImpl::OnReceiverDisconnect, + &FileSystemAccessFileModificationHostImpl::OnReceiverDisconnect, base::Unretained(this))); } // Constructor for testing. -FileSystemAccessCapacityAllocationHostImpl:: - FileSystemAccessCapacityAllocationHostImpl( +FileSystemAccessFileModificationHostImpl:: + FileSystemAccessFileModificationHostImpl( FileSystemAccessManagerImpl* manager, const storage::FileSystemURL& url, - base::PassKey<FileSystemAccessCapacityAllocationHostImplTest> pass_key, + base::PassKey<FileSystemAccessFileModificationHostImplTest> pass_key, mojo::PendingReceiver< - blink::mojom::FileSystemAccessCapacityAllocationHost> receiver, + blink::mojom::FileSystemAccessFileModificationHost> receiver, int64_t file_size) : manager_(manager), url_(url), @@ -55,23 +55,23 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(manager_); // base::Unretained is safe here because this - // FileSystemAccessCapacityAllocationHostImpl owns `receiver_`. So, the - // unretained FileSystemAccessCapacityAllocationHostImpl is guaranteed to + // FileSystemAccessFileModificationHostImpl owns `receiver_`. So, the + // unretained FileSystemAccessFileModificationHostImpl is guaranteed to // outlive `receiver_` and the closure that it uses. receiver_.set_disconnect_handler(base::BindOnce( - &FileSystemAccessCapacityAllocationHostImpl::OnReceiverDisconnect, + &FileSystemAccessFileModificationHostImpl::OnReceiverDisconnect, base::Unretained(this))); } -FileSystemAccessCapacityAllocationHostImpl:: - ~FileSystemAccessCapacityAllocationHostImpl() = default; +FileSystemAccessFileModificationHostImpl:: + ~FileSystemAccessFileModificationHostImpl() = default; -void FileSystemAccessCapacityAllocationHostImpl::OnReceiverDisconnect() { +void FileSystemAccessFileModificationHostImpl::OnReceiverDisconnect() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); receiver_.reset(); } -void FileSystemAccessCapacityAllocationHostImpl::RequestCapacityChange( +void FileSystemAccessFileModificationHostImpl::RequestCapacityChange( int64_t capacity_delta, RequestCapacityChangeCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -92,11 +92,11 @@ url_.storage_key(), blink::mojom::StorageType::kTemporary, base::SequencedTaskRunner::GetCurrentDefault(), base::BindOnce( - &FileSystemAccessCapacityAllocationHostImpl::DidGetUsageAndQuota, + &FileSystemAccessFileModificationHostImpl::DidGetUsageAndQuota, weak_factory_.GetWeakPtr(), capacity_delta, std::move(callback))); } -void FileSystemAccessCapacityAllocationHostImpl::DidGetUsageAndQuota( +void FileSystemAccessFileModificationHostImpl::DidGetUsageAndQuota( int64_t capacity_delta, RequestCapacityChangeCallback callback, blink::mojom::QuotaStatusCode status, @@ -121,7 +121,7 @@ std::move(callback).Run(capacity_delta); } -void FileSystemAccessCapacityAllocationHostImpl::OnContentsModified() { +void FileSystemAccessFileModificationHostImpl::OnContentsModified() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (const storage::ChangeObserverList* change_observers =
diff --git a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h b/content/browser/file_system_access/file_system_access_file_modification_host_impl.h similarity index 65% rename from content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h rename to content/browser/file_system_access/file_system_access_file_modification_host_impl.h index 1d0a91c..8ab49c7 100644 --- a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h +++ b/content/browser/file_system_access/file_system_access_file_modification_host_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_CAPACITY_ALLOCATION_HOST_IMPL_H_ -#define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_CAPACITY_ALLOCATION_HOST_IMPL_H_ +#ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_FILE_MODIFICATION_HOST_IMPL_H_ +#define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_FILE_MODIFICATION_HOST_IMPL_H_ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" @@ -15,7 +15,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/browser/file_system/file_system_url.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom.h" namespace storage { class QuotaManagerProxy; @@ -23,43 +23,43 @@ namespace content { -class FileSystemAccessCapacityAllocationHostImplTest; +class FileSystemAccessFileModificationHostImplTest; // This is the browser side implementation of the -// FileSystemAccessCapacityAllocationHost mojom interface. Instances of this +// FileSystemAccessFileModificationHost mojom interface. Instances of this // class are owned by the FileSystemAccessHandleHost instance passed in to the // constructor. -class CONTENT_EXPORT FileSystemAccessCapacityAllocationHostImpl - : public blink::mojom::FileSystemAccessCapacityAllocationHost { +class CONTENT_EXPORT FileSystemAccessFileModificationHostImpl + : public blink::mojom::FileSystemAccessFileModificationHost { public: - // Creates a FileSystemAccessCapacityAllocationHost that manages capacity - // reservations for the file. CapacityAllocationHosts should only be created + // Creates a FileSystemAccessFileModificationHost that manages capacity + // reservations for the file. FileModificationHosts should only be created // via the FileSystemAccessHandleHost. - FileSystemAccessCapacityAllocationHostImpl( + FileSystemAccessFileModificationHostImpl( FileSystemAccessManagerImpl* manager, const storage::FileSystemURL& url, base::PassKey<FileSystemAccessAccessHandleHostImpl> pass_key, - mojo::PendingReceiver< - blink::mojom::FileSystemAccessCapacityAllocationHost> receiver, + mojo::PendingReceiver<blink::mojom::FileSystemAccessFileModificationHost> + receiver, int64_t file_size); // Constructor for testing - FileSystemAccessCapacityAllocationHostImpl( + FileSystemAccessFileModificationHostImpl( FileSystemAccessManagerImpl* manager, const storage::FileSystemURL& url, - base::PassKey<FileSystemAccessCapacityAllocationHostImplTest> pass_key, - mojo::PendingReceiver< - blink::mojom::FileSystemAccessCapacityAllocationHost> receiver, + base::PassKey<FileSystemAccessFileModificationHostImplTest> pass_key, + mojo::PendingReceiver<blink::mojom::FileSystemAccessFileModificationHost> + receiver, int64_t file_size); - FileSystemAccessCapacityAllocationHostImpl( - const FileSystemAccessCapacityAllocationHostImpl&) = delete; - FileSystemAccessCapacityAllocationHostImpl& operator=( - const FileSystemAccessCapacityAllocationHostImpl&) = delete; + FileSystemAccessFileModificationHostImpl( + const FileSystemAccessFileModificationHostImpl&) = delete; + FileSystemAccessFileModificationHostImpl& operator=( + const FileSystemAccessFileModificationHostImpl&) = delete; - ~FileSystemAccessCapacityAllocationHostImpl() override; + ~FileSystemAccessFileModificationHostImpl() override; - // blink::mojom::FileSystemAccessCapacityAllocationHost: + // blink::mojom::FileSystemAccessFileModificationHost: void RequestCapacityChange(int64_t capacity_delta, RequestCapacityChangeCallback callback) override; void OnContentsModified() override; @@ -94,7 +94,7 @@ // URL of the file whose capacity is managed through this host. const storage::FileSystemURL url_; - mojo::Receiver<blink::mojom::FileSystemAccessCapacityAllocationHost> receiver_ + mojo::Receiver<blink::mojom::FileSystemAccessFileModificationHost> receiver_ GUARDED_BY_CONTEXT(sequence_checker_); // Total capacity granted to the file managed through this host. Initially, @@ -102,10 +102,10 @@ // reaching `RequestCapacityChange()`. int64_t granted_capacity_ GUARDED_BY_CONTEXT(sequence_checker_); - base::WeakPtrFactory<FileSystemAccessCapacityAllocationHostImpl> weak_factory_ + base::WeakPtrFactory<FileSystemAccessFileModificationHostImpl> weak_factory_ GUARDED_BY_CONTEXT(sequence_checker_){this}; }; } // namespace content -#endif // CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_CAPACITY_ALLOCATION_HOST_IMPL_H_ +#endif // CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_FILE_MODIFICATION_HOST_IMPL_H_
diff --git a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc b/content/browser/file_system_access/file_system_access_file_modification_host_impl_browsertest.cc similarity index 96% rename from content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc rename to content/browser/file_system_access/file_system_access_file_modification_host_impl_browsertest.cc index 07b1644..d371423 100644 --- a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc +++ b/content/browser/file_system_access/file_system_access_file_modification_host_impl_browsertest.cc
@@ -26,8 +26,8 @@ namespace content { // This browser test implements end-to-end tests for -// FileSystemAccessCapacityAllocationHostImpl. -class FileSystemAccessCapacityAllocationHostImplBrowserTest +// FileSystemAccessFileModificationHostImpl. +class FileSystemAccessFileModificationHostImplBrowserTest : public ContentBrowserTest { public: void SetUpOnMainThread() override { @@ -73,7 +73,7 @@ base::ScopedTempDir temp_dir_; }; -IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest, +IN_PROC_BROWSER_TEST_F(FileSystemAccessFileModificationHostImplBrowserTest, QuotaUsageAfterClosing) { const GURL& test_url = embedded_test_server()->GetURL("/run_async_code_on_worker.html"); @@ -114,7 +114,7 @@ EXPECT_EQ(usage_after_operation, usage_before_operation + 10); } -IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest, +IN_PROC_BROWSER_TEST_F(FileSystemAccessFileModificationHostImplBrowserTest, QuotaUsageAfterForNonemptyFile) { const GURL& test_url = embedded_test_server()->GetURL("/run_async_code_on_worker.html"); @@ -168,7 +168,7 @@ #else #define MAYBE_QuotaUsageOverallocation QuotaUsageOverallocation #endif -IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest, +IN_PROC_BROWSER_TEST_F(FileSystemAccessFileModificationHostImplBrowserTest, MAYBE_QuotaUsageOverallocation) { // TODO(https://crbug.com/1240056): Implement a more sophisticated test suite // for this feature. @@ -215,7 +215,7 @@ // TODO(crbug.com/1304977): Failing on Mac, Linux, and ChromeOS builders. // TODO(crbug.com/1459385): Re-enable this test -IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest, +IN_PROC_BROWSER_TEST_F(FileSystemAccessFileModificationHostImplBrowserTest, DISABLED_QuotaUsageShrinks) { const GURL& test_url = embedded_test_server()->GetURL("/run_async_code_on_worker.html"); @@ -258,7 +258,7 @@ 1024 * 1024); } -IN_PROC_BROWSER_TEST_F(FileSystemAccessCapacityAllocationHostImplBrowserTest, +IN_PROC_BROWSER_TEST_F(FileSystemAccessFileModificationHostImplBrowserTest, QuotaUsageWrite) { const GURL& test_url = embedded_test_server()->GetURL("/run_async_code_on_worker.html");
diff --git a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_unittest.cc b/content/browser/file_system_access/file_system_access_file_modification_host_impl_unittest.cc similarity index 88% rename from content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_unittest.cc rename to content/browser/file_system_access/file_system_access_file_modification_host_impl_unittest.cc index 91a8ce4..c19b170 100644 --- a/content/browser/file_system_access/file_system_access_capacity_allocation_host_impl_unittest.cc +++ b/content/browser/file_system_access/file_system_access_file_modification_host_impl_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/file_system_access/file_system_access_capacity_allocation_host_impl.h" +#include "content/browser/file_system_access/file_system_access_file_modification_host_impl.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" @@ -29,10 +29,10 @@ namespace { -// Synchronous proxy to FileSystemAccessCapacityAllocationHostImpl's +// Synchronous proxy to FileSystemAccessFileModificationHostImpl's // RequestCapacityChange. int64_t RequestCapacityChangeSync( - FileSystemAccessCapacityAllocationHostImpl* allocation_host, + FileSystemAccessFileModificationHostImpl* allocation_host, int64_t capacity_delta) { base::test::TestFuture<int64_t> future; allocation_host->RequestCapacityChange(capacity_delta, future.GetCallback()); @@ -42,9 +42,9 @@ } // namespace -class FileSystemAccessCapacityAllocationHostImplTest : public testing::Test { +class FileSystemAccessFileModificationHostImplTest : public testing::Test { public: - FileSystemAccessCapacityAllocationHostImplTest() + FileSystemAccessFileModificationHostImplTest() : special_storage_policy_( base::MakeRefCounted<storage::MockSpecialStoragePolicy>()), task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {} @@ -74,12 +74,12 @@ base::FilePath::FromUTF8Unsafe("test")); test_file_url.SetBucket( storage::BucketLocator::ForDefaultBucket(kTestStorageKey)); - mojo::Remote<blink::mojom::FileSystemAccessCapacityAllocationHost> + mojo::Remote<blink::mojom::FileSystemAccessFileModificationHost> allocation_host_remote; allocation_host_ = - std::make_unique<FileSystemAccessCapacityAllocationHostImpl>( + std::make_unique<FileSystemAccessFileModificationHostImpl>( manager_.get(), test_file_url, - base::PassKey<FileSystemAccessCapacityAllocationHostImplTest>(), + base::PassKey<FileSystemAccessFileModificationHostImplTest>(), allocation_host_remote.BindNewPipeAndPassReceiver(), 0); } @@ -111,12 +111,12 @@ scoped_refptr<ChromeBlobStorageContext> chrome_blob_context_; scoped_refptr<FileSystemAccessManagerImpl> manager_; - std::unique_ptr<FileSystemAccessCapacityAllocationHostImpl> allocation_host_; + std::unique_ptr<FileSystemAccessFileModificationHostImpl> allocation_host_; scoped_refptr<storage::MockQuotaManager> quota_manager_; scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_; }; -TEST_F(FileSystemAccessCapacityAllocationHostImplTest, +TEST_F(FileSystemAccessFileModificationHostImplTest, RequestCapacityChange_PositiveCapacity) { const int64_t requested_capacity = 50; int64_t granted_capacity = @@ -126,7 +126,7 @@ requested_capacity); } -TEST_F(FileSystemAccessCapacityAllocationHostImplTest, +TEST_F(FileSystemAccessFileModificationHostImplTest, RequestCapacityChange_PositiveAndNegativeCapacity) { const int64_t positive_requested_capacity = 50; const int64_t negative_requested_capacity = -40; @@ -146,7 +146,7 @@ EXPECT_EQ(quota_manager_proxy_->notify_bucket_modified_count(), 2); } -TEST_F(FileSystemAccessCapacityAllocationHostImplTest, +TEST_F(FileSystemAccessFileModificationHostImplTest, RequestCapacityChange_IllegalNegativeCapacity) { mojo::test::BadMessageObserver bad_message_observer; mojo::FakeMessageDispatchContext fake_dispatch_context;
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc index 80a00595..d917e8a 100644 --- a/content/browser/file_system_access/file_system_access_manager_impl.cc +++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -58,9 +58,9 @@ #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/common/file_system/file_system_types.h" #include "third_party/blink/public/common/storage_key/storage_key.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-forward.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h" #include "ui/shell_dialogs/select_file_dialog.h" @@ -1217,8 +1217,8 @@ const storage::FileSystemURL& url, mojo::PendingReceiver<blink::mojom::FileSystemAccessFileDelegateHost> file_delegate_receiver, - mojo::PendingReceiver<blink::mojom::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_receiver, + mojo::PendingReceiver<blink::mojom::FileSystemAccessFileModificationHost> + file_modification_host_receiver, int64_t file_size, scoped_refptr<FileSystemAccessLockManager::LockHandle> lock, base::ScopedClosureRunner on_close_callback) { @@ -1230,7 +1230,7 @@ std::make_unique<FileSystemAccessAccessHandleHostImpl>( this, url, std::move(lock), PassKey(), std::move(receiver), std::move(file_delegate_receiver), - std::move(capacity_allocation_host_receiver), file_size, + std::move(file_modification_host_receiver), file_size, std::move(on_close_callback)); access_handle_host_receivers_.insert(std::move(access_handle_host));
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.h b/content/browser/file_system_access/file_system_access_manager_impl.h index 0e5c005..de72cea 100644 --- a/content/browser/file_system_access/file_system_access_manager_impl.h +++ b/content/browser/file_system_access/file_system_access_manager_impl.h
@@ -40,9 +40,9 @@ #include "storage/browser/file_system/file_system_operation_runner.h" #include "storage/browser/file_system/file_system_url.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_writer.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_observer_host.mojom.h" @@ -269,9 +269,8 @@ const storage::FileSystemURL& url, mojo::PendingReceiver<blink::mojom::FileSystemAccessFileDelegateHost> file_delegate_receiver, - mojo::PendingReceiver< - blink::mojom::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_receiver, + mojo::PendingReceiver<blink::mojom::FileSystemAccessFileModificationHost> + file_modification_host_receiver, int64_t file_size, scoped_refptr<FileSystemAccessLockManager::LockHandle> lock, base::ScopedClosureRunner on_close_callback); @@ -528,7 +527,7 @@ mojo::PendingReceiver<blink::mojom::FileSystemAccessTransferToken> token, const storage::FileSystemURL& url); - // FileSystemAccessCapacityAllocationHosts may reserve too much capacity + // FileSystemAccessFileModificationHosts may reserve too much capacity // from the quota system. This function determines the file's actual size // and corrects its capacity usage in the quota system. void CleanupAccessHandleCapacityAllocation(const storage::FileSystemURL& url,
diff --git a/content/browser/loader/navigation_url_loader.cc b/content/browser/loader/navigation_url_loader.cc index 66769358..a178bfa 100644 --- a/content/browser/loader/navigation_url_loader.cc +++ b/content/browser/loader/navigation_url_loader.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/command_line.h" +#include "base/trace_event/trace_event.h" #include "content/browser/loader/cached_navigation_url_loader.h" #include "content/browser/loader/navigation_loader_interceptor.h" #include "content/browser/loader/navigation_url_loader_factory.h" @@ -42,6 +43,7 @@ network::mojom::URLResponseHeadPtr cached_response_head, std::vector<std::unique_ptr<NavigationLoaderInterceptor>> initial_interceptors) { + TRACE_EVENT0("navigation", "NavigationURLLoader::Create"); // Prioritize CachedNavigationURLLoader over `g_loader_factory` even for tests // as prerendered page activation needs to run synchronously and // CachedNavigationURLLoader serves a fake response synchronously.
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index 4fecbda..0e7ea72b 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -457,6 +457,9 @@ // TODO(kinuko): Fix the method ordering and move these methods after the ctor. NavigationURLLoaderImpl::~NavigationURLLoaderImpl() { + TRACE_EVENT_WITH_FLOW0("navigation", + "NavigationURLLoaderImpl::~NavigationURLLoaderImpl", + TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN); // If neither OnCompleted nor OnReceivedResponse has been invoked, the // request was canceled before receiving a response, so log a cancellation. // Results after receiving a non-error response are logged in the renderer, @@ -472,6 +475,9 @@ } void NavigationURLLoaderImpl::Start() { + TRACE_EVENT_WITH_FLOW0("navigation", "NavigationURLLoaderImpl::Start", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!started_); started_ = true; @@ -532,6 +538,10 @@ } void NavigationURLLoaderImpl::CreateInterceptors() { + TRACE_EVENT_WITH_FLOW0("navigation", + "NavigationURLLoaderImpl::CreateInterceptors", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (prefetched_signed_exchange_cache_) { std::unique_ptr<NavigationLoaderInterceptor> prefetched_signed_exchange_interceptor = @@ -866,6 +876,10 @@ scoped_refptr<network::SharedURLLoaderFactory> factory, std::vector<std::unique_ptr<blink::URLLoaderThrottle>> additional_throttles) { + TRACE_EVENT_WITH_FLOW0( + "navigation", "NavigationURLLoaderImpl::CreateThrottlingLoaderAndStart", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); CHECK(!url_loader_); std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles = @@ -1313,6 +1327,10 @@ std::vector<std::unique_ptr<blink::URLLoaderThrottle>> NavigationURLLoaderImpl::CreateURLLoaderThrottles() { + TRACE_EVENT_WITH_FLOW0("navigation", + "NavigationURLLoaderImpl::CreateURLLoaderThrottles", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); auto throttles = CreateContentBrowserURLLoaderThrottles( *resource_request_, browser_context_, web_contents_getter_, navigation_ui_data_.get(), frame_tree_node_id_, @@ -1436,6 +1454,9 @@ ukm_source_id_(FrameTreeNode::GloballyFindByID(frame_tree_node_id_) ->navigation_request() ->GetNextPageUkmSourceId()) { + TRACE_EVENT_WITH_FLOW0("navigation", + "NavigationURLLoaderImpl::NavigationURLLoaderImpl", + TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT); DCHECK_CURRENTLY_ON(BrowserThread::UI); TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc index 162de4c..693e14e 100644 --- a/content/browser/media/capture/desktop_capture_device.cc +++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -647,7 +647,7 @@ gfx::Size(output_size.width(), output_size.height()), requested_frame_rate_, media::PIXEL_FORMAT_ARGB), frame_color_space, 0 /* clockwise_rotation */, false /* flip_y */, now, - now - first_ref_time_); + now - first_ref_time_, std::nullopt); ScheduleNextCaptureFrame(); }
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc index cd17a39..ecd3d23 100644 --- a/content/browser/media/capture/desktop_capture_device_unittest.cc +++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -282,6 +282,7 @@ bool /* flip_y */, base::TimeTicks /* reference_time */, base::TimeDelta /* timestamp */, + std::optional<base::TimeTicks> /* capture_begin_time */, int /* frame_feedback_id */) { ASSERT_TRUE(output_frame_); ASSERT_EQ(output_frame_->stride() * output_frame_->size().height(), size); @@ -340,9 +341,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly( DoAll(SaveArg<1>(&frame_size), SaveArg<2>(&format), InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); @@ -379,9 +380,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), @@ -425,9 +426,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), @@ -475,9 +476,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), @@ -527,9 +528,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly( DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame), SaveArg<1>(&frame_size), @@ -576,9 +577,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly( DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame), SaveArg<1>(&frame_size), @@ -621,10 +622,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()).Times(0); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) - .Times(0); + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted).Times(0); + EXPECT_CALL(*client, OnIncomingCapturedData).Times(0); capture_device_->RequestRefreshFrame(); capture_device_->StopAndDeAllocate(); @@ -646,9 +646,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .Times(1) .WillRepeatedly( InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)); @@ -689,9 +689,9 @@ // Ensure that we receive two calls to OnIncomingCapturedData(). std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); - EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnError).Times(0); + EXPECT_CALL(*client, OnStarted); + EXPECT_CALL(*client, OnIncomingCapturedData) .Times(2) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, @@ -846,9 +846,9 @@ std::unique_ptr<media::MockVideoCaptureDeviceClient> client( CreateMockVideoCaptureDeviceClient()); - EXPECT_CALL(*client, OnError(_, _, _)).Times(0); + EXPECT_CALL(*client, OnError).Times(0); // On started is called from the capture thread. - EXPECT_CALL(*client, OnStarted()) + EXPECT_CALL(*client, OnStarted) .WillOnce(InvokeWithoutArgs([this, &task_runner, &message_loop_task_runner] { message_loop_task_runner = @@ -860,7 +860,7 @@ task_runner, task_runner->GetMockTickClock()); })); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData) .WillRepeatedly(DoAll( WithArg<2>( Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)),
diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.cc b/content/browser/media/capture/io_surface_capture_device_base_mac.cc index b638d51..8a77491 100644 --- a/content/browser/media/capture/io_surface_capture_device_base_mac.cc +++ b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
@@ -67,7 +67,7 @@ media::CapturedExternalVideoBuffer(std::move(handle), last_received_capture_format_, gfx::ColorSpace::CreateREC709()), - now, now - first_frame_time_, last_visible_rect_); + now, now - first_frame_time_, std::nullopt, last_visible_rect_); } void IOSurfaceCaptureDeviceBase::ComputeFrameSizeAndDestRect(
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc index d3c5677..db594a9 100644 --- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -217,7 +217,7 @@ media::VideoFrame::AllocationSize(stub_frame->format(), stub_frame->coded_size()), format, color_space, rotation, false /* flip_y */, base::TimeTicks(), - base::TimeDelta(), frame_feedback_id); + base::TimeDelta(), std::nullopt, frame_feedback_id); } BrowserTaskEnvironment task_environment_; @@ -426,7 +426,7 @@ device_client_->OnIncomingCapturedBuffer(std::move(buffer), device_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get()); @@ -460,7 +460,7 @@ device_client_->OnIncomingCapturedBuffer(std::move(buffer2), device_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); // The frame should be delivered to the clients in any order. EXPECT_CALL(*client_a_, DoBufferReady(ControllerIDAndSize( @@ -491,9 +491,9 @@ auto buffer3_access = buffer3.handle_provider->GetHandleForInProcessAccess(); memset(buffer3_access->data(), buffer_no++, buffer3_access->mapped_size()); - device_client_->OnIncomingCapturedBuffer(std::move(buffer3), device_format, - arbitrary_reference_time_, - arbitrary_timestamp_); + device_client_->OnIncomingCapturedBuffer( + std::move(buffer3), device_format, arbitrary_reference_time_, + arbitrary_timestamp_, std::nullopt); } // ReserveOutputBuffer ought to fail now, because the pool is depleted. media::VideoCaptureDevice::Client::Buffer buffer_fail; @@ -546,7 +546,7 @@ memset(buffer3_access->data(), buffer_no++, buffer3_access->mapped_size()); device_client_->OnIncomingCapturedBuffer(std::move(buffer3), device_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); media::VideoCaptureDevice::Client::Buffer buffer4; const auto result_code_4 = device_client_->ReserveOutputBuffer( @@ -564,7 +564,7 @@ memset(buffer4_access->data(), buffer_no++, buffer4_access->mapped_size()); device_client_->OnIncomingCapturedBuffer(std::move(buffer4), device_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); // B2 is the only client left, and is the only one that should // get the buffer. EXPECT_CALL(*client_b_, DoBufferReady(ControllerIDAndSize( @@ -632,7 +632,7 @@ reserve_result); device_client_->OnIncomingCapturedBuffer(std::move(buffer), device_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); base::RunLoop().RunUntilIdle(); } @@ -673,7 +673,7 @@ "Test Error"); device_client_->OnIncomingCapturedBuffer(std::move(buffer), device_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); EXPECT_CALL( *client_a_, @@ -743,7 +743,7 @@ result_code); device_client_->OnIncomingCapturedBuffer( std::move(buffer), arbitrary_format, arbitrary_reference_time_, - arbitrary_timestamp_); + arbitrary_timestamp_, std::nullopt); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get());
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc index 6a64180a..17771461 100644 --- a/content/browser/renderer_host/navigation_throttle_runner.cc +++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -149,6 +149,8 @@ } void NavigationThrottleRunner::RegisterNavigationThrottles() { + TRACE_EVENT0("navigation", + "NavigationThrottleRunner::RegisterNavigationThrottles"); // Note: |throttle_| might not be empty. Some NavigationThrottles might have // been registered with RegisterThrottleForTesting. These must reside at the // end of |throttles_|. TestNavigationManagerThrottle expects that the @@ -299,11 +301,16 @@ void NavigationThrottleRunner::AddThrottle( std::unique_ptr<NavigationThrottle> navigation_throttle) { - if (navigation_throttle) + if (navigation_throttle) { + TRACE_EVENT1("navigation", "NavigationThrottleRunner::AddThrottle", + "navigation_throttle", + navigation_throttle->GetNameForLogging()); throttles_.push_back(std::move(navigation_throttle)); + } } void NavigationThrottleRunner::ProcessInternal() { + TRACE_EVENT0("navigation", "NavigationThrottleRunner::ProcessInternal"); DCHECK_NE(Event::NoEvent, current_event_); base::WeakPtr<NavigationThrottleRunner> weak_ref = weak_factory_.GetWeakPtr(); @@ -313,6 +320,8 @@ int64_t local_navigation_id = navigation_id_; for (size_t i = next_index_; i < throttles_.size(); ++i) { + TRACE_EVENT0("navigation", + "NavigationThrottleRunner::ProcessInternal.loop"); TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( "navigation", GetEventName(current_event_), local_navigation_id, "throttle", throttles_[i]->GetNameForLogging());
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index d77da7d..7904069 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1595,6 +1595,9 @@ GetProcess()->GetStoragePartition()->GetGeneratedCodeCacheContext()), fenced_frame_status_(fenced_frame_status), devtools_frame_token_(devtools_frame_token) { + TRACE_EVENT_WITH_FLOW0("navigation", + "RenderFrameHostImpl::RenderFrameHostImpl", + TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT); TRACE_EVENT_BEGIN("navigation", "RenderFrameHostImpl", perfetto::Track::FromPointer(this), "render_frame_host_when_created", this); @@ -1733,6 +1736,9 @@ } RenderFrameHostImpl::~RenderFrameHostImpl() { + TRACE_EVENT_WITH_FLOW0("navigation", + "RenderFrameHostImpl::~RenderFrameHostImpl", + TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN); SCOPED_CRASH_KEY_STRING256("Bug1407526", "lifecycle", LifecycleStateImplToString(lifecycle_state())); TRACE_EVENT("navigation", "~RenderFrameHostImpl()", @@ -5326,6 +5332,10 @@ const base::TimeTicks& renderer_before_unload_start_time, const base::TimeTicks& renderer_before_unload_end_time, bool for_legacy) { + TRACE_EVENT_WITH_FLOW0("navigation", + "RenderFrameHostImpl::ProcessBeforeUnloadCompleted", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TRACE_EVENT_NESTABLE_ASYNC_END1( "navigation", "RenderFrameHostImpl BeforeUnload", TRACE_ID_LOCAL(this), "render_frame_host", this); @@ -9822,6 +9832,10 @@ void RenderFrameHostImpl::DispatchBeforeUnload(BeforeUnloadType type, bool is_reload) { + TRACE_EVENT_WITH_FLOW0("navigation", + "RenderFrameHostImpl::DispatchBeforeUnload", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); bool for_navigation = type == BeforeUnloadType::BROWSER_INITIATED_NAVIGATION || type == BeforeUnloadType::RENDERER_INITIATED_NAVIGATION; @@ -14238,6 +14252,9 @@ bool is_reload, base::WeakPtr<RenderFrameHostImpl> rfh, bool for_legacy) { + TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameHostImpl::SendBeforeUnload", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); auto before_unload_closure = base::BindOnce( [](base::WeakPtr<RenderFrameHostImpl> impl, bool for_legacy, bool proceed, base::TimeTicks renderer_before_unload_start_time,
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc index ea9aefe..67bd27d 100644 --- a/content/browser/service_worker/service_worker_container_host.cc +++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -9,6 +9,7 @@ #include "base/containers/adapters.h" #include "base/containers/contains.h" +#include "base/debug/crash_logging.h" #include "base/functional/callback_helpers.h" #include "base/functional/overloaded.h" #include "base/strings/stringprintf.h" @@ -619,6 +620,7 @@ void ServiceWorkerContainerHost::CountFeature( blink::mojom::WebFeature feature) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + SCOPED_CRASH_KEY_NUMBER("SWCH_CF", "feature", static_cast<int32_t>(feature)); // CountFeature is a message about the client's controller. It should be sent // only for clients.
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index fdc1defc..55765cb 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -212,6 +212,8 @@ "pseudonymization_salt.h", "service_worker/forwarded_race_network_request_url_loader_factory.cc", "service_worker/forwarded_race_network_request_url_loader_factory.h", + "service_worker/race_network_request_read_buffer_manager.cc", + "service_worker/race_network_request_read_buffer_manager.h", "service_worker/race_network_request_url_loader_client.cc", "service_worker/race_network_request_url_loader_client.h", "service_worker/race_network_request_write_buffer_manager.cc",
diff --git a/content/common/service_worker/race_network_request_read_buffer_manager.cc b/content/common/service_worker/race_network_request_read_buffer_manager.cc new file mode 100644 index 0000000..7e76de6 --- /dev/null +++ b/content/common/service_worker/race_network_request_read_buffer_manager.cc
@@ -0,0 +1,71 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/common/service_worker/race_network_request_read_buffer_manager.h" +#include "base/debug/crash_logging.h" +#include "base/memory/scoped_refptr.h" +#include "mojo/public/c/system/types.h" +#include "net/base/io_buffer.h" +#include "services/network/public/cpp/features.h" + +namespace content { +RaceNetworkRequestReadBufferManager::RaceNetworkRequestReadBufferManager( + mojo::ScopedDataPipeConsumerHandle consumer_handle) + : consumer_handle_(std::move(consumer_handle)), + watcher_(FROM_HERE, + mojo::SimpleWatcher::ArmingPolicy::MANUAL, + base::SequencedTaskRunner::GetCurrentDefault()) {} + +RaceNetworkRequestReadBufferManager::~RaceNetworkRequestReadBufferManager() = + default; + +void RaceNetworkRequestReadBufferManager::Watch( + mojo::SimpleWatcher::ReadyCallbackWithState callback) { + watcher_.Watch(consumer_handle_.get(), + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_WATCH_CONDITION_SATISFIED, std::move(callback)); +} + +void RaceNetworkRequestReadBufferManager::ArmOrNotify() { + watcher_.ArmOrNotify(); +} + + +void RaceNetworkRequestReadBufferManager::CancelWatching() { + watcher_.Cancel(); +} + +std::pair<MojoResult, base::span<const char>> +RaceNetworkRequestReadBufferManager::ReadData() { + CHECK_EQ(BytesRemaining(), 0u); + uint32_t num_bytes = network::features::GetDataPipeDefaultAllocationSize( + network::features::DataPipeAllocationSize::kLargerSizeIfPossible); + scoped_refptr<net::IOBuffer> buffer = + base::MakeRefCounted<net::IOBufferWithSize>(num_bytes); + MojoResult result = consumer_handle_->ReadData(buffer->data(), &num_bytes, + MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_OK) { + buffer_ = base::MakeRefCounted<net::DrainableIOBuffer>(std::move(buffer), + num_bytes); + } + + return std::make_pair(result, + buffer_ ? buffer_->span() : base::span<const char>()); +} + +void RaceNetworkRequestReadBufferManager::ConsumeData(size_t num_bytes_read) { + CHECK(buffer_); + buffer_->DidConsume(num_bytes_read); +} + +size_t RaceNetworkRequestReadBufferManager::BytesRemaining() const { + return buffer_ ? buffer_->BytesRemaining() : 0; +} + +base::span<const char> RaceNetworkRequestReadBufferManager::RemainingBuffer() + const { + CHECK(buffer_); + return buffer_->span(); +} +} // namespace content
diff --git a/content/common/service_worker/race_network_request_read_buffer_manager.h b/content/common/service_worker/race_network_request_read_buffer_manager.h new file mode 100644 index 0000000..77d965e --- /dev/null +++ b/content/common/service_worker/race_network_request_read_buffer_manager.h
@@ -0,0 +1,50 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_SERVICE_WORKER_RACE_NETWORK_REQUEST_READ_BUFFER_MANAGER_H_ +#define CONTENT_COMMON_SERVICE_WORKER_RACE_NETWORK_REQUEST_READ_BUFFER_MANAGER_H_ + +#include <optional> + +#include "base/containers/span.h" +#include "base/memory/scoped_refptr.h" +#include "content/common/content_export.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "net/base/io_buffer.h" + +namespace content { +class CONTENT_EXPORT RaceNetworkRequestReadBufferManager { + public: + explicit RaceNetworkRequestReadBufferManager( + mojo::ScopedDataPipeConsumerHandle consumer_handle); + RaceNetworkRequestReadBufferManager( + const RaceNetworkRequestReadBufferManager&) = delete; + RaceNetworkRequestReadBufferManager& operator=( + const RaceNetworkRequestReadBufferManager&) = delete; + ~RaceNetworkRequestReadBufferManager(); + + void Watch(mojo::SimpleWatcher::ReadyCallbackWithState callback); + void ArmOrNotify(); + void CancelWatching(); + bool IsWatching() { return watcher_.IsWatching(); } + + // Returns MojoResult of DataPipe::ReadData() result, and actual read data. + // The caller must call this only when |RemainingBuffer()| size is zero. + std::pair<MojoResult, base::span<const char>> ReadData(); + // Consumes |buffer_| by given |num_bytes_read| bytes. + void ConsumeData(size_t num_bytes_read); + + size_t BytesRemaining() const; + base::span<const char> RemainingBuffer() const; + + private: + mojo::ScopedDataPipeConsumerHandle consumer_handle_; + mojo::SimpleWatcher watcher_; + scoped_refptr<net::DrainableIOBuffer> buffer_; +}; +} // namespace content + +#endif // CONTENT_COMMON_SERVICE_WORKER_RACE_NETWORK_REQUEST_READ_BUFFER_MANAGER_H_
diff --git a/content/common/service_worker/race_network_request_read_buffer_manager_unittest.cc b/content/common/service_worker/race_network_request_read_buffer_manager_unittest.cc new file mode 100644 index 0000000..fc30c36 --- /dev/null +++ b/content/common/service_worker/race_network_request_read_buffer_manager_unittest.cc
@@ -0,0 +1,72 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/common/service_worker/race_network_request_read_buffer_manager.h" +#include <string_view> + +#include "base/containers/span.h" +#include "base/run_loop.h" +#include "base/task/single_thread_task_runner.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/handle_signals_state.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { +namespace { + +TEST(RaceNetworkRequestReadBufferManagerTest, ReadData) { + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + EXPECT_EQ(mojo::CreateDataPipe(10u, producer_handle, consumer_handle), + MOJO_RESULT_OK); + + const char expected_data[] = "abcde"; + uint32_t num_bytes = sizeof(expected_data); + base::test::SingleThreadTaskEnvironment task_environment; + base::RunLoop run_loop; + + mojo::SimpleWatcher producer_watcher( + FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, + base::SequencedTaskRunner::GetCurrentDefault()); + RaceNetworkRequestReadBufferManager buffer_manager( + std::move(consumer_handle)); + + producer_watcher.Watch( + producer_handle.get(), + MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_WATCH_CONDITION_SATISFIED, + base::BindLambdaForTesting( + [&](MojoResult result, const mojo::HandleSignalsState& state) { + if (state.writable()) { + EXPECT_EQ(result, MOJO_RESULT_OK); + result = producer_handle->WriteData(expected_data, &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE); + EXPECT_EQ(result, MOJO_RESULT_OK); + buffer_manager.ArmOrNotify(); + producer_handle.reset(); + } + })); + + buffer_manager.Watch(base::BindLambdaForTesting( + [&](MojoResult result, const mojo::HandleSignalsState& state) { + EXPECT_EQ(result, MOJO_RESULT_OK); + auto [read_result, buffer] = buffer_manager.ReadData(); + EXPECT_EQ(read_result, MOJO_RESULT_OK); + EXPECT_EQ(result, MOJO_RESULT_OK); + EXPECT_EQ(buffer.size(), num_bytes); + std::string_view expected_str(expected_data); + EXPECT_EQ(buffer.data(), expected_str); + buffer_manager.ConsumeData(num_bytes); + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, run_loop.QuitClosure()); + })); + + producer_watcher.ArmOrNotify(); + run_loop.Run(); +} +} // namespace +} // namespace content
diff --git a/content/common/service_worker/race_network_request_url_loader_client.cc b/content/common/service_worker/race_network_request_url_loader_client.cc index 8417862..9500ec9 100644 --- a/content/common/service_worker/race_network_request_url_loader_client.cc +++ b/content/common/service_worker/race_network_request_url_loader_client.cc
@@ -39,9 +39,6 @@ : request_(request), owner_(std::move(owner)), forwarding_client_(std::move(forwarding_client)), - body_consumer_watcher_(FROM_HERE, - mojo::SimpleWatcher::ArmingPolicy::MANUAL, - base::SequencedTaskRunner::GetCurrentDefault()), is_main_resource_(owner_->IsMainResourceLoader()), request_start_(base::TimeTicks::Now()), request_start_time_(base::Time::Now()) { @@ -118,7 +115,7 @@ head_->load_timing.request_start = request_start_; head_->load_timing.request_start_time = request_start_time_; cached_metadata_ = std::move(cached_metadata); - body_ = std::move(body); + read_buffer_manager_.emplace(std::move(body)); WatchDataUpdate(); break; case DataConsumePolicy::kForwardingOnly: @@ -354,7 +351,9 @@ // complete the response. write_buffer_manager_for_race_network_request_.CancelWatching(); write_buffer_manager_for_fetch_handler_.CancelWatching(); - body_consumer_watcher_.Cancel(); + if (read_buffer_manager_.has_value() && read_buffer_manager_->IsWatching()) { + read_buffer_manager_->CancelWatching(); + } } void ServiceWorkerRaceNetworkRequestURLLoaderClient:: @@ -384,47 +383,77 @@ } void ServiceWorkerRaceNetworkRequestURLLoaderClient::WatchDataUpdate() { - auto callback_func = + auto write_callback = base::GetFieldTrialParamByFeatureAsBool( features::kServiceWorkerAutoPreload, "use_two_phase_write", true) - ? &ServiceWorkerRaceNetworkRequestURLLoaderClient:: - ReadAndTwoPhaseWrite - : &ServiceWorkerRaceNetworkRequestURLLoaderClient::ReadAndWrite; - - body_consumer_watcher_.Watch( - body_.get(), MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_WATCH_CONDITION_SATISFIED, - base::BindRepeating(callback_func, weak_factory_.GetWeakPtr())); - body_consumer_watcher_.ArmOrNotify(); + ? &ServiceWorkerRaceNetworkRequestURLLoaderClient::TwoPhaseWrite + : &ServiceWorkerRaceNetworkRequestURLLoaderClient::Write; + CHECK(read_buffer_manager_.has_value()); + read_buffer_manager_->Watch( + base::BindRepeating(&ServiceWorkerRaceNetworkRequestURLLoaderClient::Read, + weak_factory_.GetWeakPtr())); + read_buffer_manager_->ArmOrNotify(); write_buffer_manager_for_race_network_request_.Watch( - base::BindRepeating(callback_func, weak_factory_.GetWeakPtr())); + base::BindRepeating(write_callback, weak_factory_.GetWeakPtr())); write_buffer_manager_for_fetch_handler_.Watch( - base::BindRepeating(callback_func, weak_factory_.GetWeakPtr())); + base::BindRepeating(write_callback, weak_factory_.GetWeakPtr())); } -void ServiceWorkerRaceNetworkRequestURLLoaderClient::ReadAndTwoPhaseWrite( +void ServiceWorkerRaceNetworkRequestURLLoaderClient::Read( MojoResult result, const mojo::HandleSignalsState& state) { - std::optional<base::span<const char>> read_buffer = StartReadData(result); - if (!read_buffer.has_value()) { + if (!IsReadyToHandleReadWrite(result)) { return; } - TwoPhaseWrite(read_buffer.value()); -} -void ServiceWorkerRaceNetworkRequestURLLoaderClient::ReadAndWrite( - MojoResult result, - const mojo::HandleSignalsState& state) { - std::optional<base::span<const char>> read_buffer = StartReadData(result); - if (!read_buffer.has_value()) { + CHECK(read_buffer_manager_.has_value()); + if (read_buffer_manager_->BytesRemaining() > 0) { + write_buffer_manager_for_race_network_request_.ArmOrNotify(); + write_buffer_manager_for_fetch_handler_.ArmOrNotify(); return; } - Write(read_buffer.value()); + + auto [read_result, read_buffer] = read_buffer_manager_->ReadData(); + TRACE_EVENT_WITH_FLOW2("ServiceWorker", + "ServiceWorkerRaceNetworkRequestURLLoaderClient::Read", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + "url", request_.url, "read_data_result", result); + RecordMojoResultForDataTransfer(result, "Read"); + switch (read_result) { + case MOJO_RESULT_OK: + CHECK(state.readable()); + write_buffer_manager_for_race_network_request_.ArmOrNotify(); + write_buffer_manager_for_fetch_handler_.ArmOrNotify(); + return; + case MOJO_RESULT_FAILED_PRECONDITION: + // Successfully read the whole data. This case the peer is already closed. + CHECK(state.peer_closed()); + OnDataTransferComplete(); + return; + case MOJO_RESULT_BUSY: + case MOJO_RESULT_SHOULD_WAIT: + return; + default: + NOTREACHED() << "ReadData result:" << result; + return; + } } void ServiceWorkerRaceNetworkRequestURLLoaderClient::TwoPhaseWrite( - base::span<const char> read_buffer) { - MojoResult result; + MojoResult result, + const mojo::HandleSignalsState& state) { + if (!IsReadyToHandleReadWrite(result)) { + return; + } + + CHECK(read_buffer_manager_.has_value()); + if (read_buffer_manager_->BytesRemaining() == 0) { + read_buffer_manager_->ArmOrNotify(); + return; + } + base::span<const char> read_buffer = read_buffer_manager_->RemainingBuffer(); + uint32_t num_bytes_to_consume = 0; if (write_buffer_manager_for_race_network_request_.IsWatching() && write_buffer_manager_for_fetch_handler_.IsWatching()) { @@ -436,7 +465,6 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); // The data pipe consumer is aborted. TransitionState(State::kAborted); Abort(); @@ -444,7 +472,6 @@ case MOJO_RESULT_SHOULD_WAIT: // The data pipe is not writable yet. We don't consume data from |body_| // and write any data in this case. And retry it later. - body_->EndReadData(0); write_buffer_manager_for_race_network_request_.EndWriteData(0); write_buffer_manager_for_race_network_request_.ArmOrNotify(); return; @@ -455,7 +482,6 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); TransitionState(State::kAborted); Abort(); return; @@ -464,7 +490,6 @@ // not consumed yet but the buffer is full. Stop processing the data // pipe for the fetch handler side, not to make the data transfer // process for the race network request side being stuck. - body_->EndReadData(0); write_buffer_manager_for_race_network_request_.EndWriteData(0); write_buffer_manager_for_fetch_handler_.EndWriteData(0); write_buffer_manager_for_fetch_handler_.CancelWatching(); @@ -499,12 +524,10 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); TransitionState(State::kAborted); Abort(); return; case MOJO_RESULT_SHOULD_WAIT: - body_->EndReadData(0); write_buffer_manager_for_race_network_request_.EndWriteData(0); write_buffer_manager_for_race_network_request_.ArmOrNotify(); return; @@ -521,12 +544,10 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); TransitionState(State::kAborted); Abort(); return; case MOJO_RESULT_SHOULD_WAIT: - body_->EndReadData(0); write_buffer_manager_for_fetch_handler_.EndWriteData(0); write_buffer_manager_for_fetch_handler_.ArmOrNotify(); return; @@ -539,8 +560,19 @@ } void ServiceWorkerRaceNetworkRequestURLLoaderClient::Write( - base::span<const char> read_buffer) { - MojoResult result; + MojoResult result, + const mojo::HandleSignalsState& state) { + if (!IsReadyToHandleReadWrite(result)) { + return; + } + + CHECK(read_buffer_manager_.has_value()); + if (read_buffer_manager_->BytesRemaining() == 0) { + read_buffer_manager_->ArmOrNotify(); + return; + } + base::span<const char> read_buffer = read_buffer_manager_->RemainingBuffer(); + size_t num_bytes_to_consume = read_buffer.size(); if (write_buffer_manager_for_race_network_request_.IsWatching() && write_buffer_manager_for_fetch_handler_.IsWatching()) { @@ -553,7 +585,6 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); // The data pipe consumer is aborted. TransitionState(State::kAborted); Abort(); @@ -562,7 +593,6 @@ case MOJO_RESULT_OUT_OF_RANGE: // The data pipe is not writable yet. We don't consume data from |body_| // and write any data in this case. And retry it later. - body_->EndReadData(0); write_buffer_manager_for_race_network_request_.ArmOrNotify(); return; } @@ -573,7 +603,6 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); TransitionState(State::kAborted); Abort(); return; @@ -583,7 +612,6 @@ // not consumed yet but the buffer is full. Stop processing the data // pipe for the fetch handler side, not to make the data transfer // process for the race network request side being stuck. - body_->EndReadData(read_buffer.size()); write_buffer_manager_for_fetch_handler_.CancelWatching(); write_buffer_manager_for_race_network_request_.ArmOrNotify(); return; @@ -602,13 +630,11 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); TransitionState(State::kAborted); Abort(); return; case MOJO_RESULT_SHOULD_WAIT: case MOJO_RESULT_OUT_OF_RANGE: - body_->EndReadData(0); write_buffer_manager_for_race_network_request_.ArmOrNotify(); return; } @@ -622,13 +648,11 @@ case MOJO_RESULT_OK: break; case MOJO_RESULT_FAILED_PRECONDITION: - body_->EndReadData(0); TransitionState(State::kAborted); Abort(); return; case MOJO_RESULT_SHOULD_WAIT: case MOJO_RESULT_OUT_OF_RANGE: - body_->EndReadData(0); write_buffer_manager_for_fetch_handler_.ArmOrNotify(); return; } @@ -636,64 +660,21 @@ CompleteReadData(num_bytes_to_consume); } -std::optional<base::span<const char>> -ServiceWorkerRaceNetworkRequestURLLoaderClient::StartReadData( - MojoResult initial_mojo_result) { +bool ServiceWorkerRaceNetworkRequestURLLoaderClient::IsReadyToHandleReadWrite( + MojoResult result) { if (!owner_) { - return std::nullopt; + return false; } if (state_ == State::kDataTransferFinished) { - return std::nullopt; + return false; } - RecordMojoResultForDataTransfer(initial_mojo_result, "Initial"); - if (initial_mojo_result != MOJO_RESULT_OK) { - return std::nullopt; + RecordMojoResultForDataTransfer(result, "Initial"); + if (result != MOJO_RESULT_OK) { + return false; } - auto [result, read_buffer] = BeginReadData(); - TRACE_EVENT_WITH_FLOW2( - "ServiceWorker", - "ServiceWorkerRaceNetworkRequestURLLoaderClient::ReadAndWrite", - TRACE_ID_LOCAL(this), - TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "url", request_.url, - "read_data_result", result); - RecordMojoResultForDataTransfer(result, "Read"); - switch (result) { - case MOJO_RESULT_OK: - return read_buffer; - case MOJO_RESULT_FAILED_PRECONDITION: - // Successfully read the whole data. - OnDataTransferComplete(); - return std::nullopt; - case MOJO_RESULT_BUSY: - case MOJO_RESULT_SHOULD_WAIT: - return std::nullopt; - default: - NOTREACHED() << "BeginReadData result:" << result; - return std::nullopt; - } -} - -std::pair<MojoResult, base::span<const char>> -ServiceWorkerRaceNetworkRequestURLLoaderClient::BeginReadData() { - const void* buffer; - uint32_t buffer_num_bytes = 0; - base::span<const char> read_buffer; - MojoResult result = body_->BeginReadData(&buffer, &buffer_num_bytes, - MOJO_BEGIN_READ_DATA_FLAG_NONE); - if (result == MOJO_RESULT_OK) { - SCOPED_CRASH_KEY_NUMBER("SWRace", "num_bytes_read_buffer", - buffer_num_bytes); - volatile const char* buffer_v = static_cast<volatile const char*>(buffer); - for (size_t i = 0; i < buffer_num_bytes; ++i) { - buffer_v[i]; - } - read_buffer = - base::make_span(static_cast<const char*>(buffer), buffer_num_bytes); - } - - return std::make_pair(result, read_buffer); + return true; } void ServiceWorkerRaceNetworkRequestURLLoaderClient::RecordMojoResultForWrite( @@ -703,17 +684,19 @@ void ServiceWorkerRaceNetworkRequestURLLoaderClient::CompleteReadData( uint32_t num_bytes_to_consume) { - MojoResult result = body_->EndReadData(num_bytes_to_consume); - CHECK_EQ(result, MOJO_RESULT_OK); + CHECK(read_buffer_manager_.has_value()); + read_buffer_manager_->ConsumeData(num_bytes_to_consume); // Once data is written to the data pipe, start the commit process. MaybeCommitResponse(); - body_consumer_watcher_.ArmOrNotify(); + read_buffer_manager_->ArmOrNotify(); } void ServiceWorkerRaceNetworkRequestURLLoaderClient::Abort() { write_buffer_manager_for_race_network_request_.Abort(); write_buffer_manager_for_fetch_handler_.Abort(); - body_consumer_watcher_.Cancel(); + if (read_buffer_manager_.has_value()) { + read_buffer_manager_->CancelWatching(); + } } void ServiceWorkerRaceNetworkRequestURLLoaderClient::SetFetchHandlerEndTiming( @@ -828,8 +811,8 @@ // again as we create two data pipes and propergate data from the consumer // handle |body_|. Even though one data pipe is canceled, the data transfer // process to the other data pipe has to be continued. - if (body_consumer_watcher_.IsWatching()) { - body_consumer_watcher_.ArmOrNotify(); + if (read_buffer_manager_.has_value() && read_buffer_manager_->IsWatching()) { + read_buffer_manager_->ArmOrNotify(); } }
diff --git a/content/common/service_worker/race_network_request_url_loader_client.h b/content/common/service_worker/race_network_request_url_loader_client.h index 6816b323..ec6ead1 100644 --- a/content/common/service_worker/race_network_request_url_loader_client.h +++ b/content/common/service_worker/race_network_request_url_loader_client.h
@@ -9,6 +9,7 @@ #include "base/containers/span.h" #include "base/time/time.h" #include "content/common/content_export.h" +#include "content/common/service_worker/race_network_request_read_buffer_manager.h" #include "content/common/service_worker/race_network_request_write_buffer_manager.h" #include "content/common/service_worker/service_worker_resource_loader.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -177,14 +178,19 @@ void OnDataTransferComplete(); void TransitionState(State new_state); - // Reads data from |body_|, and writes it into the data pipe producer handles - // for both the race network request and the fetch handler respectively. + // Reads data from RaceNetworkRequestReadBufferManager. If there is a buffer + // to read, notifies the write buffer manager to start write operations. + // If no buffer to read, calls |OnDataTransferComplete()| and return nothing. + void Read(MojoResult result, const mojo::HandleSignalsState& state); + // Writes data in RaceNetworkRequestReadBufferManager into the data + // pipe producer that handles for both the race network request and the fetch + // handler respectively. // // To guarantee the consistent data between the race network request and the // fetch handler, this method always writes a same chunk of data into two data // pipe handles. If one side fails the data write process for some reason, we - // don't consume |body_| data, and retry it later. |body_| data is consumed - // only when the both producer handles successfully write data. + // don't consume the buffer, and retry it later. the buffer is consumed only + // when the both producer handles successfully write data. // // When the first chunk of data is written to the data pipes, this starts the // commit process. And when the data transfer is finished, this complete the @@ -194,31 +200,21 @@ // process, and there could be the case if the response is not returned due to // the long fetch handler execution. and test case the mechanism to wait for // the fetch handler - void ReadAndTwoPhaseWrite(MojoResult result, - const mojo::HandleSignalsState& state); - // Reads data from |body_|, and writes it into the data pipe producer handles - // for both the race network request and the fetch handler respectively. + void TwoPhaseWrite(MojoResult result, const mojo::HandleSignalsState& state); + // Writes data in RaceNetworkRequestReadBufferManager into the data + // pipe producer that handles for both the race network request and the fetch + // handler respectively. // - // Unlike |ReadAndTwoPhaseWrite()|, this doesn't use two-phase operations to + // Unlike |TwoPhaseWrite()|, this doesn't use two-phase operations to // write data into data pipes. However, the result should be the same as - // |ReadAndTwoPhaseWrite()| because mojo's |WriteData()| is expected to write + // |TwoPhaseWrite()| because mojo's |WriteData()| is expected to write // the same amount of data from the given data pipe consumer handle to read. - // also |ReadAndWrite()| has CHECK to guarantee that the actual written sizes + // also |Write()| has CHECK to guarantee that the actual written sizes // to data pips are exactly same. - void ReadAndWrite(MojoResult result, const mojo::HandleSignalsState& state); + void Write(MojoResult result, const mojo::HandleSignalsState& state); - void Write(base::span<const char> read_buffer); - void TwoPhaseWrite(base::span<const char> read_buffer); + bool IsReadyToHandleReadWrite(MojoResult result); - // Begins a two-phase read from |body_|, the data pipe consumer. If succeed, - // the read buffer is returned. If there are no data to read from the data - // pipe, this internally calls |OnDataTransferComplete()| and return nothing. - // - // Since this starts a two-phase read process, `EndReadData()` in |body_| - // has to be called after calling this function. - std::optional<base::span<const char>> StartReadData( - MojoResult initial_mojo_result); - std::pair<MojoResult, base::span<const char>> BeginReadData(); void CompleteReadData(uint32_t num_bytes_to_consume); void WatchDataUpdate(); @@ -240,12 +236,11 @@ const network::ResourceRequest request_; base::WeakPtr<ServiceWorkerResourceLoader> owner_; mojo::Remote<network::mojom::URLLoaderClient> forwarding_client_; - mojo::SimpleWatcher body_consumer_watcher_; - mojo::ScopedDataPipeConsumerHandle body_; network::mojom::URLResponseHeadPtr head_; std::optional<mojo_base::BigBuffer> cached_metadata_; + std::optional<RaceNetworkRequestReadBufferManager> read_buffer_manager_; RaceNetworkRequestWriteBufferManager write_buffer_manager_for_race_network_request_; RaceNetworkRequestWriteBufferManager write_buffer_manager_for_fetch_handler_;
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc index 1e013ce..194c919b 100644 --- a/content/public/renderer/content_renderer_client.cc +++ b/content/public/renderer/content_renderer_client.cc
@@ -282,4 +282,9 @@ } #endif +std::unique_ptr<blink::WebLinkPreviewTriggerer> +ContentRendererClient::CreateLinkPreviewTriggerer() { + return nullptr; +} + } // namespace content
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h index ff20508..0b708ec 100644 --- a/content/public/renderer/content_renderer_client.h +++ b/content/public/renderer/content_renderer_client.h
@@ -30,6 +30,7 @@ #include "third_party/blink/public/platform/url_loader_throttle_provider.h" #include "third_party/blink/public/platform/web_content_settings_client.h" #include "third_party/blink/public/platform/websocket_handshake_throttle_provider.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/public/web/web_navigation_policy.h" #include "third_party/blink/public/web/web_navigation_type.h" #include "ui/base/page_transition_types.h" @@ -438,6 +439,13 @@ virtual std::unique_ptr<cast_streaming::ResourceProvider> CreateCastStreamingResourceProvider(); #endif + + // Creates a WebLinkPreviewTriggerer if an embedder wants to observe events + // and trigger preview. It is allowed to return nullptr. + // + // See blink::WebLinkPreviewTriggerer for more details. + virtual std::unique_ptr<blink::WebLinkPreviewTriggerer> + CreateLinkPreviewTriggerer(); }; } // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 9dbf4ba..67034e1 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -215,6 +215,7 @@ #include "third_party/blink/public/web/web_frame_serializer.h" #include "third_party/blink/public/web/web_frame_widget.h" #include "third_party/blink/public/web/web_input_method_controller.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_navigation_control.h" #include "third_party/blink/public/web/web_navigation_policy.h" @@ -6767,6 +6768,11 @@ return web_view; } +std::unique_ptr<blink::WebLinkPreviewTriggerer> +RenderFrameImpl::CreateLinkPreviewTriggerer() { + return GetContentClient()->renderer()->CreateLinkPreviewTriggerer(); +} + void RenderFrameImpl::ResetMembersUsedForDurationOfCommit() { pending_loader_factories_ = nullptr; pending_code_cache_host_.reset();
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index 85da2609..f44502d 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -107,6 +107,7 @@ #include "third_party/blink/public/web/web_frame_load_type.h" #include "third_party/blink/public/web/web_frame_serializer_client.h" #include "third_party/blink/public/web/web_history_commit_type.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_local_frame_client.h" #include "third_party/blink/public/web/web_meaningful_layout.h" @@ -674,6 +675,8 @@ const std::optional<blink::Impression>& impression, const std::optional<blink::WebPictureInPictureWindowOptions>& pip_options, const blink::WebURL& base_url) override; + std::unique_ptr<blink::WebLinkPreviewTriggerer> CreateLinkPreviewTriggerer() + override; // Dispatches the current state of selection on the webpage to the browser if // it has changed or if the forced flag is passed. The forced flag is used
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 4cf1462..c163f72 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1406,7 +1406,7 @@ "../browser/file_system/file_system_url_drag_drop_browsertest.cc", "../browser/file_system/file_system_url_loader_factory_browsertest.cc", "../browser/file_system/fileapi_browsertest.cc", - "../browser/file_system_access/file_system_access_capacity_allocation_host_impl_browsertest.cc", + "../browser/file_system_access/file_system_access_file_modification_host_impl_browsertest.cc", "../browser/file_system_access/file_system_access_observer_browsertest.cc", "../browser/find_request_manager_browsertest.cc", "../browser/first_party_sets/first_party_sets_handler_impl_browsertest.cc", @@ -2414,9 +2414,9 @@ "../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", - "../browser/file_system_access/file_system_access_capacity_allocation_host_impl_unittest.cc", "../browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc", "../browser/file_system_access/file_system_access_file_handle_impl_unittest.cc", + "../browser/file_system_access/file_system_access_file_modification_host_impl_unittest.cc", "../browser/file_system_access/file_system_access_file_writer_impl_unittest.cc", "../browser/file_system_access/file_system_access_handle_base_unittest.cc", "../browser/file_system_access/file_system_access_lock_manager_unittest.cc", @@ -2792,6 +2792,7 @@ "../common/input/touch_event_stream_validator_unittest.cc", "../common/input/touchpad_pinch_event_queue_unittest.cc", "../common/pseudonymization_salt_unittest.cc", + "../common/service_worker/race_network_request_read_buffer_manager_unittest.cc", "../common/service_worker/race_network_request_url_loader_client_unittest.cc", "../common/service_worker/race_network_request_write_buffer_manager_unittest.cc", "../common/service_worker/service_worker_router_evaluator_unittest.cc",
diff --git a/content/test/data/accessibility/event/add-alert-with-role-change-expected-auralinux.txt b/content/test/data/accessibility/event/add-alert-with-role-change-expected-auralinux.txt index e693afa..7fab4c0 100644 --- a/content/test/data/accessibility/event/add-alert-with-role-change-expected-auralinux.txt +++ b/content/test/data/accessibility/event/add-alert-with-role-change-expected-auralinux.txt
@@ -1,4 +1,2 @@ CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -PARENT-CHANGED PARENT:(role=ROLE_NOTIFICATION name='(null)') role=ROLE_STATIC name='This is an alert' ENABLED,SENSITIVE,SHOWING,VISIBLE -TEXT-INSERT (start=0 length=16 'This is an alert') role=ROLE_NOTIFICATION name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE +CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/add-dialog-described-by-expected-auralinux.txt b/content/test/data/accessibility/event/add-dialog-described-by-expected-auralinux.txt index e312b64..19d94ff5 100644 --- a/content/test/data/accessibility/event/add-dialog-described-by-expected-auralinux.txt +++ b/content/test/data/accessibility/event/add-dialog-described-by-expected-auralinux.txt
@@ -1 +1,4 @@ CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_DIALOG) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_DIALOG name='(null)') role=ROLE_HEADING name='Described by dialog title' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_HEADING name='Described by dialog title' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_HEADING name='Described by dialog title' ENABLED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/add-dialog-described-by-expected-mac.txt b/content/test/data/accessibility/event/add-dialog-described-by-expected-mac.txt index 8104513c..823651a9 100644 --- a/content/test/data/accessibility/event/add-dialog-described-by-expected-mac.txt +++ b/content/test/data/accessibility/event/add-dialog-described-by-expected-mac.txt
@@ -1 +1,2 @@ +AXTitleChanged on AXHeading AXTitle='Described by dialog title' AXValue=2 AXValueChanged on AXTextField
diff --git a/content/test/data/accessibility/event/add-dialog-expected-auralinux.txt b/content/test/data/accessibility/event/add-dialog-expected-auralinux.txt index e312b64..899cb05 100644 --- a/content/test/data/accessibility/event/add-dialog-expected-auralinux.txt +++ b/content/test/data/accessibility/event/add-dialog-expected-auralinux.txt
@@ -1 +1,4 @@ CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_DIALOG) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_DIALOG name='Dialog title') role=ROLE_HEADING name='Dialog title' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_HEADING name='Dialog title' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_HEADING name='Dialog title' ENABLED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/add-dialog-expected-mac.txt b/content/test/data/accessibility/event/add-dialog-expected-mac.txt index 8104513c..be2868b 100644 --- a/content/test/data/accessibility/event/add-dialog-expected-mac.txt +++ b/content/test/data/accessibility/event/add-dialog-expected-mac.txt
@@ -1 +1,2 @@ +AXTitleChanged on AXHeading AXTitle='Dialog title' AXValue=2 AXValueChanged on AXTextField
diff --git a/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt b/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt index dac2ebc4..9e004b98 100644 --- a/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt +++ b/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt
@@ -1,3 +1,3 @@ EVENT_OBJECT_HIDE on <div#item3> role=ROLE_SYSTEM_LISTITEM name="Item 3" level=1 PosInSet=3 SetSize=3 EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_LIST SetSize=2 -IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_LIST SetSize=2 old_text={'<obj>' start=2 end=3} +IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_LIST SetSize=2 old_text={'<obj>' start=2 end=3} \ No newline at end of file
diff --git a/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt b/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt index 239d0ec4..04e8e63 100644 --- a/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt +++ b/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt
@@ -1,3 +1,3 @@ EVENT_OBJECT_HIDE on <li#item3> role=ROLE_SYSTEM_LISTITEM level=1 PosInSet=3 SetSize=3 EVENT_OBJECT_REORDER on <ul> role=ROLE_SYSTEM_LIST SetSize=2 -IA2_EVENT_TEXT_REMOVED on <ul> role=ROLE_SYSTEM_LIST SetSize=2 old_text={'<obj>' start=2 end=3} +IA2_EVENT_TEXT_REMOVED on <ul> role=ROLE_SYSTEM_LIST SetSize=2 old_text={'<obj>' start=2 end=3} \ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-hidden-changed-expected-auralinux.txt b/content/test/data/accessibility/event/aria-hidden-changed-expected-auralinux.txt index 0f3dbf3..8586c49c 100644 --- a/content/test/data/accessibility/event/aria-hidden-changed-expected-auralinux.txt +++ b/content/test/data/accessibility/event/aria-hidden-changed-expected-auralinux.txt
@@ -1,5 +1,3 @@ -CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_HEADING) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_HEADING) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE CHILDREN-CHANGED:ADD index:2 CHILD:(role=ROLE_HEADING) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_HEADING) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE CHILDREN-CHANGED:REMOVE index:1 CHILD:(role=ROLE_HEADING) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-uia-win.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-uia-win.txt index 6838414..8210bcb 100644 --- a/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-uia-win.txt +++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-uia-win.txt
@@ -1 +1 @@ -=== Start Continuation === +=== Start Continuation === \ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-win.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-win.txt index 43448fa..b6e0fcf 100644 --- a/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-win.txt +++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-win.txt
@@ -1,5 +1,7 @@ EVENT_OBJECT_HIDE on <div.test-case> role=ROLE_SYSTEM_GROUPING -EVENT_OBJECT_SHOW on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect invisible subtree" INVISIBLE,FOCUSABLE +EVENT_OBJECT_STATECHANGE on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect invisible subtree" INVISIBLE,FOCUSABLE === Start Continuation === -EVENT_OBJECT_HIDE on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect visible subtree" INVISIBLE,FOCUSABLE EVENT_OBJECT_SHOW on <div.test-case> role=ROLE_SYSTEM_GROUPING +EVENT_OBJECT_STATECHANGE on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect visible subtree" FOCUSABLE +EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_STATICTEXT name="expect visible subtree" +IA2_EVENT_TEXT_ATTRIBUTE_CHANGED on role=ROLE_SYSTEM_STATICTEXT name="expect visible subtree"
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-auralinux.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-auralinux.txt index 5ae764a..dd78fad 100644 --- a/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-auralinux.txt +++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-auralinux.txt
@@ -1,7 +1,10 @@ === Start Continuation === === Start Continuation === -CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_PUSH_BUTTON name='expect invisible subtree' ENABLED,FOCUSABLE,SENSITIVE +PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_PUSH_BUTTON name='expect visible subtree' ENABLED,FOCUSABLE,SENSITIVE === Start Continuation === CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_SECTION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:1 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_PUSH_BUTTON name='expect invisible subtree' ENABLED,FOCUSABLE,SENSITIVE +PARENT-CHANGED PARENT:(role=ROLE_SECTION name='(null)') role=ROLE_PUSH_BUTTON name='expect visible subtree' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_PUSH_BUTTON name='expect visible subtree' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-win.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-win.txt index 24dcd174..35cf7e1 100644 --- a/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-win.txt +++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-win.txt
@@ -1,7 +1,9 @@ === Start Continuation === === Start Continuation === EVENT_OBJECT_HIDE on <div.test-case> role=ROLE_SYSTEM_GROUPING -EVENT_OBJECT_SHOW on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect invisible subtree" INVISIBLE,FOCUSABLE +EVENT_OBJECT_STATECHANGE on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect invisible subtree" INVISIBLE,FOCUSABLE === Start Continuation === -EVENT_OBJECT_HIDE on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect visible subtree" INVISIBLE,FOCUSABLE EVENT_OBJECT_SHOW on <div.test-case> role=ROLE_SYSTEM_GROUPING +EVENT_OBJECT_STATECHANGE on <button> role=ROLE_SYSTEM_PUSHBUTTON name="expect visible subtree" FOCUSABLE +EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_STATICTEXT name="expect visible subtree" +IA2_EVENT_TEXT_ATTRIBUTE_CHANGED on role=ROLE_SYSTEM_STATICTEXT name="expect visible subtree"
diff --git a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-android.txt b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-android.txt index e69de29..aec4f94b 100644 --- a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-android.txt +++ b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-android.txt
@@ -0,0 +1 @@ +TYPE_WINDOW_CONTENT_CHANGED - [contentTypes=64]
diff --git a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-auralinux.txt b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-auralinux.txt index 15a15a2..3075190 100644 --- a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-auralinux.txt +++ b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-auralinux.txt
@@ -1,5 +1,7 @@ CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_TOGGLE_BUTTON) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE +CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_TOGGLE_BUTTON) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE +STATE-CHANGE:PRESSED:TRUE role=ROLE_TOGGLE_BUTTON name='(null)' ENABLED,FOCUSABLE,PRESSED,SENSITIVE,SHOWING,VISIBLE === Start Continuation === CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_TOGGLE_BUTTON) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE +CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_SECTION ENABLED,SENSITIVE,SHOWING,VISIBLE +STATE-CHANGE:PRESSED:TRUE role=ROLE_PUSH_BUTTON name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-mac.txt b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-mac.txt index 6838414..918902b 100644 --- a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-mac.txt +++ b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-mac.txt
@@ -1 +1,3 @@ +AXValueChanged on AXCheckBox AXSubrole=AXToggleButton AXValue=1 === Start Continuation === +AXValueChanged on AXButton \ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-uia-win.txt b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-uia-win.txt index ca8cdff..fd801e6 100644 --- a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-uia-win.txt +++ b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-uia-win.txt
@@ -1,7 +1,7 @@ -StructureChanged/ChildAdded on role=button -StructureChanged/ChildRemoved on role=document -StructureChanged/ChildrenReordered on role=document +AriaProperties changed on role=button +AriaRole changed on role=button +ToggleToggleState changed on role=button === Start Continuation === -StructureChanged/ChildAdded on role=button -StructureChanged/ChildRemoved on role=document -StructureChanged/ChildrenReordered on role=document +AriaProperties changed on role=button +AriaRole changed on role=button +ToggleToggleState changed on role=button \ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-win.txt b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-win.txt index c1f1345..fe7cb46c 100644 --- a/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-win.txt +++ b/content/test/data/accessibility/event/aria-pressed-changes-button-role-expected-win.txt
@@ -1,11 +1,5 @@ -EVENT_OBJECT_HIDE on <button#test> role=ROLE_SYSTEM_PUSHBUTTON FOCUSABLE -EVENT_OBJECT_REORDER on <body> role=ROLE_SYSTEM_GROUPING -EVENT_OBJECT_SHOW on <button#test> role=ROLE_SYSTEM_PUSHBUTTON PRESSED,FOCUSABLE -IA2_EVENT_TEXT_INSERTED on <body> role=ROLE_SYSTEM_GROUPING new_text={'<obj>' start=0 end=1} -IA2_EVENT_TEXT_REMOVED on <body> role=ROLE_SYSTEM_GROUPING old_text={'<obj>' start=0 end=1} +EVENT_OBJECT_STATECHANGE on <button#test> role=ROLE_SYSTEM_PUSHBUTTON PRESSED,FOCUSABLE +IA2_EVENT_ROLE_CHANGED on <button#test> role=ROLE_SYSTEM_PUSHBUTTON PRESSED,FOCUSABLE === Start Continuation === -EVENT_OBJECT_HIDE on <button#test> role=ROLE_SYSTEM_PUSHBUTTON PRESSED,FOCUSABLE -EVENT_OBJECT_REORDER on <body> role=ROLE_SYSTEM_GROUPING -EVENT_OBJECT_SHOW on <button#test> role=ROLE_SYSTEM_PUSHBUTTON FOCUSABLE -IA2_EVENT_TEXT_INSERTED on <body> role=ROLE_SYSTEM_GROUPING new_text={'<obj>' start=0 end=1} -IA2_EVENT_TEXT_REMOVED on <body> role=ROLE_SYSTEM_GROUPING old_text={'<obj>' start=0 end=1} +EVENT_OBJECT_STATECHANGE on <button#test> role=ROLE_SYSTEM_PUSHBUTTON FOCUSABLE +IA2_EVENT_ROLE_CHANGED on <button#test> role=ROLE_SYSTEM_PUSHBUTTON FOCUSABLE \ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-textbox-children-change-expected-auralinux.txt b/content/test/data/accessibility/event/aria-textbox-children-change-expected-auralinux.txt index be72da9..f734d430 100644 --- a/content/test/data/accessibility/event/aria-textbox-children-change-expected-auralinux.txt +++ b/content/test/data/accessibility/event/aria-textbox-children-change-expected-auralinux.txt
@@ -4,5 +4,4 @@ === Start Continuation === CHILDREN-CHANGED:REMOVE index:1 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_ENTRY EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT === Start Continuation === -CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_ENTRY ENABLED,SENSITIVE,SHOWING,SINGLE-LINE,VISIBLE,SELECTABLE-TEXT -CHILDREN-CHANGED:REMOVE index:4 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_ENTRY name='role only, plain') role=ROLE_PUSH_BUTTON name='ok' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE \ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-textbox-children-change-expected-win.txt b/content/test/data/accessibility/event/aria-textbox-children-change-expected-win.txt index 5863299..33c20402 100644 --- a/content/test/data/accessibility/event/aria-textbox-children-change-expected-win.txt +++ b/content/test/data/accessibility/event/aria-textbox-children-change-expected-win.txt
@@ -7,6 +7,4 @@ EVENT_OBJECT_HIDE on <button#btn3> role=ROLE_SYSTEM_PUSHBUTTON name="ok" FOCUSABLE IA2_STATE_EDITABLE EVENT_OBJECT_VALUECHANGE on <div> role=ROLE_SYSTEM_TEXT name="editable" value="foo" FOCUSABLE IA2_STATE_EDITABLE,IA2_STATE_MULTI_LINE,IA2_STATE_SELECTABLE_TEXT === Start Continuation === -EVENT_OBJECT_HIDE on <button#btn4> role=ROLE_SYSTEM_PUSHBUTTON name="ok" FOCUSABLE -EVENT_OBJECT_SHOW on <button#btn4> role=ROLE_SYSTEM_PUSHBUTTON name="ok" FOCUSABLE -EVENT_OBJECT_VALUECHANGE on <div#txt4> role=ROLE_SYSTEM_TEXT name="role only, plain" value="foook" IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE +EVENT_OBJECT_VALUECHANGE on <div#txt4> role=ROLE_SYSTEM_TEXT name="role only, plain" value="foook" IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE \ No newline at end of file
diff --git a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt index 6713fb73..1ff02349 100644 --- a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt +++ b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt
@@ -1,9 +1,18 @@ -CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_TREE_ITEM) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_TREE_ITEM) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -CHILDREN-CHANGED:ADD index:2 CHILD:(role=ROLE_TREE_ITEM) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_ARTICLE) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_TREE name='(null)') role=ROLE_TREE_ITEM name='grandchild1' ENABLED,FOCUSABLE,SELECTABLE,SENSITIVE +PARENT-CHANGED PARENT:(role=ROLE_TREE name='(null)') role=ROLE_TREE_ITEM name='grandchild2' ENABLED,FOCUSABLE,SELECTABLE,SENSITIVE +PARENT-CHANGED PARENT:(role=ROLE_TREE name='(null)') role=ROLE_TREE_ITEM name='grandchild3' ENABLED,FOCUSABLE,SELECTABLE,SENSITIVE === Start Continuation === CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_ARTICLE) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_TREE_ITEM) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -CHILDREN-CHANGED:REMOVE index:1 CHILD:(role=ROLE_TREE_ITEM) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -CHILDREN-CHANGED:REMOVE index:2 CHILD:(role=ROLE_TREE_ITEM) role=ROLE_TREE ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_PANEL name='(null)') role=ROLE_TREE_ITEM name='grandchild1' ENABLED,FOCUSABLE,SELECTABLE,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_PANEL name='(null)') role=ROLE_TREE_ITEM name='grandchild2' ENABLED,FOCUSABLE,SELECTABLE,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_PANEL name='(null)') role=ROLE_TREE_ITEM name='grandchild3' ENABLED,FOCUSABLE,SELECTABLE,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LINK name='grandchild1' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LINK name='grandchild1' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LINK name='grandchild2' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LINK name='grandchild2' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LINK name='grandchild3' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LINK name='grandchild3' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_SECTION name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_SECTION name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_SECTION name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt index 8b7e880..1d7f65d 100644 --- a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt +++ b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt
@@ -1,11 +1,14 @@ EVENT_OBJECT_HIDE on <div#article> role=ROLE_SYSTEM_DOCUMENT EVENT_OBJECT_REORDER on <div#tree> role=ROLE_SYSTEM_OUTLINE IA2_STATE_VERTICAL -EVENT_OBJECT_SHOW on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild1" INVISIBLE,FOCUSABLE,SELECTABLE level=2 -EVENT_OBJECT_SHOW on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild2" INVISIBLE,FOCUSABLE,SELECTABLE level=2 -EVENT_OBJECT_SHOW on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild3" INVISIBLE,FOCUSABLE,SELECTABLE level=2 === Start Continuation === -EVENT_OBJECT_HIDE on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild1" INVISIBLE,FOCUSABLE,SELECTABLE level=2 -EVENT_OBJECT_HIDE on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild2" INVISIBLE,FOCUSABLE,SELECTABLE level=2 -EVENT_OBJECT_HIDE on <div> role=ROLE_SYSTEM_OUTLINEITEM name="grandchild3" INVISIBLE,FOCUSABLE,SELECTABLE level=2 EVENT_OBJECT_REORDER on <div#tree> role=ROLE_SYSTEM_OUTLINE IA2_STATE_VERTICAL EVENT_OBJECT_SHOW on <div#article> role=ROLE_SYSTEM_DOCUMENT +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#child1> role=ROLE_SYSTEM_LINK name="grandchild1" LINKED +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#child2> role=ROLE_SYSTEM_LINK name="grandchild2" LINKED +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#child3> role=ROLE_SYSTEM_LINK name="grandchild3" LINKED +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#grandchild1> role=ROLE_SYSTEM_GROUPING +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#grandchild2> role=ROLE_SYSTEM_GROUPING +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#grandchild3> role=ROLE_SYSTEM_GROUPING +IA2_EVENT_TEXT_ATTRIBUTE_CHANGED on <div#child1> role=ROLE_SYSTEM_LINK name="grandchild1" LINKED +IA2_EVENT_TEXT_ATTRIBUTE_CHANGED on <div#child2> role=ROLE_SYSTEM_LINK name="grandchild2" LINKED +IA2_EVENT_TEXT_ATTRIBUTE_CHANGED on <div#child3> role=ROLE_SYSTEM_LINK name="grandchild3" LINKED
diff --git a/content/test/data/accessibility/event/css-display-descendants-expected-win.txt b/content/test/data/accessibility/event/css-display-descendants-expected-win.txt index 89fa29f..65b8f92 100644 --- a/content/test/data/accessibility/event/css-display-descendants-expected-win.txt +++ b/content/test/data/accessibility/event/css-display-descendants-expected-win.txt
@@ -1,3 +1,3 @@ EVENT_OBJECT_HIDE on <div#heading-root.a> role=ROLE_SYSTEM_GROUPING name="Heading" level=2 EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL -EVENT_OBJECT_SHOW on <div#banner-root.b> role=ROLE_SYSTEM_GROUPING name="Banner" +EVENT_OBJECT_SHOW on <div#banner-root.b> role=ROLE_SYSTEM_GROUPING name="Banner" \ No newline at end of file
diff --git a/content/test/data/accessibility/event/expanded-changed-expected-auralinux.txt b/content/test/data/accessibility/event/expanded-changed-expected-auralinux.txt index ac303d9..b19ed295 100644 --- a/content/test/data/accessibility/event/expanded-changed-expected-auralinux.txt +++ b/content/test/data/accessibility/event/expanded-changed-expected-auralinux.txt
@@ -1,12 +1,18 @@ CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_LIST) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +NAME-CHANGED:%E2%80%A2 role=ROLE_STATIC name='%E2%80%A2 ' ENABLED,SENSITIVE,SHOWING,VISIBLE +NAME-CHANGED:%E2%80%A2 role=ROLE_STATIC name='%E2%80%A2 ' ENABLED,SENSITIVE,SHOWING,VISIBLE +NAME-CHANGED:%E2%80%A2 role=ROLE_STATIC name='%E2%80%A2 ' ENABLED,SENSITIVE,SHOWING,VISIBLE PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_LINK name='Toggle' ENABLED,EXPANDABLE,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE PARENT-CHANGED PARENT:(role=ROLE_LIST name='list') role=ROLE_LIST_ITEM name='list item 1' ENABLED,SENSITIVE,SHOWING,VISIBLE PARENT-CHANGED PARENT:(role=ROLE_LIST name='list') role=ROLE_LIST_ITEM name='list item 2' ENABLED,SENSITIVE,SHOWING,VISIBLE PARENT-CHANGED PARENT:(role=ROLE_LIST name='list') role=ROLE_LIST_ITEM name='list item 3' ENABLED,SENSITIVE,SHOWING,VISIBLE TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 1' ENABLED,SENSITIVE,SHOWING,VISIBLE TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 1' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 1' ENABLED,SENSITIVE,SHOWING,VISIBLE TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 2' ENABLED,SENSITIVE,SHOWING,VISIBLE TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 2' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 2' ENABLED,SENSITIVE,SHOWING,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 3' ENABLED,SENSITIVE,SHOWING,VISIBLE TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 3' ENABLED,SENSITIVE,SHOWING,VISIBLE TEXT-ATTRIBUTES-CHANGED role=ROLE_LIST_ITEM name='list item 3' ENABLED,SENSITIVE,SHOWING,VISIBLE === Start Continuation ===
diff --git a/content/test/data/accessibility/event/expanded-changed-expected-mac.txt b/content/test/data/accessibility/event/expanded-changed-expected-mac.txt index 14cb8d9..49196495 100644 --- a/content/test/data/accessibility/event/expanded-changed-expected-mac.txt +++ b/content/test/data/accessibility/event/expanded-changed-expected-mac.txt
@@ -1,5 +1,8 @@ AXTitleChanged on AXGroup AXDescription='list item 1' AXTitleChanged on AXGroup AXDescription='list item 2' AXTitleChanged on AXGroup AXDescription='list item 3' +AXTitleChanged on AXListMarker AXValue='%E2%80%A2 ' +AXTitleChanged on AXListMarker AXValue='%E2%80%A2 ' +AXTitleChanged on AXListMarker AXValue='%E2%80%A2 ' === Start Continuation === AXExpandedChanged on AXLink AXDescription='Toggle'
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt index ba9d708..ed98b6d 100644 --- a/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt +++ b/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt
@@ -1,9 +1,11 @@ -CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_MENU) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_MENU_ITEM) role=ROLE_MENU ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE +CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_MENU_ITEM) role=ROLE_MENU ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_MENU name='menu' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_MENU name='menu' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE +TEXT-ATTRIBUTES-CHANGED role=ROLE_MENU name='menu' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE === Start Continuation === CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_MENU) role=ROLE_MENU_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE +STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE === Start Continuation === CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_MENU) role=ROLE_MENU_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE === Start Continuation === -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_MENU) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='menu' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt index b05df0cd..3fc0b88 100644 --- a/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt +++ b/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt
@@ -1,9 +1,5 @@ -AXMenuClosed on AXWebArea -AXMenuClosed on AXWebArea +AXTitleChanged on AXMenu AXDescription='menu' === Start Continuation === -AXMenuClosed on AXWebArea +AXMenuOpened on AXMenu === Start Continuation === -AXMenuClosed on AXWebArea === Start Continuation === -AXMenuClosed on AXWebArea -AXMenuClosed on AXWebArea
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt index 960f13a..b9c69a1 100644 --- a/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt +++ b/content/test/data/accessibility/event/menu-opened-closed-expected-win.txt
@@ -1,5 +1,9 @@ +EVENT_OBJECT_NAMECHANGE on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL SetSize=2 +EVENT_OBJECT_STATECHANGE on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL SetSize=2 +IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL SetSize=2 +IA2_EVENT_TEXT_ATTRIBUTE_CHANGED on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL SetSize=2 === Start Continuation === +EVENT_SYSTEM_MENUPOPUPSTART on <div#submenu> role=ROLE_SYSTEM_MENUPOPUP IA2_STATE_VERTICAL SetSize=1 === Start Continuation === -EVENT_SYSTEM_MENUPOPUPEND on <div#submenu> role=ROLE_SYSTEM_MENUPOPUP IA2_STATE_VERTICAL SetSize=1 +EVENT_SYSTEM_MENUPOPUPEND on <div> role=ROLE_SYSTEM_MENUPOPUP INVISIBLE === Start Continuation === -EVENT_SYSTEM_MENUPOPUPEND on <div#menu> role=ROLE_SYSTEM_MENUPOPUP name="menu" IA2_STATE_VERTICAL SetSize=2
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt index b1be3d2..15ab2fc 100644 --- a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt +++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt
@@ -1,18 +1,8 @@ STATE-CHANGE:EXPANDED:TRUE role=ROLE_MENU_ITEM name='File' ENABLED,EXPANDABLE,EXPANDED,SENSITIVE,SHOWING,VISIBLE,HAS-POPUP -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE === Start Continuation === STATE-CHANGE:EXPANDED:FALSE role=ROLE_MENU_ITEM name='File' ENABLED,EXPANDABLE,SENSITIVE,SHOWING,VISIBLE,HAS-POPUP -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='File' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='New' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE === Start Continuation === === Start Continuation === STATE-CHANGE:EXPANDED:TRUE role=ROLE_MENU_ITEM name='File' ENABLED,EXPANDABLE,EXPANDED,SENSITIVE,SHOWING,VISIBLE,HAS-POPUP -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE === Start Continuation === -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='File' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='New' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE === Start Continuation === -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE -STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt index dfc8925..143cfea 100644 --- a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt +++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt
@@ -1,6 +1,7 @@ AXExpandedChanged on AXMenuItem AXDescription='File' -AXMenuClosed on AXWebArea -AXMenuClosed on AXWebArea +AXExpandedChanged on AXMenuItem AXDescription='New' +AXMenuOpened on AXMenu AXDescription='File' +AXMenuOpened on AXMenu AXDescription='New' === Start Continuation === AXExpandedChanged on AXMenuItem AXDescription='File' AXMenuClosed on AXWebArea @@ -8,8 +9,9 @@ === Start Continuation === === Start Continuation === AXExpandedChanged on AXMenuItem AXDescription='File' -AXMenuClosed on AXWebArea -AXMenuClosed on AXWebArea +AXExpandedChanged on AXMenuItem AXDescription='New' +AXMenuOpened on AXMenu AXDescription='File' +AXMenuOpened on AXMenu AXDescription='New' === Start Continuation === AXMenuClosed on AXWebArea AXMenuClosed on AXWebArea
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt index 31f3e1f..e5b0ed7d 100644 --- a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt +++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt
@@ -1,22 +1,18 @@ ExpandCollapseExpandCollapseState changed on role=menuitem, name=File Name changed on role=group, name=open file and new done +Name changed on role=menu, name=File +Name changed on role=menu, name=New === Start Continuation === ExpandCollapseExpandCollapseState changed on role=menuitem, name=File -MenuClosed -MenuClosed -MenuClosed -MenuClosed Name changed on role=group, name=close file and new done === Start Continuation === Name changed on role=group, name=open new done === Start Continuation === ExpandCollapseExpandCollapseState changed on role=menuitem, name=File Name changed on role=group, name=open file done +Name changed on role=menu, name=File +Name changed on role=menu, name=New === Start Continuation === -MenuClosed -MenuClosed -MenuClosed -MenuClosed Name changed on role=group, name=hide menubar done === Start Continuation === Name changed on role=group, name=show menubar done
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt index 1dfd790..6b52a9b 100644 --- a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt +++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt
@@ -1,12 +1,12 @@ EVENT_OBJECT_STATECHANGE on <li#file-menuitem> role=ROLE_SYSTEM_MENUITEM name="File" EXPANDED,HASPOPUP PosInSet=1 SetSize=2 +EVENT_OBJECT_STATECHANGE on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3 +EVENT_OBJECT_STATECHANGE on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3 === Start Continuation === EVENT_OBJECT_STATECHANGE on <li#file-menuitem> role=ROLE_SYSTEM_MENUITEM name="File" COLLAPSED,HASPOPUP PosInSet=1 SetSize=2 -EVENT_SYSTEM_MENUPOPUPEND on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3 -EVENT_SYSTEM_MENUPOPUPEND on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3 === Start Continuation === === Start Continuation === EVENT_OBJECT_STATECHANGE on <li#file-menuitem> role=ROLE_SYSTEM_MENUITEM name="File" EXPANDED,HASPOPUP PosInSet=1 SetSize=2 +EVENT_OBJECT_STATECHANGE on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3 +EVENT_OBJECT_STATECHANGE on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3 === Start Continuation === -EVENT_SYSTEM_MENUPOPUPEND on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3 -EVENT_SYSTEM_MENUPOPUPEND on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3 === Start Continuation ===
diff --git a/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-auralinux.txt b/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-auralinux.txt index bec1d9d..85c85da 100644 --- a/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-auralinux.txt +++ b/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-auralinux.txt
@@ -9,9 +9,7 @@ STATE-CHANGE:FOCUSED:TRUE role=ROLE_MENU_ITEM name='Open...' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE === Start Continuation === CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_PANEL) role=ROLE_MENU_BAR ENABLED,HORIZONTAL,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_MENU_BAR ENABLED,HORIZONTAL,SENSITIVE,SHOWING,VISIBLE -PARENT-CHANGED PARENT:(role=ROLE_PANEL name='(null)') role=ROLE_MENU_ITEM name='About' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE -PARENT-CHANGED PARENT:(role=ROLE_PANEL name='(null)') role=ROLE_MENU_ITEM name='File' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE,HAS-POPUP +CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_PANEL) role=ROLE_MENU_BAR ENABLED,HORIZONTAL,SENSITIVE,SHOWING,VISIBLE === Start Continuation === FOCUS-EVENT:FALSE role=ROLE_MENU_ITEM name='Open...' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE FOCUS-EVENT:TRUE role=ROLE_MENU_ITEM name='Quit' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-win.txt b/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-win.txt index fa6c831..7ed90a5 100644 --- a/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-win.txt +++ b/content/test/data/accessibility/event/reparent-element-with-active-descendant-expected-win.txt
@@ -4,11 +4,7 @@ EVENT_OBJECT_FOCUS on <div#open> role=ROLE_SYSTEM_MENUITEM name="Open..." FOCUSED,FOCUSABLE PosInSet=2 SetSize=3 IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on <div#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" FOCUSABLE IA2_STATE_VERTICAL SetSize=3 === Start Continuation === -EVENT_OBJECT_HIDE on <div#extra> role=ROLE_SYSTEM_GROUPING -EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_MENUBAR FOCUSED IA2_STATE_HORIZONTAL SetSize=2 -EVENT_OBJECT_SHOW on <div#extra> role=ROLE_SYSTEM_GROUPING -IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_MENUBAR FOCUSED IA2_STATE_HORIZONTAL SetSize=2 new_text={'<obj>' start=0 end=1} -IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_MENUBAR FOCUSED IA2_STATE_HORIZONTAL SetSize=2 old_text={'<obj>' start=0 end=1} +IA2_EVENT_ROLE_CHANGED on <div#extra> role=ROLE_SYSTEM_GROUPING === Start Continuation === EVENT_OBJECT_FOCUS on <div#quit> role=ROLE_SYSTEM_MENUITEM name="Quit" FOCUSED,FOCUSABLE PosInSet=3 SetSize=3 IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on <div#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" FOCUSABLE IA2_STATE_VERTICAL SetSize=3
diff --git a/content/test/data/accessibility/event/role-changed-expected-auralinux.txt b/content/test/data/accessibility/event/role-changed-expected-auralinux.txt index 016b37a..c40a938f 100644 --- a/content/test/data/accessibility/event/role-changed-expected-auralinux.txt +++ b/content/test/data/accessibility/event/role-changed-expected-auralinux.txt
@@ -1,3 +1,3 @@ CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_PANEL) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE -PARENT-CHANGED PARENT:(role=ROLE_PUSH_BUTTON name='Role will change') role=ROLE_STATIC name='Role will change' ENABLED,SENSITIVE,SHOWING,VISIBLE +CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_PUSH_BUTTON) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +NAME-CHANGED:Role will change role=ROLE_PUSH_BUTTON name='Role will change' ENABLED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/role-changed-expected-mac.txt b/content/test/data/accessibility/event/role-changed-expected-mac.txt index e69de29..28576b0 100644 --- a/content/test/data/accessibility/event/role-changed-expected-mac.txt +++ b/content/test/data/accessibility/event/role-changed-expected-mac.txt
@@ -0,0 +1 @@ +AXTitleChanged on AXButton AXTitle='Role will change'
diff --git a/content/test/data/accessibility/event/role-changed-expected-win.txt b/content/test/data/accessibility/event/role-changed-expected-win.txt index 6bed0387a..5971aee8 100644 --- a/content/test/data/accessibility/event/role-changed-expected-win.txt +++ b/content/test/data/accessibility/event/role-changed-expected-win.txt
@@ -1,5 +1 @@ -EVENT_OBJECT_HIDE on <div#a> role=ROLE_SYSTEM_GROUPING -EVENT_OBJECT_REORDER on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE -EVENT_OBJECT_SHOW on <div#a> role=ROLE_SYSTEM_PUSHBUTTON name="Role will change" -IA2_EVENT_TEXT_INSERTED on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE new_text={'<obj>' start=0 end=1} -IA2_EVENT_TEXT_REMOVED on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE old_text={'<obj>' start=0 end=1} +IA2_EVENT_ROLE_CHANGED on <div#a> role=ROLE_SYSTEM_PUSHBUTTON name="Role will change"
diff --git a/content/test/data/accessibility/event/selectlist-expected-win.txt b/content/test/data/accessibility/event/selectlist-expected-win.txt index c644a135..b7df6d3 100644 --- a/content/test/data/accessibility/event/selectlist-expected-win.txt +++ b/content/test/data/accessibility/event/selectlist-expected-win.txt
@@ -4,8 +4,8 @@ EVENT_OBJECT_STATECHANGE on <option> role=ROLE_SYSTEM_LISTITEM name="Option 1" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3 === Start Continuation === EVENT_OBJECT_FOCUS on <option> role=ROLE_SYSTEM_LISTITEM name="Option 4" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3 -EVENT_OBJECT_HIDE on <listbox> role=ROLE_SYSTEM_LIST IA2_STATE_VERTICAL SetSize=3 +EVENT_OBJECT_HIDE on <listbox> role=ROLE_SYSTEM_LIST INVISIBLE EVENT_OBJECT_SHOW on <listbox> role=ROLE_SYSTEM_LIST IA2_STATE_VERTICAL SetSize=3 EVENT_OBJECT_STATECHANGE on <button#ButtonA> role=ROLE_SYSTEM_COMBOBOX name="Combobox A" value="Option 1" COLLAPSED,FOCUSABLE,HASPOPUP EVENT_OBJECT_STATECHANGE on <button#ButtonB> role=ROLE_SYSTEM_COMBOBOX name="Combobox B" value="Option 4" EXPANDED,FOCUSABLE,HASPOPUP -EVENT_OBJECT_STATECHANGE on <option> role=ROLE_SYSTEM_LISTITEM name="Option 4" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3 +EVENT_OBJECT_STATECHANGE on <option> role=ROLE_SYSTEM_LISTITEM name="Option 4" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3 \ No newline at end of file
diff --git a/content/test/data/accessibility/event/text-selection-inside-hidden-element-expected-auralinux.txt b/content/test/data/accessibility/event/text-selection-inside-hidden-element-expected-auralinux.txt index 892b7aa6..43bd8b4 100644 --- a/content/test/data/accessibility/event/text-selection-inside-hidden-element-expected-auralinux.txt +++ b/content/test/data/accessibility/event/text-selection-inside-hidden-element-expected-auralinux.txt
@@ -1,4 +1,5 @@ -CHILDREN-CHANGED:ADD index:1 CHILD:(role=ROLE_SECTION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_SECTION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE +PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_COMBO_BOX name='(null)' ENABLED,EXPANDABLE,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE,HAS-POPUP +PARENT-CHANGED PARENT:(role=ROLE_DOCUMENT_WEB name='(null)') role=ROLE_SECTION name='(null)' ENABLED,FOCUSABLE,SENSITIVE TEXT-CARET-MOVED role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE TEXT-SELECTION-CHANGED role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/html/a-onclick-expected-android.txt b/content/test/data/accessibility/html/a-onclick-expected-android.txt index e8d052d..73f0ef1 100644 --- a/content/test/data/accessibility/html/a-onclick-expected-android.txt +++ b/content/test/data/accessibility/html/a-onclick-expected-android.txt
@@ -3,5 +3,4 @@ ++android.view.View role_description='link' clickable link name='link with no href but onclick' ++++android.widget.TextView name='link with no href but onclick' ++android.view.View role_description='link' clickable link name='link with no href and click handler added via script' -++++android.widget.TextView name='link with no href and click handler added via script' -++android.widget.TextView name='Link with no event handler' +++android.widget.TextView name='Link with no event handler' \ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt index 494ce14..51a51321 100644 --- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -359,7 +359,6 @@ # Failing likely due to protected memory, but cause TBD. crbug.com/1420067 [ fuchsia fuchsia-board-astro ] Pixel_Video* [ Failure ] -crbug.com/1420067 [ fuchsia fuchsia-board-nelson ] Pixel_Video* [ Failure ] crbug.com/1420067 [ fuchsia fuchsia-board-sherlock ] Pixel_Video* [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index 92ff37f..72a1bf2 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -456,17 +456,9 @@ # Flaky tests -crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/more/conformance/quickCheck* [ Failure ] crbug.com/1489477 [ fuchsia web-engine-shell ] WebglExtension_EXT_float_blend [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/state/gl-object-get-calls.html [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ] crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-size-limit.html [ Failure ] crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/textures/misc/texture-size-limit.html [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/textures/misc/texture-sub-image-cube-maps.html [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-upload-size.html [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/textures/misc/texture-upload-size.html [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/textures/svg_image/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] -crbug.com/1489477 [ fuchsia fuchsia-board-nelson ] conformance/textures/svg_image/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] # Anti-aliasing disabled on Fuchsia [ fuchsia ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ] @@ -684,8 +676,6 @@ # Failing test in passthrough mode when experiment DrDc is enabled via fieldtrial_testing_config.json. crbug.com/1276552 [ android-pixel-4 android-r passthrough qualcomm renderer-skia-gl target-cpu-32 ] conformance/canvas/render-after-resize-test.html [ Failure ] -# Failing test when experiment DrDc is enabled via fieldtrial_testing_config.json. -crbug.com/1289303 [ android-pixel-4 android-r angle-disabled no-passthrough ] conformance/textures/misc/texture-video-transparent.html [ Failure ] ## Samsung failures ##
diff --git a/content/web_test/renderer/web_ax_object_proxy.cc b/content/web_test/renderer/web_ax_object_proxy.cc index 3e2559a..984026e 100644 --- a/content/web_test/renderer/web_ax_object_proxy.cc +++ b/content/web_test/renderer/web_ax_object_proxy.cc
@@ -2107,19 +2107,22 @@ return v8::Local<v8::Object>(); } - // Return existing object if there is a match. + // Return existing object if there is a match and it hasn't been detached. + bool found = false; auto persistent = ax_objects_.find(object.AxID()); if (persistent != ax_objects_.end()) { - auto local = v8::Local<v8::Object>::New(isolate_, persistent->second); - -#if DCHECK_IS_ON() + found = true; WebAXObjectProxy* proxy = nullptr; + // TODO(accessibility): Can this detached check be simplified? + auto local = v8::Local<v8::Object>::New(isolate_, persistent->second); bool ok = gin::ConvertFromV8(isolate_, local, &proxy); DCHECK(ok); - DCHECK(proxy->IsEqualToObject(object)); + if (!proxy->accessibility_object().IsDetached()) { +#if DCHECK_IS_ON() + DCHECK(proxy->IsEqualToObject(object)); #endif - - return local; + return local; + } } // Create a new object. @@ -2128,6 +2131,10 @@ v8::Local<v8::Object> handle; if (value_handle.IsEmpty() || !value_handle->ToObject(isolate_->GetCurrentContext()).ToLocal(&handle)) { + if (found) { + // Remove old detached object. + ax_objects_.erase(object.AxID()); + } return {}; }
diff --git a/content/web_test/renderer/web_ax_object_proxy.h b/content/web_test/renderer/web_ax_object_proxy.h index 7fd0f814..6a1daa7 100644 --- a/content/web_test/renderer/web_ax_object_proxy.h +++ b/content/web_test/renderer/web_ax_object_proxy.h
@@ -57,11 +57,11 @@ const std::vector<ui::AXEventIntent>& event_intents); void Reset(); - protected: const blink::WebAXObject& accessibility_object() const { return accessibility_object_; } + protected: Factory* factory() const { return factory_; } bool IsDetached() const { return !factory_ || !factory_->GetAXContext(); }
diff --git a/docs/website b/docs/website index b13d20e..7918c58 160000 --- a/docs/website +++ b/docs/website
@@ -1 +1 @@ -Subproject commit b13d20e7483a7a2b3e600b5981693d476d43d19d +Subproject commit 7918c586771aed5e170cb5ce33fdfc38f6203063
diff --git a/extensions/browser/url_request_util.cc b/extensions/browser/url_request_util.cc index 74bc76d..2af3580b 100644 --- a/extensions/browser/url_request_util.cc +++ b/extensions/browser/url_request_util.cc
@@ -91,11 +91,10 @@ return true; } - // When navigating in subframe, allow if it is the same origin - // as the top-level frame. This can only be the case if the subframe - // request is coming from the extension process. + // When navigating in subframe, verify that the extension the resource is + // loaded from matches the process loading it. if (network::IsRequestDestinationEmbeddedFrame(destination) && - process_map.Contains(child_id)) { + process_map.Contains(extension->id(), child_id)) { *allowed = true; return true; }
diff --git a/google_apis/gaia/core_account_id.cc b/google_apis/gaia/core_account_id.cc index 51fbecea..64113806 100644 --- a/google_apis/gaia/core_account_id.cc +++ b/google_apis/gaia/core_account_id.cc
@@ -6,6 +6,7 @@ #include "base/check.h" #include "base/containers/contains.h" +#include "base/containers/to_vector.h" #include "google_apis/gaia/gaia_auth_util.h" namespace { @@ -96,8 +97,5 @@ std::vector<std::string> ToStringList( const std::vector<CoreAccountId>& account_ids) { - std::vector<std::string> account_ids_string; - for (const auto& account_id : account_ids) - account_ids_string.push_back(account_id.ToString()); - return account_ids_string; + return base::ToVector(account_ids, &CoreAccountId::ToString); }
diff --git a/gpu/command_buffer/service/dawn_context_provider.cc b/gpu/command_buffer/service/dawn_context_provider.cc index d5e7427..63fb96b6 100644 --- a/gpu/command_buffer/service/dawn_context_provider.cc +++ b/gpu/command_buffer/service/dawn_context_provider.cc
@@ -371,11 +371,16 @@ wgpu::FeatureName::MultiPlanarRenderTargets, wgpu::FeatureName::Norm16TextureFormats, - // The following features are always supported when running on the - // Metal backend. + // The following features are always supported by the the Metal backend on + // the Mac versions on which Chrome runs. wgpu::FeatureName::SharedTextureMemoryIOSurface, wgpu::FeatureName::SharedFenceMTLSharedEvent, + // The following features are always supported when running on the Vulkan + // backend on Android. + wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer, + wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD, + wgpu::FeatureName::TransientAttachments, };
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc index 0045abd..5adfc14b 100644 --- a/gpu/command_buffer/service/webgpu_decoder_impl.cc +++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1367,6 +1367,18 @@ required_features.push_back(wgpu::FeatureName::SharedFenceMTLSharedEvent); } +#if BUILDFLAG(IS_ANDROID) + if (adapter_obj.HasFeature( + wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer)) { + required_features.push_back( + wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer); + } + if (adapter_obj.HasFeature(wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD)) { + required_features.push_back( + wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD); + } +#endif + #if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) // On Desktop GL via ANGLE, require GL texture sharing. if (use_webgpu_adapter_ == WebGPUAdapterName::kOpenGLES && @@ -1707,6 +1719,9 @@ // NOTE: These platforms should be switched to the corresponding // SharedTextureMemory feature check as they are converted to using // SharedTextureMemory. + // TODO(crbug.com/327111284): Change this to check for + // SharedTextureMemoryAHardwareBuffer on Android-Vulkan once we've made the + // switch there. supports_external_textures = adapter.SupportsExternalImages(); #endif if (!(supports_external_textures || is_swiftshader)) {
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl index ac7dc153..89c62e4 100644 --- a/infra/config/generated/testing/variants.pyl +++ b/infra/config/generated/testing/variants.pyl
@@ -307,16 +307,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'identifier': 'Lacros version skew testing ash canary', - 'description': 'Run with ash-chrome version 124.0.6328.0', + 'description': 'Run with ash-chrome version 124.0.6329.0', 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome', ], 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v124.0.6328.0', - 'revision': 'version:124.0.6328.0', + 'location': 'lacros_version_skew_tests_v124.0.6329.0', + 'revision': 'version:124.0.6329.0', }, ], },
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json index 0b3c1fb..4d5e7cb 100644 --- a/infra/config/targets/lacros-version-skew-variants.json +++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@ { "LACROS_VERSION_SKEW_CANARY": { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "identifier": "Lacros version skew testing ash canary", "swarming": { "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ] }
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd index 2c3d1a4e..7747bc60 100644 --- a/ios/chrome/app/strings/ios_chromium_strings.grd +++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -206,9 +206,6 @@ <message name="IDS_IOS_AUTOFILL_ADDRESS_MIGRATE_IN_ACCOUNT_FOOTER" desc="Footer text shown in the address migration prompt to account. [iOS only]"> This address is currently saved to Chromium. To use it across Google products, save it in your Google Account, <ph name="USER_EMAIL">$1<ex>janedoe@google.com</ex></ph>. </message> - <message name="IDS_IOS_AUTOFILL_DESCRIBE_LOCAL_COPY" desc="Text label that describes a Wallet credit card which has been copied to the local Chromium instance. Title case. [Length: 20em] [iOS only]"> - Copied to Chromium - </message> <message name="IDS_IOS_BANDWIDTH_MANAGEMENT_DESCRIPTION_LEARN_MORE" desc="Description text bandwidth management panel in settings with explicit Learn More link [iOS only]"> Chromium has features that help you manage your internet data and how quickly you're able to load webpages. <ph name="BEGIN_LINK">BEGIN_LINK</ph>Learn more<ph name="END_LINK">END_LINK</ph>
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd index ca409f12..7c1bfce 100644 --- a/ios/chrome/app/strings/ios_google_chrome_strings.grd +++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -206,9 +206,6 @@ <message name="IDS_IOS_AUTOFILL_ADDRESS_MIGRATE_IN_ACCOUNT_FOOTER" desc="Footer text shown in the address migration prompt to account. [iOS only]"> This address is currently saved to Chrome. To use it across Google products, save it in your Google Account, <ph name="USER_EMAIL">$1<ex>janedoe@google.com</ex></ph>. </message> - <message name="IDS_IOS_AUTOFILL_DESCRIBE_LOCAL_COPY" desc="Text label that describes a Wallet credit card which has been copied to the local Chrome instance. Title case. [Length: 20em] [iOS only]"> - Copied to Chrome - </message> <message name="IDS_IOS_BANDWIDTH_MANAGEMENT_DESCRIPTION_LEARN_MORE" desc="Description text bandwidth management panel in settings with explicit Learn More link [iOS only]"> Chrome has features that help you manage your internet data and how quickly you're able to load webpages. <ph name="BEGIN_LINK">BEGIN_LINK</ph>Learn more<ph name="END_LINK">END_LINK</ph>
diff --git a/ios/chrome/browser/autocomplete/model/in_memory_url_index_factory.cc b/ios/chrome/browser/autocomplete/model/in_memory_url_index_factory.cc index 2b47eba..4b435c7f9 100644 --- a/ios/chrome/browser/autocomplete/model/in_memory_url_index_factory.cc +++ b/ios/chrome/browser/autocomplete/model/in_memory_url_index_factory.cc
@@ -11,8 +11,7 @@ #include "components/keyed_service/core/service_access_type.h" #include "components/keyed_service/ios/browser_state_dependency_manager.h" #include "components/omnibox/browser/in_memory_url_index.h" -#include "ios/chrome/browser/bookmarks/model/legacy_bookmark_model.h" -#include "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h" +#include "ios/chrome/browser/bookmarks/model/bookmark_model_factory.h" #include "ios/chrome/browser/history/model/history_service_factory.h" #include "ios/chrome/browser/search_engines/model/template_url_service_factory.h" #include "ios/chrome/browser/shared/model/browser_state/browser_state_otr_helper.h" @@ -34,8 +33,7 @@ // Do not force creation of the HistoryService if saving history is disabled. std::unique_ptr<InMemoryURLIndex> in_memory_url_index(new InMemoryURLIndex( - ios::LocalOrSyncableBookmarkModelFactory::GetForBrowserState( - browser_state), + ios::BookmarkModelFactory::GetForBrowserState(browser_state), ios::HistoryServiceFactory::GetForBrowserState( browser_state, ServiceAccessType::IMPLICIT_ACCESS), ios::TemplateURLServiceFactory::GetForBrowserState(browser_state), @@ -63,8 +61,7 @@ : BrowserStateKeyedServiceFactory( "InMemoryURLIndex", BrowserStateDependencyManager::GetInstance()) { - DependsOn(ios::LocalOrSyncableBookmarkModelFactory::GetInstance()); - // TODO(crbug.com/1425459): Add AccountBookmarkModelFactory support. + DependsOn(ios::BookmarkModelFactory::GetInstance()); DependsOn(ios::HistoryServiceFactory::GetInstance()); DependsOn(ios::TemplateURLServiceFactory::GetInstance()); }
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index cbd9b1e..3015996 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1657,6 +1657,10 @@ flag_descriptions::kLinkedServicesSettingIosName, flag_descriptions::kLinkedServicesSettingIosDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kLinkedServicesSettingIos)}, + {"disable-fullscreen-scrolling", + flag_descriptions::kDisableFullscreenScrollingName, + flag_descriptions::kDisableFullscreenScrollingDescription, + flags_ui::kOsIos, FEATURE_VALUE_TYPE(kDisableFullscreenScrolling)}, }; bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index a4a09ff9..fe71555 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -300,6 +300,11 @@ "A crash report will be uploaded if the main thread is frozen more than " "the time specified by this flag."; +const char kDisableFullscreenScrollingName[] = "Disable fullscreen scrolling"; +const char kDisableFullscreenScrollingDescription[] = + "When this flag is enabled and a user scroll a web page, toolbars will " + "stay extanded and the user will not enter in fullscreen mode."; + const char kDiscoverFeedSportCardName[] = "Sport card in Discover feed"; const char kDiscoverFeedSportCardDescription[] = "Enables the live sport card in the NTP's Discover feed";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index d0ebf5c..6150638 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -248,6 +248,10 @@ extern const char kDetectMainThreadFreezeName[]; extern const char kDetectMainThreadFreezeDescription[]; +// Title and description for the flag to disable the fullscreen scrolling logic. +extern const char kDisableFullscreenScrollingName[]; +extern const char kDisableFullscreenScrollingDescription[]; + // Title and description for the flag that adds the sport card to the Discover // feed. extern const char kDiscoverFeedSportCardName[];
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h index 12616ef..1aca3e99 100644 --- a/ios/chrome/browser/shared/public/features/features.h +++ b/ios/chrome/browser/shared/public/features/features.h
@@ -588,4 +588,7 @@ // Returns true if the MagicStack UICollectionView implementation is enabled. bool IsIOSMagicStackCollectionViewEnabled(); +// Feature flag to disable fullscreen scrolling logic. +BASE_DECLARE_FEATURE(kDisableFullscreenScrolling); + #endif // IOS_CHROME_BROWSER_SHARED_PUBLIC_FEATURES_FEATURES_H_
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm index 6a2dede5..cf9d49e 100644 --- a/ios/chrome/browser/shared/public/features/features.mm +++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -738,3 +738,7 @@ bool IsIOSMagicStackCollectionViewEnabled() { return base::FeatureList::IsEnabled(kIOSMagicStackCollectionView); } + +BASE_FEATURE(kDisableFullscreenScrolling, + "DisableFullscreenScrolling", + base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm index 91d70f6..6757cf9 100644 --- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm +++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
@@ -5,6 +5,8 @@ #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h" #import "base/memory/ptr_util.h" +#import "base/metrics/user_metrics.h" +#import "base/metrics/user_metrics_action.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/browser_state/browser_state_otr_helper.h" #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" @@ -151,14 +153,17 @@ } void FullscreenControllerImpl::EnterFullscreen() { + base::RecordAction(base::UserMetricsAction("MobileFullscreenEntered")); mediator_.EnterFullscreen(); } void FullscreenControllerImpl::ExitFullscreen() { + base::RecordAction(base::UserMetricsAction("MobileFullscreenExited")); mediator_.ExitFullscreen(); } void FullscreenControllerImpl::ExitFullscreenWithoutAnimation() { + base::RecordAction(base::UserMetricsAction("MobileFullscreenExited")); mediator_.ExitFullscreenWithoutAnimation(); }
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm index 21d5e74..7f22012 100644 --- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm +++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
@@ -222,6 +222,9 @@ } void FullscreenModel::SetScrollViewIsScrolling(bool scrolling) { + if (base::FeatureList::IsEnabled(kDisableFullscreenScrolling)) { + return; + } if (scrolling_ == scrolling) return; scrolling_ = scrolling; @@ -255,6 +258,9 @@ } void FullscreenModel::SetScrollViewIsDragging(bool dragging) { + if (base::FeatureList::IsEnabled(kDisableFullscreenScrolling)) { + return; + } if (dragging_ == dragging) return; dragging_ = dragging;
diff --git a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h index 21c8760f..6090eaf 100644 --- a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h +++ b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h
@@ -48,7 +48,7 @@ bool IsDefaultSearchProviderEnabled() const override; SessionID GetSessionID() const override; PrefService* GetPrefs() override; - bookmarks::BookmarkModel* GetBookmarkModel() override; + bookmarks::CoreBookmarkModel* GetBookmarkModel() override; AutocompleteControllerEmitter* GetAutocompleteControllerEmitter() override; TemplateURLService* GetTemplateURLService() override; const AutocompleteSchemeClassifier& GetSchemeClassifier() const override;
diff --git a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm index 1ab14eb..0981b1ab 100644 --- a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm +++ b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm
@@ -22,9 +22,8 @@ #import "ios/chrome/browser/autocomplete/model/autocomplete_classifier_factory.h" #import "ios/chrome/browser/autocomplete/model/autocomplete_provider_client_impl.h" #import "ios/chrome/browser/autocomplete/model/shortcuts_backend_factory.h" +#import "ios/chrome/browser/bookmarks/model/bookmark_model_factory.h" #import "ios/chrome/browser/bookmarks/model/bookmarks_utils.h" -#import "ios/chrome/browser/bookmarks/model/legacy_bookmark_model.h" -#import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h" #import "ios/chrome/browser/default_browser/model/utils.h" #import "ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h" #import "ios/chrome/browser/intents/intents_donation_helper.h" @@ -94,9 +93,8 @@ return browser_state_->GetPrefs(); } -bookmarks::BookmarkModel* ChromeOmniboxClientIOS::GetBookmarkModel() { - return ios::LocalOrSyncableBookmarkModelFactory::GetForBrowserState( - browser_state_); +bookmarks::CoreBookmarkModel* ChromeOmniboxClientIOS::GetBookmarkModel() { + return ios::BookmarkModelFactory::GetForBrowserState(browser_state_); } AutocompleteControllerEmitter*
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm index 1f1796d..589c1e4 100644 --- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
@@ -29,7 +29,6 @@ #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" #import "ios/chrome/browser/ui/settings/autofill/autofill_constants.h" #import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_util.h" -#import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h" #import "ios/chrome/grit/ios_branded_strings.h" #import "ios/chrome/grit/ios_strings.h" #import "ui/base/l10n/l10n_util.h" @@ -40,7 +39,6 @@ typedef NS_ENUM(NSInteger, SectionIdentifier) { SectionIdentifierFields = kSectionIdentifierEnumZero, - SectionIdentifierCopiedToChrome, }; typedef NS_ENUM(NSInteger, ItemType) { @@ -48,7 +46,6 @@ ItemTypeCardNumber, ItemTypeExpirationMonth, ItemTypeExpirationYear, - ItemTypeCopiedToChrome, ItemTypeNickname, }; @@ -95,8 +92,6 @@ - (void)editButtonPressed { // In the case of server cards, open the Payments editing page instead. if (_creditCard.record_type() == - autofill::CreditCard::RecordType::kFullServerCard || - _creditCard.record_type() == autofill::CreditCard::RecordType::kMaskedServerCard) { GURL paymentsURL = autofill::payments::GetManageInstrumentsUrl(); OpenNewTabCommand* command = @@ -170,16 +165,6 @@ for (AutofillEditItem* item in editItems) { [model addItem:item toSectionWithIdentifier:SectionIdentifierFields]; } - - if (_creditCard.record_type() == - autofill::CreditCard::RecordType::kFullServerCard) { - // Add CopiedToChrome cell in its own section. - [model addSectionWithIdentifier:SectionIdentifierCopiedToChrome]; - CopiedToChromeItem* copiedToChromeItem = - [[CopiedToChromeItem alloc] initWithType:ItemTypeCopiedToChrome]; - [model addItem:copiedToChromeItem - toSectionWithIdentifier:SectionIdentifierCopiedToChrome]; - } } #pragma mark - TableViewTextEditItemDelegate @@ -280,14 +265,6 @@ case ItemTypeExpirationYear: case ItemTypeNickname: break; - case ItemTypeCopiedToChrome: { - CopiedToChromeCell* copiedToChromeCell = - base::apple::ObjCCastStrict<CopiedToChromeCell>(cell); - [copiedToChromeCell.button addTarget:self - action:@selector(buttonTapped:) - forControlEvents:UIControlEventTouchUpInside]; - break; - } default: break; } @@ -331,7 +308,6 @@ case ItemTypeExpirationMonth: case ItemTypeExpirationYear: case ItemTypeNickname: - case ItemTypeCopiedToChrome: return YES; } NOTREACHED();
diff --git a/ios/chrome/browser/ui/settings/cells/BUILD.gn b/ios/chrome/browser/ui/settings/cells/BUILD.gn index 6b375d1..bdaad12 100644 --- a/ios/chrome/browser/ui/settings/cells/BUILD.gn +++ b/ios/chrome/browser/ui/settings/cells/BUILD.gn
@@ -8,8 +8,6 @@ "account_sign_in_item.mm", "byo_textfield_item.h", "byo_textfield_item.mm", - "copied_to_chrome_item.h", - "copied_to_chrome_item.mm", "inline_promo_cell.h", "inline_promo_cell.mm", "inline_promo_item.h", @@ -81,7 +79,6 @@ testonly = true sources = [ "byo_textfield_item_unittest.mm", - "copied_to_chrome_item_unittest.mm", "inline_promo_item_unittest.mm", "passphrase_error_item_unittest.mm", "settings_check_item_unittest.mm",
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h deleted file mode 100644 index 403426f6..0000000 --- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_COPIED_TO_CHROME_ITEM_H_ -#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_COPIED_TO_CHROME_ITEM_H_ - -#import <UIKit/UIKit.h> - -#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_item.h" - -// Item that configures a CopiedToChromeCell. -@interface CopiedToChromeItem : TableViewItem -@end - -// A cell indicating that the credit card has been copied to Chrome. Includes a -// button to clear the copy. -@interface CopiedToChromeCell : TableViewCell - -// Text label displaying the item's text. -@property(nonatomic, readonly, strong) UILabel* textLabel; - -// Button to clear the copy. -@property(nonatomic, readonly, strong) UIButton* button; - -@end - -#endif // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_COPIED_TO_CHROME_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm deleted file mode 100644 index 60da100..0000000 --- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm +++ /dev/null
@@ -1,89 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h" - -#import "components/strings/grit/components_strings.h" -#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h" -#import "ios/chrome/common/ui/colors/semantic_color_names.h" -#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h" -#import "ios/chrome/common/ui/util/constraints_ui_util.h" -#import "ios/chrome/grit/ios_branded_strings.h" -#import "ui/base/l10n/l10n_util_mac.h" - -@implementation CopiedToChromeItem - -- (instancetype)initWithType:(NSInteger)type { - self = [super initWithType:type]; - if (self) { - self.cellClass = [CopiedToChromeCell class]; - } - return self; -} - -@end - -@implementation CopiedToChromeCell - -@synthesize textLabel = _textLabel; - -- (instancetype)initWithStyle:(UITableViewCellStyle)style - reuseIdentifier:(NSString*)reuseIdentifier { - self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; - if (self) { - UIView* contentView = self.contentView; - - _textLabel = [[UILabel alloc] init]; - _textLabel.translatesAutoresizingMaskIntoConstraints = NO; - _textLabel.text = - l10n_util::GetNSString(IDS_IOS_AUTOFILL_DESCRIBE_LOCAL_COPY); - _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; - _textLabel.adjustsFontForContentSizeCategory = YES; - _textLabel.textColor = [UIColor colorNamed:kTextPrimaryColor]; - [_textLabel - setContentCompressionResistancePriority:UILayoutPriorityDefaultLow - forAxis: - UILayoutConstraintAxisHorizontal]; - [contentView addSubview:_textLabel]; - - _button = [UIButton buttonWithType:UIButtonTypeCustom]; - [_button setTitleColor:[UIColor colorNamed:kBlueColor] - forState:UIControlStateNormal]; - - _button.translatesAutoresizingMaskIntoConstraints = NO; - [_button - setTitle:l10n_util::GetNSString(IDS_AUTOFILL_REMOVE_LOCAL_COPY_BUTTON) - forState:UIControlStateNormal]; - [contentView addSubview:_button]; - - // Set up the constraints. - [NSLayoutConstraint activateConstraints:@[ - [_textLabel.leadingAnchor - constraintEqualToAnchor:contentView.leadingAnchor - constant:kTableViewHorizontalSpacing], - [_textLabel.trailingAnchor - constraintLessThanOrEqualToAnchor:_button.leadingAnchor - constant:-kTableViewHorizontalSpacing], - [_textLabel.centerYAnchor - constraintEqualToAnchor:contentView.centerYAnchor], - [_button.trailingAnchor - constraintEqualToAnchor:contentView.trailingAnchor - constant:-kTableViewHorizontalSpacing], - [_button.firstBaselineAnchor - constraintEqualToAnchor:_textLabel.firstBaselineAnchor], - ]]; - AddOptionalVerticalPadding(contentView, _textLabel, - kTableViewOneLabelCellVerticalSpacing); - } - return self; -} - -- (void)prepareForReuse { - [super prepareForReuse]; - [self.button removeTarget:nil - action:nil - forControlEvents:UIControlEventAllEvents]; -} - -@end
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item_unittest.mm b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item_unittest.mm deleted file mode 100644 index cb9356fcf..0000000 --- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item_unittest.mm +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h" - -#import "components/strings/grit/components_strings.h" -#import "ios/chrome/grit/ios_branded_strings.h" -#import "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#import "testing/platform_test.h" -#import "ui/base/l10n/l10n_util_mac.h" - -namespace { - -using CopiedToChromeItemTest = PlatformTest; - -// Tests that the cell created out of a CopiedToChromeItem is set up properly. -TEST_F(CopiedToChromeItemTest, InitializeCell) { - CopiedToChromeItem* item = [[CopiedToChromeItem alloc] initWithType:0]; - - id cell = [[[item cellClass] alloc] init]; - ASSERT_TRUE([cell isMemberOfClass:[CopiedToChromeCell class]]); - - CopiedToChromeCell* copiedToChromeCell = cell; - EXPECT_NSEQ(l10n_util::GetNSString(IDS_IOS_AUTOFILL_DESCRIBE_LOCAL_COPY), - copiedToChromeCell.textLabel.text); - - NSString* buttonText = - l10n_util::GetNSString(IDS_AUTOFILL_REMOVE_LOCAL_COPY_BUTTON); - EXPECT_NSEQ(buttonText, - [copiedToChromeCell.button titleForState:UIControlStateNormal]); -} - -} // namespace
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm index 12ab7a52..a72d2e59 100644 --- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm +++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -35,7 +35,6 @@ #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" #import "ios/chrome/browser/ui/settings/address_bar_preference/cells/address_bar_options_item.h" #import "ios/chrome/browser/ui/settings/cells/account_sign_in_item.h" -#import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h" #import "ios/chrome/browser/ui/settings/cells/inline_promo_item.h" #import "ios/chrome/browser/ui/settings/cells/settings_check_cell.h" #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h" @@ -574,11 +573,6 @@ [model setHeader:autofillHeader forSectionWithIdentifier:SectionIdentifierAutofill]; - CopiedToChromeItem* copiedToChrome = - [[CopiedToChromeItem alloc] initWithType:ItemTypeAutofillData]; - [model addItem:copiedToChrome - toSectionWithIdentifier:SectionIdentifierAutofill]; - // SectionIdentifierAccount. UIImage* signinPromoAvatar = ios::provider::GetSigninDefaultAvatar(); CGSize avatarSize =
diff --git a/media/audio/audio_device_thread.cc b/media/audio/audio_device_thread.cc index d000377..fac2d9b 100644 --- a/media/audio/audio_device_thread.cc +++ b/media/audio/audio_device_thread.cc
@@ -63,6 +63,7 @@ } AudioDeviceThread::~AudioDeviceThread() { + in_shutdown_.Set(); socket_.Shutdown(); if (thread_handle_.is_null()) return; @@ -112,6 +113,10 @@ if (bytes_sent != sizeof(buffer_index)) break; } + + if (!in_shutdown_.IsSet()) { + callback_->OnSocketError(); + } } } // namespace media.
diff --git a/media/audio/audio_device_thread.h b/media/audio/audio_device_thread.h index c613f2e..0e94d537 100644 --- a/media/audio/audio_device_thread.h +++ b/media/audio/audio_device_thread.h
@@ -9,6 +9,7 @@ #include "base/memory/raw_ptr.h" #include "base/sync_socket.h" +#include "base/synchronization/atomic_flag.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_checker.h" #include "build/buildflag.h" @@ -46,6 +47,9 @@ // Called whenever we receive notifications about pending input data. virtual void Process(uint32_t pending_data) = 0; + // Called if the socket closes outside of destruction. + virtual void OnSocketError() = 0; + base::TimeDelta buffer_duration() const { return audio_parameters_.GetBufferDuration(); } @@ -88,6 +92,9 @@ #endif void ThreadMain() final; + // Set to true in destruction, but before closing the socket. + base::AtomicFlag in_shutdown_; + const raw_ptr<Callback> callback_; const char* thread_name_; base::CancelableSyncSocket socket_;
diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc index 9677510..6ebd6099b 100644 --- a/media/audio/audio_input_device.cc +++ b/media/audio/audio_input_device.cc
@@ -82,6 +82,8 @@ // Called whenever we receive notifications about pending data. void Process(uint32_t pending_data) override; + void OnSocketError() override; + private: const bool enable_uma_; base::ReadOnlySharedMemoryRegion shared_memory_region_; @@ -495,4 +497,10 @@ "now_time (ms)", (now_time - base::TimeTicks()).InMillisecondsF()); } +void AudioInputDevice::AudioThreadCallback::OnSocketError() { + capture_callback_->OnCaptureError( + AudioCapturerSource::ErrorCode::kSocketError, + "Socket closed unexpectedly"); +} + } // namespace media
diff --git a/media/audio/audio_input_device_unittest.cc b/media/audio/audio_input_device_unittest.cc index ba432d9..93726c9 100644 --- a/media/audio/audio_input_device_unittest.cc +++ b/media/audio/audio_input_device_unittest.cc
@@ -13,6 +13,7 @@ #include "base/sync_socket.h" #include "base/synchronization/waitable_event.h" #include "base/task/single_thread_task_runner.h" +#include "base/test/gmock_callback_support.h" #include "base/test/task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -95,7 +96,63 @@ } // namespace. class AudioInputDeviceTest - : public ::testing::TestWithParam<AudioInputDevice::DeadStreamDetection> {}; + : public ::testing::TestWithParam<AudioInputDevice::DeadStreamDetection> { + protected: + void CreateInputDevice() { + AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, + ChannelLayoutConfig::Stereo(), 48000, 480); + + const uint32_t memory_size = + ComputeAudioInputBufferSize(params, kMemorySegmentCount); + + shared_memory_ = base::ReadOnlySharedMemoryRegion::Create(memory_size); + ASSERT_TRUE(shared_memory_.IsValid()); + memset(shared_memory_.mapping.memory(), 0xff, memory_size); + + ASSERT_TRUE( + CancelableSyncSocket::CreatePair(&browser_socket_, &renderer_socket_)); + + MockAudioInputIPC* input_ipc = new MockAudioInputIPC(); + + device_ = base::MakeRefCounted<AudioInputDevice>( + base::WrapUnique(input_ipc), AudioInputDevice::Purpose::kUserInput, + AudioInputDeviceTest::GetParam()); + + const base::TimeTicks capture_time = + base::TimeTicks() + base::Microseconds(123); + // The AssertingCaptureCallback will check that the capture time is correct + // upon the call to Capture(). + capture_callback_.emplace(capture_time); + device_->Initialize(params, &capture_callback_.value()); + + EXPECT_CALL(*input_ipc, CreateStream(_, _, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + auto duplicated_shared_memory_region = + shared_memory_.region.Duplicate(); + CHECK(duplicated_shared_memory_region.IsValid()); + static_cast<AudioInputIPCDelegate*>(device_.get()) + ->OnStreamCreated(std::move(duplicated_shared_memory_region), + renderer_socket_.Take(), false); + })); + EXPECT_CALL(*input_ipc, RecordStream()); + EXPECT_CALL(*capture_callback_, OnCaptureStarted()); + EXPECT_CALL(*input_ipc, CloseStream()); + + uint8_t* ptr = static_cast<uint8_t*>(shared_memory_.mapping.memory()); + AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); + buffer->params.id = 0; + buffer->params.capture_time_us = + (capture_time - base::TimeTicks()).InMicroseconds(); + buffer->params.glitch_duration_us = 0; + buffer->params.glitch_count = 0; + } + + base::MappedReadOnlyRegion shared_memory_; + CancelableSyncSocket browser_socket_; + CancelableSyncSocket renderer_socket_; + std::optional<AssertingCaptureCallback> capture_callback_; + scoped_refptr<AudioInputDevice> device_; +}; // Regular construction. TEST_P(AudioInputDeviceTest, Noop) { @@ -109,7 +166,7 @@ ACTION_P(ReportStateChange, device) { static_cast<AudioInputIPCDelegate*>(device)->OnError( - media::AudioCapturerSource::ErrorCode::kUnknown); + AudioCapturerSource::ErrorCode::kUnknown); } // Verify that we get an OnCaptureError() callback if CreateStream fails. @@ -133,107 +190,52 @@ } TEST_P(AudioInputDeviceTest, CreateStream) { - AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, - ChannelLayoutConfig::Stereo(), 48000, 480); - base::MappedReadOnlyRegion shared_memory; - CancelableSyncSocket browser_socket; - CancelableSyncSocket renderer_socket; - - const uint32_t memory_size = - media::ComputeAudioInputBufferSize(params, kMemorySegmentCount); - - shared_memory = base::ReadOnlySharedMemoryRegion::Create(memory_size); - ASSERT_TRUE(shared_memory.IsValid()); - memset(shared_memory.mapping.memory(), 0xff, memory_size); - - ASSERT_TRUE( - CancelableSyncSocket::CreatePair(&browser_socket, &renderer_socket)); - base::ReadOnlySharedMemoryRegion duplicated_shared_memory_region = - shared_memory.region.Duplicate(); - ASSERT_TRUE(duplicated_shared_memory_region.IsValid()); - base::test::TaskEnvironment ste; - MockCaptureCallback callback; - MockAudioInputIPC* input_ipc = new MockAudioInputIPC(); - scoped_refptr<AudioInputDevice> device(new AudioInputDevice( - base::WrapUnique(input_ipc), AudioInputDevice::Purpose::kUserInput, - AudioInputDeviceTest::GetParam())); - device->Initialize(params, &callback); + CreateInputDevice(); - EXPECT_CALL(*input_ipc, CreateStream(_, _, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - static_cast<AudioInputIPCDelegate*>(device.get()) - ->OnStreamCreated(std::move(duplicated_shared_memory_region), - renderer_socket.Take(), false); - })); - EXPECT_CALL(*input_ipc, RecordStream()); - - EXPECT_CALL(callback, OnCaptureStarted()); - device->Start(); - EXPECT_CALL(*input_ipc, CloseStream()); - device->Stop(); + device_->Start(); + device_->Stop(); } TEST_P(AudioInputDeviceTest, CaptureCallback) { base::test::TaskEnvironment ste; - AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, - ChannelLayoutConfig::Stereo(), 48000, 480); - base::MappedReadOnlyRegion shared_memory; - CancelableSyncSocket browser_socket; - CancelableSyncSocket renderer_socket; + CreateInputDevice(); - const uint32_t memory_size = - media::ComputeAudioInputBufferSize(params, kMemorySegmentCount); - - shared_memory = base::ReadOnlySharedMemoryRegion::Create(memory_size); - ASSERT_TRUE(shared_memory.IsValid()); - memset(shared_memory.mapping.memory(), 0xff, memory_size); - - ASSERT_TRUE( - CancelableSyncSocket::CreatePair(&browser_socket, &renderer_socket)); - base::ReadOnlySharedMemoryRegion duplicated_shared_memory_region = - shared_memory.region.Duplicate(); - ASSERT_TRUE(duplicated_shared_memory_region.IsValid()); - - MockAudioInputIPC* input_ipc = new MockAudioInputIPC(); - scoped_refptr<AudioInputDevice> device(new AudioInputDevice( - base::WrapUnique(input_ipc), AudioInputDevice::Purpose::kUserInput, - AudioInputDeviceTest::GetParam())); - - const base::TimeTicks capture_time = - base::TimeTicks() + base::Microseconds(123); - // The AssertingCaptureCallback will check that the capture time is correct - // upon the call to Capture(). - AssertingCaptureCallback callback(capture_time); - device->Initialize(params, &callback); - - EXPECT_CALL(*input_ipc, CreateStream(_, _, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - static_cast<AudioInputIPCDelegate*>(device.get()) - ->OnStreamCreated(std::move(duplicated_shared_memory_region), - renderer_socket.Take(), false); - })); - EXPECT_CALL(*input_ipc, RecordStream()); - EXPECT_CALL(callback, OnCaptureStarted()); - - uint8_t* ptr = static_cast<uint8_t*>(shared_memory.mapping.memory()); - AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); - buffer->params.id = 0; - buffer->params.capture_time_us = - (capture_time - base::TimeTicks()).InMicroseconds(); - buffer->params.glitch_duration_us = 0; - buffer->params.glitch_count = 0; uint32_t buffer_index = 0; - browser_socket.Send(&buffer_index, sizeof(buffer_index)); + browser_socket_.Send(&buffer_index, sizeof(buffer_index)); - device->Start(); + EXPECT_CALL(*capture_callback_, OnCaptureError(_, _)).Times(0); + + device_->Start(); ste.RunUntilIdle(); // The capture occurs on another thread, wait for it. - callback.WaitForCapture(); + capture_callback_->WaitForCapture(); - EXPECT_CALL(*input_ipc, CloseStream()); - device->Stop(); + device_->Stop(); +} + +TEST_P(AudioInputDeviceTest, CaptureCallbackSocketError) { + base::test::TaskEnvironment ste; + CreateInputDevice(); + + uint32_t buffer_index = 0; + browser_socket_.Send(&buffer_index, sizeof(buffer_index)); + + EXPECT_CALL(*capture_callback_, + OnCaptureError(AudioCapturerSource::ErrorCode::kSocketError, _)) + .WillOnce(base::test::RunClosure(ste.QuitClosure())); + + device_->Start(); + ste.RunUntilIdle(); + + // The capture occurs on another thread, wait for it. + capture_callback_->WaitForCapture(); + + browser_socket_.Close(); + ste.RunUntilQuit(); + + device_->Stop(); } INSTANTIATE_TEST_SUITE_P(
diff --git a/media/audio/audio_output_device_thread_callback.cc b/media/audio/audio_output_device_thread_callback.cc index 98f54db..4600038 100644 --- a/media/audio/audio_output_device_thread_callback.cc +++ b/media/audio/audio_output_device_thread_callback.cc
@@ -106,6 +106,10 @@ "delay (ms)", delay.InMillisecondsF()); } +void AudioOutputDeviceThreadCallback::OnSocketError() { + render_callback_->OnRenderError(); +} + bool AudioOutputDeviceThreadCallback::CurrentThreadIsAudioDeviceThread() { return thread_checker_.CalledOnValidThread(); }
diff --git a/media/audio/audio_output_device_thread_callback.h b/media/audio/audio_output_device_thread_callback.h index 904c1fa..63594586 100644 --- a/media/audio/audio_output_device_thread_callback.h +++ b/media/audio/audio_output_device_thread_callback.h
@@ -39,6 +39,10 @@ // Called whenever we receive notifications about pending data. void Process(uint32_t control_signal) override; + // Called when the AudioDeviceThread shuts down. Unexpected calls are treated + // as errors. + void OnSocketError() override; + // Returns whether the current thread is the audio device thread or not. // Will always return true if DCHECKs are not enabled. bool CurrentThreadIsAudioDeviceThread();
diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc index d094d6e8..11ed572 100644 --- a/media/audio/audio_output_device_unittest.cc +++ b/media/audio/audio_output_device_unittest.cc
@@ -19,6 +19,8 @@ #include "base/sync_socket.h" #include "base/task/single_thread_task_runner.h" #include "base/task/task_runner.h" +#include "base/test/bind.h" +#include "base/test/gmock_callback_support.h" #include "base/test/task_environment.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" @@ -102,6 +104,9 @@ MOCK_METHOD1(OnDeviceInfoReceived, void(OutputDeviceInfo)); protected: + void Render(); + void CloseBrowserSocket(); + MockAudioOutputIPC* audio_output_ipc() { return static_cast<MockAudioOutputIPC*>(audio_device_->GetIpcForTesting()); } @@ -113,13 +118,12 @@ OutputDeviceStatus device_status_; private: - int CalculateMemorySize(); - // These may need to outlive `audio_device_`. UnsafeSharedMemoryRegion shared_memory_region_; WritableSharedMemoryMapping shared_memory_mapping_; CancelableSyncSocket browser_socket_; CancelableSyncSocket renderer_socket_; + uint32_t counter_ = 0; protected: scoped_refptr<AudioOutputDevice> audio_device_; @@ -227,6 +231,15 @@ task_env_.FastForwardBy(base::TimeDelta()); } +void AudioOutputDeviceTest::Render() { + browser_socket_.Send(&counter_, sizeof(counter_)); + ++counter_; +} + +void AudioOutputDeviceTest::CloseBrowserSocket() { + browser_socket_.Close(); +} + TEST_F(AudioOutputDeviceTest, Initialize) { // Tests that the object can be constructed, initialized and destructed // without having ever been started. @@ -262,6 +275,45 @@ CallOnStreamCreated(); } +TEST_F(AudioOutputDeviceTest, NoErrorForNormalShutdown) { + StartAudioDevice(); + CallOnStreamCreated(); + + base::RunLoop run_loop; + EXPECT_CALL(callback_, Render(_, _, _, _)) + .WillOnce(DoAll(base::test::RunClosure(run_loop.QuitWhenIdleClosure()), + Return(0))) + .WillRepeatedly(Return(0)); + + EXPECT_CALL(callback_, OnRenderError()).Times(0); + + Render(); + run_loop.Run(); + + StopAudioDevice(); +} + +// TODO(crbug.com/327577325) Re-enable this test +TEST_F(AudioOutputDeviceTest, DISABLED_ErrorFiredForSocketClose) { + StartAudioDevice(); + CallOnStreamCreated(); + + base::RunLoop run_loop; + EXPECT_CALL(callback_, Render(_, _, _, _)) + .WillOnce(DoAll(base::test::RunClosure(base::BindLambdaForTesting( + [&]() { CloseBrowserSocket(); })), + Return(0))) + .WillRepeatedly(Return(0)); + + EXPECT_CALL(callback_, OnRenderError()) + .WillOnce(base::test::RunClosure(run_loop.QuitWhenIdleClosure())); + + Render(); + run_loop.Run(); + + StopAudioDevice(); +} + // Multiple start/stop with nondefault device TEST_F(AudioOutputDeviceTest, NonDefaultStartStopStartStop) { SetDevice(kNonDefaultDeviceId);
diff --git a/media/base/audio_capturer_source.h b/media/base/audio_capturer_source.h index 3745397..0310661c 100644 --- a/media/base/audio_capturer_source.h +++ b/media/base/audio_capturer_source.h
@@ -31,6 +31,7 @@ kUnknown = 0, kSystemPermissions = 1, kDeviceInUse = 2, + kSocketError = 3, }; class CaptureCallback {
diff --git a/media/base/audio_processing.h b/media/base/audio_processing.h index 5b129640..1ddea83d 100644 --- a/media/base/audio_processing.h +++ b/media/base/audio_processing.h
@@ -20,6 +20,7 @@ bool echo_cancellation = true; bool noise_suppression = true; // Keytap removal, sometimes called "experimental noise suppression". + // TODO(https://webrtc.com/7494): Deprecate this setting. bool transient_noise_suppression = true; bool automatic_gain_control = true; bool high_pass_filter = true;
diff --git a/media/base/audio_renderer_mixer.cc b/media/base/audio_renderer_mixer.cc index 54cad65..c177a1a 100644 --- a/media/base/audio_renderer_mixer.cc +++ b/media/base/audio_renderer_mixer.cc
@@ -115,6 +115,11 @@ pause_delay_ = delay; } +bool AudioRendererMixer::HasSinkError() { + base::AutoLock auto_lock(lock_); + return sink_error_; +} + int AudioRendererMixer::Render(base::TimeDelta delay, base::TimeTicks delay_timestamp, const AudioGlitchInfo& glitch_info, @@ -147,6 +152,7 @@ void AudioRendererMixer::OnRenderError() { // Call each mixer input and signal an error. base::AutoLock auto_lock(lock_); + sink_error_ = true; for (AudioRendererMixerInput* input : error_callbacks_) { input->OnRenderError(); }
diff --git a/media/base/audio_renderer_mixer.h b/media/base/audio_renderer_mixer.h index 037e506..44f512a60 100644 --- a/media/base/audio_renderer_mixer.h +++ b/media/base/audio_renderer_mixer.h
@@ -56,6 +56,9 @@ return output_params_; } + // Return true if this mixer has ever received an error from its sink. + bool HasSinkError(); + private: // AudioRendererSink::RenderCallback implementation. int Render(base::TimeDelta delay, @@ -99,6 +102,10 @@ base::TimeDelta pause_delay_ GUARDED_BY(lock_); base::TimeTicks last_play_time_ GUARDED_BY(lock_); bool playing_ GUARDED_BY(lock_); + + // Set if the mixer receives an error from the sink. Indicates that this + // mixer and sink should no longer be reused. + bool sink_error_ GUARDED_BY(lock_) = false; }; } // namespace media
diff --git a/media/base/audio_renderer_mixer_unittest.cc b/media/base/audio_renderer_mixer_unittest.cc index b59e2700..4219fb61 100644 --- a/media/base/audio_renderer_mixer_unittest.cc +++ b/media/base/audio_renderer_mixer_unittest.cc
@@ -503,9 +503,13 @@ EXPECT_CALL(*fake_callbacks_[i], OnRenderError()).Times(1); } + EXPECT_FALSE(mixer_->HasSinkError()); + mixer_callback_->OnRenderError(); for (size_t i = 0; i < mixer_inputs_.size(); ++i) mixer_inputs_[i]->Stop(); + + EXPECT_TRUE(mixer_->HasSinkError()); } TEST_P(AudioRendererMixerBehavioralTest, OnRenderErrorPausedInput) {
diff --git a/media/capture/video/android/video_capture_device_android.cc b/media/capture/video/android/video_capture_device_android.cc index 52a0c05..112e9dda 100644 --- a/media/capture/video/android/video_capture_device_android.cc +++ b/media/capture/video/android/video_capture_device_android.cc
@@ -623,7 +623,7 @@ return; client_->OnIncomingCapturedData( data, length, capture_format_, capture_color_space_, rotation, - false /* flip_y */, reference_time, timestamp); + false /* flip_y */, reference_time, timestamp, std::nullopt); } VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() {
diff --git a/media/capture/video/apple/video_capture_device_apple.mm b/media/capture/video/apple/video_capture_device_apple.mm index ba170f42..4de63e40 100644 --- a/media/capture/video/apple/video_capture_device_apple.mm +++ b/media/capture/video/apple/video_capture_device_apple.mm
@@ -237,7 +237,7 @@ client_->OnIncomingCapturedData( video_frame, video_frame_length, frame_format, color_space, rotation /* clockwise_rotation */, false /* flip_y */, - base::TimeTicks::Now(), timestamp); + base::TimeTicks::Now(), timestamp, std::nullopt); } void VideoCaptureDeviceApple::ReceiveExternalGpuMemoryBufferFrame( @@ -251,7 +251,7 @@ return; } client_->OnIncomingCapturedExternalBuffer( - std::move(frame), base::TimeTicks::Now(), timestamp, + std::move(frame), base::TimeTicks::Now(), timestamp, std::nullopt, gfx::Rect(capture_format_.frame_size)); }
diff --git a/media/capture/video/chromeos/camera_device_context.cc b/media/capture/video/chromeos/camera_device_context.cc index 10d1f6c..a6d4be8 100644 --- a/media/capture/video/chromeos/camera_device_context.cc +++ b/media/capture/video/chromeos/camera_device_context.cc
@@ -93,7 +93,8 @@ client->second->OnIncomingCapturedBufferExt( std::move(buffer), frame_format, gfx::ColorSpace(), reference_time, - timestamp, gfx::Rect(frame_format.frame_size), std::move(metadata)); + timestamp, std::nullopt, gfx::Rect(frame_format.frame_size), + std::move(metadata)); } void CameraDeviceContext::SubmitCapturedGpuMemoryBuffer( @@ -108,9 +109,9 @@ return; } - client->second->OnIncomingCapturedGfxBuffer(buffer, frame_format, - GetCameraFrameRotation(), - reference_time, timestamp); + client->second->OnIncomingCapturedGfxBuffer( + buffer, frame_format, GetCameraFrameRotation(), reference_time, timestamp, + std::nullopt); } void CameraDeviceContext::SetSensorOrientation(int sensor_orientation) {
diff --git a/media/capture/video/chromeos/mock_video_capture_client.cc b/media/capture/video/chromeos/mock_video_capture_client.cc index 6943af7..ad1f802f 100644 --- a/media/capture/video/chromeos/mock_video_capture_client.cc +++ b/media/capture/video/chromeos/mock_video_capture_client.cc
@@ -47,6 +47,7 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id) { ASSERT_GT(length, 0); ASSERT_TRUE(data); @@ -60,6 +61,7 @@ int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id) { ASSERT_TRUE(buffer); ASSERT_GT(buffer->GetSize().width() * buffer->GetSize().height(), 0); @@ -71,6 +73,7 @@ CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, const gfx::Rect& visible_rect) { if (frame_cb_) std::move(frame_cb_).Run(); @@ -92,7 +95,8 @@ Buffer buffer, const VideoCaptureFormat& format, base::TimeTicks reference_time, - base::TimeDelta timestamp) { + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) { DoOnIncomingCapturedBuffer(); } @@ -102,6 +106,7 @@ const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) { DoOnIncomingCapturedVideoFrame();
diff --git a/media/capture/video/chromeos/mock_video_capture_client.h b/media/capture/video/chromeos/mock_video_capture_client.h index 5a1d72d..e5280e25 100644 --- a/media/capture/video/chromeos/mock_video_capture_client.h +++ b/media/capture/video/chromeos/mock_video_capture_client.h
@@ -48,33 +48,40 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id) override; - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id) override; + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id) override; void OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, const gfx::Rect& visible_rect) override; // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>. ReserveResult ReserveOutputBuffer(const gfx::Size& dimensions, VideoPixelFormat format, int frame_feedback_id, Buffer* buffer) override; - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_time, - base::TimeDelta timestamp) override; + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) override; void OnIncomingCapturedBufferExt( Buffer buffer, const VideoCaptureFormat& format, const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) override;
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc index cc1f9b9..6e74a92 100644 --- a/media/capture/video/fake_video_capture_device.cc +++ b/media/capture/video/fake_video_capture_device.cc
@@ -806,7 +806,7 @@ buffer_.get(), frame_size, device_state()->format, GetDefaultColorSpace(device_state()->format.pixel_format), 0 /* rotation */, false /* flip_y */, now, - CalculateTimeSinceFirstInvocation(now)); + CalculateTimeSinceFirstInvocation(now), std::nullopt); } ClientBufferFrameDeliverer::ClientBufferFrameDeliverer( @@ -840,9 +840,9 @@ buffer_access.reset(); // Can't outlive `capture_buffer.handle_provider'. base::TimeTicks now = base::TimeTicks::Now(); - client()->OnIncomingCapturedBuffer(std::move(capture_buffer), - device_state()->format, now, - CalculateTimeSinceFirstInvocation(now)); + client()->OnIncomingCapturedBuffer( + std::move(capture_buffer), device_state()->format, now, + CalculateTimeSinceFirstInvocation(now), std::nullopt); } JpegEncodingFrameDeliverer::JpegEncodingFrameDeliverer( @@ -881,7 +881,7 @@ client()->OnIncomingCapturedData( &jpeg_buffer_[0], frame_size, device_state()->format, gfx::ColorSpace::CreateJpeg(), 0 /* rotation */, false /* flip_y */, now, - CalculateTimeSinceFirstInvocation(now)); + CalculateTimeSinceFirstInvocation(now), std::nullopt); } GpuMemoryBufferFrameDeliverer::GpuMemoryBufferFrameDeliverer( @@ -936,9 +936,9 @@ // When GpuMemoryBuffer is used, the frame data is opaque to the CPU for most // of the time. Currently the only supported underlying format is NV12. modified_format.pixel_format = PIXEL_FORMAT_NV12; - client()->OnIncomingCapturedBuffer(std::move(capture_buffer), modified_format, - now, - CalculateTimeSinceFirstInvocation(now)); + client()->OnIncomingCapturedBuffer( + std::move(capture_buffer), modified_format, now, + CalculateTimeSinceFirstInvocation(now), std::nullopt); } void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
diff --git a/media/capture/video/file_video_capture_device.cc b/media/capture/video/file_video_capture_device.cc index 3dfb02f..dd7af09f 100644 --- a/media/capture/video/file_video_capture_device.cc +++ b/media/capture/video/file_video_capture_device.cc
@@ -5,6 +5,7 @@ #include "media/capture/video/file_video_capture_device.h" #include <stddef.h> +#include <optional> #include <algorithm> #include <memory> @@ -692,16 +693,16 @@ // NV12. VideoCaptureFormat gmb_format = ptz_format; gmb_format.pixel_format = PIXEL_FORMAT_NV12; - client_->OnIncomingCapturedBuffer(std::move(capture_buffer), gmb_format, - current_time, - current_time - first_ref_time_); + client_->OnIncomingCapturedBuffer( + std::move(capture_buffer), gmb_format, current_time, + current_time - first_ref_time_, std::nullopt); } else { // Leave the color space unset for compatibility purposes but this // information should be retrieved from the container when possible. client_->OnIncomingCapturedData( ptz_frame.data(), ptz_frame.size(), ptz_format, gfx::ColorSpace(), 0 /* clockwise_rotation */, false /* flip_y */, current_time, - current_time - first_ref_time_); + current_time - first_ref_time_, std::nullopt); } // Process waiting photo callbacks
diff --git a/media/capture/video/fuchsia/video_capture_device_fuchsia.cc b/media/capture/video/fuchsia/video_capture_device_fuchsia.cc index 949aa56..0fcf392 100644 --- a/media/capture/video/fuchsia/video_capture_device_fuchsia.cc +++ b/media/capture/video/fuchsia/video_capture_device_fuchsia.cc
@@ -432,7 +432,7 @@ client_->OnIncomingCapturedBufferExt( std::move(buffer), capture_format, gfx::ColorSpace(), reference_time, - timestamp, gfx::Rect(visible_size), VideoFrameMetadata()); + timestamp, std::nullopt, gfx::Rect(visible_size), VideoFrameMetadata()); // Frame buffer is returned to the device by dropping the |frame_info|. }
diff --git a/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc b/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc index 163857b2..a886a1c 100644 --- a/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc +++ b/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc
@@ -128,6 +128,7 @@ const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) override { EXPECT_TRUE(started_); @@ -148,28 +149,34 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id) override { NOTREACHED_NORETURN(); } - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id) override { + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id) override { NOTREACHED_NORETURN(); } void OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, const gfx::Rect& visible_rect) override { NOTREACHED_NORETURN(); } - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_time, - base::TimeDelta timestamp) override { + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) override { NOTREACHED_NORETURN(); } void OnError(VideoCaptureError error,
diff --git a/media/capture/video/linux/v4l2_capture_delegate.cc b/media/capture/video/linux/v4l2_capture_delegate.cc index 973d24c..70defc27 100644 --- a/media/capture/video/linux/v4l2_capture_delegate.cc +++ b/media/capture/video/linux/v4l2_capture_delegate.cc
@@ -1161,7 +1161,7 @@ client_->OnIncomingCapturedData( buffer_tracker->start(), buffer_tracker->payload_size(), capture_format_, gfx::ColorSpace(), rotation_, false /* flip_y */, - now, timestamp); + now, timestamp, std::nullopt); } while (!take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/linux/v4l2_capture_delegate_gpu_helper.cc b/media/capture/video/linux/v4l2_capture_delegate_gpu_helper.cc index dbe13cc1..acd8f5d 100644 --- a/media/capture/video/linux/v4l2_capture_delegate_gpu_helper.cc +++ b/media/capture/video/linux/v4l2_capture_delegate_gpu_helper.cc
@@ -158,8 +158,8 @@ std::move(capture_buffer), VideoCaptureFormat(dimensions, capture_format.frame_rate, kTargetPixelFormat), - gfx::ColorSpace(), reference_time, timestamp, gfx::Rect(dimensions), - VideoFrameMetadata()); + gfx::ColorSpace(), reference_time, timestamp, std::nullopt, + gfx::Rect(dimensions), VideoFrameMetadata()); return status; }
diff --git a/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc b/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc index a0a13662..0f8a0ae 100644 --- a/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc +++ b/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc
@@ -34,19 +34,23 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id = 0) override {} - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id = 0) override {} + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id = 0) override {} void OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, const gfx::Rect& visible_rect) override {} void OnCaptureConfigurationChanged() override {} @@ -54,17 +58,20 @@ MOCK_METHOD4(ReserveOutputBuffer, ReserveResult(const gfx::Size&, VideoPixelFormat, int, Buffer*)); - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_, - base::TimeDelta timestamp) override {} + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) override {} - MOCK_METHOD7(OnIncomingCapturedBufferExt, + MOCK_METHOD8(OnIncomingCapturedBufferExt, void(Buffer, const VideoCaptureFormat&, const gfx::ColorSpace&, base::TimeTicks, base::TimeDelta, + std::optional<base::TimeTicks> capture_begin_time, gfx::Rect, const VideoFrameMetadata&)); @@ -210,7 +217,7 @@ } } - EXPECT_CALL(client, ReserveOutputBuffer(_, _, _, _)) + EXPECT_CALL(client, ReserveOutputBuffer) .WillRepeatedly( Invoke([](const gfx::Size& size, VideoPixelFormat pixel_format, int feedback_id, @@ -242,7 +249,7 @@ std::unique_ptr<std::vector<uint8_t>> sample = ReadSampleData(capture_format); MockV4l2GpuClient client; - EXPECT_CALL(client, ReserveOutputBuffer(_, _, _, _)) + EXPECT_CALL(client, ReserveOutputBuffer) .WillRepeatedly( Invoke([](const gfx::Size& size, VideoPixelFormat pixel_format, int feedback_id, @@ -273,7 +280,7 @@ std::unique_ptr<std::vector<uint8_t>> sample = ReadSampleData(capture_format); MockV4l2GpuClient client; - EXPECT_CALL(client, ReserveOutputBuffer(_, _, _, _)) + EXPECT_CALL(client, ReserveOutputBuffer) .WillRepeatedly( Invoke([](const gfx::Size& size, VideoPixelFormat pixel_format, int feedback_id, @@ -307,7 +314,7 @@ std::unique_ptr<std::vector<uint8_t>> sample = ReadSampleData(capture_format); MockV4l2GpuClient client; - EXPECT_CALL(client, ReserveOutputBuffer(_, _, _, _)) + EXPECT_CALL(client, ReserveOutputBuffer) .WillRepeatedly( Invoke([](const gfx::Size& size, VideoPixelFormat pixel_format, int feedback_id, @@ -318,7 +325,7 @@ size, gfx::BufferFormat::YUV_420_BIPLANAR); return VideoCaptureDevice::Client::ReserveResult::kSucceeded; })); - EXPECT_CALL(client, OnIncomingCapturedBufferExt(_, _, _, _, _, _, _)) + EXPECT_CALL(client, OnIncomingCapturedBufferExt) .WillRepeatedly(InvokeWithoutArgs([]() {})); int status = v4l2_gpu_helper_->OnIncomingCapturedData( @@ -338,7 +345,7 @@ ReadSampleData(capture_format); MockV4l2GpuClient client; - EXPECT_CALL(client, ReserveOutputBuffer(_, _, _, _)) + EXPECT_CALL(client, ReserveOutputBuffer) .WillRepeatedly( Invoke([](const gfx::Size& size, VideoPixelFormat pixel_format, int feedback_id, @@ -349,7 +356,7 @@ size, gfx::BufferFormat::YUV_420_BIPLANAR); return VideoCaptureDevice::Client::ReserveResult::kSucceeded; })); - EXPECT_CALL(client, OnIncomingCapturedBufferExt(_, _, _, _, _, _, _)) + EXPECT_CALL(client, OnIncomingCapturedBufferExt) .WillRepeatedly(InvokeWithoutArgs([]() {})); int status = v4l2_gpu_helper_->OnIncomingCapturedData(
diff --git a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc index eda5dbdd..a119235 100644 --- a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc +++ b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc
@@ -248,19 +248,23 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id = 0) override {} - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id = 0) override {} + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id = 0) override {} void OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, const gfx::Rect& visible_rect) override {} void OnCaptureConfigurationChanged() override {} @@ -268,17 +272,20 @@ MOCK_METHOD4(ReserveOutputBuffer, ReserveResult(const gfx::Size&, VideoPixelFormat, int, Buffer*)); - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_, - base::TimeDelta timestamp) override {} + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) override {} - MOCK_METHOD7(OnIncomingCapturedBufferExt, + MOCK_METHOD8(OnIncomingCapturedBufferExt, void(Buffer, const VideoCaptureFormat&, const gfx::ColorSpace&, base::TimeTicks, base::TimeDelta, + std::optional<base::TimeTicks>, gfx::Rect, const VideoFrameMetadata&)); @@ -392,7 +399,7 @@ base::RunLoop run_loop; base::RepeatingClosure quit_closure = run_loop.QuitClosure(); - EXPECT_CALL(*client_ptr, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client_ptr, OnIncomingCapturedData) .Times(1) .WillOnce(RunClosure(quit_closure)); run_loop.Run(); @@ -431,7 +438,7 @@ std::unique_ptr<MockV4l2GpuClient> client = std::make_unique<MockV4l2GpuClient>(); MockV4l2GpuClient* client_ptr = client.get(); - EXPECT_CALL(*client_ptr, ReserveOutputBuffer(_, _, _, _)) + EXPECT_CALL(*client_ptr, ReserveOutputBuffer) .WillRepeatedly(Invoke( [](const gfx::Size& size, VideoPixelFormat format, int feedback_id, VideoCaptureDevice::Client::Buffer* capture_buffer) { @@ -443,7 +450,7 @@ })); base::RunLoop wait_loop; - EXPECT_CALL(*client_ptr, OnIncomingCapturedBufferExt(_, _, _, _, _, _, _)) + EXPECT_CALL(*client_ptr, OnIncomingCapturedBufferExt) .WillRepeatedly(InvokeWithoutArgs([&wait_loop, this]() { this->received_frame_count_++; if (this->received_frame_count_ == kFrameToReceive) {
diff --git a/media/capture/video/linux/video_capture_device_factory_v4l2_unittest.cc b/media/capture/video/linux/video_capture_device_factory_v4l2_unittest.cc index d4d3e371..dd82c26 100644 --- a/media/capture/video/linux/video_capture_device_factory_v4l2_unittest.cc +++ b/media/capture/video/linux/video_capture_device_factory_v4l2_unittest.cc
@@ -114,7 +114,7 @@ base::RunLoop wait_loop; static const int kFrameToReceive = 3; - EXPECT_CALL(*client_ptr, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*client_ptr, OnIncomingCapturedData) .WillRepeatedly(InvokeWithoutArgs([&wait_loop]() { static int received_frame_count = 0; received_frame_count++;
diff --git a/media/capture/video/mac/video_capture_device_decklink_mac.mm b/media/capture/video/mac/video_capture_device_decklink_mac.mm index 8d84049..80c1351 100644 --- a/media/capture/video/mac/video_capture_device_decklink_mac.mm +++ b/media/capture/video/mac/video_capture_device_decklink_mac.mm
@@ -457,7 +457,8 @@ if (!client_) return; client_->OnIncomingCapturedData(data, length, frame_format, color_space, - rotation, flip_y, reference_time, timestamp); + rotation, flip_y, reference_time, timestamp, + std::nullopt); } void VideoCaptureDeviceDeckLinkMac::SendErrorString(
diff --git a/media/capture/video/mock_device.cc b/media/capture/video/mock_device.cc index 73f32b01..8089ab4 100644 --- a/media/capture/video/mock_device.cc +++ b/media/capture/video/mock_device.cc
@@ -22,7 +22,7 @@ static_cast<int>(media::VideoFrame::AllocationSize( stub_frame->format(), stub_frame->coded_size())), format, gfx::ColorSpace(), rotation, false /* flip_y */, - base::TimeTicks(), base::TimeDelta(), frame_feedback_id); + base::TimeTicks(), base::TimeDelta(), std::nullopt, frame_feedback_id); } void MockDevice::SendOnStarted() {
diff --git a/media/capture/video/mock_video_capture_device_client.cc b/media/capture/video/mock_video_capture_device_client.cc index a66af33..165280ca 100644 --- a/media/capture/video/mock_video_capture_device_client.cc +++ b/media/capture/video/mock_video_capture_device_client.cc
@@ -7,10 +7,12 @@ #include <utility> #include "base/memory/raw_ptr.h" +#include "base/time/time.h" #include "media/base/video_frame.h" using testing::_; using testing::Invoke; +using testing::WithArgs; namespace media { @@ -87,7 +89,8 @@ Buffer buffer, const media::VideoCaptureFormat& format, base::TimeTicks reference_time, - base::TimeDelta timestamp) { + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) { DoOnIncomingCapturedBuffer(buffer, format, reference_time, timestamp); } void MockVideoCaptureDeviceClient::OnIncomingCapturedBufferExt( @@ -96,6 +99,7 @@ const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, gfx::Rect visible_rect, const media::VideoFrameMetadata& additional_metadata) { DoOnIncomingCapturedBufferExt(buffer, format, color_space, reference_time, @@ -110,7 +114,7 @@ result->fake_frame_captured_callback_ = std::move(frame_captured_callback); auto* raw_result_ptr = result.get(); - ON_CALL(*result, ReserveOutputBuffer(_, _, _, _)) + ON_CALL(*result, ReserveOutputBuffer) .WillByDefault( Invoke([](const gfx::Size& dimensions, VideoPixelFormat format, int, VideoCaptureDevice::Client::Buffer* buffer) { @@ -121,37 +125,26 @@ frame_format.frame_size)); return VideoCaptureDevice::Client::ReserveResult::kSucceeded; })); - ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) - .WillByDefault( - Invoke([raw_result_ptr](const uint8_t*, int, - const media::VideoCaptureFormat& frame_format, - const gfx::ColorSpace&, int, bool, - base::TimeTicks, base::TimeDelta, int) { + ON_CALL(*result, OnIncomingCapturedData) + .WillByDefault(WithArgs<2>(Invoke( + [raw_result_ptr](const media::VideoCaptureFormat& frame_format) { raw_result_ptr->fake_frame_captured_callback_.Run(frame_format); - })); - ON_CALL(*result, OnIncomingCapturedGfxBuffer(_, _, _, _, _, _)) - .WillByDefault( - Invoke([raw_result_ptr](gfx::GpuMemoryBuffer*, - const media::VideoCaptureFormat& frame_format, - int, base::TimeTicks, base::TimeDelta, int) { + }))); + ON_CALL(*result, OnIncomingCapturedGfxBuffer) + .WillByDefault(WithArgs<1>(Invoke( + [raw_result_ptr](const media::VideoCaptureFormat& frame_format) { raw_result_ptr->fake_frame_captured_callback_.Run(frame_format); - })); - ON_CALL(*result, DoOnIncomingCapturedBuffer(_, _, _, _)) - .WillByDefault( - Invoke([raw_result_ptr](media::VideoCaptureDevice::Client::Buffer&, - const media::VideoCaptureFormat& frame_format, - base::TimeTicks, base::TimeDelta) { + }))); + ON_CALL(*result, DoOnIncomingCapturedBuffer) + .WillByDefault(WithArgs<1>(Invoke( + [raw_result_ptr](const media::VideoCaptureFormat& frame_format) { raw_result_ptr->fake_frame_captured_callback_.Run(frame_format); - })); - ON_CALL(*result, DoOnIncomingCapturedBufferExt(_, _, _, _, _, _, _)) - .WillByDefault( - Invoke([raw_result_ptr](media::VideoCaptureDevice::Client::Buffer&, - const media::VideoCaptureFormat& frame_format, - const gfx::ColorSpace&, base::TimeTicks, - base::TimeDelta, gfx::Rect, - const media::VideoFrameMetadata&) { + }))); + ON_CALL(*result, DoOnIncomingCapturedBufferExt) + .WillByDefault(WithArgs<1>(Invoke( + [raw_result_ptr](const media::VideoCaptureFormat& frame_format) { raw_result_ptr->fake_frame_captured_callback_.Run(frame_format); - })); + }))); return result; }
diff --git a/media/capture/video/mock_video_capture_device_client.h b/media/capture/video/mock_video_capture_device_client.h index 9aa5760..9544e05 100644 --- a/media/capture/video/mock_video_capture_device_client.h +++ b/media/capture/video/mock_video_capture_device_client.h
@@ -20,65 +20,86 @@ MockVideoCaptureDeviceClient(); ~MockVideoCaptureDeviceClient() override; - MOCK_METHOD0(OnCaptureConfigurationChanged, void(void)); - MOCK_METHOD9(OnIncomingCapturedData, - void(const uint8_t* data, - int length, - const VideoCaptureFormat& frame_format, - const gfx::ColorSpace& color_space, - int rotation, - bool flip_y, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id)); - MOCK_METHOD6(OnIncomingCapturedGfxBuffer, - void(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id)); - MOCK_METHOD4(OnIncomingCapturedExternalBuffer, - void(CapturedExternalVideoBuffer buffer, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - const gfx::Rect& visible_rect)); - MOCK_METHOD4(ReserveOutputBuffer, - ReserveResult(const gfx::Size&, VideoPixelFormat, int, Buffer*)); - MOCK_METHOD3(OnError, - void(media::VideoCaptureError error, - const base::Location& from_here, - const std::string& reason)); - MOCK_METHOD1(OnFrameDropped, void(VideoCaptureFrameDropReason reason)); - MOCK_METHOD0(OnStarted, void(void)); - MOCK_CONST_METHOD0(GetBufferPoolUtilization, double(void)); + MOCK_METHOD(void, OnCaptureConfigurationChanged, (), (override)); + MOCK_METHOD(void, + OnIncomingCapturedData, + (const uint8_t* data, + int length, + const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, + int rotation, + bool flip_y, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id), + (override)); + MOCK_METHOD(void, + OnIncomingCapturedGfxBuffer, + (gfx::GpuMemoryBuffer * buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id), + (override)); + MOCK_METHOD(void, + OnIncomingCapturedExternalBuffer, + (CapturedExternalVideoBuffer buffer, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + const gfx::Rect& visible_rect), + (override)); + MOCK_METHOD(ReserveResult, + ReserveOutputBuffer, + (const gfx::Size&, VideoPixelFormat, int, Buffer*), + (override)); + MOCK_METHOD(void, + OnError, + (media::VideoCaptureError error, + const base::Location& from_here, + const std::string& reason), + (override)); + MOCK_METHOD(void, + OnFrameDropped, + (VideoCaptureFrameDropReason reason), + (override)); + MOCK_METHOD(void, OnStarted, (), (override)); + MOCK_METHOD(double, GetBufferPoolUtilization, (), (const override)); - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_time, - base::TimeDelta timestamp) override; + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) override; void OnIncomingCapturedBufferExt( Buffer buffer, const VideoCaptureFormat& format, const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) override; - MOCK_METHOD4(DoOnIncomingCapturedBuffer, - void(Buffer&, - const VideoCaptureFormat&, - base::TimeTicks, - base::TimeDelta)); - MOCK_METHOD7(DoOnIncomingCapturedBufferExt, - void(Buffer& buffer, - const VideoCaptureFormat& format, - const gfx::ColorSpace& color_space, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - gfx::Rect visible_rect, - const VideoFrameMetadata& additional_metadata)); + MOCK_METHOD( + void, + DoOnIncomingCapturedBuffer, + (Buffer&, const VideoCaptureFormat&, base::TimeTicks, base::TimeDelta), + ()); + MOCK_METHOD(void, + DoOnIncomingCapturedBufferExt, + (Buffer & buffer, + const VideoCaptureFormat& format, + const gfx::ColorSpace& color_space, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + gfx::Rect visible_rect, + const VideoFrameMetadata& additional_metadata), + ()); static std::unique_ptr<MockVideoCaptureDeviceClient> CreateMockClientWithBufferAllocator(
diff --git a/media/capture/video/mock_video_frame_receiver.h b/media/capture/video/mock_video_frame_receiver.h index c52d4e3..0538383e 100644 --- a/media/capture/video/mock_video_frame_receiver.h +++ b/media/capture/video/mock_video_frame_receiver.h
@@ -16,13 +16,7 @@ ~MockVideoFrameReceiver() override; MOCK_METHOD1(MockOnNewBufferHandle, void(int buffer_id)); - MOCK_METHOD3( - MockOnFrameReadyInBuffer, - void(int buffer_id, - std::unique_ptr< - VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>* - buffer_read_permission, - const gfx::Size&)); + MOCK_METHOD1(MockOnFrameReadyInBuffer, void(ReadyFrameInBuffer frame)); MOCK_METHOD0(OnCaptureConfigurationChanged, void()); MOCK_METHOD1(OnError, void(media::VideoCaptureError error)); MOCK_METHOD1(OnFrameDropped, void(media::VideoCaptureFrameDropReason reason)); @@ -40,8 +34,7 @@ } void OnFrameReadyInBuffer(ReadyFrameInBuffer frame) override { - MockOnFrameReadyInBuffer(frame.buffer_id, &frame.buffer_read_permission, - frame.frame_info->coded_size); + MockOnFrameReadyInBuffer(std::move(frame)); } };
diff --git a/media/capture/video/video_capture_device.cc b/media/capture/video/video_capture_device.cc index 214dd600..ef875f2 100644 --- a/media/capture/video/video_capture_device.cc +++ b/media/capture/video/video_capture_device.cc
@@ -87,9 +87,11 @@ int clockwise_rotation, bool flip_y, base::TimeTicks reference_time, - base::TimeDelta timestamp) { + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp) { OnIncomingCapturedData(data, length, frame_format, color_space, clockwise_rotation, flip_y, reference_time, timestamp, + capture_begin_timestamp, /*frame_feedback_id=*/0); } @@ -98,9 +100,11 @@ const VideoCaptureFormat& frame_format, int clockwise_rotation, base::TimeTicks reference_time, - base::TimeDelta timestamp) { + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp) { OnIncomingCapturedGfxBuffer(buffer, frame_format, clockwise_rotation, reference_time, timestamp, + capture_begin_timestamp, /*frame_feedback_id=*/0); }
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h index bb89eea..607d240f 100644 --- a/media/capture/video/video_capture_device.h +++ b/media/capture/video/video_capture_device.h
@@ -185,24 +185,28 @@ // OnConsumerReportingUtilization(). This identifier is needed because // frames are consumed asynchronously and multiple frames can be "in flight" // at the same time. - virtual void OnIncomingCapturedData(const uint8_t* data, - int length, - const VideoCaptureFormat& frame_format, - const gfx::ColorSpace& color_space, - int clockwise_rotation, - bool flip_y, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id) = 0; + virtual void OnIncomingCapturedData( + const uint8_t* data, + int length, + const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, + int clockwise_rotation, + bool flip_y, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, + int frame_feedback_id) = 0; // Convenience wrapper that passes in 0 as |frame_feedback_id|. - void OnIncomingCapturedData(const uint8_t* data, - int length, - const VideoCaptureFormat& frame_format, - const gfx::ColorSpace& color_space, - int clockwise_rotation, - bool flip_y, - base::TimeTicks reference_time, - base::TimeDelta timestamp); + void OnIncomingCapturedData( + const uint8_t* data, + int length, + const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, + int clockwise_rotation, + bool flip_y, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp); // Captured a new video frame, data for which is stored in the // GpuMemoryBuffer pointed to by |buffer|. The format of the frame is @@ -218,13 +222,16 @@ int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, int frame_feedback_id) = 0; // Convenience wrapper that passes in 0 as |frame_feedback_id|. - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp); + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp); // Captured a new video frame. The data for this frame is in // |buffer.handle|, which is owned by the platform-specific capture device. @@ -239,6 +246,7 @@ CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, const gfx::Rect& visible_rect) = 0; // Reserve an output buffer into which contents can be captured directly. @@ -262,10 +270,12 @@ // ReserveOutputBuffer(). // See OnIncomingCapturedData for details of |reference_time| and // |timestamp|. - virtual void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_time, - base::TimeDelta timestamp) = 0; + virtual void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp) = 0; // Extended version of OnIncomingCapturedBuffer() allowing clients to // pass a custom |visible_rect| and |additional_metadata|. @@ -275,6 +285,7 @@ const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) = 0;
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc index 75567523..689127b2 100644 --- a/media/capture/video/video_capture_device_client.cc +++ b/media/capture/video/video_capture_device_client.cc
@@ -15,6 +15,7 @@ #include "base/location.h" #include "base/ranges/algorithm.h" #include "base/strings/stringprintf.h" +#include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -295,6 +296,7 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, int frame_feedback_id) { DFAKE_SCOPED_RECURSIVE_LOCK(call_from_producer_); TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"), @@ -329,7 +331,8 @@ if (format.pixel_format == PIXEL_FORMAT_Y16) { return OnIncomingCapturedY16Data(data, length, format, reference_time, - timestamp, frame_feedback_id); + timestamp, capture_begin_timestamp, + frame_feedback_id); } // |new_unrotated_{width,height}| are the dimensions of the output buffer that @@ -406,9 +409,9 @@ const VideoCaptureFormat output_format = VideoCaptureFormat(dimensions, format.frame_rate, PIXEL_FORMAT_I420); - OnIncomingCapturedBufferExt(std::move(buffer), output_format, color_space, - reference_time, timestamp, gfx::Rect(dimensions), - VideoFrameMetadata()); + OnIncomingCapturedBufferExt( + std::move(buffer), output_format, color_space, reference_time, timestamp, + capture_begin_timestamp, gfx::Rect(dimensions), VideoFrameMetadata()); } void VideoCaptureDeviceClient::OnIncomingCapturedGfxBuffer( @@ -417,6 +420,7 @@ int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, int frame_feedback_id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"), "VideoCaptureDeviceClient::OnIncomingCapturedGfxBuffer"); @@ -495,18 +499,19 @@ const VideoCaptureFormat output_format = VideoCaptureFormat( dimensions, frame_format.frame_rate, PIXEL_FORMAT_I420); OnIncomingCapturedBuffer(std::move(output_buffer), output_format, - reference_time, timestamp); + reference_time, timestamp, capture_begin_timestamp); } void VideoCaptureDeviceClient::OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, const gfx::Rect& visible_rect) { ReadyFrameInBuffer ready_frame; if (CreateReadyFrameFromExternalBuffer( - std::move(buffer), reference_time, timestamp, visible_rect, - &ready_frame) != ReserveResult::kSucceeded) { + std::move(buffer), reference_time, timestamp, capture_begin_timestamp, + visible_rect, &ready_frame) != ReserveResult::kSucceeded) { DVLOG(2) << __func__ << " CreateReadyFrameFromExternalBuffer failed: reservation " "trakcer failed."; @@ -520,6 +525,7 @@ CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, const gfx::Rect& visible_rect, ReadyFrameInBuffer* ready_buffer) { // Reserve an ID for this buffer that will not conflict with any of the IDs @@ -577,6 +583,7 @@ info->visible_rect = visible_rect; info->metadata.frame_rate = buffer.format.frame_rate; info->metadata.reference_time = reference_time; + info->metadata.capture_begin_time = capture_begin_timestamp; buffer_pool_->HoldForConsumers(buffer_id, 1); buffer_pool_->RelinquishProducerReservation(buffer_id); @@ -651,11 +658,13 @@ Buffer buffer, const VideoCaptureFormat& format, base::TimeTicks reference_time, - base::TimeDelta timestamp) { + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp) { DFAKE_SCOPED_RECURSIVE_LOCK(call_from_producer_); OnIncomingCapturedBufferExt( std::move(buffer), format, gfx::ColorSpace(), reference_time, timestamp, - gfx::Rect(format.frame_size), VideoFrameMetadata()); + capture_begin_timestamp, gfx::Rect(format.frame_size), + VideoFrameMetadata()); } void VideoCaptureDeviceClient::OnIncomingCapturedBufferExt( @@ -664,6 +673,7 @@ const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"), @@ -673,6 +683,7 @@ VideoFrameMetadata metadata = additional_metadata; metadata.frame_rate = format.frame_rate; metadata.reference_time = reference_time; + metadata.capture_begin_time = capture_begin_timestamp; mojom::VideoFrameInfoPtr info = mojom::VideoFrameInfo::New(); info->timestamp = timestamp; @@ -727,6 +738,7 @@ const VideoCaptureFormat& format, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, int frame_feedback_id) { Buffer buffer; const auto reservation_result_code = ReserveOutputBuffer( @@ -747,6 +759,6 @@ const VideoCaptureFormat output_format = VideoCaptureFormat( format.frame_size, format.frame_rate, PIXEL_FORMAT_Y16); OnIncomingCapturedBuffer(std::move(buffer), output_format, reference_time, - timestamp); + timestamp, capture_begin_timestamp); } } // namespace media
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h index abf0cba..9ebdad2 100644 --- a/media/capture/video/video_capture_device_client.h +++ b/media/capture/video/video_capture_device_client.h
@@ -76,40 +76,48 @@ // VideoCaptureDevice::Client implementation. void OnCaptureConfigurationChanged() override; - void OnIncomingCapturedData(const uint8_t* data, - int length, - const VideoCaptureFormat& frame_format, - const gfx::ColorSpace& color_space, - int clockwise_rotation, - bool flip_y, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id) override; - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id) override; + void OnIncomingCapturedData( + const uint8_t* data, + int length, + const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, + int clockwise_rotation, + bool flip_y, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, + int frame_feedback_id) override; + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, + int frame_feedback_id) override; void OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, const gfx::Rect& visible_rect) override; ReserveResult ReserveOutputBuffer(const gfx::Size& dimensions, VideoPixelFormat format, int frame_feedback_id, Buffer* buffer) override; - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_time, - base::TimeDelta timestamp) override; + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp) override; void OnIncomingCapturedBufferExt( Buffer buffer, const VideoCaptureFormat& format, const gfx::ColorSpace& color_space, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, gfx::Rect visible_rect, const VideoFrameMetadata& additional_metadata) override; void OnError(VideoCaptureError error, @@ -125,16 +133,19 @@ CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, const gfx::Rect& visible_rect, ReadyFrameInBuffer* ready_buffer); // A branch of OnIncomingCapturedData for Y16 frame_format.pixel_format. - void OnIncomingCapturedY16Data(const uint8_t* data, - int length, - const VideoCaptureFormat& frame_format, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id); + void OnIncomingCapturedY16Data( + const uint8_t* data, + int length, + const VideoCaptureFormat& frame_format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_timestamp, + int frame_feedback_id); // The receiver to which we post events. const std::unique_ptr<VideoFrameReceiver> receiver_;
diff --git a/media/capture/video/video_capture_device_client_unittest.cc b/media/capture/video/video_capture_device_client_unittest.cc index f2f3611..55e1804 100644 --- a/media/capture/video/video_capture_device_client_unittest.cc +++ b/media/capture/video/video_capture_device_client_unittest.cc
@@ -12,13 +12,18 @@ #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" #include "base/test/task_environment.h" +#include "base/time/time.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "media/base/limits.h" #include "media/base/video_frame.h" +#include "media/base/video_types.h" +#include "media/capture/mojom/video_capture_buffer.mojom.h" #include "media/capture/video/mock_gpu_memory_buffer_manager.h" #include "media/capture/video/mock_video_frame_receiver.h" #include "media/capture/video/video_capture_buffer_pool_impl.h" +#include "media/capture/video/video_capture_buffer_tracker.h" +#include "media/capture/video/video_capture_buffer_tracker_factory.h" #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h" #include "media/capture/video/video_frame_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -31,10 +36,13 @@ using ::testing::_; using ::testing::AtLeast; +using ::testing::Field; using ::testing::InSequence; using ::testing::Invoke; using ::testing::Mock; using ::testing::NiceMock; +using ::testing::Optional; +using ::testing::Pointee; using ::testing::SaveArg; namespace media { @@ -58,6 +66,54 @@ observer) override {} }; +class FakeVideoCaptureBufferHandle : public VideoCaptureBufferHandle { + public: + size_t mapped_size() const override { return 1024; } + uint8_t* data() const override { return nullptr; } + const uint8_t* const_data() const override { return nullptr; } +}; + +class FakeVideoCaptureBufferTracker : public VideoCaptureBufferTracker { + public: + bool Init(const gfx::Size& dimensions, + VideoPixelFormat format, + const mojom::PlaneStridesPtr& strides) override { + return true; + } + bool IsReusableForFormat(const gfx::Size& dimensions, + VideoPixelFormat format, + const mojom::PlaneStridesPtr& strides) override { + return true; + } + uint32_t GetMemorySizeInBytes() override { return 1024; } + std::unique_ptr<VideoCaptureBufferHandle> GetMemoryMappedAccess() override { + return std::make_unique<FakeVideoCaptureBufferHandle>(); + } + base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() override { + return base::UnsafeSharedMemoryRegion(); + } + gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override { + return gfx::GpuMemoryBufferHandle(); + } + VideoCaptureBufferType GetBufferType() override { + return VideoCaptureBufferType::kGpuMemoryBuffer; + } +}; + +class FakeVideoCaptureBufferTrackerFactory + : public VideoCaptureBufferTrackerFactory { + public: + std::unique_ptr<VideoCaptureBufferTracker> CreateTracker( + VideoCaptureBufferType buffer_type) override { + return std::make_unique<FakeVideoCaptureBufferTracker>(); + } + std::unique_ptr<VideoCaptureBufferTracker> + CreateTrackerForExternalGpuMemoryBuffer( + gfx::GpuMemoryBufferHandle handle) override { + return std::make_unique<FakeVideoCaptureBufferTracker>(); + } +}; + } // namespace // Test fixture for testing a unit consisting of an instance of @@ -67,10 +123,35 @@ // production. class VideoCaptureDeviceClientTest : public ::testing::Test { public: - VideoCaptureDeviceClientTest() { + void InitWithSharedMemoryBufferPool() { scoped_refptr<VideoCaptureBufferPoolImpl> buffer_pool( new VideoCaptureBufferPoolImpl(VideoCaptureBufferType::kSharedMemory, 2)); + Init(std::move(buffer_pool)); + } + + void InitWithGmbBufferPool() { + scoped_refptr<VideoCaptureBufferPoolImpl> buffer_pool( + new VideoCaptureBufferPoolImpl( + VideoCaptureBufferType::kSharedMemory, 2, + std::make_unique<FakeVideoCaptureBufferTrackerFactory>())); + Init(std::move(buffer_pool)); + } + + protected: + base::test::TaskEnvironment task_environment_; + std::unique_ptr<unittest_internal::MockGpuMemoryBufferManager> + gpu_memory_buffer_manager_; + FakeVideoEffectsManagerImpl fake_video_effects_manager_; + mojo::Receiver<video_capture::mojom::VideoEffectsManager> + video_effects_manager_receiver_{&fake_video_effects_manager_}; + + // Must outlive `receiver_`. + std::unique_ptr<VideoCaptureDeviceClient> device_client_; + raw_ptr<NiceMock<MockVideoFrameReceiver>> receiver_; + + private: + void Init(scoped_refptr<VideoCaptureBufferPoolImpl> buffer_pool) { auto controller = std::make_unique<NiceMock<MockVideoFrameReceiver>>(); receiver_ = controller.get(); gpu_memory_buffer_manager_ = @@ -85,29 +166,12 @@ video_effects_manager_receiver_.BindNewPipeAndPassRemote()); #endif // BUILDFLAG(IS_CHROMEOS_ASH) } - - VideoCaptureDeviceClientTest(const VideoCaptureDeviceClientTest&) = delete; - VideoCaptureDeviceClientTest& operator=(const VideoCaptureDeviceClientTest&) = - delete; - - ~VideoCaptureDeviceClientTest() override = default; - - protected: - base::test::TaskEnvironment task_environment_; - std::unique_ptr<unittest_internal::MockGpuMemoryBufferManager> - gpu_memory_buffer_manager_; - FakeVideoEffectsManagerImpl fake_video_effects_manager_; - mojo::Receiver<video_capture::mojom::VideoEffectsManager> - video_effects_manager_receiver_{&fake_video_effects_manager_}; - - // Must outlive `receiver_`. - std::unique_ptr<VideoCaptureDeviceClient> device_client_; - raw_ptr<NiceMock<MockVideoFrameReceiver>> receiver_; }; // A small test for reference and to verify VideoCaptureDeviceClient is // minimally functional. TEST_F(VideoCaptureDeviceClientTest, Minimal) { + InitWithSharedMemoryBufferPool(); const size_t kScratchpadSizeInBytes = 400; unsigned char data[kScratchpadSizeInBytes] = {}; const VideoCaptureFormat kFrameFormat(gfx::Size(10, 10), 30.0f /*frame_rate*/, @@ -119,12 +183,14 @@ const int expected_buffer_id = 0; EXPECT_CALL(*receiver_, OnLog(_)); EXPECT_CALL(*receiver_, MockOnNewBufferHandle(expected_buffer_id)); - EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(expected_buffer_id, _, _)); + EXPECT_CALL(*receiver_, + MockOnFrameReadyInBuffer( + Field(&ReadyFrameInBuffer::buffer_id, expected_buffer_id))); } device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, 0 /* clockwise rotation */, false /* flip_y */, base::TimeTicks(), - base::TimeDelta()); + base::TimeDelta(), std::nullopt); const gfx::Size kBufferDimensions(10, 10); const VideoCaptureFormat kFrameFormatNV12( @@ -138,20 +204,85 @@ InSequence s; const int expected_buffer_id = 0; EXPECT_CALL(*receiver_, OnLog(_)); - EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(expected_buffer_id, _, _)); + EXPECT_CALL(*receiver_, + MockOnFrameReadyInBuffer( + Field(&ReadyFrameInBuffer::buffer_id, expected_buffer_id))); EXPECT_CALL(*receiver_, OnBufferRetired(expected_buffer_id)); } device_client_->VideoCaptureDevice::Client::OnIncomingCapturedGfxBuffer( buffer.get(), kFrameFormatNV12, 0 /*clockwise rotation*/, - base::TimeTicks(), base::TimeDelta()); + base::TimeTicks(), base::TimeDelta(), std::nullopt); // Releasing |device_client_| will also release |receiver_|. receiver_ = nullptr; // Avoid dangling reference. device_client_.reset(); } +TEST_F(VideoCaptureDeviceClientTest, + ProgressesCaptureBeginTimestampsForOnIncomingCapturedData) { + InitWithSharedMemoryBufferPool(); + auto expected_timestamp = base::TimeTicks() + base::Seconds(66); + EXPECT_CALL( + *receiver_, + MockOnFrameReadyInBuffer(Field( + &ReadyFrameInBuffer::frame_info, + Pointee(Field(&mojom::VideoFrameInfo::metadata, + Field(&media::VideoFrameMetadata::capture_begin_time, + Optional(expected_timestamp))))))); + constexpr size_t kScratchpadSizeInBytes = 400; + unsigned char data[kScratchpadSizeInBytes] = {}; + device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( + data, kScratchpadSizeInBytes, + VideoCaptureFormat(gfx::Size(10, 10), 30.0f, PIXEL_FORMAT_I420), + gfx::ColorSpace::CreateREC601(), 0, false, base::TimeTicks(), + base::TimeDelta(), expected_timestamp); +} + +TEST_F(VideoCaptureDeviceClientTest, + ProgressesCaptureBeginTimestampsForOnIncomingCapturedGfxBuffer) { + InitWithSharedMemoryBufferPool(); + auto expected_timestamp = base::TimeTicks() + base::Seconds(77); + EXPECT_CALL( + *receiver_, + MockOnFrameReadyInBuffer(Field( + &ReadyFrameInBuffer::frame_info, + Pointee(Field(&mojom::VideoFrameInfo::metadata, + Field(&media::VideoFrameMetadata::capture_begin_time, + Optional(expected_timestamp))))))); + auto resolution = gfx::Size(32, 32); + auto buffer = gpu_memory_buffer_manager_->CreateFakeGpuMemoryBuffer( + resolution, gfx::BufferFormat::YUV_420_BIPLANAR, + gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gpu::kNullSurfaceHandle, + nullptr); + device_client_->VideoCaptureDevice::Client::OnIncomingCapturedGfxBuffer( + buffer.get(), VideoCaptureFormat(resolution, 30.0f, PIXEL_FORMAT_NV12), 0, + base::TimeTicks(), base::TimeDelta(), expected_timestamp); +} + +TEST_F(VideoCaptureDeviceClientTest, + ProgressesCaptureBeginTimestampsForOnIncomingCapturedExternalBuffer) { + InitWithGmbBufferPool(); + auto expected_timestamp = base::TimeTicks() + base::Seconds(88); + EXPECT_CALL( + *receiver_, + MockOnFrameReadyInBuffer(Field( + &ReadyFrameInBuffer::frame_info, + Pointee(Field(&mojom::VideoFrameInfo::metadata, + Field(&media::VideoFrameMetadata::capture_begin_time, + Optional(expected_timestamp))))))); + auto resolution = gfx::Size(32, 32); + device_client_->OnIncomingCapturedExternalBuffer( + CapturedExternalVideoBuffer( + gfx::GpuMemoryBufferHandle(), + VideoCaptureFormat(resolution, 30, PIXEL_FORMAT_NV12), + gfx::ColorSpace::CreateREC601()), + base::TimeTicks(), base::TimeDelta(), expected_timestamp, + gfx::Rect(resolution)); +} + // Tests that we fail silently if no available buffers to use. TEST_F(VideoCaptureDeviceClientTest, DropsFrameIfNoBuffer) { + InitWithSharedMemoryBufferPool(); const size_t kScratchpadSizeInBytes = 400; unsigned char data[kScratchpadSizeInBytes] = {}; const VideoCaptureFormat kFrameFormat(gfx::Size(10, 10), 30.0f /*frame_rate*/, @@ -165,35 +296,30 @@ std::vector<std::unique_ptr< VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>> read_permission; - EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)) + EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer) .Times(2) - .WillRepeatedly(Invoke( - [&read_permission]( - int buffer_id, - std::unique_ptr< - VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>* - buffer_read_permission, - const gfx::Size&) { - read_permission.push_back(std::move(*buffer_read_permission)); - })); + .WillRepeatedly(Invoke([&read_permission](ReadyFrameInBuffer frame) { + read_permission.push_back(std::move(frame.buffer_read_permission)); + })); // Pass three frames. The third will be dropped. device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, 0 /* clockwise rotation */, false /* flip_y */, base::TimeTicks(), - base::TimeDelta()); + base::TimeDelta(), std::nullopt); device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, 0 /* clockwise rotation */, false /* flip_y */, base::TimeTicks(), - base::TimeDelta()); + base::TimeDelta(), std::nullopt); device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, 0 /* clockwise rotation */, false /* flip_y */, base::TimeTicks(), - base::TimeDelta()); + base::TimeDelta(), std::nullopt); Mock::VerifyAndClearExpectations(receiver_); } // Tests that buffer-based capture API accepts some memory-backed pixel formats. TEST_F(VideoCaptureDeviceClientTest, DataCaptureGoodPixelFormats) { + InitWithSharedMemoryBufferPool(); // The usual ReserveOutputBuffer() -> OnIncomingCapturedVideoFrame() cannot // be used since it does not accept all pixel formats. The memory backed // buffer OnIncomingCapturedData() is used instead, with a dummy scratchpad @@ -229,13 +355,13 @@ params.requested_format.pixel_format = format; EXPECT_CALL(*receiver_, OnLog(_)).Times(1); - EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)).Times(1); + EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer).Times(1); device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( data, media::VideoFrame::AllocationSize(params.requested_format.pixel_format, params.requested_format.frame_size), params.requested_format, kColorSpace, 0 /* clockwise_rotation */, - false /* flip_y */, base::TimeTicks(), base::TimeDelta()); + false /* flip_y */, base::TimeTicks(), base::TimeDelta(), std::nullopt); Mock::VerifyAndClearExpectations(receiver_); } } @@ -243,6 +369,7 @@ // Test that we receive the expected resolution for a given captured frame // resolution and rotation. Odd resolutions are also cropped. TEST_F(VideoCaptureDeviceClientTest, CheckRotationsAndCrops) { + InitWithSharedMemoryBufferPool(); const struct SizeAndRotation { gfx::Size input_resolution; int rotation; @@ -269,15 +396,17 @@ params.requested_format = VideoCaptureFormat( size_and_rotation.input_resolution, 30.0f, PIXEL_FORMAT_ARGB); gfx::Size coded_size; - EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)) + EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer) .Times(1) - .WillOnce(SaveArg<2>(&coded_size)); + .WillOnce(Invoke([&coded_size](ReadyFrameInBuffer frame) { + coded_size = frame.frame_info->coded_size; + })); device_client_->VideoCaptureDevice::Client::OnIncomingCapturedData( data, media::VideoFrame::AllocationSize(params.requested_format.pixel_format, params.requested_format.frame_size), params.requested_format, gfx::ColorSpace(), size_and_rotation.rotation, - false /* flip_y */, base::TimeTicks(), base::TimeDelta()); + false /* flip_y */, base::TimeTicks(), base::TimeDelta(), std::nullopt); EXPECT_EQ(coded_size.width(), size_and_rotation.output_resolution.width()); EXPECT_EQ(coded_size.height(), @@ -303,12 +432,14 @@ gpu::kNullSurfaceHandle, nullptr); gfx::Size coded_size; - EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)) + EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer) .Times(1) - .WillOnce(SaveArg<2>(&coded_size)); + .WillOnce(Invoke([&coded_size](ReadyFrameInBuffer frame) { + coded_size = frame.frame_info->coded_size; + })); device_client_->VideoCaptureDevice::Client::OnIncomingCapturedGfxBuffer( buffer.get(), params.requested_format, size_and_rotation.rotation, - base::TimeTicks(), base::TimeDelta()); + base::TimeTicks(), base::TimeDelta(), std::nullopt); EXPECT_EQ(coded_size.width(), size_and_rotation.output_resolution.width()); EXPECT_EQ(coded_size.height(), @@ -322,6 +453,7 @@ // Tests that the VideoEffectsManager remote is closed on the correct task // runner. Destruction on the wrong task runner will cause a crash. TEST_F(VideoCaptureDeviceClientTest, DestructionClosesVideoEffectsManager) { + InitWithSharedMemoryBufferPool(); base::RunLoop run_loop; video_effects_manager_receiver_.set_disconnect_handler( run_loop.QuitClosure());
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc index 6113f79..01298898 100644 --- a/media/capture/video/video_capture_device_unittest.cc +++ b/media/capture/video/video_capture_device_unittest.cc
@@ -150,6 +150,7 @@ using ::testing::Invoke; using ::testing::Return; using ::testing::SaveArg; +using ::testing::WithArgs; namespace media { namespace { @@ -337,36 +338,33 @@ std::unique_ptr<MockVideoCaptureDeviceClient> CreateDeviceClient() { auto result = std::make_unique<NiceMockVideoCaptureDeviceClient>(); - ON_CALL(*result, OnError(_, _, _)).WillByDefault(Invoke(DumpError)); - EXPECT_CALL(*result, ReserveOutputBuffer(_, _, _, _)).Times(0); - EXPECT_CALL(*result, DoOnIncomingCapturedBuffer(_, _, _, _)).Times(0); - EXPECT_CALL(*result, DoOnIncomingCapturedBufferExt(_, _, _, _, _, _, _)) - .Times(0); - ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _, _, _)) - .WillByDefault( + ON_CALL(*result, OnError).WillByDefault(Invoke(DumpError)); + EXPECT_CALL(*result, ReserveOutputBuffer).Times(0); + EXPECT_CALL(*result, DoOnIncomingCapturedBuffer).Times(0); + EXPECT_CALL(*result, DoOnIncomingCapturedBufferExt).Times(0); + ON_CALL(*result, OnIncomingCapturedData) + .WillByDefault(WithArgs<0, 1, 2>( Invoke([this](const uint8_t* data, int length, - const media::VideoCaptureFormat& frame_format, - const gfx::ColorSpace&, int, bool, base::TimeTicks, - base::TimeDelta, int) { + const media::VideoCaptureFormat& frame_format) { ASSERT_GT(length, 0); ASSERT_TRUE(data); main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&VideoCaptureDeviceTest::OnFrameCaptured, base::Unretained(this), frame_format)); - })); - ON_CALL(*result, OnIncomingCapturedGfxBuffer(_, _, _, _, _, _)) - .WillByDefault(Invoke([this]( - gfx::GpuMemoryBuffer* buffer, - const media::VideoCaptureFormat& frame_format, - int, base::TimeTicks, base::TimeDelta, int) { - ASSERT_TRUE(buffer); - ASSERT_GT(buffer->GetSize().width() * buffer->GetSize().height(), 0); - main_thread_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&VideoCaptureDeviceTest::OnFrameCaptured, - base::Unretained(this), frame_format)); - })); + }))); + ON_CALL(*result, OnIncomingCapturedGfxBuffer) + .WillByDefault(WithArgs<0, 1>( + Invoke([this](gfx::GpuMemoryBuffer* buffer, + const media::VideoCaptureFormat& frame_format) { + ASSERT_TRUE(buffer); + ASSERT_GT(buffer->GetSize().width() * buffer->GetSize().height(), + 0); + main_thread_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&VideoCaptureDeviceTest::OnFrameCaptured, + base::Unretained(this), frame_format)); + }))); return result; }
diff --git a/media/capture/video/win/video_capture_device_mf_win.cc b/media/capture/video/win/video_capture_device_mf_win.cc index 1ac52e6..5db187cc 100644 --- a/media/capture/video/win/video_capture_device_mf_win.cc +++ b/media/capture/video/win/video_capture_device_mf_win.cc
@@ -2275,8 +2275,8 @@ VideoCaptureFormat( texture_size, selected_video_capability_->supported_format.frame_rate, pixel_format), - color_space_, reference_time, timestamp, gfx::Rect(texture_size), - frame_metadata); + color_space_, reference_time, timestamp, std::nullopt, + gfx::Rect(texture_size), frame_metadata); return hr; } @@ -2344,9 +2344,9 @@ selected_video_capability_->supported_format.frame_rate, pixel_format), gfx::ColorSpace()); - client_->OnIncomingCapturedExternalBuffer(std::move(external_buffer), - reference_time, timestamp, - gfx::Rect(texture_size)); + client_->OnIncomingCapturedExternalBuffer( + std::move(external_buffer), reference_time, timestamp, std::nullopt, + gfx::Rect(texture_size)); return hr; } @@ -2409,8 +2409,8 @@ client_->OnIncomingCapturedData( locked_buffer.data(), locked_buffer.length(), selected_video_capability_->supported_format, color_space_, - camera_rotation_.value(), false /* flip_y */, reference_time, - timestamp); + camera_rotation_.value(), false /* flip_y */, reference_time, timestamp, + std::nullopt); } while (!video_stream_take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/win/video_capture_device_mf_win_unittest.cc b/media/capture/video/win/video_capture_device_mf_win_unittest.cc index 64c3233..64fefd7d 100644 --- a/media/capture/video/win/video_capture_device_mf_win_unittest.cc +++ b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
@@ -74,35 +74,42 @@ bool flip_y, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, int frame_feedback_id) override {} - void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - int frame_feedback_id) override {} + void OnIncomingCapturedGfxBuffer( + gfx::GpuMemoryBuffer* buffer, + const VideoCaptureFormat& frame_format, + int clockwise_rotation, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, + int frame_feedback_id) override {} void OnIncomingCapturedExternalBuffer( CapturedExternalVideoBuffer buffer, base::TimeTicks reference_time, base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time, const gfx::Rect& visible_rect) override {} MOCK_METHOD4(ReserveOutputBuffer, ReserveResult(const gfx::Size&, VideoPixelFormat, int, Buffer*)); - void OnIncomingCapturedBuffer(Buffer buffer, - const VideoCaptureFormat& format, - base::TimeTicks reference_, - base::TimeDelta timestamp) override {} + void OnIncomingCapturedBuffer( + Buffer buffer, + const VideoCaptureFormat& format, + base::TimeTicks reference_, + base::TimeDelta timestamp, + std::optional<base::TimeTicks> capture_begin_time) override {} - MOCK_METHOD7(OnIncomingCapturedBufferExt, + MOCK_METHOD8(OnIncomingCapturedBufferExt, void(Buffer, const VideoCaptureFormat&, const gfx::ColorSpace&, base::TimeTicks, base::TimeDelta, + std::optional<base::TimeTicks>, gfx::Rect, const VideoFrameMetadata&)); @@ -2251,7 +2258,8 @@ EXPECT_CALL(*client_, OnIncomingCapturedBufferExt) .WillOnce(Invoke([](VideoCaptureDevice::Client::Buffer buffer, const VideoCaptureFormat&, const gfx::ColorSpace&, - base::TimeTicks, base::TimeDelta, gfx::Rect, + base::TimeTicks, base::TimeDelta, + std::optional<base::TimeTicks>, gfx::Rect, const VideoFrameMetadata&) { gfx::GpuMemoryBufferHandle gmb_handle = buffer.handle_provider->GetGpuMemoryBufferHandle();
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc index 81ceb425..74fa8ce 100644 --- a/media/capture/video/win/video_capture_device_win.cc +++ b/media/capture/video/win/video_capture_device_win.cc
@@ -891,9 +891,9 @@ // DXVA_ExtendedFormat. Then use its fields DXVA_VideoPrimaries, // DXVA_VideoTransferMatrix, DXVA_VideoTransferFunction and // DXVA_NominalRangeto build a gfx::ColorSpace. See http://crbug.com/959992. - client_->OnIncomingCapturedData(buffer, length, format, gfx::ColorSpace(), - camera_rotation_.value(), flip_y, - base::TimeTicks::Now(), timestamp); + client_->OnIncomingCapturedData( + buffer, length, format, gfx::ColorSpace(), camera_rotation_.value(), + flip_y, base::TimeTicks::Now(), timestamp, std::nullopt); } while (!take_photo_callbacks_.empty()) {
diff --git a/media/webrtc/audio_processor_test.cc b/media/webrtc/audio_processor_test.cc index 0ba3512..55838a2b4 100644 --- a/media/webrtc/audio_processor_test.cc +++ b/media/webrtc/audio_processor_test.cc
@@ -180,18 +180,11 @@ EXPECT_EQ(config.noise_suppression.level, webrtc::AudioProcessing::Config::NoiseSuppression::kHigh); -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) - EXPECT_FALSE(config.echo_canceller.mobile_mode); - EXPECT_FALSE(config.transient_suppression.enabled); -#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) - // Android and iOS use echo cancellation optimized for mobiles, and does not - // support keytap suppression. +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + // Android and iOS use echo cancellation optimized for mobiles. EXPECT_TRUE(config.echo_canceller.mobile_mode); - EXPECT_FALSE(config.transient_suppression.enabled); #else EXPECT_FALSE(config.echo_canceller.mobile_mode); - EXPECT_TRUE(config.transient_suppression.enabled); #endif }
diff --git a/media/webrtc/helpers.cc b/media/webrtc/helpers.cc index 4cca3ea..e8ef7ce 100644 --- a/media/webrtc/helpers.cc +++ b/media/webrtc/helpers.cc
@@ -159,12 +159,6 @@ #else apm_config.echo_canceller.mobile_mode = false; #endif -#if !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) || \ - BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)) - apm_config.transient_suppression.enabled = - settings.transient_noise_suppression; -#endif ConfigAutomaticGainControl(settings, apm_config); return ap_builder.SetConfig(apm_config).Create(); }
diff --git a/media/webrtc/helpers_unittests.cc b/media/webrtc/helpers_unittests.cc index 472ae8a..2e2d3b02 100644 --- a/media/webrtc/helpers_unittests.cc +++ b/media/webrtc/helpers_unittests.cc
@@ -57,17 +57,11 @@ EXPECT_EQ(config.noise_suppression.level, webrtc::AudioProcessing::Config::NoiseSuppression::kHigh); -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) - EXPECT_FALSE(config.transient_suppression.enabled); -#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) - // Android and iOS use echo cancellation optimized for mobiles, and does not - // support keytap suppression. +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + // Android and iOS use echo cancellation optimized for mobiles. EXPECT_TRUE(config.echo_canceller.mobile_mode); - EXPECT_FALSE(config.transient_suppression.enabled); #else EXPECT_FALSE(config.echo_canceller.mobile_mode); - EXPECT_TRUE(config.transient_suppression.enabled); #endif } @@ -189,23 +183,5 @@ } } -TEST(CreateWebRtcAudioProcessingModuleTest, ToggleTransientSuppression) { - for (bool transient_suppression_enabled : {true, false}) { - SCOPED_TRACE(transient_suppression_enabled); - auto config = CreateApmGetConfig(/*settings=*/{ - .transient_noise_suppression = transient_suppression_enabled}); - -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) || \ - BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) - // Transient suppression is not supported (nor useful) on mobile platforms. - EXPECT_FALSE(config.transient_suppression.enabled); -#else - EXPECT_EQ(config.transient_suppression.enabled, - transient_suppression_enabled); -#endif - } -} - } // namespace } // namespace media
diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src index 42dab6b..6fed964 160000 --- a/net/third_party/quiche/src +++ b/net/third_party/quiche/src
@@ -1 +1 @@ -Subproject commit 42dab6be059fbe2a3d98367e1d5ad19d887156d3 +Subproject commit 6fed96483ed2f0fd9567211f50df8beef43ac140
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc index c2eb732d..0f8eb451 100644 --- a/services/network/cors/cors_url_loader.cc +++ b/services/network/cors/cors_url_loader.cc
@@ -723,6 +723,7 @@ if (request_.redirect_mode == mojom::RedirectMode::kManual) { CheckTainted(redirect_info); + redirect_info_ = redirect_info; deferred_redirect_url_ = std::make_unique<GURL>(redirect_info.new_url); forwarding_client_->OnReceiveRedirect(redirect_info, std::move(response_head));
diff --git a/services/network/ip_protection/ip_protection_token_cache_manager_impl.cc b/services/network/ip_protection/ip_protection_token_cache_manager_impl.cc index 24887f1c..286f364 100644 --- a/services/network/ip_protection/ip_protection_token_cache_manager_impl.cc +++ b/services/network/ip_protection/ip_protection_token_cache_manager_impl.cc
@@ -84,8 +84,10 @@ VLOG(2) << "IPPATC::MaybeRefillCache calling TryGetAuthTokens"; config_getter_->get()->TryGetAuthTokens( batch_size_, proxy_layer_, - base::BindOnce(&IpProtectionTokenCacheManagerImpl::OnGotAuthTokens, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce( + &IpProtectionTokenCacheManagerImpl::OnGotAuthTokens, + weak_ptr_factory_.GetWeakPtr(), + /*attempt_start_time_for_metrics=*/base::TimeTicks::Now())); } ScheduleMaybeRefillCache(); @@ -133,6 +135,7 @@ } void IpProtectionTokenCacheManagerImpl::OnGotAuthTokens( + const base::TimeTicks attempt_start_time_for_metrics, std::optional<std::vector<network::mojom::BlindSignedAuthTokenPtr>> tokens, std::optional<base::Time> try_again_after) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -160,6 +163,10 @@ network::mojom::BlindSignedAuthTokenPtr& b) { return a->expiration < b->expiration; }); + + base::UmaHistogramMediumTimes( + "NetworkService.IpProtection.TokenBatchGenerationTime", + base::TimeTicks::Now() - attempt_start_time_for_metrics); } else { VLOG(2) << "IPPATC::OnGotAuthTokens back off until " << *try_again_after; try_get_auth_tokens_after_ = *try_again_after; @@ -266,8 +273,10 @@ CHECK(on_try_get_auth_tokens_completed_for_testing_); config_getter_->get()->TryGetAuthTokens( batch_size_, proxy_layer_, - base::BindOnce(&IpProtectionTokenCacheManagerImpl::OnGotAuthTokens, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce( + &IpProtectionTokenCacheManagerImpl::OnGotAuthTokens, + weak_ptr_factory_.GetWeakPtr(), + /*attempt_start_time_for_metrics=*/base::TimeTicks::Now())); } } // namespace network
diff --git a/services/network/ip_protection/ip_protection_token_cache_manager_impl.h b/services/network/ip_protection/ip_protection_token_cache_manager_impl.h index 9d227fc..4c1696d 100644 --- a/services/network/ip_protection/ip_protection_token_cache_manager_impl.h +++ b/services/network/ip_protection/ip_protection_token_cache_manager_impl.h
@@ -73,6 +73,7 @@ private: void OnGotAuthTokens( + base::TimeTicks attempt_start_time_for_metrics, std::optional<std::vector<network::mojom::BlindSignedAuthTokenPtr>> tokens, std::optional<base::Time> try_again_after);
diff --git a/services/network/ip_protection/ip_protection_token_cache_manager_impl_unittest.cc b/services/network/ip_protection/ip_protection_token_cache_manager_impl_unittest.cc index d409e15..3872d85 100644 --- a/services/network/ip_protection/ip_protection_token_cache_manager_impl_unittest.cc +++ b/services/network/ip_protection/ip_protection_token_cache_manager_impl_unittest.cc
@@ -32,6 +32,8 @@ "NetworkService.IpProtection.ProxyB.TokenSpendRate"; constexpr char kProxyBTokenExpirationRateHistogram[] = "NetworkService.IpProtection.ProxyB.TokenExpirationRate"; +constexpr char kTokenBatchGenerationTimeHistogram[] = + "NetworkService.IpProtection.TokenBatchGenerationTime"; const base::TimeDelta kTokenRateMeasurementInterval = base::Minutes(5); struct ExpectedTryGetAuthTokensCall { @@ -103,10 +105,12 @@ } // namespace struct HistogramState { - // Number of successful requests (true). + // Number of successful calls to GetAuthToken (true). int success; - // Number of failed requests (false). + // Number of failed calls to GetAuthToken (false). int failure; + // Number of successful token batch generations. + int generated; }; class IpProtectionTokenCacheManagerImplTest : public testing::Test { @@ -138,6 +142,8 @@ state.success); histogram_tester_.ExpectBucketCount(kGetAuthTokenResultHistogram, false, state.failure); + histogram_tester_.ExpectTotalCount(kTokenBatchGenerationTimeHistogram, + state.generated); } // Create a batch of tokens. @@ -251,7 +257,8 @@ ASSERT_TRUE(token); EXPECT_EQ((*token)->token, "token-0"); EXPECT_EQ((*token)->expiration, kFutureExpiration); - ExpectHistogramState(HistogramState{.success = 1, .failure = 0}); + ExpectHistogramState( + HistogramState{.success = 1, .failure = 0, .generated = 1}); } // `GetAuthToken()` returns nullopt on a cache containing expired tokens. @@ -261,7 +268,8 @@ CallTryGetAuthTokensAndWait(network::mojom::IpProtectionProxyLayer::kProxyA); ASSERT_TRUE(mock_.GotAllExpectedMockCalls()); EXPECT_FALSE(ipp_proxy_a_token_cache_manager_->GetAuthToken()); - ExpectHistogramState(HistogramState{.success = 0, .failure = 1}); + ExpectHistogramState( + HistogramState{.success = 0, .failure = 1, .generated = 1}); } // If `TryGetAuthTokens()` returns an empty batch, the cache remains empty. @@ -273,7 +281,8 @@ ASSERT_FALSE(ipp_proxy_a_token_cache_manager_->IsAuthTokenAvailable()); ASSERT_FALSE(ipp_proxy_a_token_cache_manager_->GetAuthToken()); - ExpectHistogramState(HistogramState{.success = 0, .failure = 1}); + ExpectHistogramState( + HistogramState{.success = 0, .failure = 1, .generated = 1}); } // If `TryGetAuthTokens()` returns an backoff due to an error, the cache remains @@ -287,7 +296,8 @@ ASSERT_FALSE(ipp_proxy_a_token_cache_manager_->IsAuthTokenAvailable()); ASSERT_FALSE(ipp_proxy_a_token_cache_manager_->GetAuthToken()); - ExpectHistogramState(HistogramState{.success = 0, .failure = 1}); + ExpectHistogramState( + HistogramState{.success = 0, .failure = 1, .generated = 0}); } // `GetAuthToken()` skips expired tokens and returns a non-expired token, @@ -304,7 +314,8 @@ auto got_token = ipp_proxy_a_token_cache_manager_->GetAuthToken(); EXPECT_EQ(got_token.value()->token, "good-token"); EXPECT_EQ(got_token.value()->expiration, kFutureExpiration); - ExpectHistogramState(HistogramState{.success = 1, .failure = 0}); + ExpectHistogramState( + HistogramState{.success = 1, .failure = 0, .generated = 1}); } TEST_F(IpProtectionTokenCacheManagerImplTest, TokenExpirationFuzzed) { @@ -332,7 +343,8 @@ EXPECT_FALSE(ipp_proxy_a_token_cache_manager_->IsAuthTokenAvailable()); auto token = ipp_token_cache_manager.GetAuthToken(); ASSERT_FALSE(token); - ExpectHistogramState(HistogramState{.success = 0, .failure = 1}); + ExpectHistogramState( + HistogramState{.success = 0, .failure = 1, .generated = 0}); } // Verify that the token spend rate for ProxyA is measured correctly.
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index dd83db8..1086e19 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5377,9 +5377,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -5389,8 +5389,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -5533,9 +5533,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -5545,8 +5545,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json index 1cb8702..d0a4f8aa 100644 --- a/testing/buildbot/chromium.coverage.json +++ b/testing/buildbot/chromium.coverage.json
@@ -20313,9 +20313,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -20325,8 +20325,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -20463,9 +20463,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -20475,8 +20475,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index be1c5e4b..4b52eb7 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -39981,9 +39981,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -39992,8 +39992,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -40131,9 +40131,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -40142,8 +40142,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -41480,9 +41480,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -41492,8 +41492,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -41636,9 +41636,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -41648,8 +41648,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -42961,9 +42961,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -42972,8 +42972,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -43111,9 +43111,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -43122,8 +43122,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index b6b67a60..aaedcd2fe 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -16343,12 +16343,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -16358,8 +16358,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": { @@ -16519,12 +16519,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 124.0.6328.0", + "description": "Run with ash-chrome version 124.0.6329.0", "isolate_profile_data": true, "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -16534,8 +16534,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v124.0.6328.0", - "revision": "version:124.0.6328.0" + "location": "lacros_version_skew_tests_v124.0.6329.0", + "revision": "version:124.0.6329.0" } ], "dimensions": {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index ac7dc153..89c62e4 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -307,16 +307,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'identifier': 'Lacros version skew testing ash canary', - 'description': 'Run with ash-chrome version 124.0.6328.0', + 'description': 'Run with ash-chrome version 124.0.6329.0', 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6328.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6329.0/test_ash_chrome', ], 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v124.0.6328.0', - 'revision': 'version:124.0.6328.0', + 'location': 'lacros_version_skew_tests_v124.0.6329.0', + 'revision': 'version:124.0.6329.0', }, ], },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index f0e2d00..42f16ff2 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -6713,6 +6713,21 @@ ] } ], + "EnableWifiQos": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "EnableWifiQos" + ] + } + ] + } + ], "EndOfLifeIncentive": [ { "platforms": [ @@ -19166,27 +19181,6 @@ ] } ], - "UsernameFirstFlowWithIntermediateValues": [ - { - "platforms": [ - "android", - "chromeos", - "chromeos_lacros", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "UsernameFirstFlowWithIntermediateValues" - ] - } - ] - } - ], "UsernameFirstFlowWithIntermediateValuesPredictions": [ { "platforms": [
diff --git a/third_party/angle b/third_party/angle index a971e5b4..19e725e 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit a971e5b42e1e8e61ccb0d8e4b3f2245aaa4f2d4d +Subproject commit 19e725e49c7d23f810ebd709b79d91323af921c1
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index c4abecb..c853bfe 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -1375,6 +1375,15 @@ BASE_FEATURE(kLinkPreview, "LinkPreview", base::FEATURE_DISABLED_BY_DEFAULT); +constexpr base::FeatureParam<LinkPreviewTriggerType>::Option + link_preview_trigger_type_options[] = { + {LinkPreviewTriggerType::kAltClick, "alt_click"}, + {LinkPreviewTriggerType::kAltHover, "alt_hover"}, + {LinkPreviewTriggerType::kLongPress, "long_press"}}; +const base::FeatureParam<LinkPreviewTriggerType> kLinkPreviewTriggerType{ + &kLinkPreview, "trigger_type", LinkPreviewTriggerType::kAltHover, + &link_preview_trigger_type_options}; + // A feature to control whether the loading phase should be extended beyond // First Meaningful Paint by a configurable buffer. BASE_FEATURE(kLoadingPhaseBufferTimeAfterFirstMeaningfulPaint, @@ -2536,6 +2545,11 @@ base::FeatureList::IsEnabled(kFetchLaterAPI); } +bool IsLinkPreviewTriggerTypeEnabled(LinkPreviewTriggerType type) { + return base::FeatureList::IsEnabled(blink::features::kLinkPreview) && + type == blink::features::kLinkPreviewTriggerType.Get(); +} + BASE_FEATURE(kExpandCompositedCullRect, "ExpandCompositedCullRect", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/third_party/blink/common/loader/throttling_url_loader.cc b/third_party/blink/common/loader/throttling_url_loader.cc index 5098dbd..e549048 100644 --- a/third_party/blink/common/loader/throttling_url_loader.cc +++ b/third_party/blink/common/loader/throttling_url_loader.cc
@@ -286,6 +286,8 @@ } ThrottlingURLLoader::~ThrottlingURLLoader() { + TRACE_EVENT_WITH_FLOW0("loading", "ThrottlingURLLoader::~ThrottlingURLLoader", + TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN); if (inside_delegate_calls_ > 0) { // A throttle is calling into this object. In this case, delay destruction // of the throttles, so that throttles don't need to worry about any @@ -408,6 +410,8 @@ network::mojom::URLLoaderClient* client, const net::NetworkTrafficAnnotationTag& traffic_annotation) : forwarding_client_(client), traffic_annotation_(traffic_annotation) { + TRACE_EVENT_WITH_FLOW0("loading", "ThrottlingURLLoader::ThrottlingURLLoader", + TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT); throttles_.reserve(throttles.size()); for (auto& throttle : throttles) throttles_.emplace_back(this, std::move(throttle)); @@ -420,6 +424,9 @@ network::ResourceRequest* url_request, scoped_refptr<base::SequencedTaskRunner> task_runner, std::optional<std::vector<std::string>> cors_exempt_header_list) { + TRACE_EVENT_WITH_FLOW0("loading", "ThrottlingURLLoader::Start", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK_EQ(DEFERRED_NONE, deferred_stage_); DCHECK(!loader_completed_); @@ -486,6 +493,9 @@ } void ThrottlingURLLoader::StartNow() { + TRACE_EVENT_WITH_FLOW0("loading", "ThrottlingURLLoader::StartNow", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(start_info_); if (!throttle_will_start_redirect_url_.is_empty()) { auto first_party_url_policy = @@ -620,8 +630,10 @@ DCHECK_EQ(DEFERRED_NONE, deferred_stage_); DCHECK(!loader_completed_); DCHECK(deferring_throttles_.empty()); - TRACE_EVENT1("loading", "ThrottlingURLLoader::OnReceiveResponse", "url", - response_url_.possibly_invalid_spec()); + TRACE_EVENT_WITH_FLOW1("loading", "ThrottlingURLLoader::OnReceiveResponse", + TRACE_ID_LOCAL(this), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + "url", response_url_.possibly_invalid_spec()); if (start_info_ && start_info_->url_request.keepalive) { base::UmaHistogramBoolean("FetchKeepAlive.Renderer.Total.ReceivedResponse", true);
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn index a6591c2..91072938 100644 --- a/third_party/blink/public/BUILD.gn +++ b/third_party/blink/public/BUILD.gn
@@ -330,6 +330,7 @@ "web/web_label_element.h", "web/web_language_detection_details.h", "web/web_lifecycle_update.h", + "web/web_link_preview_triggerer.h", "web/web_local_frame.h", "web/web_local_frame_client.h", "web/web_local_frame_observer.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index be0bff2d..537c7ef 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -830,6 +830,18 @@ // Tracking bug: go/launch/4269184 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kLinkPreview); +enum class LinkPreviewTriggerType { + // Alt + left click + kAltClick, + // Alt + mousehover + kAltHover, + // Long left click down + kLongPress, +}; + +BLINK_COMMON_EXPORT extern const base::FeatureParam<LinkPreviewTriggerType> + kLinkPreviewTriggerType; + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kLoadingTasksUnfreezable); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE( @@ -1620,6 +1632,10 @@ // specific to one of them should not rely on this function. BLINK_COMMON_EXPORT bool IsKeepAliveURLLoaderServiceEnabled(); +// Returns true if Link Preview and the given trigger type is enabled. +BLINK_COMMON_EXPORT bool IsLinkPreviewTriggerTypeEnabled( + LinkPreviewTriggerType type); + // Kill-switch for removing Authorization header upon cross origin redirects. BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE( kRemoveAuthroizationOnCrossOriginRedirect);
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn index 68454e1..739be81 100644 --- a/third_party/blink/public/mojom/BUILD.gn +++ b/third_party/blink/public/mojom/BUILD.gn
@@ -76,13 +76,13 @@ "fetch/fetch_api_response.mojom", "file/file_utilities.mojom", "file_system_access/file_system_access_access_handle_host.mojom", - "file_system_access/file_system_access_capacity_allocation_host.mojom", "file_system_access/file_system_access_cloud_identifier.mojom", "file_system_access/file_system_access_data_transfer_token.mojom", "file_system_access/file_system_access_directory_handle.mojom", "file_system_access/file_system_access_error.mojom", "file_system_access/file_system_access_file_delegate_host.mojom", "file_system_access/file_system_access_file_handle.mojom", + "file_system_access/file_system_access_file_modification_host.mojom", "file_system_access/file_system_access_file_writer.mojom", "file_system_access/file_system_access_manager.mojom", "file_system_access/file_system_access_observer.mojom",
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom index 663a3957..66516f31 100644 --- a/third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom +++ b/third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom
@@ -9,7 +9,7 @@ import "third_party/blink/public/mojom/blob/blob.mojom"; import "third_party/blink/public/mojom/blob/serialized_blob.mojom"; import "third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom"; -import "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom"; +import "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom"; import "third_party/blink/public/mojom/file_system_access/file_system_access_cloud_identifier.mojom"; import "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom"; import "third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom"; @@ -20,7 +20,7 @@ // Representation of a regular file for an AccessHandle. // `os_file` is an OS-level file which provides direct read/write access. // `file_size` is the file size of `os_file` (in bytes). -// `capacity_allocation_host` is a mojo pipe to manage capacity allocations for +// `file_modification_host` is a mojo pipe to manage capacity allocations for // this file. // // Each file has an associated capacity, which is the number of bytes of @@ -30,7 +30,7 @@ // file's size on disk, such as truncate() create capacity. // // Each file starts out with zero (0) capacity, and may call -// RequestCapacityChange() on the capacity_allocation_host to obtain capacity +// RequestCapacityChange() on the file_modification_host to obtain capacity // from the quota system. Capacity counts as quota usage by the storage key. // When the instance's mojo pipe is closed, any available capacity is // automatically returned to the quota system. @@ -40,7 +40,7 @@ struct FileSystemAccessRegularFile { mojo_base.mojom.File os_file; int64 file_size; - pending_remote<FileSystemAccessCapacityAllocationHost> capacity_allocation_host; + pending_remote<FileSystemAccessFileModificationHost> file_modification_host; }; // Representation of a file for an AccessHandle.
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom similarity index 77% rename from third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom rename to third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom index 4c2d5d88..c0f5b61 100644 --- a/third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom +++ b/third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom
@@ -4,15 +4,12 @@ module blink.mojom; -// Manages storge capacity allocations for a non-incognito file handle held by +// Manages storage file modifications for a non-incognito file handle held by // an AccessHandle of FileSystemAccess. // -// Capacity allocations are used for non-incognito files only. In incognito -// mode, quota is fully managed in the browser process. -// -// TODO(https://crbug.com/1490686): Consider renaming this to be more generic, -// since it now does more than just handle quota. -interface FileSystemAccessCapacityAllocationHost { +// This interface is used for non-incognito files only. In incognito mode, quota +// is fully managed in the browser process. +interface FileSystemAccessFileModificationHost { // Requests or releases storage capacity. // // This interface's consumer must only expand the file handle associated with @@ -29,7 +26,7 @@ // to succeed. In other words, if `capacity_delta` < 0, // `granted_capacity_delta` is guaranteed to == `capacity_delta`. // - // The synchronous interface is needed as File System Access Access Handles + // The synchronous method is needed as File System Access Access Handles // implement synchronous write operations. [Sync] RequestCapacityChange(int64 capacity_delta) =>
diff --git a/third_party/blink/public/web/web_document.h b/third_party/blink/public/web/web_document.h index b377ae4..a52f557 100644 --- a/third_party/blink/public/web/web_document.h +++ b/third_party/blink/public/web/web_document.h
@@ -185,6 +185,11 @@ // Returns the referrer for this document. WebString OutgoingReferrer() const; + // (Experimental) Initiates Link Preview for `url`. + // + // It is intended to be used in WebLinkPreviewTriggerer. + void InitiatePreview(const WebURL& url); + #if INSIDE_BLINK WebDocument(Document*); WebDocument& operator=(Document*);
diff --git a/third_party/blink/public/web/web_element.h b/third_party/blink/public/web/web_element.h index 9cd08e8..103cbab7 100644 --- a/third_party/blink/public/web/web_element.h +++ b/third_party/blink/public/web/web_element.h
@@ -95,6 +95,12 @@ // Otherwise returns the empty string. WebString SelectedText() const; + // Selects the text in this element. + // If `select_all`, then the entire contents of the element is selected. + // If `!select_all`, then selects only the empty range at the end of the + // element + void SelectText(bool select_all); + // Simulates a paste of `text` event into `this` element. // // There are three different behaviors depending on `replace_all` and which
diff --git a/third_party/blink/public/web/web_link_preview_triggerer.h b/third_party/blink/public/web/web_link_preview_triggerer.h new file mode 100644 index 0000000..7a8e283 --- /dev/null +++ b/third_party/blink/public/web/web_link_preview_triggerer.h
@@ -0,0 +1,63 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_LINK_PREVIEW_TRIGGERER_H_ +#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_LINK_PREVIEW_TRIGGERER_H_ + +#include "base/functional/callback.h" +#include "third_party/blink/public/common/input/web_mouse_event.h" +#include "third_party/blink/public/platform/web_url.h" +#include "third_party/blink/public/web/web_document.h" +#include "third_party/blink/public/web/web_element.h" + +namespace blink { + +// Experimental +// Observes signals of user actions and determines to trigger Link Preview. +// +// It will be instantiated per LocalFrame. Notified events are bare and intended +// to be filtered by implementations. +// +// For concrete triggers, see the implementations. +class WebLinkPreviewTriggerer { + public: + virtual ~WebLinkPreviewTriggerer() = default; + + // Events to track current modifier state. + // + // This is called for every key event and mouse leave event. For mouse leave + // event, kNoModifier blink::WebInputEvent::kNoModifiers is passed as an + // argument. + // + // The argument `modifiers` is a bit mask consisting of + // `blink::WebInputEvent::Modifiers`. + // + // Note that we don't still have a right way to track modifiers state (But + // it's enough for Link Preview because it can trigger preview only if a mouse + // is on anchor element.) because + // 1. we can't get changes of modifiers outside the window, and 2. we don't + // have a reliable way to `KeyobardEventManager::GetCurrentModifierState` + // except for macOS. For example, consider the following cases: + // + // A. A user presses and holds Alt button, leaves the window, releases Alt, + // enters to the window. + // A'. Same for subframe -> parent frame -> subframe. + // B. A user presses and holds Alt button, leaves the window, releases Alt, + // presses Alt, enters to the window. + // + // In A and A' (resp. B), the ideal `GetCurrentModifierState` should return + // `kNoModifiers` (resp `kAltKey`), but we don't know how to correctly get to + // know it. So, for safety, mouseleave event emits kNoModifiers. + virtual void MaybeChangedKeyEventModifier(int modifiers) {} + // Called when the hover element changed. + virtual void DidChangeHoverElement(blink::WebElement element) {} + // Called when an anchor element with valid link received a mouse event. + virtual void DidAnchorElementReceiveMouseEvent( + blink::WebElement anchor_element, + blink::WebMouseEvent mouse_event) {} +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_LINK_PREVIEW_TRIGGERER_H_
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h index 58612b9..1e475f6 100644 --- a/third_party/blink/public/web/web_local_frame_client.h +++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -140,6 +140,7 @@ class WebURLRequest; class WebURLResponse; class WebView; +class WebLinkPreviewTriggerer; struct FramePolicy; struct Impression; struct JavaScriptFrameworkDetectionResult; @@ -862,6 +863,11 @@ const WebURL& base_url) { return nullptr; } + + virtual std::unique_ptr<WebLinkPreviewTriggerer> CreateLinkPreviewTriggerer(); + + virtual void SetLinkPreviewTriggererForTesting( + std::unique_ptr<WebLinkPreviewTriggerer> trigger); }; } // namespace blink
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h index c446212..61e95857 100644 --- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h +++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -196,18 +196,6 @@ virtual void OnTouchAccessibilityHover(const gfx::Point&) = 0; - // Gets the AXID of the given Node. If there is not yet an associated - // AXObject, this method will create one (along with its parent chain) and - // return the id. - virtual AXID GetAXID(Node*) = 0; - - // Crash dumps have shown there are times where AXID's are queried - // 'out-of-band' where we may not be in a state where creating AXObjects and - // repairing parent chains is possible. This will look for the current - // AXObject associated with the given node and return its id without creating - // or recomputing any state. - virtual AXID GetExistingAXID(Node*) = 0; - virtual AXObject* ObjectFromAXID(AXID) const = 0; virtual AXObject* Root() = 0;
diff --git a/third_party/blink/renderer/core/accessibility/axid.h b/third_party/blink/renderer/core/accessibility/axid.h index 4b805d8..e116ab0c 100644 --- a/third_party/blink/renderer/core/accessibility/axid.h +++ b/third_party/blink/renderer/core/accessibility/axid.h
@@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_ACCESSIBILITY_AXID_H_ namespace blink { -using AXID = unsigned; +using AXID = int; } #endif // THIRD_PARTY_BLINK_RENDERER_CORE_ACCESSIBILITY_AXID_H_
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc index c0e563e0..93a51f8 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -1871,6 +1871,10 @@ return effect.Affects(PropertyHandle(GetCSSPropertyBackgroundColor())); } +bool AffectsClipPath(const AnimationEffect& effect) { + return effect.Affects(PropertyHandle(GetCSSPropertyClipPath())); +} + void UpdateAnimationFlagsForEffect(const AnimationEffect& effect, ComputedStyleBuilder& builder) { if (effect.Affects(PropertyHandle(GetCSSPropertyOpacity()))) @@ -1889,15 +1893,18 @@ builder.SetHasCurrentBackdropFilterAnimation(true); if (AffectsBackgroundColor(effect)) builder.SetHasCurrentBackgroundColorAnimation(true); - if (effect.Affects(PropertyHandle(GetCSSPropertyClipPath()))) + if (AffectsClipPath(effect)) { builder.SetHasCurrentClipPathAnimation(true); + } } void SetCompositablePaintAnimationChangedIfAffected( const AnimationEffect& effect, ComputedStyleBuilder& builder) { - if (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled() && - AffectsBackgroundColor(effect)) { + if ((RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled() && + AffectsBackgroundColor(effect)) || + (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled() && + AffectsClipPath(effect))) { builder.SetCompositablePaintAnimationChanged(true); } }
diff --git a/third_party/blink/renderer/core/aom/computed_accessible_node.cc b/third_party/blink/renderer/core/aom/computed_accessible_node.cc index 10d4a65..9b8cc2d7 100644 --- a/third_party/blink/renderer/core/aom/computed_accessible_node.cc +++ b/third_party/blink/renderer/core/aom/computed_accessible_node.cc
@@ -114,7 +114,7 @@ ax_context_->GetDocument()->View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kAccessibility); AXObjectCache& cache = ax_context_->GetAXObjectCache(); - AXID ax_id = ax_id_ ? ax_id_ : cache.GetAXID(element_); + AXID ax_id = ax_id_ ? ax_id_ : element_->GetDomNodeId(); if (!ax_id || !cache.ObjectFromAXID(ax_id)) { resolver_->Resolve(); // No AXObject exists for this element. return;
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc index ae1fede0..c74b0efa 100644 --- a/third_party/blink/renderer/core/css/element_rule_collector.cc +++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -64,12 +64,39 @@ namespace blink { namespace { + struct CumulativeRulePerfKey { String selector; String style_sheet_id; CumulativeRulePerfKey(const String& selector, const String& style_sheet_id) : selector(selector), style_sheet_id(style_sheet_id) {} }; + +struct ContextWithStyleScopeFrame { + STACK_ALLOCATED(); + + public: + ContextWithStyleScopeFrame(Element* element, + const MatchRequest& match_request, + StyleRequest* pseudo_style_request, + StyleScopeFrame* parent_frame) + : style_scope_frame(*element, parent_frame), context(element) { + context.style_scope_frame = + &style_scope_frame.GetParentFrameOrThis(*element); + context.scope = match_request.Scope(); + context.pseudo_id = pseudo_style_request->pseudo_id; + context.pseudo_argument = &pseudo_style_request->pseudo_argument; + context.vtt_originating_element = match_request.VTTOriginatingElement(); + } + + // This StyleScopeFrame is effectively ignored if the StyleRecalcContext + // provides StyleScopeFrame already (see call to GetParentFrameOrThis above). + // This happens e.g. when we need to collect matching rules for inspector + // purposes. + StyleScopeFrame style_scope_frame; + SelectorChecker::SelectorCheckingContext context; +}; + } // namespace } // namespace blink @@ -412,22 +439,8 @@ const RuleSet* rule_set, int style_sheet_index, const SelectorChecker& checker, + SelectorChecker::SelectorCheckingContext& context, PartRequest* part_request) { - // This StyleScopeFrame is effectively ignored if the StyleRecalcContext - // provides StyleScopeFrame already (see call to GetParentFrameOrThis below). - // This happens e.g. when we need to collect matching rules for inspector - // purposes. - StyleScopeFrame style_scope_frame(context_.GetElement(), - style_recalc_context_.style_scope_frame); - - SelectorChecker::SelectorCheckingContext context(&context_.GetElement()); - context.scope = match_request.Scope(); - context.pseudo_id = pseudo_style_request_.pseudo_id; - context.pseudo_argument = &pseudo_style_request_.pseudo_argument; - context.vtt_originating_element = match_request.VTTOriginatingElement(); - context.style_scope_frame = - &style_scope_frame.GetParentFrameOrThis(context_.GetElement()); - // If we are _not_ in initial style, or we are just collecting rules, // we must skip all rules marked with @starting-style. bool reject_starting_styles = style_recalc_context_.is_ensuring_style || @@ -481,7 +494,6 @@ } SelectorChecker::MatchResult result; - context.style_scope = scope_seeker.Seek(rule_data.GetPosition()); if (context.vtt_originating_element == nullptr && rule_data.IsEntirelyCoveredByBucketing()) { // Just by seeing this rule, we know that its selector @@ -490,21 +502,27 @@ if (pseudo_style_request_.pseudo_id != kPseudoIdNone) { continue; } +#if DCHECK_IS_ON() + context.style_scope = scope_seeker.Seek(rule_data.GetPosition()); DCHECK(!context.style_scope); DCHECK(SlowMatchWithNoResultFlags(checker, context, selector, rule_data, suppress_visited_, result.proximity)); +#endif } else if (context.vtt_originating_element == nullptr && rule_data.SelectorIsEasy()) { if (pseudo_style_request_.pseudo_id != kPseudoIdNone) { continue; } bool easy_match = EasySelectorChecker::Match(&selector, context.element); +#if DCHECK_IS_ON() + context.style_scope = scope_seeker.Seek(rule_data.GetPosition()); DCHECK(!context.style_scope); DCHECK_EQ(easy_match, SlowMatchWithNoResultFlags( checker, context, selector, rule_data, suppress_visited_, result.proximity)) << "Mismatch for selector " << selector.SelectorText() << " on element " << context.element; +#endif if (!easy_match) { continue; } @@ -513,6 +531,7 @@ context.match_visited = !suppress_visited_ && rule_data.LinkMatchType() == CSSSelector::kMatchVisited; + context.style_scope = scope_seeker.Seek(rule_data.GetPosition()); bool match = checker.Match(context, result); result_.AddFlags(result.flags); if (!match) { @@ -620,6 +639,7 @@ const RuleSet* rule_set, int style_sheet_index, const SelectorChecker& checker, + SelectorChecker::SelectorCheckingContext& context, PartRequest* part_request) { // This is a very common case for many style sheets, and by putting it here // instead of inside CollectMatchingRulesForListInternal(), we're usually @@ -634,11 +654,11 @@ // when tracing is not enabled. if (!*g_selector_stats_tracing_enabled) { return CollectMatchingRulesForListInternal<stop_at_first_match, false>( - rules, match_request, rule_set, style_sheet_index, checker, + rules, match_request, rule_set, style_sheet_index, checker, context, part_request); } else { return CollectMatchingRulesForListInternal<stop_at_first_match, true>( - rules, match_request, rule_set, style_sheet_index, checker, + rules, match_request, rule_set, style_sheet_index, checker, context, part_request); } } @@ -688,6 +708,10 @@ SelectorChecker checker(nullptr, pseudo_style_request_, mode_, matching_ua_rules_); + ContextWithStyleScopeFrame context(&context_.GetElement(), match_request, + &pseudo_style_request_, + style_recalc_context_.style_scope_frame); + Element& element = context_.GetElement(); const AtomicString& pseudo_id = element.ShadowPseudoId(); if (!pseudo_id.empty()) { @@ -695,8 +719,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->UAShadowPseudoElementRules(pseudo_id), - match_request, bundle.rule_set, bundle.style_sheet_index, - checker) && + match_request, bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -707,7 +731,7 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->CuePseudoRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker) && + bundle.style_sheet_index, checker, context.context) && stop_at_first_match) { return true; } @@ -730,8 +754,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->IdRules(element.IdForStyleResolution()), - match_request, bundle.rule_set, bundle.style_sheet_index, - checker) && + match_request, bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -742,7 +766,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->ClassRules(class_name), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -819,7 +844,8 @@ } if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->AttrRules(lower_name), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -834,7 +860,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->LinkPseudoClassRules(), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -844,7 +871,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->FocusPseudoClassRules(), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -854,7 +882,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->SelectorFragmentAnchorRules(), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -864,7 +893,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->FocusVisiblePseudoClassRules(), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -874,7 +904,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->RootElementRules(), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -886,7 +917,8 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->TagRules(element_name), match_request, - bundle.rule_set, bundle.style_sheet_index, checker) && + bundle.rule_set, bundle.style_sheet_index, checker, + context.context) && stop_at_first_match) { return true; } @@ -894,7 +926,7 @@ for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList<stop_at_first_match>( bundle.rule_set->UniversalRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker) && + bundle.style_sheet_index, checker, context.context) && stop_at_first_match) { return true; } @@ -907,14 +939,18 @@ SelectorChecker checker(nullptr, pseudo_style_request_, mode_, matching_ua_rules_); + ContextWithStyleScopeFrame context(&context_.GetElement(), match_request, + &pseudo_style_request_, + style_recalc_context_.style_scope_frame); + for (const auto bundle : match_request.AllRuleSets()) { CollectMatchingRulesForList</*stop_at_first_match=*/false>( bundle.rule_set->ShadowHostRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker); + bundle.style_sheet_index, checker, context.context); if (bundle.rule_set->MayHaveScopeInUniversalBucket()) { CollectMatchingRulesForList</*stop_at_first_match=*/false>( bundle.rule_set->UniversalRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker); + bundle.style_sheet_index, checker, context.context); } } } @@ -924,16 +960,20 @@ SelectorChecker checker(nullptr, pseudo_style_request_, mode_, matching_ua_rules_); + ContextWithStyleScopeFrame context(&context_.GetElement(), match_request, + &pseudo_style_request_, + style_recalc_context_.style_scope_frame); + for (const auto bundle : match_request.AllRuleSets()) { if (CollectMatchingRulesForList</*stop_at_first_match=*/true>( bundle.rule_set->ShadowHostRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker)) { + bundle.style_sheet_index, checker, context.context)) { return true; } if (bundle.rule_set->MayHaveScopeInUniversalBucket()) { if (CollectMatchingRulesForList</*stop_at_first_match=*/true>( bundle.rule_set->UniversalRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker)) { + bundle.style_sheet_index, checker, context.context)) { return true; } } @@ -945,11 +985,14 @@ const MatchRequest& match_request) { SelectorChecker checker(nullptr, pseudo_style_request_, mode_, matching_ua_rules_); + ContextWithStyleScopeFrame context(&context_.GetElement(), match_request, + &pseudo_style_request_, + style_recalc_context_.style_scope_frame); for (const auto bundle : match_request.AllRuleSets()) { CollectMatchingRulesForList</*stop_at_first_match=*/false>( bundle.rule_set->SlottedPseudoElementRules(), match_request, - bundle.rule_set, bundle.style_sheet_index, checker); + bundle.rule_set, bundle.style_sheet_index, checker, context.context); } } @@ -961,10 +1004,14 @@ SelectorChecker checker(&part_names, pseudo_style_request_, mode_, matching_ua_rules_); + ContextWithStyleScopeFrame context(&context_.GetElement(), match_request, + &pseudo_style_request_, + style_recalc_context_.style_scope_frame); + for (const auto bundle : match_request.AllRuleSets()) { CollectMatchingRulesForList</*stop_at_first_match=*/false>( bundle.rule_set->PartPseudoRules(), match_request, bundle.rule_set, - bundle.style_sheet_index, checker, &request); + bundle.style_sheet_index, checker, context.context, &request); } }
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.h b/third_party/blink/renderer/core/css/element_rule_collector.h index cbefec34..5e30b81 100644 --- a/third_party/blink/renderer/core/css/element_rule_collector.h +++ b/third_party/blink/renderer/core/css/element_rule_collector.h
@@ -263,12 +263,14 @@ bool CollectMatchingRulesInternal(const MatchRequest&); template <bool stop_at_first_match, bool perf_trace_enabled> - bool CollectMatchingRulesForListInternal(base::span<const RuleData>, - const MatchRequest&, - const RuleSet*, - int, - const SelectorChecker&, - PartRequest* = nullptr); + bool CollectMatchingRulesForListInternal( + base::span<const RuleData>, + const MatchRequest&, + const RuleSet*, + int, + const SelectorChecker&, + SelectorChecker::SelectorCheckingContext&, + PartRequest* = nullptr); template <bool stop_at_first_match> bool CollectMatchingRulesForList(base::span<const RuleData>, @@ -276,6 +278,7 @@ const RuleSet*, int, const SelectorChecker&, + SelectorChecker::SelectorCheckingContext&, PartRequest* = nullptr); bool Match(SelectorChecker&,
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index fc65223..f07d78a 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -75,6 +75,7 @@ #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/web_content_settings_client.h" #include "third_party/blink/public/platform/web_theme_engine.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/public/web/web_print_page_description.h" #include "third_party/blink/renderer/bindings/core/v8/frozen_array.h" #include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h" @@ -3201,6 +3202,7 @@ GetStyleEngine().DidDetach(); + GetFrame()->DocumentDetached(); GetFrame()->GetEventHandlerRegistry().DocumentDetached(*this); // Signal destruction to mutation observers. @@ -5322,8 +5324,26 @@ GetStyleEngine().MarkViewportUnitDirty(ViewportUnitFlag::kDynamic); } +void EmitDidChangeHoverElement(Document& document, Element* new_hover_element) { + LocalFrame* local_frame = document.GetFrame(); + if (!local_frame) { + return; + } + + WebLinkPreviewTriggerer* triggerer = + local_frame->GetOrCreateLinkPreviewTriggerer(); + if (!triggerer) { + return; + } + + WebElement web_element = WebElement(DynamicTo<Element>(new_hover_element)); + triggerer->DidChangeHoverElement(web_element); +} + void Document::SetHoverElement(Element* new_hover_element) { HTMLElement::HoveredElementChanged(hover_element_, new_hover_element); + EmitDidChangeHoverElement(*this, new_hover_element); + hover_element_ = new_hover_element; }
diff --git a/third_party/blink/renderer/core/dom/dom_node_ids.cc b/third_party/blink/renderer/core/dom/dom_node_ids.cc index 5de3c55db..3325b0f 100644 --- a/third_party/blink/renderer/core/dom/dom_node_ids.cc +++ b/third_party/blink/renderer/core/dom/dom_node_ids.cc
@@ -17,6 +17,11 @@ } // static +DOMNodeId DOMNodeIds::ExistingIdForNode(const Node* node) { + return ExistingIdForNode(const_cast<Node*>(node)); +} + +// static DOMNodeId DOMNodeIds::IdForNode(Node* node) { return node ? WeakIdentifierMap<Node, DOMNodeId>::Identifier(node) : kInvalidDOMNodeId;
diff --git a/third_party/blink/renderer/core/dom/dom_node_ids.h b/third_party/blink/renderer/core/dom/dom_node_ids.h index 7f31040..2e3bd46 100644 --- a/third_party/blink/renderer/core/dom/dom_node_ids.h +++ b/third_party/blink/renderer/core/dom/dom_node_ids.h
@@ -22,6 +22,9 @@ // Return a DOMNodeID or 0 if one hasn't been assigned. static DOMNodeId ExistingIdForNode(Node*); + // Return a DOMNodeID or 0 if one hasn't been assigned. + static DOMNodeId ExistingIdForNode(const Node*); + // Return the existing DOMNodeID if it has already been assigned, otherwise, // assign a new DOMNodeID and return that. static DOMNodeId IdForNode(Node*);
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.cc b/third_party/blink/renderer/core/editing/finder/text_finder.cc index 7cb3c84..8ced6fd6 100644 --- a/third_party/blink/renderer/core/editing/finder/text_finder.cc +++ b/third_party/blink/renderer/core/editing/finder/text_finder.cc
@@ -483,8 +483,8 @@ Node* end_node = active_match_->endContainer(); ax_object_cache->HandleTextMarkerDataAdded(start_node, end_node); - int32_t start_id = ax_object_cache->GetAXID(start_node); - int32_t end_id = ax_object_cache->GetAXID(end_node); + int32_t start_id = start_node->GetDomNodeId(); + int32_t end_id = end_node->GetDomNodeId(); auto params = mojom::blink::FindInPageResultAXParams::New( identifier, active_match_index_ + 1, start_id,
diff --git a/third_party/blink/renderer/core/exported/build.gni b/third_party/blink/renderer/core/exported/build.gni index 745c1e9..c3fb023 100644 --- a/third_party/blink/renderer/core/exported/build.gni +++ b/third_party/blink/renderer/core/exported/build.gni
@@ -86,6 +86,7 @@ "web_frame_serializer_test_helper.h", "web_image_test.cc", "web_meaningful_layouts_test.cc", + "web_link_preview_triggerer_test.cc", "web_node_test.cc", "web_plugin_container_test.cc", "web_range_test.cc",
diff --git a/third_party/blink/renderer/core/exported/web_document.cc b/third_party/blink/renderer/core/exported/web_document.cc index cc236fea..c063db4 100644 --- a/third_party/blink/renderer/core/exported/web_document.cc +++ b/third_party/blink/renderer/core/exported/web_document.cc
@@ -67,6 +67,7 @@ #include "third_party/blink/renderer/core/html/plugin_document.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/wtf/casting.h" @@ -368,4 +369,18 @@ return WebString(ConstUnwrap<Document>()->domWindow()->OutgoingReferrer()); } +void WebDocument::InitiatePreview(const WebURL& url) { + if (!url.IsValid()) { + return; + } + + Document* document = blink::To<Document>(private_.Get()); + if (!document) { + return; + } + + KURL kurl(url); + DocumentSpeculationRules::From(*document).InitiatePreview(kurl); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_element.cc b/third_party/blink/renderer/core/exported/web_element.cc index 5cc36b7..3c71a93 100644 --- a/third_party/blink/renderer/core/exported/web_element.cc +++ b/third_party/blink/renderer/core/exported/web_element.cc
@@ -169,6 +169,37 @@ .Build()); } +void WebElement::SelectText(bool select_all) { + auto* element = Unwrap<Element>(); + LocalFrame* frame = element->GetDocument().GetFrame(); + if (!frame) { + return; + } + + // Makes sure the selection is inside `element`: if `select_all`, selects + // all inside `element`; otherwise, selects an empty range at the end. + if (auto* text_control_element = + blink::DynamicTo<TextControlElement>(element)) { + if (select_all) { + text_control_element->select(); + } else { + text_control_element->Focus(FocusParams(SelectionBehaviorOnFocus::kNone, + mojom::blink::FocusType::kScript, + nullptr, FocusOptions::Create())); + text_control_element->setSelectionStart(std::numeric_limits<int>::max()); + } + } else { + Position base = FirstPositionInOrBeforeNode(*element); + Position extent = LastPositionInOrAfterNode(*element); + if (!select_all) { + base = extent; + } + frame->Selection().SetSelection( + SelectionInDOMTree::Builder().SetBaseAndExtent(base, extent).Build(), + SetSelectionOptions()); + } +} + void WebElement::PasteText(const WebString& text, bool replace_all) { if (!IsEditable()) { return; @@ -185,29 +216,7 @@ }; if (replace_all || !ContainsFrameSelection()) { - // Makes sure the selection is inside `element`: if `replace_all`, selects - // all inside `element`; otherwise, selects an empty range at the end. - if (auto* text_control_element = - blink::DynamicTo<TextControlElement>(element)) { - if (replace_all) { - text_control_element->select(); - } else { - text_control_element->Focus(FocusParams( - SelectionBehaviorOnFocus::kNone, mojom::blink::FocusType::kScript, - nullptr, FocusOptions::Create())); - text_control_element->setSelectionStart( - std::numeric_limits<int>::max()); - } - } else { - Position base = FirstPositionInOrBeforeNode(*element); - Position extent = LastPositionInOrAfterNode(*element); - if (!replace_all) { - base = extent; - } - frame->Selection().SetSelection( - SelectionInDOMTree::Builder().SetBaseAndExtent(base, extent).Build(), - SetSelectionOptions()); - } + SelectText(replace_all); // JavaScript handlers may have destroyed the frame or moved the selection. if (is_destroyed(*frame) || !ContainsFrameSelection()) { return;
diff --git a/third_party/blink/renderer/core/exported/web_element_test.cc b/third_party/blink/renderer/core/exported/web_element_test.cc index 9822fc9d..02a8d8b 100644 --- a/third_party/blink/renderer/core/exported/web_element_test.cc +++ b/third_party/blink/renderer/core/exported/web_element_test.cc
@@ -180,6 +180,34 @@ EXPECT_EQ(test_element.SelectedText().Utf8(), ""); } +// Tests SelectText() with a textarea. +TEST_F(WebElementTest, SelectTextOfTextArea) { + InsertHTML( + R"(<div>Foo</div> + <textarea id=testElement>Some plain text here.</textarea> + <div>Bar</div>)"); + + TestElement().SelectText(/*select_all=*/false); + EXPECT_EQ(Selection().SelectedText(), ""); + + TestElement().SelectText(/*select_all=*/true); + EXPECT_EQ(Selection().SelectedText(), "Some plain text here."); +} + +// Tests SelectText() with a contenteditable. +TEST_F(WebElementTest, SelectTextOfContentEditable) { + InsertHTML( + R"(<div>Foo</div> + <div id=testElement contenteditable>Some <b>rich text</b> here.</div> + <textarea>Some plain text here.</textarea>)"); + + TestElement().SelectText(/*select_all=*/false); + EXPECT_EQ(Selection().SelectedText(), ""); + + TestElement().SelectText(/*select_all=*/true); + EXPECT_EQ(Selection().SelectedText(), "Some rich text here."); +} + TEST_F(WebElementTest, PasteTextIntoContentEditable) { InsertHTML( "<div id=testElement contenteditable>Some <b>rich text</b> here.</div>"
diff --git a/third_party/blink/renderer/core/exported/web_link_preview_triggerer_test.cc b/third_party/blink/renderer/core/exported/web_link_preview_triggerer_test.cc new file mode 100644 index 0000000..b77531de --- /dev/null +++ b/third_party/blink/renderer/core/exported/web_link_preview_triggerer_test.cc
@@ -0,0 +1,203 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/public/web/web_link_preview_triggerer.h" + +#include <memory> +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/input/web_keyboard_event.h" +#include "third_party/blink/public/web/web_element.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/exported/web_view_impl.h" +#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" +#include "third_party/blink/renderer/core/input/event_handler.h" +#include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/testing/page_test_base.h" +#include "third_party/blink/renderer/platform/testing/url_test_helpers.h" +#include "ui/events/keycodes/dom/dom_code.h" + +namespace blink { + +class MockWebLinkPreviewTriggerer : public WebLinkPreviewTriggerer { + public: + MockWebLinkPreviewTriggerer() = default; + ~MockWebLinkPreviewTriggerer() override = default; + + int LastKeyEventModifiers() const { return last_key_event_modifiers_; } + + const WebElement& HoverElement() const { return hover_element_; } + + const WebElement& Element() const { return element_; } + + const std::optional<WebMouseEvent>& MouseEvent() const { + return mouse_event_; + } + + void MaybeChangedKeyEventModifier(int modifiers) override { + last_key_event_modifiers_ = modifiers; + } + + void DidChangeHoverElement(blink::WebElement element) override { + hover_element_ = element; + } + + void DidAnchorElementReceiveMouseEvent( + blink::WebElement anchor_element, + blink::WebMouseEvent mouse_event) override { + element_ = anchor_element; + mouse_event_ = mouse_event; + } + + private: + int last_key_event_modifiers_ = blink::WebInputEvent::kNoModifiers; + WebElement hover_element_; + WebElement element_; + std::optional<WebMouseEvent> mouse_event_ = std::nullopt; +}; + +class WebLinkPreviewTriggererTest : public PageTestBase { + protected: + void Initialize() { + LocalFrame* local_frame = GetDocument().GetFrame(); + CHECK(local_frame); + + local_frame->SetLinkPreviewTriggererForTesting( + std::make_unique<MockWebLinkPreviewTriggerer>()); + } + + void SetInnerHTML(const String& html) { + GetDocument().documentElement()->setInnerHTML(html); + } +}; + +TEST_F(WebLinkPreviewTriggererTest, MaybeChangedKeyEventModifierCalled) { + Initialize(); + SetHtmlInnerHTML("<div></div>"); + MockWebLinkPreviewTriggerer* triggerer = + static_cast<MockWebLinkPreviewTriggerer*>( + GetDocument().GetFrame()->GetOrCreateLinkPreviewTriggerer()); + + EXPECT_EQ(WebInputEvent::kNoModifiers, triggerer->LastKeyEventModifiers()); + + WebKeyboardEvent e0{WebInputEvent::Type::kRawKeyDown, WebInputEvent::kAltKey, + WebInputEvent::GetStaticTimeStampForTests()}; + e0.dom_code = static_cast<int>(ui::DomCode::ALT_LEFT); + GetDocument().GetFrame()->GetEventHandler().KeyEvent(e0); + + EXPECT_EQ(WebInputEvent::kAltKey, triggerer->LastKeyEventModifiers()); + + WebKeyboardEvent e1{WebInputEvent::Type::kKeyUp, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()}; + e1.dom_code = static_cast<int>(ui::DomCode::ALT_LEFT); + GetDocument().GetFrame()->GetEventHandler().KeyEvent(e1); + + EXPECT_EQ(WebInputEvent::kNoModifiers, triggerer->LastKeyEventModifiers()); +} + +TEST_F(WebLinkPreviewTriggererTest, + MaybeChangedKeyEventModifierCalledWithNoModifiersOnMouseLeave) { + Initialize(); + SetHtmlInnerHTML("<div></div>"); + MockWebLinkPreviewTriggerer* triggerer = + static_cast<MockWebLinkPreviewTriggerer*>( + GetDocument().GetFrame()->GetOrCreateLinkPreviewTriggerer()); + + EXPECT_EQ(WebInputEvent::kNoModifiers, triggerer->LastKeyEventModifiers()); + + WebKeyboardEvent e0{WebInputEvent::Type::kRawKeyDown, WebInputEvent::kAltKey, + WebInputEvent::GetStaticTimeStampForTests()}; + e0.dom_code = static_cast<int>(ui::DomCode::ALT_LEFT); + GetDocument().GetFrame()->GetEventHandler().KeyEvent(e0); + + EXPECT_EQ(WebInputEvent::kAltKey, triggerer->LastKeyEventModifiers()); + + WebMouseEvent e1(WebMouseEvent::Type::kMouseLeave, gfx::PointF(262, 29), + gfx::PointF(329, 67), + WebPointerProperties::Button::kNoButton, 1, + WebInputEvent::Modifiers::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()); + GetDocument().GetFrame()->GetEventHandler().HandleMouseLeaveEvent(e1); + + EXPECT_EQ(WebInputEvent::kNoModifiers, triggerer->LastKeyEventModifiers()); +} + +TEST_F(WebLinkPreviewTriggererTest, DidChangeHoverElementCalledOnHoverChanged) { + Initialize(); + SetHtmlInnerHTML( + "<style>" + " body { margin:0px; }" + " a { display:block; width:100px; height:100px; }" + "</style>" + "<body>" + " <a href=\"https://example.com\">anchor</a>" + "</body>"); + MockWebLinkPreviewTriggerer* triggerer = + static_cast<MockWebLinkPreviewTriggerer*>( + GetDocument().GetFrame()->GetOrCreateLinkPreviewTriggerer()); + + { + gfx::PointF point(50, 50); + WebMouseEvent mouse_move_event(WebInputEvent::Type::kMouseMove, point, + point, + WebPointerProperties::Button::kNoButton, 0, + WebInputEvent::Modifiers::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()); + + GetDocument().GetFrame()->GetEventHandler().HandleMouseMoveEvent( + mouse_move_event, Vector<WebMouseEvent>(), Vector<WebMouseEvent>()); + + EXPECT_FALSE(triggerer->HoverElement().IsNull()); + EXPECT_EQ("A", triggerer->HoverElement().TagName()); + EXPECT_EQ("https://example.com", + triggerer->HoverElement().GetAttribute("href")); + } + + { + gfx::PointF point(200, 200); + WebMouseEvent mouse_move_event(WebInputEvent::Type::kMouseMove, point, + point, + WebPointerProperties::Button::kNoButton, 0, + WebInputEvent::Modifiers::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()); + + GetDocument().GetFrame()->GetEventHandler().HandleMouseMoveEvent( + mouse_move_event, Vector<WebMouseEvent>(), Vector<WebMouseEvent>()); + + EXPECT_FALSE(triggerer->Element().IsNull()); + EXPECT_EQ("HTML", triggerer->HoverElement().TagName()); + } +} + +TEST_F(WebLinkPreviewTriggererTest, + DidAnchorElementReceiveMouseEventCalledOnMousePress) { + Initialize(); + SetHtmlInnerHTML( + "<style>" + " body { margin:0px; }" + " a { display:block; width:100px; height:100px; }" + "</style>" + "<body>" + " <a href=\"https://example.com\">anchor</a>" + "</body>"); + MockWebLinkPreviewTriggerer* triggerer = + static_cast<MockWebLinkPreviewTriggerer*>( + GetDocument().GetFrame()->GetOrCreateLinkPreviewTriggerer()); + + gfx::PointF point(50, 50); + WebMouseEvent mouse_down_event(WebInputEvent::Type::kMouseDown, point, point, + WebPointerProperties::Button::kLeft, 1, + WebInputEvent::Modifiers::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()); + + GetDocument().GetFrame()->GetEventHandler().HandleMousePressEvent( + mouse_down_event); + + EXPECT_FALSE(triggerer->Element().IsNull()); + EXPECT_EQ("https://example.com", triggerer->Element().GetAttribute("href")); + EXPECT_TRUE(triggerer->MouseEvent()); + EXPECT_EQ(WebInputEvent::Type::kMouseDown, + triggerer->MouseEvent()->GetType()); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc index 4f33aff..d7fc8c4b 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -82,6 +82,7 @@ #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_content_capture_client.h" #include "third_party/blink/public/web/web_frame.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/public/web/web_local_frame_client.h" #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" @@ -1015,6 +1016,13 @@ return DomWindow() ? DomWindow()->document() : nullptr; } +void LocalFrame::DocumentDetached() { + // Resets WebLinkPreviewTrigerer when the document detached as + // WebLinkPreviewInitiator depends on document. + is_link_preivew_triggerer_initialized_ = false; + link_preview_triggerer_.reset(); +} + void LocalFrame::SetPagePopupOwner(Element& owner) { page_popup_owner_ = &owner; } @@ -3884,4 +3892,32 @@ return print_params_; } +WebLinkPreviewTriggerer* LocalFrame::GetOrCreateLinkPreviewTriggerer() { + EnsureLinkPreviewTriggererInitialized(); + return link_preview_triggerer_.get(); +} + +void LocalFrame::EnsureLinkPreviewTriggererInitialized() { + if (is_link_preivew_triggerer_initialized_) { + return; + } + + CHECK(!link_preview_triggerer_); + + WebLocalFrameImpl* web_local_frame = WebLocalFrameImpl::FromFrame(this); + if (!web_local_frame) { + return; + } + + link_preview_triggerer_ = + web_local_frame->Client()->CreateLinkPreviewTriggerer(); + is_link_preivew_triggerer_initialized_ = true; +} + +void LocalFrame::SetLinkPreviewTriggererForTesting( + std::unique_ptr<WebLinkPreviewTriggerer> trigger) { + link_preview_triggerer_ = std::move(trigger); + is_link_preivew_triggerer_initialized_ = true; +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h index d3faff9..1f659eb 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -149,6 +149,7 @@ class Node; class NodeTraversal; class PerformanceMonitor; +class WebLinkPreviewTriggerer; class PluginData; class PolicyContainer; class ScrollSnapshotClient; @@ -289,6 +290,7 @@ void SetDOMWindow(LocalDOMWindow*); LocalFrameView* View() const override; Document* GetDocument() const; + void DocumentDetached(); void SetPagePopupOwner(Element&); Element* PagePopupOwner() const { return page_popup_owner_.Get(); } bool HasPagePopupOwner() const { return page_popup_owner_ != nullptr; } @@ -932,6 +934,10 @@ const WebPrintParams& GetPrintParams() const; + WebLinkPreviewTriggerer* GetOrCreateLinkPreviewTriggerer(); + void SetLinkPreviewTriggererForTesting( + std::unique_ptr<WebLinkPreviewTriggerer> trigger); + private: friend class FrameNavigationDisabler; // LocalFrameMojoHandler is a part of LocalFrame. @@ -1015,6 +1021,8 @@ void MaybeUpdateWindowControlsOverlayWithNewZoomLevel(); #endif + void EnsureLinkPreviewTriggererInitialized(); + std::unique_ptr<FrameScheduler> frame_scheduler_; // Holds all PauseSubresourceLoadingHandles allowing either |this| to delete @@ -1197,6 +1205,12 @@ feature_handle_for_scheduler_; WebPrintParams print_params_; + + // Holds WebLinkPreviewTriggerer instance if content renderer client wants to + // inject it. Note that `link_preview_triggerer_` may be nullptr after + // initialization. + bool is_link_preivew_triggerer_initialized_ = false; + std::unique_ptr<WebLinkPreviewTriggerer> link_preview_triggerer_; }; inline FrameLoader& LocalFrame::Loader() const {
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_client.cc b/third_party/blink/renderer/core/frame/web_local_frame_client.cc index 3adcd7c..bae55d3 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_client.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_client.cc
@@ -7,6 +7,7 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/renderer/platform/loader/fetch/url_loader/url_loader.h" namespace blink { @@ -31,4 +32,12 @@ return nullptr; } +std::unique_ptr<WebLinkPreviewTriggerer> +WebLocalFrameClient::CreateLinkPreviewTriggerer() { + return nullptr; +} + +void WebLocalFrameClient::SetLinkPreviewTriggererForTesting( + std::unique_ptr<WebLinkPreviewTriggerer> trigger) {} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc index b7fb563..d45816a 100644 --- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -648,24 +648,13 @@ int32_t HTMLFormControlElement::GetAxId() const { Document& document = GetDocument(); - if (!document.IsActive() || !document.View()) + if (!document.IsActive() || !document.View()) { return 0; - // TODO(accessibility) Simplify this once AXIDs use DOMNodeIds. At that - // point it will be safe to get the AXID at any time. - if (AXObjectCache* cache = document.ExistingAXObjectCache()) { - LocalFrameView* local_frame_view = document.View(); - if (local_frame_view->IsUpdatingLifecycle()) { - // Autofill (the caller of this code) can end up making calls to get AXIDs - // of form elements during, e.g. resize observer callbacks, which are - // in the middle up updating the document lifecycle. In these cases, just - // return the existing AXID of the element. - return cache->GetExistingAXID(const_cast<HTMLFormControlElement*>(this)); - } - - return cache->GetAXID(const_cast<HTMLFormControlElement*>(this)); } - - return 0; + // The AXId is the same as the DOM node id. + int32_t result = DOMNodeIds::ExistingIdForNode(this); + CHECK(result) << "May need to call GetDomNodeId() from a non-const function"; + return result; } } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc index e69d12b..bee4233 100644 --- a/third_party/blink/renderer/core/html/html_anchor_element.cc +++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -35,10 +35,12 @@ #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h" #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/events/keyboard_event.h" #include "third_party/blink/renderer/core/events/mouse_event.h" #include "third_party/blink/renderer/core/events/pointer_event.h" +#include "third_party/blink/renderer/core/events/web_input_event_conversion.h" #include "third_party/blink/renderer/core/frame/ad_tracker.h" #include "third_party/blink/renderer/core/frame/attribution_src_loader.h" #include "third_party/blink/renderer/core/frame/deprecation/deprecation.h" @@ -209,8 +211,33 @@ url.AppendNumber(clamped_point.y()); } +void EmitDidAnchorElementReceiveMouseEvent(HTMLAnchorElement& anchor_element, + Event& event) { + LocalFrame* local_frame = anchor_element.GetDocument().GetFrame(); + if (!local_frame) { + return; + } + + WebLinkPreviewTriggerer* triggerer = + local_frame->GetOrCreateLinkPreviewTriggerer(); + if (!triggerer) { + return; + } + + auto* mev = DynamicTo<MouseEvent>(event); + if (!mev) { + return; + } + + WebElement web_element = WebElement(DynamicTo<Element>(&anchor_element)); + WebMouseEventBuilder web_mouse_event(anchor_element.GetLayoutObject(), *mev); + triggerer->DidAnchorElementReceiveMouseEvent(web_element, web_mouse_event); +} + void HTMLAnchorElement::DefaultEventHandler(Event& event) { if (IsLink()) { + EmitDidAnchorElementReceiveMouseEvent(*this, event); + if (isConnected() && base::FeatureList::IsEnabled( features::kSpeculativeServiceWorkerWarmUp)) { Document& top_document = GetDocument().TopDocument();
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc index d29f7f63..4fdb000f 100644 --- a/third_party/blink/renderer/core/input/event_handler.cc +++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -38,6 +38,7 @@ #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h" #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h" #include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/renderer/core/clipboard/data_transfer.h" #include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -1058,6 +1059,13 @@ Page* page = frame_->GetPage(); if (page) page->GetChromeClient().ClearToolTip(*frame_); + + WebLinkPreviewTriggerer* triggerer = + frame_->GetOrCreateLinkPreviewTriggerer(); + if (triggerer) { + triggerer->MaybeChangedKeyEventModifier(WebInputEvent::kNoModifiers); + } + HandleMouseMoveOrLeaveEvent(event, Vector<WebMouseEvent>(), Vector<WebMouseEvent>()); pointer_event_manager_->RemoveLastMousePosition();
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.cc b/third_party/blink/renderer/core/input/keyboard_event_manager.cc index e4709d1..7d92e590 100644 --- a/third_party/blink/renderer/core/input/keyboard_event_manager.cc +++ b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h" #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/web/web_link_preview_triggerer.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h" #include "third_party/blink/renderer/core/dom/focus_params.h" @@ -209,6 +210,8 @@ if (initial_key_event.windows_key_code == VK_CAPITAL) CapsLockStateMayHaveChanged(); + KeyEventModifierMayHaveChanged(initial_key_event.GetModifiers()); + if (scroll_manager_->MiddleClickAutoscrollInProgress()) { DCHECK(RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()); // If a key is pressed while the middleClickAutoscroll is in progress then @@ -405,6 +408,16 @@ } } +void KeyboardEventManager::KeyEventModifierMayHaveChanged(int modifiers) { + WebLinkPreviewTriggerer* triggerer = + frame_->GetOrCreateLinkPreviewTriggerer(); + if (!triggerer) { + return; + } + + triggerer->MaybeChangedKeyEventModifier(modifiers); +} + void KeyboardEventManager::DefaultKeyboardEventHandler( KeyboardEvent* event, Node* possible_focused_node) {
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.h b/third_party/blink/renderer/core/input/keyboard_event_manager.h index 15d7f8e4..cb10de5 100644 --- a/third_party/blink/renderer/core/input/keyboard_event_manager.h +++ b/third_party/blink/renderer/core/input/keyboard_event_manager.h
@@ -58,6 +58,8 @@ bool is_handling_key_event() const { return is_handling_key_event_; } private: + void KeyEventModifierMayHaveChanged(int modifiers); + friend class Internals; // Allows overriding the current caps lock state for testing purposes. static void SetCurrentCapsLockState(OverrideCapsLockState);
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_agent.h b/third_party/blink/renderer/core/inspector/inspector_audits_agent.h index 892f854..18a8069aa 100644 --- a/third_party/blink/renderer/core/inspector/inspector_audits_agent.h +++ b/third_party/blink/renderer/core/inspector/inspector_audits_agent.h
@@ -12,14 +12,12 @@ #include "third_party/blink/renderer/core/inspector/inspector_contrast.h" #include "third_party/blink/renderer/core/inspector/protocol/audits.h" -namespace protocol { -namespace Audits { -class InspectorIssue; -} // namespace Audits -} // namespace protocol - namespace blink { +namespace protocol::Audits { +class InspectorIssue; +} // namespace protocol::Audits + class InspectorIssueStorage; class WebAutofillClient;
diff --git a/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc b/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc index 239acfe8..eddba51 100644 --- a/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc +++ b/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc
@@ -16,6 +16,7 @@ #include "third_party/blink/renderer/core/layout/inline/transformed_string.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_text.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h" #include "third_party/blink/renderer/platform/wtf/text/text_offset_map.h" @@ -525,17 +526,15 @@ AppendText(TransformedString(layout_text->TransformedText()), *layout_text); return; } - String original = layout_text->OriginalText(); - TextOffsetMap offset_map; - String transformed = - layout_text->TransformAndSecureText(original, offset_map); - if (layout_text->TransformedText().length() != transformed.length()) { - NOTREACHED() << "Mismatch; class=" << layout_text->GetName() - << " stored=" << layout_text->TransformedText() - << " live=" << transformed; - } + // Do not use LayoutText::OriginalText() here. This code is used when + // OriginalText() was updated but TransformedText() is not updated yet, and we + // need to use TransformedText() in that case. It is required to make + // InlineNode::SetTextWithOffset() workable. + auto [original_length, offset_map] = + layout_text->GetVariableLengthTransformResult(); + String transformed = layout_text->TransformedText(); const Vector<unsigned> length_map = TransformedString::CreateLengthMap( - original.length(), transformed.length(), offset_map); + original_length, transformed.length(), offset_map); CHECK(transformed.length() == length_map.size() || length_map.size() == 0); AppendText( TransformedString(transformed, {length_map.data(), length_map.size()}),
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node.cc b/third_party/blink/renderer/core/layout/inline/inline_node.cc index 2f23b88..895e242 100644 --- a/third_party/blink/renderer/core/layout/inline/inline_node.cc +++ b/third_party/blink/renderer/core/layout/inline/inline_node.cc
@@ -934,7 +934,7 @@ return false; } layout_text->SetTextInternal(new_text); - layout_text->SetHasVariableLengthTransform(false); + layout_text->ClearHasVariableLengthTransform(); InlineNode node(editor.GetLayoutBlockFlow()); InlineNodeData* data = node.MutableData();
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node_test.cc b/third_party/blink/renderer/core/layout/inline/inline_node_test.cc index 0ace3fd..d69d6be 100644 --- a/third_party/blink/renderer/core/layout/inline/inline_node_test.cc +++ b/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
@@ -672,6 +672,20 @@ EXPECT_FALSE(next->GetLayoutObject()->NeedsCollectInlines()); } +// crbug.com/325306591 +// We had a crash in OffsetMapping building during SetTextWithOffset(). +TEST_F(InlineNodeTest, SetTextWithOffsetWithTextTransform) { + SetBodyInnerHTML(R"HTML( + <div id="container" style="text-transform:uppercase">ßX</div>)HTML"); + + Element* container = GetElementById("container"); + auto* text = To<Text>(container->firstChild()); + + text->deleteData(1, 1, ASSERT_NO_EXCEPTION); + UpdateAllLifecyclePhasesForTest(); + // Pass if no crash in InlineItemsBuilder. +} + struct StyleChangeData { const char* css; enum ChangedElements {
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index 789acc94..7925c6cc 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -222,6 +222,36 @@ ElementAnimations::CompositedPaintStatus::kComposited; } +// If there's a composited clip path animation, and it doesn't have a cached +// value for CompositedClipPathStatus, that means we need to regenerate the +// paint properties, as the composited clip path status is calculated then. See +// HasCompositeClipPathAnimation in clip_path_clipper.cc +bool ShouldRefreshPaintPropertiesForClipPath(Node* node, + const ComputedStyle* style) { + if (!RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled()) { + return false; + } + + // We don't care what the composited clip path status is if there's no + // composited clip path animation. + if (!style->HasCurrentClipPathAnimation()) { + return false; + } + + Element* element = DynamicTo<Element>(node); + if (!element) { + return false; + } + + ElementAnimations* element_animations = element->GetElementAnimations(); + if (!element_animations) { + return false; + } + + return element_animations->CompositedClipPathStatus() == + ElementAnimations::CompositedPaintStatus::kNeedsRepaintOrNoAnimation; +} + StyleDifference AdjustForCompositableAnimationPaint( const ComputedStyle* old_style, const ComputedStyle* new_style, @@ -2784,10 +2814,9 @@ // Clip Path animations need a property update when they're composited, as it // changes between mask based and path based clip. - if (diff.NeedsNormalPaintInvalidation() && old_style && - (!old_style->ClipPathDataEquivalent(*style_) || - (old_style->HasCurrentClipPathAnimation() && - !style_->HasCurrentClipPathAnimation()))) { + if ((diff.NeedsNormalPaintInvalidation() && old_style && + !old_style->ClipPathDataEquivalent(*style_)) || + ShouldRefreshPaintPropertiesForClipPath(GetNode(), style_)) { SetNeedsPaintPropertyUpdate(); PaintingLayer()->SetNeedsCompositingInputsUpdate(); }
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc index 8610bb0..d0609c5 100644 --- a/third_party/blink/renderer/core/layout/layout_text.cc +++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -989,6 +989,31 @@ return std::make_pair(masked, TextOffsetMap()); } +void LayoutText::SetVariableLengthTransformResult( + wtf_size_t original_length, + const TextOffsetMap& offset_map) { + if (offset_map.IsEmpty()) { + ClearHasVariableLengthTransform(); + return; + } + has_variable_length_transform_ = true; + View()->RegisterVariableLengthTransformResult(*this, + {original_length, offset_map}); +} + +VariableLengthTransformResult LayoutText::GetVariableLengthTransformResult() + const { + return View()->GetVariableLengthTransformResult(*this); +} + +void LayoutText::ClearHasVariableLengthTransform() { + NOT_DESTROYED(); + if (has_variable_length_transform_) { + View()->UnregisterVariableLengthTransformResult(*this); + } + has_variable_length_transform_ = false; +} + void LayoutText::SetTextIfNeeded(String text) { NOT_DESTROYED(); DCHECK(text); @@ -1037,8 +1062,9 @@ void LayoutText::TextDidChangeWithoutInvalidation() { NOT_DESTROYED(); TextOffsetMap offset_map; + wtf_size_t original_length = text_.length(); text_ = TransformAndSecureText(text_, offset_map); - has_variable_length_transform_ = !offset_map.IsEmpty(); + SetVariableLengthTransformResult(original_length, offset_map); if (auto* secure_text_timer = SecureTextTimer::ActiveInstanceFor(this)) { // text_ may be updated later before timer fires. We invalidate the // last_typed_character_offset_ to avoid inconsistency.
diff --git a/third_party/blink/renderer/core/layout/layout_text.h b/third_party/blink/renderer/core/layout/layout_text.h index bef8349..d7012d7f 100644 --- a/third_party/blink/renderer/core/layout/layout_text.h +++ b/third_party/blink/renderer/core/layout/layout_text.h
@@ -45,6 +45,7 @@ struct InlineItemsData; struct InlineItemSpan; struct TextDiffRange; +struct VariableLengthTransformResult; // LayoutText is the root class for anything that represents // a text node (see core/dom/text.h). @@ -134,10 +135,8 @@ NOT_DESTROYED(); return has_variable_length_transform_; } - void SetHasVariableLengthTransform(bool flag) { - NOT_DESTROYED(); - has_variable_length_transform_ = flag; - } + VariableLengthTransformResult GetVariableLengthTransformResult() const; + void ClearHasVariableLengthTransform(); // Returns first letter part of |LayoutTextFragment|. virtual LayoutText* GetFirstLetterPart() const { @@ -403,6 +402,8 @@ std::pair<String, TextOffsetMap> SecureText(const String& plain, UChar mask) const; + void SetVariableLengthTransformResult(wtf_size_t original_length, + const TextOffsetMap& offset_map); bool IsText() const final { NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc index 803df05..ab56812 100644 --- a/third_party/blink/renderer/core/layout/layout_view.cc +++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -141,6 +141,7 @@ void LayoutView::Trace(Visitor* visitor) const { visitor->Trace(frame_view_); visitor->Trace(svg_text_descendants_); + visitor->Trace(text_to_variable_length_transform_result_); visitor->Trace(hit_test_cache_); visitor->Trace(initial_containing_block_resize_handled_list_); LayoutNGBlockFlow::Trace(visitor); @@ -414,6 +415,24 @@ return *svg_text_descendants_; } +void LayoutView::RegisterVariableLengthTransformResult( + const LayoutText& text, + const VariableLengthTransformResult& result) { + CHECK(text.HasVariableLengthTransform()); + text_to_variable_length_transform_result_.Set(&text, result); +} + +void LayoutView::UnregisterVariableLengthTransformResult( + const LayoutText& text) { + text_to_variable_length_transform_result_.erase(&text); +} + +VariableLengthTransformResult LayoutView::GetVariableLengthTransformResult( + const LayoutText& text) { + CHECK(text.HasVariableLengthTransform()); + return text_to_variable_length_transform_result_.at(&text); +} + LayoutViewTransitionRoot* LayoutView::GetViewTransitionRoot() const { // Returns nullptr if LastChild isn't a ViewTransitionRoot. return DynamicTo<LayoutViewTransitionRoot>(LastChild());
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h index 2f4fdb38..359ae58 100644 --- a/third_party/blink/renderer/core/layout/layout_view.h +++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -33,15 +33,22 @@ #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/wtf/casting.h" +#include "third_party/blink/renderer/platform/wtf/text/text_offset_map.h" namespace blink { class HitTestCache; class HitTestLocation; class HitTestResult; +class LayoutText; class LayoutViewTransitionRoot; class LocalFrameView; +struct VariableLengthTransformResult { + wtf_size_t original_length; + TextOffsetMap offset_map; +}; + // LayoutView is the root of the layout tree and the Document's LayoutObject. // // It corresponds to the CSS concept of 'initial containing block' (or ICB). @@ -334,6 +341,14 @@ TrackedDescendantsMap& SvgTextDescendantsMap(); + // Manage rare data of LayoutText. + void RegisterVariableLengthTransformResult( + const LayoutText& text, + const VariableLengthTransformResult& result); + void UnregisterVariableLengthTransformResult(const LayoutText& text); + VariableLengthTransformResult GetVariableLengthTransformResult( + const LayoutText& text); + LayoutViewTransitionRoot* GetViewTransitionRoot() const; private: @@ -387,6 +402,9 @@ // computed with ancestor transforms. Member<TrackedDescendantsMap> svg_text_descendants_; + HeapHashMap<WeakMember<const LayoutText>, VariableLengthTransformResult> + text_to_variable_length_transform_result_; + unsigned hit_test_count_; unsigned hit_test_cache_hits_; Member<HitTestCache> hit_test_cache_;
diff --git a/third_party/blink/renderer/core/loader/navigation_policy.cc b/third_party/blink/renderer/core/loader/navigation_policy.cc index 01e86be..90f6f32 100644 --- a/third_party/blink/renderer/core/loader/navigation_policy.cc +++ b/third_party/blink/renderer/core/loader/navigation_policy.cc
@@ -140,8 +140,8 @@ NavigationPolicy NavigationPolicyFromEvent(const Event* event) { // TODO(b:298160400): Add a setting to disable Link Preview. - bool is_link_preview_enabled = - base::FeatureList::IsEnabled(features::kLinkPreview); + bool is_link_preview_enabled = IsLinkPreviewTriggerTypeEnabled( + features::LinkPreviewTriggerType::kAltClick); NavigationPolicy event_policy = NavigationPolicyFromEventInternal(event, is_link_preview_enabled);
diff --git a/third_party/blink/renderer/core/loader/navigation_policy_test.cc b/third_party/blink/renderer/core/loader/navigation_policy_test.cc index ffff6dbc..5cf24b39 100644 --- a/third_party/blink/renderer/core/loader/navigation_policy_test.cc +++ b/third_party/blink/renderer/core/loader/navigation_policy_test.cc
@@ -104,7 +104,8 @@ class NavigationPolicyWithLinkPreviewEnabledTest : public NavigationPolicyTest { protected: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature(features::kLinkPreview); + scoped_feature_list_.InitAndEnableFeatureWithParameters( + features::kLinkPreview, {{"trigger_type", "alt_click"}}); } };
diff --git a/third_party/blink/renderer/core/svg/animation/smil_time_container.cc b/third_party/blink/renderer/core/svg/animation/smil_time_container.cc index 8c63166..3244dba4 100644 --- a/third_party/blink/renderer/core/svg/animation/smil_time_container.cc +++ b/third_party/blink/renderer/core/svg/animation/smil_time_container.cc
@@ -31,6 +31,7 @@ #include "third_party/blink/renderer/core/animation/document_timeline.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/element_traversal.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/frame/web_feature.h" @@ -635,6 +636,27 @@ } } +namespace { + +bool CanThrottleTarget(const SVGElement& target) { + // Don't throttle if the target is in the layout tree. + if (target.GetLayoutObject()) { + return false; + } + // Don't throttle if the target has computed style (for example <stop> + // elements). + if (ComputedStyle::NullifyEnsured(target.GetComputedStyle())) { + return false; + } + // Don't throttle if the target has use instances. + if (!target.InstancesForElement().empty()) { + return false; + } + return true; +} + +} // namespace + bool SMILTimeContainer::ApplyTimedEffects(SMILTime elapsed) { if (document_order_indexes_dirty_) UpdateDocumentOrderIndexes(); @@ -647,8 +669,7 @@ if (animations && animations->Apply(elapsed)) { did_apply_effects = true; - if (!disable_throttling && (entry.key->GetLayoutObject() || - !entry.key->InstancesForElement().empty())) { + if (!disable_throttling && !CanThrottleTarget(*entry.key)) { disable_throttling = true; } }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index 21cc8fa9..8d23f1a 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -7876,8 +7876,6 @@ string_builder = string_builder + " axid#" + String::Number(AXObjectID()); // Add useful HTML element info, like <div.myClass#myId>. if (GetNode()) { - string_builder = - string_builder + " node#" + String::Number(GetNode()->GetDomNodeId()); string_builder = string_builder + " " + GetNodeString(GetNode()); if (IsRoot()) { string_builder = string_builder + " isRoot";
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index 7580ffd..9d1c260 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1008,12 +1008,15 @@ } #endif - auto iter = node_object_mapping_.find(node); - if (iter == node_object_mapping_.end()) { + AXID node_id = static_cast<AXID>(DOMNodeIds::ExistingIdForNode(node)); + if (!node_id) { + // An ID hasn't yet been generated for this DOM node, but ::CreateAndInit() + // will ensure a DOMNodeID is generated by using node->GetDomNodeId(). + // Therefore if an id doesn't exist for a DOM node, it means that it can't + // have an associated AXObject. return nullptr; } - AXID node_id = iter->value; auto it_result = objects_.find(node_id); if (it_result == objects_.end()) { return nullptr; @@ -1041,9 +1044,9 @@ auto it_ax = inline_text_box_object_mapping_.find(inline_text_box); AXID ax_id = it_ax != inline_text_box_object_mapping_.end() ? it_ax->value : 0; - DCHECK(!WTF::IsHashTraitsDeletedValue<HashTraits<AXID>>(ax_id)); if (!ax_id) return nullptr; + DCHECK(!WTF::IsHashTraitsEmptyOrDeletedValue<HashTraits<AXID>>(ax_id)); auto it_result = objects_.find(ax_id); AXObject* result = it_result != objects_.end() ? it_result->value : nullptr; @@ -1058,22 +1061,6 @@ return result; } -AXID AXObjectCacheImpl::GetAXID(Node* node) { - AXID existing_axid = GetExistingAXID(node); - if (existing_axid != ui::AXNodeData::kInvalidAXID) { - return existing_axid; - } - UpdateAXForAllDocuments(); - return GetExistingAXID(node); -} - -AXID AXObjectCacheImpl::GetExistingAXID(Node* node) { - AXObject* ax_object = Get(node); - if (!ax_object) - return ui::AXNodeData::kInvalidAXID; - return ax_object->AXObjectID(); -} - AXObject* AXObjectCacheImpl::Get(AccessibleNode* accessible_node) { if (!accessible_node) return nullptr; @@ -1090,9 +1077,9 @@ auto it_ax = accessible_node_mapping_.find(accessible_node); AXID ax_id = it_ax != accessible_node_mapping_.end() ? it_ax->value : 0; - DCHECK(!WTF::IsHashTraitsDeletedValue<HashTraits<AXID>>(ax_id)); if (!ax_id) return nullptr; + DCHECK(!WTF::IsHashTraitsEmptyOrDeletedValue<HashTraits<AXID>>(ax_id)); auto it_result = objects_.find(ax_id); AXObject* result = it_result != objects_.end() ? it_result->value : nullptr; @@ -1587,24 +1574,31 @@ return nullptr; } - AXID axid = GenerateAXID(); - DCHECK(!base::Contains(objects_, axid)); - + // If there is a DOM node, use its dom_node_id, otherwise, generate an AXID. + // The dom_node_id can be used even if there is also a layout object. + AXID axid; if (node) { - DCHECK(!node_object_mapping_.Contains(node)) - << "Already have an AXObject for " << node; - node_object_mapping_.Set(node, axid); + axid = static_cast<AXID>(node->GetDomNodeId()); + if (ax_tree_serializer_) { + // In the case where axid is being reused, because a previous AXObject + // existed for the same node, ensure that the serializer sees it as new. + ax_tree_serializer_->MarkNodeDirty(axid); + } } else { - DCHECK(!layout_object_mapping_.Contains(layout_object)) - << "Already have an AXObject for " << layout_object; - layout_object_mapping_.Set(layout_object, axid); + axid = GenerateAXID(); } + DCHECK(!base::Contains(objects_, axid)); // Create the new AXObject. AXObject* new_obj = nullptr; if (ax_type == kAXLayoutObject) { // Prefer to create from renderer if there is a layout object because // AXLayoutObjects can provide information about bounding boxes. + if (!node) { + DCHECK(!layout_object_mapping_.Contains(layout_object)) + << "Already have an AXObject for " << layout_object; + layout_object_mapping_.Set(layout_object, axid); + } new_obj = CreateFromRenderer(layout_object); } else { new_obj = CreateFromNode(node); @@ -1714,7 +1708,7 @@ AXObject* new_obj = CreateFromInlineTextBox(inline_text_box); - const AXID axid = AssociateAXID(new_obj); + AXID axid = AssociateAXID(new_obj); inline_text_box_object_mapping_.Set(inline_text_box, axid); new_obj->Init(parent); @@ -1797,10 +1791,12 @@ } } - obj->Detach(); - + // Remove references to AXID before detaching, so that nothing will retrieve a + // detached object, which is illegal. RemoveReferencesToAXID(ax_id); + obj->Detach(); + // Remove the object. // TODO(accessibility) We don't use the return value, can we use .erase() // and it will still make sure that the object is cleaned up? @@ -1896,26 +1892,21 @@ } void AXObjectCacheImpl::Remove(Node* node, bool notify_parent) { - if (!node) - return; + DCHECK(node); + LayoutObject* layout_object = node->GetLayoutObject(); + DCHECK(!layout_object || layout_object_mapping_.find(layout_object) == + layout_object_mapping_.end()) + << "AXObject cannot be backed by both a layout object and node."; - whitespace_ignored_map_.erase(node->GetDomNodeId()); + AXID axid = node->GetDomNodeId(); + whitespace_ignored_map_.erase(axid); if (node == active_aria_modal_dialog_) { UpdateActiveAriaModalDialog(FocusedNode()); } - auto iter = node_object_mapping_.find(node); - if (iter != node_object_mapping_.end()) { - LayoutObject* layout_object = node->GetLayoutObject(); - DCHECK(!layout_object || layout_object_mapping_.find(layout_object) == - layout_object_mapping_.end()) - << "AXObject cannot be backed by both a layout object and node."; - AXID ax_id = iter->value; - DCHECK(ax_id); - node_object_mapping_.erase(iter); - Remove(ax_id, notify_parent); - } + DCHECK_GE(axid, 1); + Remove(axid, notify_parent); } void AXObjectCacheImpl::RemovePopup(Document* popup_document) { @@ -2087,15 +2078,30 @@ } } +// All generated AXIDs are negative, ranging from kFirstGeneratedRendererNodeID +// to kLastGeneratedRendererNodeID, in order to avoid conflict with the ids +// reused from dom_node_ids, which are positive, and generated IDs on the +// browser side, which are negative, starting at -1. AXID AXObjectCacheImpl::GenerateAXID() const { - static AXID last_used_id = 0; + // The first id is close to INT_MIN/2, leaving plenty of room for negative + // generated IDs both here and on the browser side, but starting at an even + // number makes it easier to read when debugging. + static AXID last_used_id = ui::kFirstGeneratedRendererNodeID; // Generate a new ID. AXID obj_id = last_used_id; do { - ++obj_id; - } while (!obj_id || WTF::IsHashTraitsDeletedValue<HashTraits<AXID>>(obj_id) || - objects_.Contains(obj_id)); + if (--obj_id == ui::kLastGeneratedRendererNodeID) { + // This is very unlikely to happen, but if we find that it happens, we + // could gracefully turn off a11y instead of crashing the renderer. + CHECK(!has_axid_generator_looped_) + << "Not enough room more generated accessibility objects."; + has_axid_generator_looped_ = true; + obj_id = ui::kFirstGeneratedRendererNodeID; + } + } while (has_axid_generator_looped_ && objects_.Contains(obj_id)); + + DCHECK(!WTF::IsHashTraitsEmptyOrDeletedValue<HashTraits<AXID>>(obj_id)); last_used_id = obj_id; @@ -2112,7 +2118,12 @@ // Check for already-assigned ID. DCHECK(!obj->AXObjectID()) << "Object should not already have an AXID"; - const AXID new_axid = use_axid ? use_axid : GenerateAXID(); + AXID new_axid = use_axid ? use_axid : GenerateAXID(); + + bool should_have_node_id = obj->IsAXNodeObject() && obj->GetNode(); + DCHECK_EQ(should_have_node_id, IsDOMNodeID(new_axid)) + << "An AXID is also a DOMNodeID (positive integer) if any only if the " + "AXObject is an AXNodeObject with a DOM node."; obj->SetAXObjectID(new_axid); objects_.Set(new_axid, obj); @@ -2125,15 +2136,28 @@ // Clear AXIDs from maps. Note: do not need to erase id from // changed_bounds_ids_, a set which is cleared each time - // SerializeLocationChanges() is finished. - autofill_suggestion_availability_map_.erase(obj_id); - fixed_or_sticky_node_ids_.erase(obj_id); - cached_bounding_boxes_.erase(obj_id); - computed_node_mapping_.erase(obj_id); + // SerializeLocationChanges() is finished. Also, do not need to erase id from + // invalidated_ids_main_ or invalidated_ids_popup_, which are cleared each + // time ProcessInvalidatedObjects() finishes, and having extra ids in those + // sets is not harmful. - // Clear id from relation cache. - if (relation_cache_) { - relation_cache_->RemoveAXID(obj_id); + cached_bounding_boxes_.erase(obj_id); + + if (IsDOMNodeID(obj_id)) { + // Optimization: these maps only contain ids for AXObjects with a DOM node. + fixed_or_sticky_node_ids_.erase(obj_id); + // Only objects with a DOM node can be in the relation cache. + if (relation_cache_) { + relation_cache_->RemoveAXID(obj_id); + } + // Allow the new AXObject for the same node to be serialized correctly. + nodes_with_pending_children_changed_.erase(obj_id); + computed_node_mapping_.erase(obj_id); + } else { + // Non-DOM ids should never find their way into these maps. + DCHECK(!fixed_or_sticky_node_ids_.Contains(obj_id)); + DCHECK(!computed_node_mapping_.Contains(obj_id)); + DCHECK(!nodes_with_pending_children_changed_.Contains(obj_id)); } } @@ -2336,8 +2360,11 @@ return; // A text changed event is redundant with children changed on the same node. - if (base::Contains(nodes_with_pending_children_changed_, node)) { - return; + if (AXID node_id = static_cast<AXID>(node->GetDomNodeId())) { + if (nodes_with_pending_children_changed_.find(node_id) != + nodes_with_pending_children_changed_.end()) { + return; + } } DeferTreeUpdate(TreeUpdateReason::kTextChangedOnNode, node); @@ -2365,9 +2392,11 @@ // If the text changed in a pseudo element, rebuild the entire subtree. if (node->IsPseudoElement()) { RemoveAXObjectsInLayoutSubtree(node->GetLayoutObject()); - } else if (base::Contains(nodes_with_pending_children_changed_, node)) { + } else if (AXID node_id = static_cast<AXID>(node->GetDomNodeId())) { // Text changed is redundant with children changed on the same node. - return; + if (base::Contains(nodes_with_pending_children_changed_, node_id)) { + return; + } } DeferTreeUpdate(TreeUpdateReason::kTextChangedOnClosestNodeForLayoutObject, @@ -2581,7 +2610,8 @@ } void AXObjectCacheImpl::NodeIsAttached(Node* node) { - DCHECK(node); + CHECK(node); + CHECK(node->isConnected()); SCOPED_DISALLOW_LIFECYCLE_TRANSITION(); // It normally is not necessary to process text nodes here, because we'll @@ -2657,8 +2687,27 @@ } AXObject* obj = Get(node); + CHECK(obj); + CHECK(obj->CachedParentObject()); + MaybeNewRelationTarget(*node, obj); + // If there is a previous AXObject, it is being reattached with a new + // LayoutObject, in which case we should ensureinvalidation of its subtree. + // TODO(accessibility): Try to remove this by finding the specific situations + // where it is necessary and handling proactively for those. + NotifySubtreeDirty(obj); + + // Even if the node or parent are ignored, an ancestor may need to include + // descendants of the attached node, thus ChildrenChangedWithCleanLayout() + // must be called. It handles ignored logic, ensuring that the first ancestor + // that should have this as a child will be updated. + ChildrenChangedWithCleanLayout(obj->CachedParentObject()); + + if (IsA<HTMLAreaElement>(node)) { + ChildrenChangedWithCleanLayout(obj); + } + // Rare edge case: if an image is added, it could have changed the order of // images with the same usemap in the document. Only the first image for a // given <map> should have the <area> children. Therefore, get the current @@ -2730,7 +2779,7 @@ if (AXObject* ax_ancestor_for_notification = InvalidateChildren(obj)) { if (ax_ancestor_for_notification->GetNode() && nodes_with_pending_children_changed_.Contains( - ax_ancestor_for_notification->GetNode())) { + ax_ancestor_for_notification->GetNode()->GetDomNodeId())) { return; } ChildrenChangedWithCleanLayout(ax_ancestor_for_notification->GetNode(), @@ -2748,7 +2797,7 @@ CHECK(!IsFrozen()); if (ax_ancestor_for_notification->GetNode() && !nodes_with_pending_children_changed_ - .insert(ax_ancestor_for_notification->GetNode()) + .insert(ax_ancestor_for_notification->GetNode()->GetDomNodeId()) .is_new_entry) { return nullptr; } @@ -4105,18 +4154,20 @@ if (AXObject* obj = GetOrCreate(node)) { ChildrenChangedOnAncestorOf(obj); - // When the role of `obj` is changed, its AXObject needs to be destroyed and - // a new one needs to be created in its place. - if (RolePresentationPropagates(node)) { - // If role changes on a table, menu, or list invalidate the subtree of - // objects that may require a specific parent role in order to keep their - // role. For example, rows and cells require a table ancestor, and list - // items require a parent list (must be direct DOM parent). - RemoveSubtreeWithFlatTraversal(node, /* remove_root */ true, - /* notify_parent */ false); - } else { - // The children of this thing need to detach from parent. - Remove(obj, /* notify_parent */ false); + if (!obj->IsDetached()) { + // When the role of `obj` is changed, its AXObject needs to be destroyed + // and a new one needs to be created in its place. + if (RolePresentationPropagates(node)) { + // If role changes on a table, menu, or list invalidate the subtree of + // objects that may require a specific parent role in order to keep + // their role. For example, rows and cells require a table ancestor, and + // list items require a parent list (must be direct DOM parent). + RemoveSubtreeWithFlatTraversal(node, /* remove_root */ true, + /* notify_parent */ false); + } else { + // The children of this thing need to detach from parent. + Remove(obj, /* notify_parent */ false); + } } // Calling GetOrCreate(node) will not only create a new object with the @@ -4126,6 +4177,10 @@ if (AXObject* new_object = GetOrCreate(node)) { relation_cache_->UpdateAriaOwnsWithCleanLayout(new_object, true); new_object->UpdateChildrenIfNecessary(); + // Need to mark dirty because the dom_node_id-based ID remains the same, + // and therefore the serializer may not automatically serialize this node + // from the children changed on the parent. + MarkAXSubtreeDirtyWithCleanLayout(new_object); } } } @@ -5489,10 +5544,13 @@ !marker_controller.MarkersFor(*text_node, spelling_and_grammar_markers) .empty(); if (has_spelling_or_grammar_markers) { - if (nodes_with_spelling_or_grammar_markers_.insert(node).is_new_entry) + if (nodes_with_spelling_or_grammar_markers_.insert(node->GetDomNodeId()) + .is_new_entry) { ChildrenChangedWithCleanLayout(node); + } } else { - const auto& iter = nodes_with_spelling_or_grammar_markers_.find(node); + const auto& iter = + nodes_with_spelling_or_grammar_markers_.find(node->GetDomNodeId()); if (iter != nodes_with_spelling_or_grammar_markers_.end()) { nodes_with_spelling_or_grammar_markers_.erase(iter); ChildrenChangedWithCleanLayout(node); @@ -5795,7 +5853,6 @@ visitor->Trace(last_selected_from_active_descendant_); visitor->Trace(accessible_node_mapping_); visitor->Trace(layout_object_mapping_); - visitor->Trace(node_object_mapping_); visitor->Trace(inline_text_box_object_mapping_); visitor->Trace(active_aria_modal_dialog_); @@ -5806,8 +5863,6 @@ visitor->Trace(permission_observer_receiver_); visitor->Trace(tree_update_callback_queue_main_); visitor->Trace(tree_update_callback_queue_popup_); - visitor->Trace(nodes_with_pending_children_changed_); - visitor->Trace(nodes_with_spelling_or_grammar_markers_); visitor->Trace(nodes_for_subtree_removal_); visitor->Trace(render_accessibility_host_); visitor->Trace(ax_tree_source_);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h index df6bce4..478956d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -74,15 +74,10 @@ class HTMLAreaElement; class WebLocalFrameClient; -// Describes a decicion on whether to create an AXNodeObject, an AXLayoutObject, +// Describes a decision on whether to create an AXNodeObject, an AXLayoutObject, // or nothing (which will cause the AX subtree to be pruned at that point). -// Currently this also mirrors the decision on whether to back the object by a -// node or a layout object. When the AXObject is backed by a node, it's -// AXID can be looked up in node_object_mapping_, and when the AXObject is -// backed by layout, it's AXID can be looked up in layout_object_mapping_. -// TODO(accessibility) Split the decision of what to use for backing from what -// type of object to create, and use a node whenever possible, in order to -// enable more stable IDs for most objects. +// Not that AXLayoutObjects may be backed by a node, if it has one, and most do. +// Only pseudo element descendants are missing DOM nodes. enum AXObjectType { kPruneSubtree = 0, kAXNodeObject, kAXLayoutObject }; struct TextChangedOperation { @@ -358,10 +353,6 @@ // the AXObject for |child|. AXObject* RepairChildrenOfIncludedParent(Node* child); - AXID GetAXID(Node*) override; - - AXID GetExistingAXID(Node*) override; - // Return an AXObject for the AccessibleNode. If the AccessibleNode is // attached to an element, will return the AXObject for that element instead. AXObject* Get(AccessibleNode*); @@ -699,7 +690,6 @@ // Helpers for CreateAndInit(). AXObject* CreateFromRenderer(LayoutObject*); AXObject* CreateFromNode(Node*); - AXObject* CreateFromInlineTextBox(AbstractInlineTextBox*); // Removes AXObject backed by passed-in object, if there is one. @@ -730,6 +720,10 @@ bool IsPopupDocumentDirty() const; void ProcessSubtreeRemoval(Node*, bool remove_root); + // Returns true if the AXID is for a DOM node. + // All other AXIDs are generated. + bool IsDOMNodeID(AXID axid) { return axid > 0; } + HeapHashSet<WeakMember<InspectorAccessibilityAgent>> agents_; struct AXEventParams final : public GarbageCollected<AXEventParams> { @@ -872,10 +866,14 @@ ui::AXMode ax_mode_; + // AXIDs for AXNodeObjects reuse the int ids in dom_node_id, all other AXIDs + // are negative in order to avoid a conflict. HeapHashMap<AXID, Member<AXObject>> objects_; HeapHashMap<Member<AccessibleNode>, AXID> accessible_node_mapping_; + // When the AXObject is backed by layout, its AXID can be looked up in + // layout_object_mapping_. When the AXObject is backed by a node, its + // AXID can be looked up via node->GetDomNodeId(). HeapHashMap<Member<const LayoutObject>, AXID> layout_object_mapping_; - HeapHashMap<Member<const Node>, AXID> node_object_mapping_; HeapHashMap<Member<AbstractInlineTextBox>, AXID> inline_text_box_object_mapping_; #if DCHECK_IS_ON() @@ -1067,11 +1065,11 @@ TreeUpdateCallbackQueue tree_update_callback_queue_popup_; // Help de-dupe processing of repetitive events. - HeapHashSet<WeakMember<Node>> nodes_with_pending_children_changed_; + HashSet<AXID> nodes_with_pending_children_changed_; HashSet<AXID> nodes_with_pending_location_changed_; // Nodes with document markers that have received accessibility updates. - HeapHashSet<WeakMember<Node>> nodes_with_spelling_or_grammar_markers_; + HashSet<AXID> nodes_with_spelling_or_grammar_markers_; // Nodes renoved from flat tree. HeapVector<std::pair<Member<Node>, bool>> nodes_for_subtree_removal_; @@ -1172,6 +1170,8 @@ // Make sure the next serialization sends everything. bool mark_all_dirty_ = false; + mutable bool has_axid_generator_looped_ = false; + FRIEND_TEST_ALL_PREFIXES(AccessibilityTest, PauseUpdatesAfterMaxNumberQueued); FRIEND_TEST_ALL_PREFIXES(AccessibilityTest, UpdateAXForAllDocumentsAfterPausedUpdates);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc index 7040512..ad1f51a 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -1046,7 +1046,7 @@ ASSERT_NE(nullptr, timeline_node); AXObjectCache* cache = timeline_node->GetDocument().ExistingAXObjectCache(); ASSERT_NE(nullptr, cache); - AXObject* video_slider = cache->ObjectFromAXID(cache->GetAXID(timeline_node)); + AXObject* video_slider = cache->ObjectFromAXID(timeline_node->GetDomNodeId()); ASSERT_NE(nullptr, video_slider); ASSERT_EQ(video_slider->RoleValue(), ax::mojom::blink::Role::kSlider);
diff --git a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition_test.cc b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition_test.cc index af939de..6ba3106 100644 --- a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition_test.cc +++ b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition_test.cc
@@ -57,7 +57,7 @@ } }; -// Test the case where there is a background-color animation with two simple +// Test the case where there is a clip-path animation with two simple // keyframes that will not fall back to main. TEST_F(ClipPathPaintDefinitionTest, SimpleClipPathAnimationNotFallback) { ScopedCompositeClipPathAnimationForTest composite_clip_path_animation(true); @@ -111,6 +111,93 @@ animation); } +// Test the case where a 2nd composited clip path animation causes a fallback to +// the main thread. In this case, the paint properties should update to avoid +// any crashes or paint worklets existing beyond their validity. +TEST_F(ClipPathPaintDefinitionTest, FallbackOnNonCompositableSecondAnimation) { + ScopedCompositeClipPathAnimationForTest composite_clip_path_animation(true); + SetBodyInnerHTML(R"HTML( + <div id ="target" style="width: 100px; height: 100px"> + </div> + )HTML"); + + Timing timing; + timing.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(30); + + CSSPropertyID property_id = CSSPropertyID::kClipPath; + Persistent<StringKeyframe> start_keyframe = + MakeGarbageCollected<StringKeyframe>(); + start_keyframe->SetCSSPropertyValue(property_id, "circle(50% at 50% 50%)", + SecureContextMode::kInsecureContext, + nullptr); + Persistent<StringKeyframe> end_keyframe = + MakeGarbageCollected<StringKeyframe>(); + end_keyframe->SetCSSPropertyValue(property_id, "circle(30% at 30% 30%)", + SecureContextMode::kInsecureContext, + nullptr); + + StringKeyframeVector keyframes; + keyframes.push_back(start_keyframe); + keyframes.push_back(end_keyframe); + + auto* model = MakeGarbageCollected<StringKeyframeEffectModel>(keyframes); + model->SetComposite(EffectModel::kCompositeReplace); + + Element* element = GetElementById("target"); + LayoutObject* lo = element->GetLayoutObject(); + NonThrowableExceptionState exception_state; + DocumentTimeline* timeline = + MakeGarbageCollected<DocumentTimeline>(&GetDocument()); + Animation* animation = Animation::Create( + MakeGarbageCollected<KeyframeEffect>(element, model, timing), timeline, + exception_state); + animation->play(); + + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); + EXPECT_TRUE(lo->NeedsPaintPropertyUpdate()); + UpdateAllLifecyclePhasesForTest(); + + // After adding a single animation, all should be well. + EXPECT_TRUE(lo->FirstFragment().PaintProperties()->ClipPathMask()); + EXPECT_TRUE(element->GetElementAnimations()); + EXPECT_EQ(element->GetElementAnimations()->CompositedClipPathStatus(), + CompositedPaintStatus::kComposited); + EXPECT_EQ(element->GetElementAnimations()->Animations().size(), 1u); + EXPECT_EQ(ClipPathPaintDefinition::GetAnimationIfCompositable(element), + animation); + + Timing timing2; + timing2.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(30); + timing2.start_delay = Timing::Delay(ANIMATION_TIME_DELTA_FROM_SECONDS(5)); + + Animation* animation2 = Animation::Create( + MakeGarbageCollected<KeyframeEffect>(element, model, timing2), timeline, + exception_state); + animation2->play(); + + EXPECT_EQ(element->GetElementAnimations()->Animations().size(), 2u); + // If support for delayed animations is added, this check will fail. This test + // should be updated to create a non compositible animation through other + // means in this case. + EXPECT_EQ(ClipPathPaintDefinition::GetAnimationIfCompositable(element), + nullptr); + + // After adding a second animation with a delay, we gracefully fallback. + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); + EXPECT_TRUE(lo->NeedsPaintPropertyUpdate()); + UpdateAllLifecyclePhasesForTest(); + EXPECT_FALSE(lo->FirstFragment().PaintProperties()->ClipPathMask()); + + // Further frames shouldn't cause more property updates than necessary. + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); + EXPECT_FALSE(lo->NeedsPaintPropertyUpdate()); + UpdateAllLifecyclePhasesForTest(); + EXPECT_FALSE(lo->FirstFragment().PaintProperties()->ClipPathMask()); +} + TEST_F(ClipPathPaintDefinitionTest, ClipBoundingBoxEncompassesAnimation) { ScopedCompositeClipPathAnimationForTest composite_clip_path_animation(true); SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.cc index f0b8c39e..3dd64536 100644 --- a/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.cc +++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.cc
@@ -9,7 +9,7 @@ #include "base/sequence_checker.h" #include "base/task/sequenced_task_runner.h" #include "base/types/pass_key.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom-blink.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/platform/heap/persistent.h" @@ -27,16 +27,16 @@ FileSystemAccessCapacityTracker::FileSystemAccessCapacityTracker( ExecutionContext* context, - mojo::PendingRemote<mojom::blink::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_remote, + mojo::PendingRemote<mojom::blink::FileSystemAccessFileModificationHost> + file_modification_host_remote, int64_t file_size, base::PassKey<FileSystemAccessRegularFileDelegate>) - : capacity_allocation_host_(context), + : file_modification_host_(context), file_size_(file_size), file_capacity_(file_size) { - capacity_allocation_host_.Bind(std::move(capacity_allocation_host_remote), - context->GetTaskRunner(TaskType::kStorage)); - DCHECK(capacity_allocation_host_.is_bound()); + file_modification_host_.Bind(std::move(file_modification_host_remote), + context->GetTaskRunner(TaskType::kStorage)); + DCHECK(file_modification_host_.is_bound()); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } @@ -62,7 +62,7 @@ std::move(callback).Run(true); return; } - capacity_allocation_host_->RequestCapacityChange( + file_modification_host_->RequestCapacityChange( capacity_delta, WTF::BindOnce(&FileSystemAccessCapacityTracker::DidRequestCapacityChange, WrapPersistent(this), required_capacity, @@ -91,7 +91,7 @@ int64_t granted_capacity; // Request the necessary capacity from the browser process. - bool call_succeeded = capacity_allocation_host_->RequestCapacityChange( + bool call_succeeded = file_modification_host_->RequestCapacityChange( capacity_delta, &granted_capacity); DCHECK(call_succeeded) << "Mojo call failed"; @@ -112,7 +112,7 @@ file_size_ = new_size; - capacity_allocation_host_->OnContentsModified(); + file_modification_host_->OnContentsModified(); } void FileSystemAccessCapacityTracker::DidRequestCapacityChange(
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h index 534d150f..8299be1 100644 --- a/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h +++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h
@@ -8,7 +8,7 @@ #include "base/functional/callback.h" #include "base/types/pass_key.h" #include "mojo/public/cpp/bindings/pending_remote.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom-blink.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" @@ -28,8 +28,8 @@ public: explicit FileSystemAccessCapacityTracker( ExecutionContext* context, - mojo::PendingRemote<mojom::blink::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_remote, + mojo::PendingRemote<mojom::blink::FileSystemAccessFileModificationHost> + file_modification_host_remote, int64_t file_size, base::PassKey<FileSystemAccessRegularFileDelegate>); @@ -64,7 +64,7 @@ // GarbageCollected void Trace(Visitor* visitor) const { - visitor->Trace(capacity_allocation_host_); + visitor->Trace(file_modification_host_); } private: @@ -81,8 +81,8 @@ SEQUENCE_CHECKER(sequence_checker_); // Used to route capacity allocation requests to the browser. - HeapMojoRemote<mojom::blink::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_; + HeapMojoRemote<mojom::blink::FileSystemAccessFileModificationHost> + file_modification_host_; // Size of the file represented by the FileSystemAccessRegularFileDelegate // owning this.
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc index a024273..1380e233 100644 --- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc +++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
@@ -10,8 +10,8 @@ #include "base/numerics/checked_math.h" #include "base/task/sequenced_task_runner.h" #include "mojo/public/cpp/bindings/pending_remote.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom-blink.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" @@ -23,12 +23,12 @@ mojom::blink::FileSystemAccessRegularFilePtr regular_file) { base::File backing_file = std::move(regular_file->os_file); int64_t backing_file_size = regular_file->file_size; - mojo::PendingRemote<mojom::blink::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_remote = - std::move(regular_file->capacity_allocation_host); + mojo::PendingRemote<mojom::blink::FileSystemAccessFileModificationHost> + file_modification_host_remote = + std::move(regular_file->file_modification_host); return MakeGarbageCollected<FileSystemAccessRegularFileDelegate>( context, std::move(backing_file), backing_file_size, - std::move(capacity_allocation_host_remote), + std::move(file_modification_host_remote), base::PassKey<FileSystemAccessFileDelegate>()); } @@ -36,13 +36,13 @@ ExecutionContext* context, base::File backing_file, int64_t backing_file_size, - mojo::PendingRemote<mojom::blink::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_remote, + mojo::PendingRemote<mojom::blink::FileSystemAccessFileModificationHost> + file_modification_host_remote, base::PassKey<FileSystemAccessFileDelegate>) : backing_file_(std::move(backing_file)), capacity_tracker_(MakeGarbageCollected<FileSystemAccessCapacityTracker>( context, - std::move(capacity_allocation_host_remote), + std::move(file_modification_host_remote), backing_file_size, base::PassKey<FileSystemAccessRegularFileDelegate>())), task_runner_(context->GetTaskRunner(TaskType::kStorage)) {}
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h index 16c50e9..cb4f18d 100644 --- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h +++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
@@ -10,7 +10,7 @@ #include "base/task/sequenced_task_runner.h" #include "base/types/pass_key.h" #include "mojo/public/cpp/bindings/pending_remote.h" -#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom-blink.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h" #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h" @@ -29,8 +29,8 @@ ExecutionContext* context, base::File backing_file, int64_t backing_file_size, - mojo::PendingRemote<mojom::blink::FileSystemAccessCapacityAllocationHost> - capacity_allocation_host_remote, + mojo::PendingRemote<mojom::blink::FileSystemAccessFileModificationHost> + file_modification_host_remote, base::PassKey<FileSystemAccessFileDelegate>); FileSystemAccessRegularFileDelegate(
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc index c7a6bf2..0e0229c 100644 --- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc +++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc
@@ -146,7 +146,7 @@ base::AutoLock auto_lock(mixers_lock_); auto it = mixers_.find(key); - if (it != mixers_.end()) { + if (it != mixers_.end() && !it->second.mixer->HasSinkError()) { auto new_count = ++it->second.ref_count; CHECK(new_count != std::numeric_limits<decltype(new_count)>::max()); @@ -161,6 +161,12 @@ sink->Stop(); return it->second.mixer; + } else if (it != mixers_.end() && it->second.mixer->HasSinkError()) { + DVLOG(1) << "Not reusing mixer with errors: " << it->second.mixer; + + // Move bad mixers out of the reuse map. + dead_mixers_[key] = it->second; + mixers_.erase(it); } const media::AudioParameters& mixer_output_params = @@ -205,12 +211,31 @@ [](const std::pair<MixerKey, AudioRendererMixerReference>& val) { return val.second.mixer; }); - DCHECK(it != mixers_.end()); + + // If a mixer isn't in the normal map, check the map for mixers w/ errors. + bool dead_mixer = false; + if (it == mixers_.end()) { + it = base::ranges::find( + dead_mixers_, mixer, + [](const std::pair<MixerKey, AudioRendererMixerReference>& val) { + return val.second.mixer; + }); + DCHECK(it != dead_mixers_.end()); + dead_mixer = true; + } // Only remove the mixer if AudioRendererMixerManager is the last owner. it->second.ref_count--; if (it->second.ref_count == 0) { delete it->second.mixer; + if (dead_mixer) { + dead_mixers_.erase(it); + } else { + mixers_.erase(it); + } + } else if (!dead_mixer && it->second.mixer->HasSinkError()) { + // Move bad mixers out of the reuse map. + dead_mixers_[it->first] = it->second; mixers_.erase(it); } }
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h index d7e5ebc..cc3fc45 100644 --- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h +++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h
@@ -180,6 +180,11 @@ // Active mixers. AudioRendererMixerMap mixers_; + + // Mixers which encountered errors, but can't yet be destroyed since they are + // still owned by an input. + AudioRendererMixerMap dead_mixers_; + base::Lock mixers_lock_; };
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc index 3c6a324..6a22f21 100644 --- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc +++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc
@@ -118,6 +118,7 @@ // Number of instantiated mixers. size_t mixer_count() { return manager_->mixers_.size(); } + size_t dead_mixer_count() { return manager_->dead_mixers_.size(); } protected: scoped_refptr<media::MockAudioRendererSink> GetSink( @@ -203,6 +204,55 @@ EXPECT_EQ(0u, mixer_count()); } +TEST_F(AudioRendererMixerManagerTest, ReturnMixerWithError) { + mock_sink_ = CreateNormalSink(); + auto* local_sink = mock_sink_.get(); + + // There should be no mixers outstanding to start with. + EXPECT_EQ(0u, mixer_count()); + + media::AudioParameters params1( + media::AudioParameters::AUDIO_PCM_LINEAR, + media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate, + kBufferSize); + + media::AudioRendererMixer* mixer1 = + GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback, + kDefaultDeviceId, SinkUseState::kNewSink); + ASSERT_TRUE(mixer1); + EXPECT_EQ(1u, mixer_count()); + + // The same parameters should return the same mixer1. + EXPECT_EQ(mixer1, + GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback, + kDefaultDeviceId, SinkUseState::kExistingSink)); + EXPECT_EQ(1u, mixer_count()); + + // Trigger an error in mixer1. + local_sink->callback()->OnRenderError(); + + // Return the extra mixer we just acquired, it should not be deleted, but put + // into the dead mixer map. + ReturnMixer(mixer1); + EXPECT_EQ(0u, mixer_count()); + EXPECT_EQ(1u, dead_mixer_count()); + + // Using the same params should create a new mixer due to the error. + media::AudioRendererMixer* mixer2 = + GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback, + kDefaultDeviceId, SinkUseState::kNewSink); + ASSERT_TRUE(mixer2); + EXPECT_EQ(1u, mixer_count()); + EXPECT_EQ(1u, dead_mixer_count()); + EXPECT_NE(mixer1, mixer2); + + // Return both outstanding mixers. + ReturnMixer(mixer1); + EXPECT_EQ(0u, dead_mixer_count()); + ReturnMixer(mixer2); + EXPECT_EQ(0u, mixer_count()); +} + // Verify GetMixer() correctly deduplicates mixer with irrelevant AudioParameter // differences. TEST_F(AudioRendererMixerManagerTest, MixerReuse) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc index ae1dda5..f35d38ed 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
@@ -168,19 +168,13 @@ EXPECT_TRUE(config.noise_suppression.enabled); EXPECT_EQ(config.noise_suppression.level, webrtc::AudioProcessing::Config::NoiseSuppression::kHigh); + EXPECT_FALSE(config.transient_suppression.enabled); -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) - EXPECT_FALSE(config.echo_canceller.mobile_mode); - EXPECT_FALSE(config.transient_suppression.enabled); -#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) - // Android uses echo cancellation optimized for mobiles, and does not - // support keytap suppression. +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + // Android uses echo cancellation optimized for mobiles. EXPECT_TRUE(config.echo_canceller.mobile_mode); - EXPECT_FALSE(config.transient_suppression.enabled); #else EXPECT_FALSE(config.echo_canceller.mobile_mode); - EXPECT_TRUE(config.transient_suppression.enabled); #endif }
diff --git a/third_party/blink/renderer/platform/wtf/dtoa.cc b/third_party/blink/renderer/platform/wtf/dtoa.cc index 6c0dc25..24b6a52 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa.cc +++ b/third_party/blink/renderer/platform/wtf/dtoa.cc
@@ -43,6 +43,29 @@ namespace WTF { +namespace { + +double ParseDoubleFromLongString(const UChar* string, + size_t length, + size_t& parsed_length) { + wtf_size_t conversion_length = base::checked_cast<wtf_size_t>(length); + auto conversion_buffer = std::make_unique<LChar[]>(conversion_length); + for (wtf_size_t i = 0; i < conversion_length; ++i) { + conversion_buffer[i] = IsASCII(string[i]) ? string[i] : 0; + } + return ParseDouble(conversion_buffer.get(), length, parsed_length); +} + +const double_conversion::StringToDoubleConverter& GetDoubleConverter() { + static double_conversion::StringToDoubleConverter converter( + double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | + double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK, + 0.0, 0, nullptr, nullptr); + return converter; +} + +} // namespace + const char* NumberToString(double d, NumberToStringBuffer buffer) { double_conversion::StringBuilder builder(buffer, kNumberToStringBufferLength); const double_conversion::DoubleToStringConverter& converter = @@ -136,31 +159,34 @@ double ParseDouble(const LChar* string, size_t length, size_t& parsed_length) { int int_parsed_length = 0; - double d = internal::GetDoubleConverter().StringToDouble( + double d = GetDoubleConverter().StringToDouble( reinterpret_cast<const char*>(string), base::saturated_cast<int>(length), &int_parsed_length); parsed_length = int_parsed_length; return d; } -namespace internal { - -double ParseDoubleFromLongString(const UChar* string, - size_t length, - size_t& parsed_length) { - wtf_size_t conversion_length = base::checked_cast<wtf_size_t>(length); - auto conversion_buffer = std::make_unique<LChar[]>(conversion_length); - for (wtf_size_t i = 0; i < conversion_length; ++i) - conversion_buffer[i] = IsASCII(string[i]) ? string[i] : 0; - return ParseDouble(conversion_buffer.get(), length, parsed_length); +double ParseDouble(const UChar* string, size_t length, size_t& parsed_length) { + const size_t kConversionBufferSize = 64; + if (length > kConversionBufferSize) { + return ParseDoubleFromLongString(string, length, parsed_length); + } + LChar conversion_buffer[kConversionBufferSize]; + for (size_t i = 0; i < length; ++i) { + conversion_buffer[i] = + IsASCII(string[i]) ? static_cast<LChar>(string[i]) : 0; + } + return ParseDouble(conversion_buffer, length, parsed_length); } -const double_conversion::StringToDoubleConverter& GetDoubleConverter() { - static double_conversion::StringToDoubleConverter converter( - double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | - double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK, - 0.0, 0, nullptr, nullptr); - return converter; +namespace internal { + +void InitializeDoubleConverter() { + // Force initialization of static DoubleToStringConverter converter variable + // inside EcmaScriptConverter function while we are in single thread mode. + double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + + GetDoubleConverter(); } } // namespace internal
diff --git a/third_party/blink/renderer/platform/wtf/dtoa.h b/third_party/blink/renderer/platform/wtf/dtoa.h index a53cd33..777fca6 100644 --- a/third_party/blink/renderer/platform/wtf/dtoa.h +++ b/third_party/blink/renderer/platform/wtf/dtoa.h
@@ -26,10 +26,6 @@ #include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h" #include "third_party/blink/renderer/platform/wtf/wtf_export.h" -namespace double_conversion { -class StringToDoubleConverter; -} // namespace double_conversion - namespace WTF { // Size = 80 for sizeof(DtoaBuffer) + some sign bits, decimal point, 'e', @@ -54,24 +50,10 @@ size_t& parsed_length); namespace internal { -double ParseDoubleFromLongString(const UChar* string, - size_t length, - size_t& parsed_length); -const double_conversion::StringToDoubleConverter& GetDoubleConverter(); -} // namespace internal -inline double ParseDouble(const UChar* string, - size_t length, - size_t& parsed_length) { - const size_t kConversionBufferSize = 64; - if (length > kConversionBufferSize) - return internal::ParseDoubleFromLongString(string, length, parsed_length); - LChar conversion_buffer[kConversionBufferSize]; - for (size_t i = 0; i < length; ++i) - conversion_buffer[i] = - IsASCII(string[i]) ? static_cast<LChar>(string[i]) : 0; - return ParseDouble(conversion_buffer, length, parsed_length); -} +void InitializeDoubleConverter(); + +} // namespace internal } // namespace WTF
diff --git a/third_party/blink/renderer/platform/wtf/wtf.cc b/third_party/blink/renderer/platform/wtf/wtf.cc index 9424893..e72fbf4 100644 --- a/third_party/blink/renderer/platform/wtf/wtf.cc +++ b/third_party/blink/renderer/platform/wtf/wtf.cc
@@ -30,7 +30,6 @@ #include "third_party/blink/renderer/platform/wtf/wtf.h" -#include "base/third_party/double_conversion/double-conversion/double-conversion.h" #include "build/build_config.h" #include "third_party/abseil-cpp/absl/base/attributes.h" #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h" @@ -83,10 +82,7 @@ Threading::Initialize(); - // Force initialization of static DoubleToStringConverter converter variable - // inside EcmaScriptConverter function while we are in single thread mode. - double_conversion::DoubleToStringConverter::EcmaScriptConverter(); - internal::GetDoubleConverter(); + internal::InitializeDoubleConverter(); internal::InitializeMainThreadStackEstimate(); AtomicString::Init();
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index adeecda..b3ca045 100755 --- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -709,6 +709,9 @@ 'ui::AXTreeUpdate', 'ui::AXTreeID', 'ui::AXTreeIDUnknown', + 'ui::kInvalidAXNodeID', + 'ui::kFirstGeneratedRendererNodeID', + 'ui::kLastGeneratedRendererNodeID', 'ui::kAXModeBasic', 'ui::kAXModeComplete', 'ui::ToString',
diff --git a/third_party/blink/web_tests/accessibility/aom-computed-accessible-node.html b/third_party/blink/web_tests/accessibility/aom-computed-accessible-node.html index b9c4b8a..665ed69 100644 --- a/third_party/blink/web_tests/accessibility/aom-computed-accessible-node.html +++ b/third_party/blink/web_tests/accessibility/aom-computed-accessible-node.html
@@ -52,13 +52,12 @@ assert_equals(button2CAXNode.name, "axButton"); assert_equals(button2CAXNode.role, "button"); - // As button1 has no node in the accessibility tree anymore, assert that the - // its previously retrieved computed accessible node has had its attributes - // nullified. + // As button1 still has a node in the accessibility tree, but its layout has + // been removed,and therefore the name is now null. assert_equals(button1CAXNode.name, null); - assert_equals(button1CAXNode.role, null); + assert_equals(button1CAXNode.role, "button"); -}, "Deleting nodes from the accessibility tree will not cause a crash, and properties on any references to a deleted computed accessible node have been nullified."); +}, "Deleting layout from the accessibility tree will not cause a crash."); </script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 6b3b71cf..633cdec8 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -343013,6 +343013,14 @@ [] ], "insertion-removing-steps": { + "blur-event.window-expected.txt": [ + "7c476a91ba6d9ebbca436a25cccd90144bd3e23f", + [] + ], + "insertion-removing-steps-iframe.window-expected.txt": [ + "a52434e4389db97ce71166f2f9975406e5b0ed7c", + [] + ], "insertion-removing-steps-script.window-expected.txt": [ "420e94308f537d87004ad920c6fb3ea0c758ee88", [] @@ -478467,8 +478475,15 @@ ] ], "insertion-removing-steps": { + "blur-event.window.js": [ + "4c8cd85cbf5483c552047fe9698576f201a30a5f", + [ + "dom/nodes/insertion-removing-steps/blur-event.window.html", + {} + ] + ], "insertion-removing-steps-iframe.window.js": [ - "a10610f4677b159b8373ead2ff9f6fd1f49dabf7", + "60c2bec0c8aa213b1bfa1d75c99ca1745cb9871c", [ "dom/nodes/insertion-removing-steps/insertion-removing-steps-iframe.window.html", {} @@ -583917,7 +583932,7 @@ ] ], "test_serialize.html": [ - "88a9ce5221dcf90ad7c064b6e135ba5e1b37da4f", + "59226db6e72b731cf0973a3c2c0fd75d950aefc3", [ null, {}
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window.js index 4e860ad..f547386 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window.js +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window.js
@@ -149,6 +149,44 @@ expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); +subsetTestByKey( + 'from-public', promise_test_parallel, + t => anchorTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.noCorsHeader(token()), + } + }), + } + }, + expected: NavigationTestResult.FAILURE, + }), + 'public to public redirected to private: missing CORS headers.'); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => anchorTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.navigation(token()), + } + }), + } + }, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public to private: success.'); + // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address.
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt index 083cb0d..19aec266 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt
@@ -11,5 +11,7 @@ assert_equals: expected "timeout" but got "success" [FAIL] public to private: missing PNA header. assert_equals: expected "timeout" but got "success" +[FAIL] public to public redirected to private: missing CORS headers. + assert_equals: expected "timeout" but got "success" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/service-worker-fetch-all.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/service-worker-fetch-all.js new file mode 100644 index 0000000..78ac8d15 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/service-worker-fetch-all.js
@@ -0,0 +1,20 @@ +self.addEventListener("install", () => { + // Skip waiting before replacing the previously-active service worker, if any. + // This allows the bridge script to notice the controller change and query + // the install time via fetch. + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + // Claim all clients so that the bridge script notices the activation. + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("fetch", (event) => { + const url = new URL(event.request.url).searchParams.get("proxied-url"); + if (url) { + event.respondWith(fetch(url)); + } else { + event.respondWith(fetch(event.request)); + } +});
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js index 46a9d9e07..1cb432b 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js
@@ -480,6 +480,13 @@ }; async function windowOpenTest(t, { source, target, expected }) { + if (target.behavior && target.behavior.redirect) { + target.behavior.redirect.searchParams.set('file', 'openee.html'); + target.behavior.redirect.searchParams.set( + 'file-if-no-preflight-received', + 'no-preflight-received.html', + ); + } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -507,6 +514,13 @@ } async function windowOpenExistingTest(t, { source, target, expected }) { + if (target.behavior && target.behavior.redirect) { + target.behavior.redirect.searchParams.set('file', 'openee.html'); + target.behavior.redirect.searchParams.set( + 'file-if-no-preflight-received', + 'no-preflight-received.html', + ); + } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -535,6 +549,13 @@ } async function anchorTest(t, { source, target, expected }) { + if (target.behavior && target.behavior.redirect) { + target.behavior.redirect.searchParams.set('file', 'openee.html'); + target.behavior.redirect.searchParams.set( + 'file-if-no-preflight-received', + 'no-preflight-received.html', + ); + } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -855,3 +876,66 @@ assert_equals(status, expected.status, "response status"); assert_equals(body, expected.body, "response body"); } + +async function makeServiceWorkerTest(t, { source, target, expected, fetch_document=false }) { + const bridgeUrl = resolveUrl( + "resources/service-worker-bridge.html", + sourceResolveOptions({ server: source.server })); + + const scriptUrl = fetch_document? + resolveUrl("resources/service-worker-fetch-all.js", sourceResolveOptions(source)): + resolveUrl("resources/service-worker.js", sourceResolveOptions(source)); + + const realTargetUrl = preflightUrl(target); + + // Fetch a URL within the service worker's scope, but tell it which URL to + // really fetch. + const targetUrl = new URL("service-worker-proxy", scriptUrl); + targetUrl.searchParams.append("proxied-url", realTargetUrl.href); + + const iframe = await appendIframe(t, document, bridgeUrl); + + const request = (message) => { + const reply = futureMessage(); + iframe.contentWindow.postMessage(message, "*"); + return reply; + }; + + { + const { error, loaded } = await request({ + action: "register", + url: scriptUrl.href, + }); + + assert_equals(error, undefined, "register error"); + assert_true(loaded, "response loaded"); + } + + try { + const { controlled, numControllerChanges } = await request({ + action: "wait", + numControllerChanges: 1, + }); + + assert_equals(numControllerChanges, 1, "controller change"); + assert_true(controlled, "bridge script is controlled"); + + const { error, ok, body } = await request({ + action: "fetch", + url: targetUrl.href, + }); + + assert_equals(error, expected.error, "fetch error"); + assert_equals(ok, expected.ok, "response ok"); + assert_equals(body, expected.body, "response body"); + } finally { + // Always unregister the service worker. + const { error, unregistered } = await request({ + action: "unregister", + scope: new URL("./", scriptUrl).href, + }); + + assert_equals(error, undefined, "unregister error"); + assert_true(unregistered, "unregistered"); + } +}
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt new file mode 100644 index 0000000..c9433e6f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt
@@ -0,0 +1,7 @@ +This is a testharness.js-based test. +[FAIL] treat-as-public to local: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] treat-as-public to private: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js new file mode 100644 index 0000000..6fc29ce4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js
@@ -0,0 +1,101 @@ +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// +// Spec: https://wicg.github.io/private-network-access/#integration-fetch +// +// These tests check that fetches from within `ServiceWorker` scripts are +// subject to Private Network Access checks, just like fetches from within +// documents. + +// Results that may be expected in tests. +const TestResult = { + SUCCESS: { ok: true, body: "success" }, + FAILURE: { error: "TypeError" }, +}; + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.OTHER_HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "treat-as-public to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.OTHER_HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTPS_LOCAL }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to local (same-origin): no preflight required."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "treat-as-public to private: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { response: ResponseBehavior.allowCrossOrigin() }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to public: success.");
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt new file mode 100644 index 0000000..3f3485f4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt
@@ -0,0 +1,9 @@ +This is a testharness.js-based test. +[FAIL] private to local: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] public to local: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] public to private: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js new file mode 100644 index 0000000..ec38055 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js
@@ -0,0 +1,114 @@ +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// +// Spec: https://wicg.github.io/private-network-access/#integration-fetch +// +// These tests check that fetches from within `ServiceWorker` scripts are +// subject to Private Network Access checks, just like fetches from within +// documents. + +// Results that may be expected in tests. +const TestResult = { + SUCCESS: { ok: true, body: "success" }, + FAILURE: { error: "TypeError" }, +}; + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_LOCAL }, + target: { server: Server.HTTPS_LOCAL }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "local to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "private to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "private to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { server: Server.HTTPS_PRIVATE }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "private to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "public to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "public to private: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { server: Server.HTTPS_PUBLIC }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to public: success."); +
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch.tentative.https.window.js index cb6d1f79..5fc5800 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch.tentative.https.window.js +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/service-worker-fetch.tentative.https.window.js
@@ -16,84 +16,25 @@ FAILURE: { error: "TypeError" }, }; -async function makeTest(t, { source, target, expected }) { - const bridgeUrl = resolveUrl( - "resources/service-worker-bridge.html", - sourceResolveOptions({ server: source.server })); - - const scriptUrl = - resolveUrl("resources/service-worker.js", sourceResolveOptions(source)); - - const realTargetUrl = preflightUrl(target); - - // Fetch a URL within the service worker's scope, but tell it which URL to - // really fetch. - const targetUrl = new URL("service-worker-proxy", scriptUrl); - targetUrl.searchParams.append("proxied-url", realTargetUrl.href); - - const iframe = await appendIframe(t, document, bridgeUrl); - - const request = (message) => { - const reply = futureMessage(); - iframe.contentWindow.postMessage(message, "*"); - return reply; - }; - - { - const { error, loaded } = await request({ - action: "register", - url: scriptUrl.href, - }); - - assert_equals(error, undefined, "register error"); - assert_true(loaded, "response loaded"); - } - - try { - const { controlled, numControllerChanges } = await request({ - action: "wait", - numControllerChanges: 1, - }); - - assert_equals(numControllerChanges, 1, "controller change"); - assert_true(controlled, "bridge script is controlled"); - - const { error, ok, body } = await request({ - action: "fetch", - url: targetUrl.href, - }); - - assert_equals(error, expected.error, "fetch error"); - assert_equals(ok, expected.ok, "response ok"); - assert_equals(body, expected.body, "response body"); - } finally { - // Always unregister the service worker. - const { error, unregistered } = await request({ - action: "unregister", - scope: new URL("./", scriptUrl).href, - }); - - assert_equals(error, undefined, "unregister error"); - assert_true(unregistered, "unregistered"); - } -} - -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL }, target: { server: Server.HTTPS_LOCAL }, expected: TestResult.SUCCESS, }), "local to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "private to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_LOCAL, @@ -105,22 +46,25 @@ expected: TestResult.SUCCESS, }), "private to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_PRIVATE }, expected: TestResult.SUCCESS, }), "private to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "public to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_LOCAL, @@ -132,16 +76,19 @@ expected: TestResult.SUCCESS, }), "public to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PRIVATE, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "public to private: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PRIVATE, @@ -153,25 +100,28 @@ expected: TestResult.SUCCESS, }), "public to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PUBLIC }, expected: TestResult.SUCCESS, }), "public to public: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: { server: Server.OTHER_HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "treat-as-public to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -186,7 +136,7 @@ expected: TestResult.SUCCESS, }), "treat-as-public to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -195,19 +145,22 @@ expected: TestResult.SUCCESS, }), "treat-as-public to local (same-origin): no preflight required."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: { server: Server.HTTPS_PRIVATE, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "treat-as-public to private: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -222,7 +175,7 @@ expected: TestResult.SUCCESS, }), "treat-as-public to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true,
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window.js index 6a2a624f..565a2117 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window.js +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window.js
@@ -167,6 +167,44 @@ }), 'public to public: no preflight required.'); +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.noCorsHeader(token()), + } + }), + } + }, + expected: NavigationTestResult.FAILURE, + }), + 'public to public redirected to private: missing CORS headers.'); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.navigation(token()), + } + }), + } + }, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public to private: success.'); + // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address.
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt index 083cb0d..19aec266 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt
@@ -11,5 +11,7 @@ assert_equals: expected "timeout" but got "success" [FAIL] public to private: missing PNA header. assert_equals: expected "timeout" but got "success" +[FAIL] public to public redirected to private: missing CORS headers. + assert_equals: expected "timeout" but got "success" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window.js index 6793d1f3..42d70af 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window.js +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window.js
@@ -149,6 +149,44 @@ expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.noCorsHeader(token()), + } + }), + } + }, + expected: NavigationTestResult.FAILURE, + }), + 'public to public redirected to private: missing CORS headers.'); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.navigation(token()), + } + }), + } + }, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public to private: success.'); + // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address.
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt index 083cb0d..19aec266 100644 --- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt +++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt
@@ -11,5 +11,7 @@ assert_equals: expected "timeout" but got "success" [FAIL] public to private: missing PNA header. assert_equals: expected "timeout" but got "success" +[FAIL] public to public redirected to private: missing CORS headers. + assert_equals: expected "timeout" but got "success" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/channels/test_serialize.html b/third_party/blink/web_tests/external/wpt/infrastructure/channels/test_serialize.html index 88a9ce5..59226db6 100644 --- a/third_party/blink/web_tests/external/wpt/infrastructure/channels/test_serialize.html +++ b/third_party/blink/web_tests/external/wpt/infrastructure/channels/test_serialize.html
@@ -27,7 +27,7 @@ promise_test(async t => { let remoteValue = RemoteObject.from(document.head); let result = await remote.call(inputValue => { - if (!inputValue instanceof RemoteObject) { + if (!(inputValue instanceof RemoteObject)) { throw new AssertionError(`Expected RemoteObject`); } return inputValue;
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/stop-animation-01.html b/third_party/blink/web_tests/external/wpt/svg/animations/stop-animation-01.html new file mode 100644 index 0000000..d240c51 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/svg/animations/stop-animation-01.html
@@ -0,0 +1,21 @@ +<!doctype html> +<html class="reftest-wait"> +<title>Animate a <stop> element</title> +<link rel="match" href="../struct/reftests/reference/green-100x100.html"> +<script src="/common/reftest-wait.js"></script> +<script src="/common/rendering-utils.js"></script> +<script> +function test() { + waitForAtLeastOneFrame().then(takeScreenshot); +} +</script> +<svg> + <linearGradient id="g"> + <stop stop-color="red"> + <animate attributeName="stop-color" values="red; green" dur="1s" + keyTimes="0; 0.01" fill="freeze" calcMode="discrete" + onbegin="test()"/> + </stop> + </linearGradient> + <rect width="100" height="100" fill="url(#g)"/> +</svg>
diff --git a/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt index d14171b..631d8815 100644 --- a/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt +++ b/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/anchor.tentative.https.window_include=from-public-expected.txt
@@ -15,5 +15,9 @@ assert_equals: expected "timeout" but got "no preflight received" [FAIL] public to private: success. assert_equals: expected "success" but got "no preflight received" +[FAIL] public to public redirected to private: missing CORS headers. + assert_equals: expected "timeout" but got "no preflight received" +[FAIL] public to public to private: success. + assert_equals: expected "success" but got "no preflight received" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt index d14171b..631d8815 100644 --- a/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt +++ b/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open-existing.tentative.https.window_include=from-public-expected.txt
@@ -15,5 +15,9 @@ assert_equals: expected "timeout" but got "no preflight received" [FAIL] public to private: success. assert_equals: expected "success" but got "no preflight received" +[FAIL] public to public redirected to private: missing CORS headers. + assert_equals: expected "timeout" but got "no preflight received" +[FAIL] public to public to private: success. + assert_equals: expected "success" but got "no preflight received" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt index d14171b..631d8815 100644 --- a/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt +++ b/third_party/blink/web_tests/virtual/pna-navigations-disabled/external/wpt/fetch/private-network-access/window-open.tentative.https.window_include=from-public-expected.txt
@@ -15,5 +15,9 @@ assert_equals: expected "timeout" but got "no preflight received" [FAIL] public to private: success. assert_equals: expected "success" but got "no preflight received" +[FAIL] public to public redirected to private: missing CORS headers. + assert_equals: expected "timeout" but got "no preflight received" +[FAIL] public to public to private: success. + assert_equals: expected "success" but got "no preflight received" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/pna-workers-disabled/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt b/third_party/blink/web_tests/virtual/pna-workers-disabled/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt new file mode 100644 index 0000000..69a47aa --- /dev/null +++ b/third_party/blink/web_tests/virtual/pna-workers-disabled/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt
@@ -0,0 +1,11 @@ +This is a testharness.js-based test. +[FAIL] treat-as-public to local: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] treat-as-public to local: success. + assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError" +[FAIL] treat-as-public to private: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] treat-as-public to private: success. + assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/pna-workers-disabled/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt b/third_party/blink/web_tests/virtual/pna-workers-disabled/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt new file mode 100644 index 0000000..d459de6b --- /dev/null +++ b/third_party/blink/web_tests/virtual/pna-workers-disabled/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt
@@ -0,0 +1,15 @@ +This is a testharness.js-based test. +[FAIL] private to local: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] private to local: success. + assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError" +[FAIL] public to local: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] public to local: success. + assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError" +[FAIL] public to private: failed preflight. + assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined +[FAIL] public to private: success. + assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt b/third_party/blink/web_tests/virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt new file mode 100644 index 0000000..d2490db --- /dev/null +++ b/third_party/blink/web_tests/virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window-expected.txt
@@ -0,0 +1,3 @@ +This is a testharness.js-based test. +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt b/third_party/blink/web_tests/virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt new file mode 100644 index 0000000..d2490db --- /dev/null +++ b/third_party/blink/web_tests/virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/service-worker-fetch-document.tentative.https.window-expected.txt
@@ -0,0 +1,3 @@ +This is a testharness.js-based test. +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/wpt_internal/dom/abort/resources/run-async-gc.js b/third_party/blink/web_tests/wpt_internal/dom/abort/resources/run-async-gc.js index a86e6e92..548645da 100644 --- a/third_party/blink/web_tests/wpt_internal/dom/abort/resources/run-async-gc.js +++ b/third_party/blink/web_tests/wpt_internal/dom/abort/resources/run-async-gc.js
@@ -1,9 +1,11 @@ -async function runAsyncGC() { +async function runAsyncGC(args = {}) { // Run gc in a loop to ensure anything needing more than one cycle can be // collected, e.g. due to dependencies. Note this is similar to // ThreadState::CollectAllGarbageForTesting, but async and with 2 less // iterations. for (let i = 0; i < 3; i++) { - await gc({type: 'major', execution: 'async', flavor: 'last-resort'}); + // crbug.com/1474629: invoking gc({execution: 'async'}) trips leak + // detection, so use postTask and run sync gc() to do async GC. + await scheduler.postTask(() => { gc(); }, args); } }
diff --git a/third_party/chromium-variations b/third_party/chromium-variations index 1abc236..32e2e5c 160000 --- a/third_party/chromium-variations +++ b/third_party/chromium-variations
@@ -1 +1 @@ -Subproject commit 1abc236bbd98af59cac64b471b3ea6ab900350db +Subproject commit 32e2e5c8d0343434cf154f2b928775c2d95ad8c5
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal index b92185a..acc4bb2 160000 --- a/third_party/devtools-frontend-internal +++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@ -Subproject commit b92185a4c9aba77a6cbbc8d8aa65820e8be5dae7 +Subproject commit acc4bb2e6ba2063e1171c81951abd331aa9cf068
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index 5ccdd75..e13f347 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit 5ccdd75fa57a758fa15db394dd6ad6092963cfed +Subproject commit e13f34726b11afcb77bebca0e6a79c08ba3fd2c6
diff --git a/third_party/perfetto b/third_party/perfetto index 609cb8e..1553701 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 609cb8ef0288f4e1667b1034dc53ad319d43ca99 +Subproject commit 1553701a9f0a47be12af882f0d7801df5b674122
diff --git a/third_party/re2/src b/third_party/re2/src index f9550c3f..2d866a3 160000 --- a/third_party/re2/src +++ b/third_party/re2/src
@@ -1 +1 @@ -Subproject commit f9550c3f7207f946a45bbccd1814b12b136aae72 +Subproject commit 2d866a3d0753f4f4fce93cccc6c59c4b052d7db4
diff --git a/third_party/skia b/third_party/skia index ef2511b..081ba94 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit ef2511b0a6f21b4bb43414ac0571d755791ae22a +Subproject commit 081ba94858f6ee93b518e1a4107f81cbf7a224cb
diff --git a/third_party/webrtc b/third_party/webrtc index c935bb2..16ac10d 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit c935bb2141e2e616e1f9e1df4568d976ad1828c0 +Subproject commit 16ac10d9f75cde959f00df062f544c49941882da
diff --git a/tools/codeql/queries/bad_message_no_return.ql b/tools/codeql/queries/bad_message_no_return.ql index eb859046..168c776 100644 --- a/tools/codeql/queries/bad_message_no_return.ql +++ b/tools/codeql/queries/bad_message_no_return.ql
@@ -4,6 +4,7 @@ import cpp import lib.Chromium +import lib.CommonPatterns /** * @name potential ReceivedBadMessage call without return. @@ -25,27 +26,9 @@ } } -from BadMessageCall call, Function f +from BadMessageCall call where Chromium::isChromiumCode(call) and - - call.getEnclosingFunction() = f and - - // Ignore any calls with a returnStatement in the same enclosing block. - not exists(ReturnStmt returnStmt | - call.getEnclosingBlock() = returnStmt.getEnclosingBlock() - ) and - - // Ignore any calls with a returnStatement immediately after in the block - exists(Stmt stmtAfterCall | - stmtAfterCall.getEnclosingFunction() = f and - stmtAfterCall.getLocation().getStartLine() > call.getLocation().getStartLine() and - not stmtAfterCall instanceof ReturnStmt and - not exists(ReturnStmt returnBetween | - returnBetween.getEnclosingFunction() = f and - returnBetween.getLocation().getStartLine() > call.getLocation().getStartLine() and - returnBetween.getLocation().getStartLine() < stmtAfterCall.getLocation().getStartLine() - ) - ) + CommonPatterns::isCallNotFollowedByReturn(call) select call, call.getLocation().getFile().getRelativePath() + ":" + call.getLocation().getStartLine().toString()
diff --git a/tools/codeql/queries/blink_exception_no_return.ql b/tools/codeql/queries/blink_exception_no_return.ql index 05c739f6..052b789 100644 --- a/tools/codeql/queries/blink_exception_no_return.ql +++ b/tools/codeql/queries/blink_exception_no_return.ql
@@ -4,6 +4,7 @@ import cpp import lib.Chromium +import lib.CommonPatterns /** * @name Throw*Exception call without return. @@ -26,24 +27,6 @@ from ThrowExceptionCall call, Function f where Chromium::isBlinkCode(call) and - - call.getEnclosingFunction() = f and - - // Ignore any calls with a returnStatement in the same enclosing block. - not exists(ReturnStmt returnStmt | - call.getEnclosingBlock() = returnStmt.getEnclosingBlock() - ) and - - // Ignore any calls with a returnStatement immediately after in the block. - exists(Stmt stmtAfterCall | - stmtAfterCall.getEnclosingFunction() = f and - stmtAfterCall.getLocation().getStartLine() > call.getLocation().getStartLine() and - not stmtAfterCall instanceof ReturnStmt and - not exists(ReturnStmt returnBetween | - returnBetween.getEnclosingFunction() = f and - returnBetween.getLocation().getStartLine() > call.getLocation().getStartLine() and - returnBetween.getLocation().getStartLine() < stmtAfterCall.getLocation().getStartLine() - ) - ) + CommonPatterns::isCallNotFollowedByReturn(call) select call, call.getLocation().getFile().getRelativePath() + ":" + call.getLocation().getStartLine().toString()
diff --git a/tools/codeql/queries/lib/CommonPatterns.qll b/tools/codeql/queries/lib/CommonPatterns.qll new file mode 100644 index 0000000..4a91081 --- /dev/null +++ b/tools/codeql/queries/lib/CommonPatterns.qll
@@ -0,0 +1,35 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import cpp + +module CommonPatterns { + /** + * Predicate to check if a function call is not followed by a return statement + * within the same or immediately after in any block. + */ + pragma[inline] + predicate isCallNotFollowedByReturn(FunctionCall call) { + exists(Function f | + call.getEnclosingFunction() = f and + + // Ignore any calls with a returnStatement in the same enclosing block. + not exists(ReturnStmt returnStmt | + call.getEnclosingBlock() = returnStmt.getEnclosingBlock() + ) and + + // Ignore any calls with a returnStatement immediately after in the block. + exists(Stmt stmtAfterCall | + stmtAfterCall.getEnclosingFunction() = f and + stmtAfterCall.getLocation().getStartLine() > call.getLocation().getStartLine() and + not stmtAfterCall instanceof ReturnStmt and + not exists(ReturnStmt returnBetween | + returnBetween.getEnclosingFunction() = f and + returnBetween.getLocation().getStartLine() > call.getLocation().getStartLine() and + returnBetween.getLocation().getStartLine() < stmtAfterCall.getLocation().getStartLine() + ) + ) + ) + } +}
diff --git a/tools/codeql/queries/mojo_reportbadmessage_no_return.ql b/tools/codeql/queries/mojo_reportbadmessage_no_return.ql index 2c924e6..1eb6ad7 100644 --- a/tools/codeql/queries/mojo_reportbadmessage_no_return.ql +++ b/tools/codeql/queries/mojo_reportbadmessage_no_return.ql
@@ -3,6 +3,7 @@ // found in the LICENSE file. import cpp import lib.Chromium +import lib.CommonPatterns /** * @name potential ReportBadMessage call without return. @@ -21,23 +22,7 @@ from ReportBadMessageCall call, Function f where Chromium::isChromiumCode(call) and - - call.getEnclosingFunction() = f and - - // Ignore any calls with a returnStatement in the same enclosing block. - not exists(ReturnStmt returnStmt | call.getEnclosingBlock() = returnStmt.getEnclosingBlock()) and - - // Ignore any calls with a returnStatement immediately after in the block - exists(Stmt stmtAfterCall | - stmtAfterCall.getEnclosingFunction() = f and - stmtAfterCall.getLocation().getStartLine() > call.getLocation().getStartLine() and - not stmtAfterCall instanceof ReturnStmt and - not exists(ReturnStmt returnBetween | - returnBetween.getEnclosingFunction() = f and - returnBetween.getLocation().getStartLine() > call.getLocation().getStartLine() and - returnBetween.getLocation().getStartLine() < stmtAfterCall.getLocation().getStartLine() - ) - ) + CommonPatterns::isCallNotFollowedByReturn(call) select call, call.getLocation().getFile().getRelativePath() + ":" + call.getLocation().getStartLine().toString()
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec index c433cb72..bbae028 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec
@@ -219,19 +219,14 @@ "chrome/browser/resources/chromeos/app_icon/app_icon_resources.grd": { "structures": [2800], }, - "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/login/oobe_conditional_resources.grd": { - "META": {"sizes": {"includes": [150], "structures": [300]}}, + "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/login/resources.grd": { + "META": {"sizes": {"includes": [300],}}, "includes": [2820], - "structures": [2840], }, "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/lock_screen_reauth/resources.grd": { "META": {"sizes": {"includes": [30]}}, "includes": [2860], }, - "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/login/oobe_unconditional_resources.grd": { - "META": {"sizes": {"includes": [350]}}, - "includes": [2880], - }, "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/multidevice_internals/resources.grd": { "META": {"sizes": {"includes": [35]}}, "includes": [2900],
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 1141c5d..7dcc10d 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -21055,6 +21055,23 @@ </description> </action> +<action name="MobileFullscreenEntered"> + <owner>ajuma@chromium.org</owner> + <owner>alionadangla@chromium.org</owner> + <description> + Reported when user scrolled to enter in fullscreen mode. iOS only. + </description> +</action> + +<action name="MobileFullscreenExited"> + <owner>ajuma@chromium.org</owner> + <owner>alionadangla@chromium.org</owner> + <description> + Reported when user scrolled or tapped on the toolbars to exit fullscreen + mode. iOS only. + </description> +</action> + <action name="MobileFullscreenExitedManually"> <owner>ajuma@chromium.org</owner> <owner>alionadangla@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index 00f18f3..649ecc1 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -2457,9 +2457,9 @@ This metric is only recorded if a suggestion for the {AutofillFormType} was shown, accepted, and the suggestion was filled successfully. This is - recorded once per form. It is reported as "User chose to fill" - once. It does not matter if the user clears the filled values nor if the - user accepts a different suggestion later. + recorded once per navigation. It is reported as "User chose to + fill" once. It does not matter if the user clears the filled values nor + if the user accepts a different suggestion later. If the accepted suggestion is not filled (e.g., if it required authentication and the user cancelled or failed that step), then this metric @@ -2492,6 +2492,30 @@ <token key="AutofillFormType" variants="AutofillFormType"/> </histogram> +<histogram + name="Autofill.Funnel.NotClassifiedAsTargetFilling.FillAfterSuggestion{AutofillFormType}" + enum="BooleanAutofillFillAfterSuggestion" expires_after="2025-01-20"> + <owner>brunobraga@google.com</owner> + <owner>chrome-autofill-team@google.com</owner> + <summary> + Counts whether users accepted any autofill suggestion that was shown to them + for a given form. This metric is conditioned to those suggestions that were + triggered via the context menu on a field that does not match the target + filling product. + + This metric is only recorded if a suggestion for the {AutofillFormType} was + shown, accepted, and the suggestion was filled successfully. This is + recorded once per navigation. It is reported as "User chose to + fill" once. It does not matter if the user clears the filled values nor + if the user accepts a different suggestion later. + + If the accepted suggestion is not filled (e.g., if it required + authentication and the user cancelled or failed that step), then this metric + is not recorded. + </summary> + <token key="AutofillFormType" variants="AutofillFormType"/> +</histogram> + <histogram name="Autofill.Funnel.ParsedAsType{AutofillFormType}" enum="BooleanAutofillParsedAsType" expires_after="2024-12-12"> <owner>battre@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml index aafcc04..507aeaf7 100644 --- a/tools/metrics/histograms/metadata/network/histograms.xml +++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -4586,6 +4586,32 @@ </summary> </histogram> +<histogram name="NetworkService.IpProtection.TokenBatchGenerationTime" + units="ms" expires_after="2024-08-22"> + <owner>ashleynewson@chromium.org</owner> + <owner>src/android_webview/OWNERS</owner> + <owner>src/chrome/browser/ip_protection/OWNERS</owner> + <summary> + Records the time taken for a successful attempt to generate auth tokens. + This only measures the time across a single attempt (not across retries, + which may be delayed by a variable backoff). + + This measures the whole token batch generation process, from an + IpProtectionTokenCacheManagerImpl's perspective, from just before calling + IpProtectionConfigGetter::TryGetAuthTokens until OnGotAuthTokens. Note that + if OnGotAuthTokens receives a non-nullopt but empty vector of tokens this is + considered a success by this metric. + + Note that if multiple token caches exist (one for each proxy layer), the + attempts in each token cache are timed independently, but they will all feed + into the same histogram. + + This histogram will only be emitted if the MaskedDomainList and + EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent + signin is also required. + </summary> +</histogram> + <histogram name="NetworkService.IpProtection.TokenBatchRequestTime" units="ms" expires_after="2024-07-28"> <owner>djmitche@chromium.org</owner> @@ -4593,6 +4619,11 @@ <summary> Records the elapsed time for successful requests by IpProtectionConfigGetter for blind-signed tokens from BSA. + + This metric only measures part of the Chrome-specific blind-signing + implementation and does not encompass the full token batch generation + process. See NetworkService.IpProtection.TokenBatchGenerationTime for a + generic measurement of the full token batch generation process. </summary> </histogram>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml index 9b183c7..029cc2cf 100644 --- a/tools/metrics/histograms/metadata/password/histograms.xml +++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -392,7 +392,7 @@ </histogram> <histogram name="PasswordGeneration.SubmissionAvailableEvent" - enum="PasswordSubmissionEvent" expires_after="M125"> + enum="PasswordSubmissionEvent" expires_after="M131"> <owner>kazinova@google.com</owner> <owner>shaikhitdin@google.com</owner> <owner>chrome-password-manager-metrics-alerts@google.com</owner> @@ -902,7 +902,7 @@ </histogram> <histogram name="PasswordManager.BubbleSuppression.AccountsInStatisticsTable2" - units="accounts" expires_after="M125"> + units="accounts" expires_after="M131"> <owner>kazinova@google.com</owner> <owner>vasilii@chromium.org</owner> <summary> @@ -1526,7 +1526,7 @@ </histogram> <histogram name="PasswordManager.HttpPasswordMigrationMode2" - enum="HttpPasswordMigrationMode" expires_after="2024-03-31"> + enum="HttpPasswordMigrationMode" expires_after="2024-09-30"> <owner>kazinova@google.com</owner> <owner>vasilii@chromium.org</owner> <summary> @@ -1790,7 +1790,7 @@ </histogram> <histogram name="PasswordManager.JavaScriptOnlyValueInSubmittedForm" - enum="JavaScriptOnlyValueInPasswordForm" expires_after="2024-03-31"> + enum="JavaScriptOnlyValueInPasswordForm" expires_after="2024-09-30"> <owner>kazinova@google.com</owner> <owner>battre@chromium.org</owner> <summary> @@ -2703,7 +2703,7 @@ </histogram> <histogram name="PasswordManager.PasswordStore.TimesAttemptedToReenrollInUPM" - units="Times" expires_after="2024-03-31"> + units="Times" expires_after="2024-09-30"> <owner>kazinova@google.com</owner> <owner>vasilii@chromium.org</owner> <summary> @@ -3513,7 +3513,7 @@ </histogram> <histogram name="PasswordManager.StoreDecryptionResult" - enum="PasswordDecryptionResult" expires_after="2024-03-30"> + enum="PasswordDecryptionResult" expires_after="2024-09-30"> <owner>mamir@chromium.org</owner> <owner>kazinova@google.com</owner> <summary>
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc index 365f822..26dbc16 100644 --- a/ui/accessibility/ax_event_generator.cc +++ b/ui/accessibility/ax_event_generator.cc
@@ -324,7 +324,8 @@ ax::mojom::Role old_role, ax::mojom::Role new_role) { DCHECK_EQ(tree_, tree); - AddEvent(node, Event::ROLE_CHANGED); + AddEvent(node, new_role == ax::mojom::Role::kAlert ? Event::ALERT + : Event::ROLE_CHANGED); } void AXEventGenerator::OnIgnoredChanged(AXTree* tree,
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc index 868cb75..e5b15504 100644 --- a/ui/accessibility/ax_node.cc +++ b/ui/accessibility/ax_node.cc
@@ -1482,7 +1482,7 @@ } bool AXNode::IsGenerated() const { - bool is_generated_node = id() < 0; + bool is_generated_node = id() < 0 && id() > kInitialEmptyDocumentRootNodeID; #if DCHECK_IS_ON() // Currently, the only generated nodes are columns and table header // containers, and when those roles occur, they are always extra mac nodes.
diff --git a/ui/accessibility/ax_node_id_forward.h b/ui/accessibility/ax_node_id_forward.h index 0bca0b1d3..10966350 100644 --- a/ui/accessibility/ax_node_id_forward.h +++ b/ui/accessibility/ax_node_id_forward.h
@@ -5,6 +5,7 @@ #ifndef UI_ACCESSIBILITY_AX_NODE_ID_FORWARD_H_ #define UI_ACCESSIBILITY_AX_NODE_ID_FORWARD_H_ +#include <limits.h> #include <stdint.h> namespace ui { @@ -14,7 +15,20 @@ // If a node is not yet or no longer valid, its ID should have a value of // kInvalidAXNodeID. +// Note: positive AXNodeIDs match DOM node ids generated by Blin. They are used +// for objects that contain that matching DOM node. static constexpr AXNodeID kInvalidAXNodeID = 0; +// The browser needs to generate an AXNodeID for objects that it inserts in +// the hierarchy, such as "extra mac nodes" +static constexpr AXNodeID kFirstGeneratedBrowserNodeID = -1; +static constexpr AXNodeID kLastGeneratedBrowserNodeID = -999999999; +// A new AXTree is constructed with an empty document. The root must have a +// unique AXNodeID that will not match any other id. +static constexpr AXNodeID kInitialEmptyDocumentRootNodeID = -1000000000; +// The renderer needs to generate an AXNodeID for objects that don't have a DOM +// node. These start with this value and subsequent ids each subtract 1. +static constexpr AXNodeID kFirstGeneratedRendererNodeID = -1000000001; +static constexpr AXNodeID kLastGeneratedRendererNodeID = INT_MIN; } // namespace ui
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc index e6c4557..d60ca025 100644 --- a/ui/accessibility/ax_tree.cc +++ b/ui/accessibility/ax_tree.cc
@@ -1983,7 +1983,8 @@ for (AXTreeObserver& observer : observers_) observer.OnNodeDataChanged(this, old_data, new_data); - if (old_data.role != new_data.role) { + if (old_data.role != new_data.role && !old_data.IsInvisibleOrIgnored() && + !new_data.IsInvisibleOrIgnored()) { for (AXTreeObserver& observer : observers_) observer.OnRoleChanged(this, node, old_data.role, new_data.role); }
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h index 8882088..a4a4894 100644 --- a/ui/accessibility/ax_tree_serializer.h +++ b/ui/accessibility/ax_tree_serializer.h
@@ -111,7 +111,10 @@ // Invalidate the subtree rooted at this node, ensuring that the entire // subtree is re-serialized the next time any of those nodes end up // being serialized. - void MarkSubtreeDirty(AXNodeID node); + void MarkSubtreeDirty(AXNodeID id); + + // Invalidate a single node, ensuring that it is reserialized. + void MarkNodeDirty(AXNodeID id); // Return whether or not this node is in the client tree. If you call // this immediately after serializing, this indicates whether a given @@ -603,11 +606,19 @@ } template <typename AXSourceNode, typename AXSourceNodeVectorType> +void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::MarkNodeDirty( + AXNodeID id) { + if (ClientTreeNode* client_node = ClientTreeNodeById(id)) { + client_node->is_dirty = true; + } +} + +template <typename AXSourceNode, typename AXSourceNodeVectorType> void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::MarkSubtreeDirty( AXNodeID id) { - ClientTreeNode* client_node = ClientTreeNodeById(id); - if (client_node) + if (ClientTreeNode* client_node = ClientTreeNodeById(id)) { MarkClientSubtreeDirty(client_node); + } } template <typename AXSourceNode, typename AXSourceNodeVectorType>
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index bffdc2b7..f32d79c 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2458,12 +2458,19 @@ } void AXPlatformNodeAuraLinux::SetDocumentParentOnFrameIfNecessary() { - if (GetAtkRole() != ATK_ROLE_DOCUMENT_WEB) + if (GetRole() != ax::mojom::Role::kRootWebArea) { return; + } if (!GetDelegate()->IsWebContent()) return; + // If there is a parent, then this is not the root document. + if (GetDelegate()->node()->GetUnignoredParent()) { + return; + } + + // Get the ATK parent, which will cross over into the UI hierarchy. AtkObject* parent_atk_object = GetParent(); AXPlatformNodeAuraLinux* parent = AXPlatformNodeAuraLinux::FromAtkObject(parent_atk_object);
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index 8f010d95..46f557f 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -2017,6 +2017,7 @@ // convention here and when we fire events via // ::NotifyWinEvent(). *id = -GetUniqueId(); + DCHECK(*id < 0); return S_OK; }
diff --git a/ui/accessibility/platform/ax_unique_id.h b/ui/accessibility/platform/ax_unique_id.h index 285de33..b9c9736 100644 --- a/ui/accessibility/platform/ax_unique_id.h +++ b/ui/accessibility/platform/ax_unique_id.h
@@ -21,6 +21,8 @@ // // These ids must not be conflated with the int id, that comes with web node // data, which are only unique within their source frame. +// TODO(accessibility) We should be able to get rid of this, because node IDs +// are actually unique within their own OS-level window. class COMPONENT_EXPORT(AX_PLATFORM) AXUniqueId { public: AXUniqueId();
diff --git a/ui/shell_dialogs/selected_file_info.cc b/ui/shell_dialogs/selected_file_info.cc index 060aac6..fd59d72b 100644 --- a/ui/shell_dialogs/selected_file_info.cc +++ b/ui/shell_dialogs/selected_file_info.cc
@@ -4,6 +4,7 @@ #include "ui/shell_dialogs/selected_file_info.h" +#include "base/containers/to_vector.h" #include "base/ranges/algorithm.h" namespace ui { @@ -38,19 +39,13 @@ std::vector<SelectedFileInfo> FilePathListToSelectedFileInfoList( const std::vector<base::FilePath>& paths) { - std::vector<SelectedFileInfo> selected_files; - for (const auto& path : paths) { - selected_files.emplace_back(path); - } - return selected_files; + return base::ToVector( + paths, [](const auto& path) { return SelectedFileInfo(path); }); } std::vector<base::FilePath> SelectedFileInfoListToFilePathList( const std::vector<SelectedFileInfo>& files) { - std::vector<base::FilePath> paths; - base::ranges::transform(files, std::back_inserter(paths), - &SelectedFileInfo::path); - return paths; + return base::ToVector(files, &SelectedFileInfo::path); } } // namespace ui
diff --git a/v8 b/v8 index ea012d2..2c63d7b 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit ea012d232c66229b2d4f3f2a268aa999d668972b +Subproject commit 2c63d7b6b700b0a2d257d4f4f19d2948c41e9b88